@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
|
@@ -0,0 +1,736 @@
|
|
|
1
|
+
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
2
|
+
import { unlink } from "node:fs/promises";
|
|
3
|
+
import { closeDb, initDb } from "../be/db";
|
|
4
|
+
import type { ExecutorMeta } from "../types";
|
|
5
|
+
import type { ExecutorDependencies, ExecutorInput } from "../workflows/executors/base";
|
|
6
|
+
import { CodeMatchExecutor, CodeMatchOutputSchema } from "../workflows/executors/code-match";
|
|
7
|
+
import { NotifyExecutor } from "../workflows/executors/notify";
|
|
8
|
+
import {
|
|
9
|
+
PropertyMatchExecutor,
|
|
10
|
+
PropertyMatchOutputSchema,
|
|
11
|
+
} from "../workflows/executors/property-match";
|
|
12
|
+
import { RawLlmExecutor } from "../workflows/executors/raw-llm";
|
|
13
|
+
import { createExecutorRegistry } from "../workflows/executors/registry";
|
|
14
|
+
import { ScriptExecutor, ScriptOutputSchema } from "../workflows/executors/script";
|
|
15
|
+
import { ValidateExecutor, ValidateOutputSchema } from "../workflows/executors/validate";
|
|
16
|
+
import { VcsExecutor, VcsOutputSchema } from "../workflows/executors/vcs";
|
|
17
|
+
|
|
18
|
+
const TEST_DB_PATH = "./test-workflow-executors.sqlite";
|
|
19
|
+
|
|
20
|
+
// ─── Mock Dependencies ───────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
const postedMessages: { channelId: string; content: string }[] = [];
|
|
23
|
+
|
|
24
|
+
const mockDeps: ExecutorDependencies = {
|
|
25
|
+
db: {
|
|
26
|
+
postMessage: (channelId: string, _agentId: string | null, content: string) => {
|
|
27
|
+
const msg = { id: `msg-${Date.now()}`, channelId, content };
|
|
28
|
+
postedMessages.push({ channelId, content });
|
|
29
|
+
return msg;
|
|
30
|
+
},
|
|
31
|
+
} as unknown as typeof import("../be/db"),
|
|
32
|
+
eventBus: { emit: () => {}, on: () => {}, off: () => {} },
|
|
33
|
+
interpolate: (template: string, ctx: Record<string, unknown>) => {
|
|
34
|
+
return template.replace(/\{\{([^}]+)\}\}/g, (_match, path: string) => {
|
|
35
|
+
const keys = path.trim().split(".");
|
|
36
|
+
let value: unknown = ctx;
|
|
37
|
+
for (const key of keys) {
|
|
38
|
+
if (value == null || typeof value !== "object") return "";
|
|
39
|
+
value = (value as Record<string, unknown>)[key];
|
|
40
|
+
}
|
|
41
|
+
if (value == null) return "";
|
|
42
|
+
return typeof value === "object" ? JSON.stringify(value) : String(value);
|
|
43
|
+
});
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const mockMeta: ExecutorMeta = {
|
|
48
|
+
runId: "00000000-0000-0000-0000-000000000001",
|
|
49
|
+
stepId: "00000000-0000-0000-0000-000000000002",
|
|
50
|
+
nodeId: "test-node",
|
|
51
|
+
workflowId: "00000000-0000-0000-0000-000000000003",
|
|
52
|
+
dryRun: false,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
function input(
|
|
56
|
+
config: Record<string, unknown>,
|
|
57
|
+
context: Record<string, unknown> = {},
|
|
58
|
+
): ExecutorInput {
|
|
59
|
+
return { config, context, meta: mockMeta };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ─── Setup / Teardown ────────────────────────────────────────
|
|
63
|
+
|
|
64
|
+
beforeAll(async () => {
|
|
65
|
+
try {
|
|
66
|
+
await unlink(TEST_DB_PATH);
|
|
67
|
+
} catch {
|
|
68
|
+
// File doesn't exist
|
|
69
|
+
}
|
|
70
|
+
initDb(TEST_DB_PATH);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
afterAll(async () => {
|
|
74
|
+
closeDb();
|
|
75
|
+
for (const suffix of ["", "-wal", "-shm"]) {
|
|
76
|
+
try {
|
|
77
|
+
await unlink(`${TEST_DB_PATH}${suffix}`);
|
|
78
|
+
} catch {
|
|
79
|
+
// File may not exist
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// ─── PropertyMatch Executor ──────────────────────────────────
|
|
85
|
+
|
|
86
|
+
describe("PropertyMatchExecutor", () => {
|
|
87
|
+
const executor = new PropertyMatchExecutor(mockDeps);
|
|
88
|
+
|
|
89
|
+
test("config schema rejects empty conditions", () => {
|
|
90
|
+
const result = executor.configSchema.safeParse({ conditions: [] });
|
|
91
|
+
expect(result.success).toBe(false);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test("config schema rejects missing conditions", () => {
|
|
95
|
+
const result = executor.configSchema.safeParse({});
|
|
96
|
+
expect(result.success).toBe(false);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test("config schema accepts valid config", () => {
|
|
100
|
+
const result = executor.configSchema.safeParse({
|
|
101
|
+
conditions: [{ field: "data.value", op: "eq", value: 42 }],
|
|
102
|
+
});
|
|
103
|
+
expect(result.success).toBe(true);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test("config schema defaults mode to all", () => {
|
|
107
|
+
const result = executor.configSchema.parse({
|
|
108
|
+
conditions: [{ field: "x", op: "eq", value: 1 }],
|
|
109
|
+
});
|
|
110
|
+
expect(result.mode).toBe("all");
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test("returns true port when all conditions pass (mode=all)", async () => {
|
|
114
|
+
const result = await executor.run(
|
|
115
|
+
input(
|
|
116
|
+
{
|
|
117
|
+
conditions: [
|
|
118
|
+
{ field: "a", op: "eq", value: 1 },
|
|
119
|
+
{ field: "b", op: "gt", value: 0 },
|
|
120
|
+
],
|
|
121
|
+
},
|
|
122
|
+
{ a: 1, b: 5 },
|
|
123
|
+
),
|
|
124
|
+
);
|
|
125
|
+
expect(result.status).toBe("success");
|
|
126
|
+
expect(result.nextPort).toBe("true");
|
|
127
|
+
const out = result.output as { passed: boolean };
|
|
128
|
+
expect(out.passed).toBe(true);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test("returns false port when one condition fails (mode=all)", async () => {
|
|
132
|
+
const result = await executor.run(
|
|
133
|
+
input(
|
|
134
|
+
{
|
|
135
|
+
conditions: [
|
|
136
|
+
{ field: "a", op: "eq", value: 1 },
|
|
137
|
+
{ field: "b", op: "eq", value: 999 },
|
|
138
|
+
],
|
|
139
|
+
},
|
|
140
|
+
{ a: 1, b: 5 },
|
|
141
|
+
),
|
|
142
|
+
);
|
|
143
|
+
expect(result.nextPort).toBe("false");
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test("returns true port when one condition passes (mode=any)", async () => {
|
|
147
|
+
const result = await executor.run(
|
|
148
|
+
input(
|
|
149
|
+
{
|
|
150
|
+
conditions: [
|
|
151
|
+
{ field: "a", op: "eq", value: 999 },
|
|
152
|
+
{ field: "b", op: "eq", value: 5 },
|
|
153
|
+
],
|
|
154
|
+
mode: "any",
|
|
155
|
+
},
|
|
156
|
+
{ a: 1, b: 5 },
|
|
157
|
+
),
|
|
158
|
+
);
|
|
159
|
+
expect(result.nextPort).toBe("true");
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test("resolves nested dot-path fields", async () => {
|
|
163
|
+
const result = await executor.run(
|
|
164
|
+
input(
|
|
165
|
+
{ conditions: [{ field: "data.nested.value", op: "eq", value: "hello" }] },
|
|
166
|
+
{ data: { nested: { value: "hello" } } },
|
|
167
|
+
),
|
|
168
|
+
);
|
|
169
|
+
expect(result.nextPort).toBe("true");
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test("contains operator works on strings", async () => {
|
|
173
|
+
const result = await executor.run(
|
|
174
|
+
input(
|
|
175
|
+
{ conditions: [{ field: "msg", op: "contains", value: "world" }] },
|
|
176
|
+
{ msg: "hello world" },
|
|
177
|
+
),
|
|
178
|
+
);
|
|
179
|
+
expect(result.nextPort).toBe("true");
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
test("exists operator returns true for defined value", async () => {
|
|
183
|
+
const result = await executor.run(
|
|
184
|
+
input({ conditions: [{ field: "x", op: "exists" }] }, { x: 0 }),
|
|
185
|
+
);
|
|
186
|
+
expect(result.nextPort).toBe("true");
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
test("exists operator returns false for undefined value", async () => {
|
|
190
|
+
const result = await executor.run(
|
|
191
|
+
input({ conditions: [{ field: "missing", op: "exists" }] }, {}),
|
|
192
|
+
);
|
|
193
|
+
expect(result.nextPort).toBe("false");
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
test("output schema validates correctly", () => {
|
|
197
|
+
const valid = PropertyMatchOutputSchema.safeParse({
|
|
198
|
+
passed: true,
|
|
199
|
+
results: [{ field: "x", op: "eq", expected: 1, actual: 1, passed: true }],
|
|
200
|
+
});
|
|
201
|
+
expect(valid.success).toBe(true);
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
// ─── CodeMatch Executor ──────────────────────────────────────
|
|
206
|
+
|
|
207
|
+
describe("CodeMatchExecutor", () => {
|
|
208
|
+
const executor = new CodeMatchExecutor(mockDeps);
|
|
209
|
+
|
|
210
|
+
test("config schema rejects empty outputPorts", () => {
|
|
211
|
+
const result = executor.configSchema.safeParse({ code: "return true", outputPorts: [] });
|
|
212
|
+
expect(result.success).toBe(false);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
test("config schema rejects missing code", () => {
|
|
216
|
+
const result = executor.configSchema.safeParse({ outputPorts: ["a"] });
|
|
217
|
+
expect(result.success).toBe(false);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
test("config schema accepts valid config", () => {
|
|
221
|
+
const result = executor.configSchema.safeParse({
|
|
222
|
+
code: "(input) => true",
|
|
223
|
+
outputPorts: ["true", "false"],
|
|
224
|
+
});
|
|
225
|
+
expect(result.success).toBe(true);
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
test("executes code and returns port", async () => {
|
|
229
|
+
const result = await executor.run(
|
|
230
|
+
input(
|
|
231
|
+
{ code: "(input) => input.value > 10 ? 'high' : 'low'", outputPorts: ["high", "low"] },
|
|
232
|
+
{ value: 42 },
|
|
233
|
+
),
|
|
234
|
+
);
|
|
235
|
+
expect(result.status).toBe("success");
|
|
236
|
+
expect(result.nextPort).toBe("high");
|
|
237
|
+
const out = result.output as { port: string };
|
|
238
|
+
expect(out.port).toBe("high");
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
test("maps boolean result to true/false port", async () => {
|
|
242
|
+
const result = await executor.run(
|
|
243
|
+
input({ code: "(input) => input.x === 1", outputPorts: ["true", "false"] }, { x: 1 }),
|
|
244
|
+
);
|
|
245
|
+
expect(result.nextPort).toBe("true");
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
test("fails when returned port not in outputPorts", async () => {
|
|
249
|
+
const result = await executor.run(
|
|
250
|
+
input({ code: "(input) => 'unknown'", outputPorts: ["a", "b"] }, {}),
|
|
251
|
+
);
|
|
252
|
+
expect(result.status).toBe("failed");
|
|
253
|
+
expect(result.error).toContain("not in outputPorts");
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
test("fails on code execution error", async () => {
|
|
257
|
+
const result = await executor.run(
|
|
258
|
+
input({ code: "(input) => { throw new Error('boom') }", outputPorts: ["a"] }, {}),
|
|
259
|
+
);
|
|
260
|
+
expect(result.status).toBe("failed");
|
|
261
|
+
expect(result.error).toContain("boom");
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
test("sandboxes dangerous globals: process is undefined", async () => {
|
|
265
|
+
const result = await executor.run(
|
|
266
|
+
input(
|
|
267
|
+
{
|
|
268
|
+
code: "(input) => typeof process === 'undefined' ? 'safe' : 'unsafe'",
|
|
269
|
+
outputPorts: ["safe", "unsafe"],
|
|
270
|
+
},
|
|
271
|
+
{},
|
|
272
|
+
),
|
|
273
|
+
);
|
|
274
|
+
expect(result.nextPort).toBe("safe");
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
test("sandboxes dangerous globals: Bun is undefined", async () => {
|
|
278
|
+
const result = await executor.run(
|
|
279
|
+
input(
|
|
280
|
+
{
|
|
281
|
+
code: "(input) => typeof Bun === 'undefined' ? 'safe' : 'unsafe'",
|
|
282
|
+
outputPorts: ["safe", "unsafe"],
|
|
283
|
+
},
|
|
284
|
+
{},
|
|
285
|
+
),
|
|
286
|
+
);
|
|
287
|
+
expect(result.nextPort).toBe("safe");
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
test("sandboxes dangerous globals: require is undefined", async () => {
|
|
291
|
+
const result = await executor.run(
|
|
292
|
+
input(
|
|
293
|
+
{
|
|
294
|
+
code: "(input) => typeof require === 'undefined' ? 'safe' : 'unsafe'",
|
|
295
|
+
outputPorts: ["safe", "unsafe"],
|
|
296
|
+
},
|
|
297
|
+
{},
|
|
298
|
+
),
|
|
299
|
+
);
|
|
300
|
+
expect(result.nextPort).toBe("safe");
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
test("sandboxes dangerous globals: fetch is undefined", async () => {
|
|
304
|
+
const result = await executor.run(
|
|
305
|
+
input(
|
|
306
|
+
{
|
|
307
|
+
code: "(input) => typeof fetch === 'undefined' ? 'safe' : 'unsafe'",
|
|
308
|
+
outputPorts: ["safe", "unsafe"],
|
|
309
|
+
},
|
|
310
|
+
{},
|
|
311
|
+
),
|
|
312
|
+
);
|
|
313
|
+
expect(result.nextPort).toBe("safe");
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
test("output schema validates correctly", () => {
|
|
317
|
+
const valid = CodeMatchOutputSchema.safeParse({ port: "high", rawResult: "high" });
|
|
318
|
+
expect(valid.success).toBe(true);
|
|
319
|
+
});
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
// ─── Notify Executor ─────────────────────────────────────────
|
|
323
|
+
|
|
324
|
+
describe("NotifyExecutor", () => {
|
|
325
|
+
const executor = new NotifyExecutor(mockDeps);
|
|
326
|
+
|
|
327
|
+
test("config schema rejects invalid channel", () => {
|
|
328
|
+
const result = executor.configSchema.safeParse({
|
|
329
|
+
channel: "invalid",
|
|
330
|
+
template: "hi",
|
|
331
|
+
});
|
|
332
|
+
expect(result.success).toBe(false);
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
test("config schema accepts valid config", () => {
|
|
336
|
+
const result = executor.configSchema.safeParse({
|
|
337
|
+
channel: "swarm",
|
|
338
|
+
template: "hello {{name}}",
|
|
339
|
+
});
|
|
340
|
+
expect(result.success).toBe(true);
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
test("swarm channel posts message when target is set", async () => {
|
|
344
|
+
postedMessages.length = 0;
|
|
345
|
+
const result = await executor.run(
|
|
346
|
+
input({ channel: "swarm", target: "channel-1", template: "Hello {{who}}" }, { who: "world" }),
|
|
347
|
+
);
|
|
348
|
+
expect(result.status).toBe("success");
|
|
349
|
+
const out = result.output as { sent: boolean; message: string };
|
|
350
|
+
expect(out.sent).toBe(true);
|
|
351
|
+
expect(out.message).toBe("Hello world");
|
|
352
|
+
expect(postedMessages).toHaveLength(1);
|
|
353
|
+
expect(postedMessages[0].channelId).toBe("channel-1");
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
test("swarm channel returns sent=false when no target", async () => {
|
|
357
|
+
const result = await executor.run(input({ channel: "swarm", template: "no target" }, {}));
|
|
358
|
+
const out = result.output as { sent: boolean };
|
|
359
|
+
expect(out.sent).toBe(false);
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
test("slack stub returns sent=false", async () => {
|
|
363
|
+
const result = await executor.run(
|
|
364
|
+
input({ channel: "slack", target: "#general", template: "hi" }, {}),
|
|
365
|
+
);
|
|
366
|
+
const out = result.output as { sent: boolean };
|
|
367
|
+
expect(out.sent).toBe(false);
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
test("email stub returns sent=false", async () => {
|
|
371
|
+
const result = await executor.run(
|
|
372
|
+
input({ channel: "email", target: "user@test.com", template: "hi" }, {}),
|
|
373
|
+
);
|
|
374
|
+
const out = result.output as { sent: boolean };
|
|
375
|
+
expect(out.sent).toBe(false);
|
|
376
|
+
});
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
// ─── RawLlm Executor ────────────────────────────────────────
|
|
380
|
+
|
|
381
|
+
describe("RawLlmExecutor", () => {
|
|
382
|
+
const executor = new RawLlmExecutor(mockDeps);
|
|
383
|
+
|
|
384
|
+
test("config schema rejects missing prompt", () => {
|
|
385
|
+
const result = executor.configSchema.safeParse({});
|
|
386
|
+
expect(result.success).toBe(false);
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
test("config schema accepts valid config with prompt only", () => {
|
|
390
|
+
const result = executor.configSchema.safeParse({ prompt: "classify this" });
|
|
391
|
+
expect(result.success).toBe(true);
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
test("config schema accepts full config", () => {
|
|
395
|
+
const result = executor.configSchema.safeParse({
|
|
396
|
+
prompt: "classify",
|
|
397
|
+
model: "openai/gpt-4o",
|
|
398
|
+
schema: { type: "object", properties: { category: { type: "string" } } },
|
|
399
|
+
fallbackPort: "error",
|
|
400
|
+
});
|
|
401
|
+
expect(result.success).toBe(true);
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
// LLM integration tests — behavior depends on whether OPENROUTER_API_KEY is set
|
|
405
|
+
test("handles LLM call (success or fallback)", async () => {
|
|
406
|
+
const result = await executor.run(input({ prompt: "Say hello", fallbackPort: "error" }, {}));
|
|
407
|
+
// Either the LLM call succeeds or falls back
|
|
408
|
+
expect(result.status).toBe("success");
|
|
409
|
+
if (result.nextPort === "error") {
|
|
410
|
+
// Fallback path — LLM call failed (no API key or network error)
|
|
411
|
+
expect(result.error).toContain("fallback");
|
|
412
|
+
} else {
|
|
413
|
+
// Success path — LLM returned a result
|
|
414
|
+
const out = result.output as { result: unknown; model: string };
|
|
415
|
+
expect(out.model).toBeDefined();
|
|
416
|
+
expect(out.result).toBeDefined();
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
test("handles LLM call without fallback (success or failure)", async () => {
|
|
421
|
+
const result = await executor.run(input({ prompt: "Say hello" }, {}));
|
|
422
|
+
// Either the LLM call succeeds or fails
|
|
423
|
+
if (result.status === "failed") {
|
|
424
|
+
expect(result.error).toContain("LLM call failed");
|
|
425
|
+
} else {
|
|
426
|
+
expect(result.status).toBe("success");
|
|
427
|
+
const out = result.output as { result: unknown; model: string };
|
|
428
|
+
expect(out.result).toBeDefined();
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
// ─── Script Executor ─────────────────────────────────────────
|
|
434
|
+
|
|
435
|
+
describe("ScriptExecutor", () => {
|
|
436
|
+
const executor = new ScriptExecutor(mockDeps);
|
|
437
|
+
|
|
438
|
+
test("config schema rejects missing runtime", () => {
|
|
439
|
+
const result = executor.configSchema.safeParse({ script: "echo hi" });
|
|
440
|
+
expect(result.success).toBe(false);
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
test("config schema rejects invalid runtime", () => {
|
|
444
|
+
const result = executor.configSchema.safeParse({ runtime: "ruby", script: "puts 'hi'" });
|
|
445
|
+
expect(result.success).toBe(false);
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
test("config schema accepts valid config", () => {
|
|
449
|
+
const result = executor.configSchema.safeParse({
|
|
450
|
+
runtime: "bash",
|
|
451
|
+
script: "echo hello",
|
|
452
|
+
});
|
|
453
|
+
expect(result.success).toBe(true);
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
test("config schema defaults timeout to 30000", () => {
|
|
457
|
+
const result = executor.configSchema.parse({ runtime: "bash", script: "echo hi" });
|
|
458
|
+
expect(result.timeout).toBe(30_000);
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
test("runs bash script and captures stdout", async () => {
|
|
462
|
+
const result = await executor.run(input({ runtime: "bash", script: "echo 'hello world'" }, {}));
|
|
463
|
+
expect(result.status).toBe("success");
|
|
464
|
+
const out = result.output as { exitCode: number; stdout: string; stderr: string };
|
|
465
|
+
expect(out.exitCode).toBe(0);
|
|
466
|
+
expect(out.stdout).toBe("hello world");
|
|
467
|
+
expect(out.stderr).toBe("");
|
|
468
|
+
expect(result.nextPort).toBe("success");
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
test("captures stderr on failure", async () => {
|
|
472
|
+
const result = await executor.run(
|
|
473
|
+
input({ runtime: "bash", script: "echo err >&2; exit 1" }, {}),
|
|
474
|
+
);
|
|
475
|
+
expect(result.status).toBe("success"); // executor succeeds, script fails
|
|
476
|
+
const out = result.output as { exitCode: number; stdout: string; stderr: string };
|
|
477
|
+
expect(out.exitCode).toBe(1);
|
|
478
|
+
expect(out.stderr).toBe("err");
|
|
479
|
+
expect(result.nextPort).toBe("failure");
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
test("returns failure port on non-zero exit code", async () => {
|
|
483
|
+
const result = await executor.run(input({ runtime: "bash", script: "exit 42" }, {}));
|
|
484
|
+
expect(result.nextPort).toBe("failure");
|
|
485
|
+
const out = result.output as { exitCode: number };
|
|
486
|
+
expect(out.exitCode).toBe(42);
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
test("runs TypeScript script via bun", async () => {
|
|
490
|
+
const result = await executor.run(
|
|
491
|
+
input({ runtime: "ts", script: "console.log('ts works')" }, {}),
|
|
492
|
+
);
|
|
493
|
+
expect(result.status).toBe("success");
|
|
494
|
+
const out = result.output as { exitCode: number; stdout: string };
|
|
495
|
+
expect(out.exitCode).toBe(0);
|
|
496
|
+
expect(out.stdout).toBe("ts works");
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
test("output schema validates correctly", () => {
|
|
500
|
+
const valid = ScriptOutputSchema.safeParse({ exitCode: 0, stdout: "hi", stderr: "" });
|
|
501
|
+
expect(valid.success).toBe(true);
|
|
502
|
+
});
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
// ─── VCS Executor ────────────────────────────────────────────
|
|
506
|
+
|
|
507
|
+
describe("VcsExecutor", () => {
|
|
508
|
+
const executor = new VcsExecutor(mockDeps);
|
|
509
|
+
|
|
510
|
+
test("config schema rejects invalid action", () => {
|
|
511
|
+
const result = executor.configSchema.safeParse({
|
|
512
|
+
action: "delete-repo",
|
|
513
|
+
provider: "github",
|
|
514
|
+
repo: "owner/repo",
|
|
515
|
+
});
|
|
516
|
+
expect(result.success).toBe(false);
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
test("config schema rejects invalid provider", () => {
|
|
520
|
+
const result = executor.configSchema.safeParse({
|
|
521
|
+
action: "create-issue",
|
|
522
|
+
provider: "bitbucket",
|
|
523
|
+
repo: "owner/repo",
|
|
524
|
+
});
|
|
525
|
+
expect(result.success).toBe(false);
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
test("config schema accepts valid config", () => {
|
|
529
|
+
const result = executor.configSchema.safeParse({
|
|
530
|
+
action: "create-issue",
|
|
531
|
+
provider: "github",
|
|
532
|
+
repo: "owner/repo",
|
|
533
|
+
title: "Bug report",
|
|
534
|
+
body: "Something broke",
|
|
535
|
+
});
|
|
536
|
+
expect(result.success).toBe(true);
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
test("returns stub output with url and id", async () => {
|
|
540
|
+
const result = await executor.run(
|
|
541
|
+
input({ action: "create-issue", provider: "github", repo: "org/repo", title: "Test" }, {}),
|
|
542
|
+
);
|
|
543
|
+
expect(result.status).toBe("success");
|
|
544
|
+
const out = result.output as { url: string; id: string };
|
|
545
|
+
expect(out.url).toContain("github.com");
|
|
546
|
+
expect(out.url).toContain("org/repo");
|
|
547
|
+
expect(out.id).toContain("stub-");
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
test("interpolates title and body from context", async () => {
|
|
551
|
+
const result = await executor.run(
|
|
552
|
+
input(
|
|
553
|
+
{
|
|
554
|
+
action: "create-pr",
|
|
555
|
+
provider: "github",
|
|
556
|
+
repo: "org/repo",
|
|
557
|
+
title: "PR: {{task}}",
|
|
558
|
+
body: "Details: {{details}}",
|
|
559
|
+
},
|
|
560
|
+
{ task: "fix bug", details: "memory leak" },
|
|
561
|
+
),
|
|
562
|
+
);
|
|
563
|
+
expect(result.status).toBe("success");
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
test("output schema validates correctly", () => {
|
|
567
|
+
const valid = VcsOutputSchema.safeParse({ url: "https://github.com/x/y/1", id: "123" });
|
|
568
|
+
expect(valid.success).toBe(true);
|
|
569
|
+
|
|
570
|
+
const validNumeric = VcsOutputSchema.safeParse({ url: "https://github.com/x/y/1", id: 123 });
|
|
571
|
+
expect(validNumeric.success).toBe(true);
|
|
572
|
+
});
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
// ─── Validate Executor ───────────────────────────────────────
|
|
576
|
+
|
|
577
|
+
describe("ValidateExecutor", () => {
|
|
578
|
+
const executor = new ValidateExecutor(mockDeps);
|
|
579
|
+
|
|
580
|
+
test("config schema rejects missing targetNodeId", () => {
|
|
581
|
+
const result = executor.configSchema.safeParse({});
|
|
582
|
+
expect(result.success).toBe(false);
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
test("config schema accepts targetNodeId only", () => {
|
|
586
|
+
const result = executor.configSchema.safeParse({ targetNodeId: "step1" });
|
|
587
|
+
expect(result.success).toBe(true);
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
test("config schema accepts full config", () => {
|
|
591
|
+
const result = executor.configSchema.safeParse({
|
|
592
|
+
targetNodeId: "step1",
|
|
593
|
+
prompt: "Is the output valid?",
|
|
594
|
+
schema: { type: "object", properties: { name: { type: "string" } } },
|
|
595
|
+
});
|
|
596
|
+
expect(result.success).toBe(true);
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
test("fails when target node has no output", async () => {
|
|
600
|
+
const result = await executor.run(input({ targetNodeId: "missing" }, {}));
|
|
601
|
+
expect(result.status).toBe("success");
|
|
602
|
+
expect(result.nextPort).toBe("fail");
|
|
603
|
+
const out = result.output as { pass: boolean; reasoning: string };
|
|
604
|
+
expect(out.pass).toBe(false);
|
|
605
|
+
expect(out.reasoning).toContain("no output");
|
|
606
|
+
});
|
|
607
|
+
|
|
608
|
+
test("passes when target node exists and no criteria given", async () => {
|
|
609
|
+
const result = await executor.run(
|
|
610
|
+
input({ targetNodeId: "step1" }, { step1: { data: "something" } }),
|
|
611
|
+
);
|
|
612
|
+
expect(result.status).toBe("success");
|
|
613
|
+
expect(result.nextPort).toBe("pass");
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
test("schema validation passes for matching data", async () => {
|
|
617
|
+
const result = await executor.run(
|
|
618
|
+
input(
|
|
619
|
+
{
|
|
620
|
+
targetNodeId: "step1",
|
|
621
|
+
schema: {
|
|
622
|
+
type: "object",
|
|
623
|
+
properties: { stdout: { const: "good-data" } },
|
|
624
|
+
},
|
|
625
|
+
},
|
|
626
|
+
{ step1: { stdout: "good-data" } },
|
|
627
|
+
),
|
|
628
|
+
);
|
|
629
|
+
expect(result.nextPort).toBe("pass");
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
test("schema validation fails for non-matching data", async () => {
|
|
633
|
+
const result = await executor.run(
|
|
634
|
+
input(
|
|
635
|
+
{
|
|
636
|
+
targetNodeId: "step1",
|
|
637
|
+
schema: {
|
|
638
|
+
type: "object",
|
|
639
|
+
properties: { stdout: { const: "good-data" } },
|
|
640
|
+
},
|
|
641
|
+
},
|
|
642
|
+
{ step1: { stdout: "bad-data" } },
|
|
643
|
+
),
|
|
644
|
+
);
|
|
645
|
+
expect(result.nextPort).toBe("fail");
|
|
646
|
+
const out = result.output as { pass: boolean; reasoning: string };
|
|
647
|
+
expect(out.pass).toBe(false);
|
|
648
|
+
expect(out.reasoning).toContain("Schema validation failed");
|
|
649
|
+
});
|
|
650
|
+
|
|
651
|
+
test("schema validation checks type", async () => {
|
|
652
|
+
const result = await executor.run(
|
|
653
|
+
input({ targetNodeId: "step1", schema: { type: "string" } }, { step1: 42 }),
|
|
654
|
+
);
|
|
655
|
+
expect(result.nextPort).toBe("fail");
|
|
656
|
+
});
|
|
657
|
+
|
|
658
|
+
test("schema validation checks required properties", async () => {
|
|
659
|
+
const result = await executor.run(
|
|
660
|
+
input(
|
|
661
|
+
{
|
|
662
|
+
targetNodeId: "step1",
|
|
663
|
+
schema: { type: "object", required: ["name", "age"] },
|
|
664
|
+
},
|
|
665
|
+
{ step1: { name: "Alice" } },
|
|
666
|
+
),
|
|
667
|
+
);
|
|
668
|
+
expect(result.nextPort).toBe("fail");
|
|
669
|
+
const out = result.output as { reasoning: string };
|
|
670
|
+
expect(out.reasoning).toContain("age");
|
|
671
|
+
});
|
|
672
|
+
|
|
673
|
+
test("output schema validates correctly", () => {
|
|
674
|
+
const valid = ValidateOutputSchema.safeParse({
|
|
675
|
+
pass: true,
|
|
676
|
+
reasoning: "Looks good",
|
|
677
|
+
confidence: 0.95,
|
|
678
|
+
});
|
|
679
|
+
expect(valid.success).toBe(true);
|
|
680
|
+
|
|
681
|
+
const invalid = ValidateOutputSchema.safeParse({
|
|
682
|
+
pass: true,
|
|
683
|
+
reasoning: "Looks good",
|
|
684
|
+
confidence: 1.5, // Out of range
|
|
685
|
+
});
|
|
686
|
+
expect(invalid.success).toBe(false);
|
|
687
|
+
});
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
// ─── Registry Wiring ─────────────────────────────────────────
|
|
691
|
+
|
|
692
|
+
describe("createExecutorRegistry", () => {
|
|
693
|
+
test("registers all 8 executors (7 instant + 1 async)", () => {
|
|
694
|
+
const registry = createExecutorRegistry(mockDeps);
|
|
695
|
+
const types = registry.types();
|
|
696
|
+
|
|
697
|
+
expect(types).toContain("property-match");
|
|
698
|
+
expect(types).toContain("code-match");
|
|
699
|
+
expect(types).toContain("notify");
|
|
700
|
+
expect(types).toContain("raw-llm");
|
|
701
|
+
expect(types).toContain("script");
|
|
702
|
+
expect(types).toContain("vcs");
|
|
703
|
+
expect(types).toContain("validate");
|
|
704
|
+
expect(types).toContain("agent-task");
|
|
705
|
+
expect(types).toHaveLength(8);
|
|
706
|
+
});
|
|
707
|
+
|
|
708
|
+
test("instant executors have mode instant, async executors have mode async", () => {
|
|
709
|
+
const registry = createExecutorRegistry(mockDeps);
|
|
710
|
+
const instantTypes = [
|
|
711
|
+
"property-match",
|
|
712
|
+
"code-match",
|
|
713
|
+
"notify",
|
|
714
|
+
"raw-llm",
|
|
715
|
+
"script",
|
|
716
|
+
"vcs",
|
|
717
|
+
"validate",
|
|
718
|
+
];
|
|
719
|
+
for (const type of instantTypes) {
|
|
720
|
+
expect(registry.get(type).mode).toBe("instant");
|
|
721
|
+
}
|
|
722
|
+
expect(registry.get("agent-task").mode).toBe("async");
|
|
723
|
+
});
|
|
724
|
+
|
|
725
|
+
test("get() retrieves the correct executor by type", () => {
|
|
726
|
+
const registry = createExecutorRegistry(mockDeps);
|
|
727
|
+
const pm = registry.get("property-match");
|
|
728
|
+
expect(pm).toBeInstanceOf(PropertyMatchExecutor);
|
|
729
|
+
|
|
730
|
+
const cm = registry.get("code-match");
|
|
731
|
+
expect(cm).toBeInstanceOf(CodeMatchExecutor);
|
|
732
|
+
|
|
733
|
+
const sc = registry.get("script");
|
|
734
|
+
expect(sc).toBeInstanceOf(ScriptExecutor);
|
|
735
|
+
});
|
|
736
|
+
});
|