@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,86 @@
|
|
|
1
|
+
import type { WorkflowDefinition, WorkflowTemplate } from "../types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Validate that all required template variables are provided.
|
|
5
|
+
* Returns a list of missing required variable names.
|
|
6
|
+
*/
|
|
7
|
+
export function validateTemplateVariables(
|
|
8
|
+
template: WorkflowTemplate,
|
|
9
|
+
provided: Record<string, unknown>,
|
|
10
|
+
): string[] {
|
|
11
|
+
const missing: string[] = [];
|
|
12
|
+
for (const variable of template.variables) {
|
|
13
|
+
if (variable.required && !(variable.name in provided)) {
|
|
14
|
+
// Check if there's a default value
|
|
15
|
+
if (variable.default === undefined) {
|
|
16
|
+
missing.push(variable.name);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return missing;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Instantiate a workflow template with the given variables.
|
|
25
|
+
*
|
|
26
|
+
* 1. Validates all required variables are provided
|
|
27
|
+
* 2. Deep-clones the template definition
|
|
28
|
+
* 3. Replaces `{{variable}}` placeholders in all string fields
|
|
29
|
+
* 4. Returns a valid WorkflowDefinition
|
|
30
|
+
*/
|
|
31
|
+
export function instantiateTemplate(
|
|
32
|
+
template: WorkflowTemplate,
|
|
33
|
+
variables: Record<string, unknown>,
|
|
34
|
+
): WorkflowDefinition {
|
|
35
|
+
// Validate required variables
|
|
36
|
+
const missing = validateTemplateVariables(template, variables);
|
|
37
|
+
if (missing.length > 0) {
|
|
38
|
+
throw new Error(`Missing required template variables: ${missing.join(", ")}`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Build resolved variables (apply defaults for missing optional vars)
|
|
42
|
+
const resolved: Record<string, unknown> = {};
|
|
43
|
+
for (const variable of template.variables) {
|
|
44
|
+
if (variable.name in variables) {
|
|
45
|
+
resolved[variable.name] = variables[variable.name];
|
|
46
|
+
} else if (variable.default !== undefined) {
|
|
47
|
+
resolved[variable.name] = variable.default;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Deep-clone the definition and replace placeholders
|
|
52
|
+
const cloned = JSON.parse(JSON.stringify(template.definition)) as WorkflowDefinition;
|
|
53
|
+
return replaceVariables(cloned, resolved);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Deep-replace `{{variableName}}` placeholders in all string fields of an object.
|
|
58
|
+
*/
|
|
59
|
+
function replaceVariables<T>(obj: T, variables: Record<string, unknown>): T {
|
|
60
|
+
if (typeof obj === "string") {
|
|
61
|
+
return replaceStringPlaceholders(obj, variables) as T;
|
|
62
|
+
}
|
|
63
|
+
if (Array.isArray(obj)) {
|
|
64
|
+
return obj.map((item) => replaceVariables(item, variables)) as T;
|
|
65
|
+
}
|
|
66
|
+
if (obj !== null && typeof obj === "object") {
|
|
67
|
+
const result: Record<string, unknown> = {};
|
|
68
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
69
|
+
result[key] = replaceVariables(value, variables);
|
|
70
|
+
}
|
|
71
|
+
return result as T;
|
|
72
|
+
}
|
|
73
|
+
return obj;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Replace `{{variableName}}` patterns in a string with variable values.
|
|
78
|
+
*/
|
|
79
|
+
function replaceStringPlaceholders(template: string, variables: Record<string, unknown>): string {
|
|
80
|
+
return template.replace(/\{\{(\w+)\}\}/g, (_match, name: string) => {
|
|
81
|
+
const value = variables[name];
|
|
82
|
+
if (value === undefined) return _match; // Leave unresolved placeholders as-is
|
|
83
|
+
if (typeof value === "object" && value !== null) return JSON.stringify(value);
|
|
84
|
+
return String(value);
|
|
85
|
+
});
|
|
86
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import { getWorkflow, getWorkflowsByScheduleId } from "../be/db";
|
|
3
|
+
import type { ScheduledTask, TriggerConfig } from "../types";
|
|
4
|
+
import { startWorkflowExecution } from "./engine";
|
|
5
|
+
import type { ExecutorRegistry } from "./executors/registry";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Handle an incoming webhook trigger for a workflow.
|
|
9
|
+
*
|
|
10
|
+
* 1. Loads the workflow and finds a webhook trigger in `triggers[]`
|
|
11
|
+
* 2. If `hmacSecret` is set, verifies HMAC-SHA256 signature
|
|
12
|
+
* 3. Starts the workflow execution with the webhook payload
|
|
13
|
+
*/
|
|
14
|
+
export async function handleWebhookTrigger(
|
|
15
|
+
workflowId: string,
|
|
16
|
+
payload: unknown,
|
|
17
|
+
signature: string | undefined,
|
|
18
|
+
signatureHeader: string | undefined,
|
|
19
|
+
registry: ExecutorRegistry,
|
|
20
|
+
): Promise<{ runId: string }> {
|
|
21
|
+
const workflow = getWorkflow(workflowId);
|
|
22
|
+
if (!workflow) {
|
|
23
|
+
throw new WebhookError("Workflow not found", 404);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (!workflow.enabled) {
|
|
27
|
+
throw new WebhookError("Workflow is disabled", 400);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Find webhook trigger in triggers[]
|
|
31
|
+
const webhookTrigger = workflow.triggers.find((t: TriggerConfig) => t.type === "webhook");
|
|
32
|
+
|
|
33
|
+
// If the workflow has a webhook trigger with an hmacSecret, verify the signature
|
|
34
|
+
if (webhookTrigger && webhookTrigger.type === "webhook" && webhookTrigger.hmacSecret) {
|
|
35
|
+
if (!signature && !signatureHeader) {
|
|
36
|
+
throw new WebhookError("Missing signature", 401);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const rawSignature = signatureHeader || signature || "";
|
|
40
|
+
const isValid = verifyHmacSignature(
|
|
41
|
+
webhookTrigger.hmacSecret,
|
|
42
|
+
typeof payload === "string" ? payload : JSON.stringify(payload),
|
|
43
|
+
rawSignature,
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
if (!isValid) {
|
|
47
|
+
throw new WebhookError("Invalid signature", 401);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const runId = await startWorkflowExecution(workflow, payload, registry);
|
|
52
|
+
return { runId };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Handle a schedule trigger: find workflows linked to this schedule and execute them.
|
|
57
|
+
* Returns an array of workflow run IDs. Empty array means no workflows matched
|
|
58
|
+
* (caller should fall through to standalone task creation).
|
|
59
|
+
*/
|
|
60
|
+
export async function handleScheduleTrigger(
|
|
61
|
+
scheduleId: string,
|
|
62
|
+
schedule: ScheduledTask,
|
|
63
|
+
registry: ExecutorRegistry,
|
|
64
|
+
): Promise<string[]> {
|
|
65
|
+
const workflows = getWorkflowsByScheduleId(scheduleId);
|
|
66
|
+
if (workflows.length === 0) return [];
|
|
67
|
+
|
|
68
|
+
const runIds: string[] = [];
|
|
69
|
+
for (const workflow of workflows) {
|
|
70
|
+
const triggerData = {
|
|
71
|
+
scheduleId,
|
|
72
|
+
scheduleName: schedule.name,
|
|
73
|
+
firedAt: new Date().toISOString(),
|
|
74
|
+
};
|
|
75
|
+
const runId = await startWorkflowExecution(workflow, triggerData, registry);
|
|
76
|
+
runIds.push(runId);
|
|
77
|
+
console.log(
|
|
78
|
+
`[Triggers] Schedule "${schedule.name}" triggered workflow "${workflow.name}" (run: ${runId})`,
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
return runIds;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Verify HMAC-SHA256 signature.
|
|
86
|
+
* Supports both `sha256=<hex>` format and raw hex.
|
|
87
|
+
*/
|
|
88
|
+
export function verifyHmacSignature(
|
|
89
|
+
secret: string,
|
|
90
|
+
body: string,
|
|
91
|
+
providedSignature: string,
|
|
92
|
+
): boolean {
|
|
93
|
+
const hmac = crypto.createHmac("sha256", secret);
|
|
94
|
+
hmac.update(body);
|
|
95
|
+
const expectedHex = hmac.digest("hex");
|
|
96
|
+
|
|
97
|
+
// Support "sha256=<hex>" format (GitHub-style)
|
|
98
|
+
const normalizedProvided = providedSignature.startsWith("sha256=")
|
|
99
|
+
? providedSignature.slice(7)
|
|
100
|
+
: providedSignature;
|
|
101
|
+
|
|
102
|
+
// Constant-time comparison
|
|
103
|
+
try {
|
|
104
|
+
return crypto.timingSafeEqual(
|
|
105
|
+
Buffer.from(normalizedProvided, "hex"),
|
|
106
|
+
Buffer.from(expectedHex, "hex"),
|
|
107
|
+
);
|
|
108
|
+
} catch {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Error class for webhook-specific errors with HTTP status codes.
|
|
115
|
+
*/
|
|
116
|
+
export class WebhookError extends Error {
|
|
117
|
+
constructor(
|
|
118
|
+
message: string,
|
|
119
|
+
public readonly statusCode: number,
|
|
120
|
+
) {
|
|
121
|
+
super(message);
|
|
122
|
+
this.name = "WebhookError";
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import type { ExecutorMeta, WorkflowNode } from "../types";
|
|
2
|
+
import type { ExecutorRegistry } from "./executors/registry";
|
|
3
|
+
|
|
4
|
+
export type ValidationOutcome = "pass" | "halt" | "retry";
|
|
5
|
+
|
|
6
|
+
export interface ValidationRunResult {
|
|
7
|
+
outcome: ValidationOutcome;
|
|
8
|
+
/** Context additions if retry is needed */
|
|
9
|
+
retryContext?: Record<string, unknown>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Normalize executor output to a pass/fail boolean.
|
|
14
|
+
*
|
|
15
|
+
* Different executors use different output shapes to indicate success.
|
|
16
|
+
* This adapter maps each executor type's convention to a uniform boolean.
|
|
17
|
+
*/
|
|
18
|
+
export function extractPassResult(executorType: string, output: unknown): boolean {
|
|
19
|
+
if (!output || typeof output !== "object") return false;
|
|
20
|
+
const o = output as Record<string, unknown>;
|
|
21
|
+
|
|
22
|
+
switch (executorType) {
|
|
23
|
+
case "validate":
|
|
24
|
+
return o.pass === true;
|
|
25
|
+
case "script":
|
|
26
|
+
return o.exitCode === 0;
|
|
27
|
+
case "property-match":
|
|
28
|
+
return o.passed === true;
|
|
29
|
+
case "raw-llm":
|
|
30
|
+
// For raw-llm used as validator, check if the LLM output contains a structured pass result
|
|
31
|
+
if (typeof o.result === "object" && o.result !== null) {
|
|
32
|
+
return (o.result as Record<string, unknown>).pass === true;
|
|
33
|
+
}
|
|
34
|
+
return false;
|
|
35
|
+
default:
|
|
36
|
+
// Generic fallback: check for common pass indicators
|
|
37
|
+
return o.pass === true || o.passed === true || o.exitCode === 0;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Run per-step validation after a step completes.
|
|
43
|
+
*
|
|
44
|
+
* If the node has no validation config, returns "pass" immediately.
|
|
45
|
+
* Otherwise runs the validation executor and returns the outcome.
|
|
46
|
+
*/
|
|
47
|
+
export async function runStepValidation(
|
|
48
|
+
registry: ExecutorRegistry,
|
|
49
|
+
stepNode: WorkflowNode,
|
|
50
|
+
stepOutput: unknown,
|
|
51
|
+
context: Record<string, unknown>,
|
|
52
|
+
meta: ExecutorMeta,
|
|
53
|
+
): Promise<ValidationRunResult> {
|
|
54
|
+
if (!stepNode.validation) {
|
|
55
|
+
return { outcome: "pass" };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const validation = stepNode.validation;
|
|
59
|
+
const executorType = validation.executor || "validate";
|
|
60
|
+
|
|
61
|
+
const executor = registry.get(executorType);
|
|
62
|
+
const validationConfig = {
|
|
63
|
+
targetNodeId: meta.nodeId,
|
|
64
|
+
...validation.config,
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// Build a context that includes the step output under its nodeId
|
|
68
|
+
const validationContext: Record<string, unknown> = {
|
|
69
|
+
...context,
|
|
70
|
+
[meta.nodeId]: stepOutput,
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const result = await executor.run({
|
|
74
|
+
config: validationConfig,
|
|
75
|
+
context: validationContext,
|
|
76
|
+
meta: {
|
|
77
|
+
...meta,
|
|
78
|
+
stepId: crypto.randomUUID(), // Validation gets its own step ID
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const passed = result.status === "success" && extractPassResult(executorType, result.output);
|
|
83
|
+
|
|
84
|
+
if (passed) {
|
|
85
|
+
return { outcome: "pass" };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Validation failed
|
|
89
|
+
if (validation.mustPass) {
|
|
90
|
+
if (validation.retry) {
|
|
91
|
+
return {
|
|
92
|
+
outcome: "retry",
|
|
93
|
+
retryContext: {
|
|
94
|
+
previousOutput: stepOutput,
|
|
95
|
+
validationResult: result.output,
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
return { outcome: "halt" };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// mustPass is false — treat failure as pass (advisory validation)
|
|
103
|
+
return { outcome: "pass" };
|
|
104
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { createWorkflowVersion, getWorkflow, getWorkflowVersions } from "../be/db";
|
|
2
|
+
import type { WorkflowSnapshot, WorkflowVersion } from "../types";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Create a version snapshot of a workflow's current state.
|
|
6
|
+
*
|
|
7
|
+
* Call this BEFORE applying an update to preserve the pre-update state.
|
|
8
|
+
*
|
|
9
|
+
* 1. Load current workflow state
|
|
10
|
+
* 2. Get max version number for this workflow
|
|
11
|
+
* 3. Insert workflow_versions row with version+1 and full snapshot
|
|
12
|
+
*/
|
|
13
|
+
export function snapshotWorkflow(workflowId: string, changedByAgentId?: string): WorkflowVersion {
|
|
14
|
+
const workflow = getWorkflow(workflowId);
|
|
15
|
+
if (!workflow) {
|
|
16
|
+
throw new Error(`Workflow ${workflowId} not found — cannot create snapshot`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Get existing versions to determine next version number
|
|
20
|
+
const existingVersions = getWorkflowVersions(workflowId);
|
|
21
|
+
const maxVersion = existingVersions.length > 0 ? existingVersions[0]!.version : 0;
|
|
22
|
+
const nextVersion = maxVersion + 1;
|
|
23
|
+
|
|
24
|
+
// Build snapshot of current state
|
|
25
|
+
const snapshot: WorkflowSnapshot = {
|
|
26
|
+
name: workflow.name,
|
|
27
|
+
description: workflow.description,
|
|
28
|
+
definition: workflow.definition,
|
|
29
|
+
triggers: workflow.triggers,
|
|
30
|
+
cooldown: workflow.cooldown,
|
|
31
|
+
input: workflow.input,
|
|
32
|
+
triggerSchema: workflow.triggerSchema,
|
|
33
|
+
dir: workflow.dir,
|
|
34
|
+
vcsRepo: workflow.vcsRepo,
|
|
35
|
+
enabled: workflow.enabled,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
return createWorkflowVersion({
|
|
39
|
+
workflowId,
|
|
40
|
+
version: nextVersion,
|
|
41
|
+
snapshot,
|
|
42
|
+
changedByAgentId,
|
|
43
|
+
});
|
|
44
|
+
}
|
package/src/x402/cli.ts
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* x402 CLI — Test x402 payments from the command line.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* bun src/x402/cli.ts fetch <url> Make a paid request to a URL
|
|
7
|
+
* bun src/x402/cli.ts status Show spending summary
|
|
8
|
+
* bun src/x402/cli.ts check Check if x402 is configured
|
|
9
|
+
*
|
|
10
|
+
* Signer backends (auto-detected):
|
|
11
|
+
* Openfort: OPENFORT_API_KEY + OPENFORT_WALLET_SECRET
|
|
12
|
+
* Viem: EVM_PRIVATE_KEY (0x-prefixed hex)
|
|
13
|
+
*
|
|
14
|
+
* Environment:
|
|
15
|
+
* X402_SIGNER_TYPE "openfort" or "viem" (auto-detected if not set)
|
|
16
|
+
* OPENFORT_API_KEY Openfort API key (sk_test_ or sk_live_)
|
|
17
|
+
* OPENFORT_WALLET_SECRET Openfort wallet secret (base64 P-256 key)
|
|
18
|
+
* EVM_PRIVATE_KEY Raw wallet private key (0x-prefixed)
|
|
19
|
+
* X402_MAX_AUTO_APPROVE Max USD per request (default: 1.00)
|
|
20
|
+
* X402_DAILY_LIMIT Daily USD limit (default: 10.00)
|
|
21
|
+
* X402_NETWORK CAIP-2 network ID (default: eip155:84532)
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { createX402Client } from "./client.ts";
|
|
25
|
+
|
|
26
|
+
const args = process.argv.slice(2);
|
|
27
|
+
const command = args[0];
|
|
28
|
+
|
|
29
|
+
async function main() {
|
|
30
|
+
switch (command) {
|
|
31
|
+
case "fetch": {
|
|
32
|
+
const url = args[1];
|
|
33
|
+
if (!url) {
|
|
34
|
+
console.error("Usage: x402 fetch <url>");
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const method = (args[2] || "GET").toUpperCase();
|
|
39
|
+
console.log(`Making ${method} request to ${url} with x402 payment support...\n`);
|
|
40
|
+
|
|
41
|
+
const client = await createX402Client();
|
|
42
|
+
const summary = client.getSpendingSummary();
|
|
43
|
+
console.log(`Signer: ${client.config.signerType} | Wallet: ${client.walletAddress}`);
|
|
44
|
+
console.log(
|
|
45
|
+
`Spending limits: $${summary.maxPerRequest.toFixed(2)}/request, ` +
|
|
46
|
+
`$${summary.dailyLimit.toFixed(2)}/day ` +
|
|
47
|
+
`($${summary.dailyRemaining.toFixed(2)} remaining)\n`,
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
const response = await client.fetch(url, { method });
|
|
51
|
+
|
|
52
|
+
console.log(`Status: ${response.status} ${response.statusText}`);
|
|
53
|
+
console.log("Headers:");
|
|
54
|
+
for (const [key, value] of response.headers.entries()) {
|
|
55
|
+
if (key.toLowerCase().startsWith("payment") || key.toLowerCase() === "content-type") {
|
|
56
|
+
console.log(` ${key}: ${value}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const contentType = response.headers.get("content-type") || "";
|
|
61
|
+
if (contentType.includes("json")) {
|
|
62
|
+
const body = await response.json();
|
|
63
|
+
console.log("\nResponse body:");
|
|
64
|
+
console.log(JSON.stringify(body, null, 2));
|
|
65
|
+
} else {
|
|
66
|
+
const body = await response.text();
|
|
67
|
+
console.log(`\nResponse body (${body.length} chars):`);
|
|
68
|
+
console.log(body.slice(0, 2000));
|
|
69
|
+
if (body.length > 2000) console.log("... (truncated)");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const updatedSummary = client.getSpendingSummary();
|
|
73
|
+
console.log(
|
|
74
|
+
`\nSpending: $${updatedSummary.todaySpent.toFixed(2)} today ` +
|
|
75
|
+
`(${updatedSummary.todayCount} payments, ` +
|
|
76
|
+
`$${updatedSummary.dailyRemaining.toFixed(2)} remaining)`,
|
|
77
|
+
);
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
case "status": {
|
|
82
|
+
const client = await createX402Client();
|
|
83
|
+
const summary = client.getSpendingSummary();
|
|
84
|
+
console.log("x402 Spending Status");
|
|
85
|
+
console.log("====================");
|
|
86
|
+
console.log(`Signer: ${client.config.signerType}`);
|
|
87
|
+
console.log(`Wallet: ${client.walletAddress}`);
|
|
88
|
+
console.log(`Per-request limit: $${summary.maxPerRequest.toFixed(2)}`);
|
|
89
|
+
console.log(`Daily limit: $${summary.dailyLimit.toFixed(2)}`);
|
|
90
|
+
console.log(`Today spent: $${summary.todaySpent.toFixed(2)}`);
|
|
91
|
+
console.log(`Today remaining: $${summary.dailyRemaining.toFixed(2)}`);
|
|
92
|
+
console.log(`Today payments: ${summary.todayCount}`);
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
case "check": {
|
|
97
|
+
try {
|
|
98
|
+
const client = await createX402Client();
|
|
99
|
+
console.log("x402 Configuration");
|
|
100
|
+
console.log("==================");
|
|
101
|
+
console.log(`Signer type: ${client.config.signerType}`);
|
|
102
|
+
console.log(`Wallet address: ${client.walletAddress}`);
|
|
103
|
+
console.log(`Max per request: $${client.config.maxAutoApprove.toFixed(2)}`);
|
|
104
|
+
console.log(`Daily limit: $${client.config.dailyLimit.toFixed(2)}`);
|
|
105
|
+
console.log(`Network: ${client.config.network}`);
|
|
106
|
+
console.log("\nx402 is configured and ready.");
|
|
107
|
+
} catch (error) {
|
|
108
|
+
console.error(
|
|
109
|
+
`x402 configuration error: ${error instanceof Error ? error.message : String(error)}`,
|
|
110
|
+
);
|
|
111
|
+
process.exit(1);
|
|
112
|
+
}
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
default:
|
|
117
|
+
console.log("x402 CLI — Test x402 payments\n");
|
|
118
|
+
console.log("Commands:");
|
|
119
|
+
console.log(" fetch <url> [method] Make a paid request (default: GET)");
|
|
120
|
+
console.log(" status Show spending summary");
|
|
121
|
+
console.log(" check Check if x402 is configured");
|
|
122
|
+
console.log("\nSigner backends (auto-detected):");
|
|
123
|
+
console.log(" Openfort: OPENFORT_API_KEY + OPENFORT_WALLET_SECRET");
|
|
124
|
+
console.log(" Viem: EVM_PRIVATE_KEY (0x-prefixed hex)");
|
|
125
|
+
console.log("\nEnvironment variables:");
|
|
126
|
+
console.log(" X402_SIGNER_TYPE 'openfort' or 'viem' (auto-detected)");
|
|
127
|
+
console.log(" OPENFORT_API_KEY Openfort API key");
|
|
128
|
+
console.log(" OPENFORT_WALLET_SECRET Openfort wallet auth secret");
|
|
129
|
+
console.log(" EVM_PRIVATE_KEY Raw private key (0x-prefixed)");
|
|
130
|
+
console.log(" X402_MAX_AUTO_APPROVE Max USD per request (default: 1.00)");
|
|
131
|
+
console.log(" X402_DAILY_LIMIT Daily USD limit (default: 10.00)");
|
|
132
|
+
console.log(" X402_NETWORK CAIP-2 network ID (default: eip155:84532)");
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
main().catch((error) => {
|
|
138
|
+
console.error("Error:", error instanceof Error ? error.message : String(error));
|
|
139
|
+
process.exit(1);
|
|
140
|
+
});
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* x402 Payment Client
|
|
3
|
+
*
|
|
4
|
+
* A reusable payment client that wraps fetch() with automatic x402 payment handling
|
|
5
|
+
* and configurable spending limits.
|
|
6
|
+
*
|
|
7
|
+
* Supports two signer backends:
|
|
8
|
+
* - Openfort (default) — managed backend wallet, no raw keys in env
|
|
9
|
+
* - viem — raw EVM_PRIVATE_KEY for local signing
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* import { createX402Fetch, createX402Client } from "@/x402";
|
|
13
|
+
*
|
|
14
|
+
* // Quick: get a paid fetch function
|
|
15
|
+
* const paidFetch = await createX402Fetch();
|
|
16
|
+
* const response = await paidFetch("https://api.example.com/paid-endpoint");
|
|
17
|
+
*
|
|
18
|
+
* // Advanced: get the full client with spending info
|
|
19
|
+
* const client = await createX402Client();
|
|
20
|
+
* const response = await client.fetch("https://api.example.com/paid-endpoint");
|
|
21
|
+
* console.log(client.getSpendingSummary());
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { x402Client } from "@x402/core/client";
|
|
25
|
+
import type { ClientEvmSigner } from "@x402/evm";
|
|
26
|
+
import { toClientEvmSigner } from "@x402/evm";
|
|
27
|
+
import { registerExactEvmScheme } from "@x402/evm/exact/client";
|
|
28
|
+
import { wrapFetchWithPayment } from "@x402/fetch";
|
|
29
|
+
import { createPublicClient, http } from "viem";
|
|
30
|
+
import { privateKeyToAccount } from "viem/accounts";
|
|
31
|
+
import { base, baseSepolia } from "viem/chains";
|
|
32
|
+
import { loadX402Config, type X402Config, type X402SafeConfig } from "./config.ts";
|
|
33
|
+
import { createOpenfortSigner } from "./openfort-signer.ts";
|
|
34
|
+
import { SpendingTracker } from "./spending-tracker.ts";
|
|
35
|
+
|
|
36
|
+
export interface X402PaymentClient {
|
|
37
|
+
/** A fetch function that automatically handles x402 402 responses */
|
|
38
|
+
fetch: (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
|
|
39
|
+
/** The underlying x402Client instance */
|
|
40
|
+
x402Client: x402Client;
|
|
41
|
+
/** The spending tracker for monitoring limits */
|
|
42
|
+
spendingTracker: SpendingTracker;
|
|
43
|
+
/** Get a summary of today's spending */
|
|
44
|
+
getSpendingSummary: () => ReturnType<SpendingTracker["getSummary"]>;
|
|
45
|
+
/** The wallet address used for payments */
|
|
46
|
+
walletAddress: `0x${string}`;
|
|
47
|
+
/** Safe config subset (no secrets) */
|
|
48
|
+
config: X402SafeConfig;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Map CAIP-2 network ID to viem chain config.
|
|
53
|
+
*/
|
|
54
|
+
function getChainForNetwork(network: string) {
|
|
55
|
+
if (network === "eip155:8453") return base;
|
|
56
|
+
return baseSepolia; // Default to testnet
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Create a signer based on the config's signerType.
|
|
61
|
+
*/
|
|
62
|
+
async function buildSigner(
|
|
63
|
+
config: X402Config,
|
|
64
|
+
): Promise<{ signer: ClientEvmSigner; walletAddress: `0x${string}` }> {
|
|
65
|
+
if (config.signerType === "openfort") {
|
|
66
|
+
if (!config.openfortApiKey || !config.openfortWalletSecret) {
|
|
67
|
+
throw new Error("Openfort signer requires OPENFORT_API_KEY and OPENFORT_WALLET_SECRET.");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const chain = getChainForNetwork(config.network);
|
|
71
|
+
const signer = await createOpenfortSigner({
|
|
72
|
+
apiKey: config.openfortApiKey,
|
|
73
|
+
walletSecret: config.openfortWalletSecret,
|
|
74
|
+
walletAddress: config.openfortWalletAddress,
|
|
75
|
+
chain,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
return { signer, walletAddress: signer.address };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// viem signer (raw private key)
|
|
82
|
+
if (!config.evmPrivateKey) {
|
|
83
|
+
throw new Error("Viem signer requires EVM_PRIVATE_KEY.");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const account = privateKeyToAccount(config.evmPrivateKey);
|
|
87
|
+
const chain = getChainForNetwork(config.network);
|
|
88
|
+
const publicClient = createPublicClient({ chain, transport: http() });
|
|
89
|
+
const signer = toClientEvmSigner(account, publicClient);
|
|
90
|
+
|
|
91
|
+
return { signer, walletAddress: account.address };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Create a full x402 payment client with spending controls.
|
|
96
|
+
*
|
|
97
|
+
* NOTE: This is async because Openfort signer requires API calls to create/retrieve wallets.
|
|
98
|
+
*
|
|
99
|
+
* @param configOverrides - Optional config overrides (otherwise loaded from env vars)
|
|
100
|
+
* @returns An X402PaymentClient with fetch, spending tracker, and config
|
|
101
|
+
*/
|
|
102
|
+
export async function createX402Client(
|
|
103
|
+
configOverrides?: Partial<X402Config>,
|
|
104
|
+
): Promise<X402PaymentClient> {
|
|
105
|
+
const envConfig = loadX402Config();
|
|
106
|
+
const config = { ...envConfig, ...configOverrides };
|
|
107
|
+
|
|
108
|
+
// Build the signer (async for Openfort, instant for viem)
|
|
109
|
+
const { signer, walletAddress } = await buildSigner(config);
|
|
110
|
+
|
|
111
|
+
// Create and configure x402 client
|
|
112
|
+
const client = new x402Client();
|
|
113
|
+
registerExactEvmScheme(client, { signer });
|
|
114
|
+
|
|
115
|
+
// Create spending tracker
|
|
116
|
+
const spendingTracker = new SpendingTracker(config.maxAutoApprove, config.dailyLimit);
|
|
117
|
+
|
|
118
|
+
// Register a spending-limit hook that blocks over-budget payments.
|
|
119
|
+
// NOTE: This check has a TOCTOU race — concurrent requests could both pass the
|
|
120
|
+
// limit check before either records its payment. Acceptable for agent workloads
|
|
121
|
+
// (typically sequential), but not suitable for high-concurrency scenarios.
|
|
122
|
+
client.onBeforePaymentCreation(async (context) => {
|
|
123
|
+
const { selectedRequirements } = context;
|
|
124
|
+
|
|
125
|
+
// V2 PaymentRequirements uses `amount` (smallest unit, e.g. "10000" for $0.01 USDC)
|
|
126
|
+
const rawValue = selectedRequirements.amount;
|
|
127
|
+
const amountUsd = usdcToUsd(rawValue);
|
|
128
|
+
|
|
129
|
+
const url = context.paymentRequired.resource?.url || "unknown";
|
|
130
|
+
const blockReason = spendingTracker.checkSpendingLimit(amountUsd, url);
|
|
131
|
+
|
|
132
|
+
if (blockReason) {
|
|
133
|
+
return { abort: true, reason: blockReason };
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// Track successful payments
|
|
138
|
+
client.onAfterPaymentCreation(async (context) => {
|
|
139
|
+
const rawValue = context.selectedRequirements.amount;
|
|
140
|
+
const amountUsd = usdcToUsd(rawValue);
|
|
141
|
+
const url = context.paymentRequired.resource?.url || "unknown";
|
|
142
|
+
spendingTracker.recordPayment(amountUsd, url);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// Wrap fetch with payment handling
|
|
146
|
+
const paidFetch = wrapFetchWithPayment(globalThis.fetch, client);
|
|
147
|
+
|
|
148
|
+
// Expose a safe config subset — never leak secrets
|
|
149
|
+
const {
|
|
150
|
+
evmPrivateKey: _key,
|
|
151
|
+
openfortApiKey: _apiKey,
|
|
152
|
+
openfortWalletSecret: _secret,
|
|
153
|
+
...safeConfig
|
|
154
|
+
} = config;
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
fetch: paidFetch as (input: string | URL | Request, init?: RequestInit) => Promise<Response>,
|
|
158
|
+
x402Client: client,
|
|
159
|
+
spendingTracker,
|
|
160
|
+
getSpendingSummary: () => spendingTracker.getSummary(),
|
|
161
|
+
walletAddress,
|
|
162
|
+
config: safeConfig,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Create a simple paid fetch function.
|
|
168
|
+
* This is the easiest way to get started — just replace `fetch` with this.
|
|
169
|
+
*
|
|
170
|
+
* @param configOverrides - Optional config overrides
|
|
171
|
+
* @returns A fetch function that automatically handles x402 payments
|
|
172
|
+
*/
|
|
173
|
+
export async function createX402Fetch(
|
|
174
|
+
configOverrides?: Partial<X402Config>,
|
|
175
|
+
): Promise<(input: string | URL | Request, init?: RequestInit) => Promise<Response>> {
|
|
176
|
+
return (await createX402Client(configOverrides)).fetch;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Convert USDC raw value (6 decimals) to USD.
|
|
181
|
+
* USDC uses 6 decimal places, so 1000000 = $1.00
|
|
182
|
+
*/
|
|
183
|
+
function usdcToUsd(rawValue: string): number {
|
|
184
|
+
try {
|
|
185
|
+
return Number(BigInt(rawValue)) / 1_000_000;
|
|
186
|
+
} catch {
|
|
187
|
+
// If the value can't be parsed as BigInt, fall back to parseFloat
|
|
188
|
+
const parsed = Number.parseFloat(rawValue);
|
|
189
|
+
if (Number.isNaN(parsed)) return 0;
|
|
190
|
+
return parsed / 1_000_000;
|
|
191
|
+
}
|
|
192
|
+
}
|