@desplega.ai/agent-swarm 1.49.0 → 1.52.0
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 +1 -1
- package/openapi.json +2070 -728
- package/package.json +10 -1
- package/src/agentmail/handlers.ts +65 -10
- package/src/agentmail/templates.ts +111 -0
- package/src/be/db.ts +1233 -7
- 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/019_skills.sql +65 -0
- package/src/be/migrations/020_approval_requests.sql +41 -0
- package/src/be/seed.ts +62 -0
- package/src/be/skill-parser.ts +70 -0
- package/src/be/skill-sync.ts +106 -0
- package/src/commands/runner.ts +320 -132
- package/src/commands/templates.ts +172 -0
- package/src/github/handlers.ts +292 -77
- 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/gitlab/handlers.ts +63 -22
- package/src/gitlab/templates.ts +140 -0
- package/src/heartbeat/heartbeat.ts +19 -10
- package/src/heartbeat/templates.ts +30 -0
- package/src/http/active-sessions.ts +27 -0
- package/src/http/approval-requests.ts +247 -0
- package/src/http/config.ts +3 -3
- package/src/http/index.ts +9 -2
- package/src/http/poll.ts +135 -14
- package/src/http/prompt-templates.ts +412 -0
- package/src/http/schedules.ts +35 -0
- package/src/http/skills.ts +479 -0
- package/src/http/workflows.ts +8 -0
- package/src/linear/sync.ts +28 -4
- package/src/linear/templates.ts +47 -0
- package/src/prompts/base-prompt.ts +41 -490
- 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 +15 -2
- package/src/providers/pi-mono-extension.ts +5 -1
- package/src/scheduler/scheduler.ts +125 -91
- package/src/server.ts +44 -0
- package/src/slack/assistant.ts +7 -4
- package/src/slack/channel-activity.ts +177 -0
- package/src/slack/handlers.ts +21 -6
- package/src/slack/templates.ts +55 -0
- package/src/tests/approval-requests.test.ts +735 -0
- package/src/tests/artifact-sdk.test.ts +12 -12
- package/src/tests/base-prompt.test.ts +49 -49
- package/src/tests/channel-activity.test.ts +363 -0
- package/src/tests/heartbeat.test.ts +1 -0
- package/src/tests/linear-webhook.test.ts +7 -3
- package/src/tests/pool-session-logs.test.ts +199 -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/self-improvement.test.ts +8 -7
- package/src/tests/skill-parser.test.ts +178 -0
- package/src/tests/skill-sync.test.ts +171 -0
- package/src/tests/slack-metadata-inheritance.test.ts +1 -1
- package/src/tests/slack-thread-followups.test.ts +1 -1
- package/src/tests/structured-output.test.ts +0 -4
- package/src/tests/tool-annotations.test.ts +2 -1
- package/src/tests/update-profile-agentid.test.ts +248 -0
- package/src/tests/update-profile-auth.test.ts +195 -0
- package/src/tests/workflow-async-v2.test.ts +126 -4
- package/src/tests/workflow-definition-validation.test.ts +76 -0
- package/src/tests/workflow-executors.test.ts +4 -2
- package/src/tests/workflow-retry-v2.test.ts +1 -1
- package/src/tests/workflow-schedule-trigger.test.ts +104 -0
- package/src/tests/workflow-workspace.test.ts +272 -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/request-human-input.ts +106 -0
- package/src/tools/skills/index.ts +11 -0
- package/src/tools/skills/skill-create.ts +105 -0
- package/src/tools/skills/skill-delete.ts +67 -0
- package/src/tools/skills/skill-get.ts +75 -0
- package/src/tools/skills/skill-install-remote.ts +152 -0
- package/src/tools/skills/skill-install.ts +101 -0
- package/src/tools/skills/skill-list.ts +77 -0
- package/src/tools/skills/skill-publish.ts +123 -0
- package/src/tools/skills/skill-search.ts +43 -0
- package/src/tools/skills/skill-sync-remote.ts +128 -0
- package/src/tools/skills/skill-uninstall.ts +60 -0
- package/src/tools/skills/skill-update.ts +128 -0
- package/src/tools/store-progress.ts +22 -4
- package/src/tools/task-action.ts +20 -0
- package/src/tools/templates.ts +53 -0
- package/src/tools/tool-config.ts +23 -0
- package/src/tools/update-profile.ts +106 -34
- package/src/tools/workflows/create-workflow.ts +19 -1
- package/src/tools/workflows/update-workflow.ts +16 -1
- package/src/types.ts +109 -2
- package/src/workflows/definition.ts +30 -12
- package/src/workflows/engine.ts +40 -14
- package/src/workflows/executors/agent-task.ts +14 -3
- package/src/workflows/executors/human-in-the-loop.ts +160 -0
- package/src/workflows/executors/registry.ts +2 -0
- package/src/workflows/index.ts +1 -1
- package/src/workflows/recovery.ts +72 -0
- package/src/workflows/resume.ts +162 -12
- package/src/workflows/triggers.ts +31 -2
- package/src/workflows/version.ts +2 -0
- package/.claude/settings.json +0 -84
- package/.claude/settings.local.json +0 -117
- package/.dockerignore +0 -61
- package/.editorconfig +0 -15
- package/.entire/settings.json +0 -4
- package/.env.docker.example +0 -56
- package/.env.example +0 -78
- package/.github/ISSUE_TEMPLATE/bug_report.yml +0 -78
- package/.github/ISSUE_TEMPLATE/community-template.yml +0 -77
- package/.github/ISSUE_TEMPLATE/config.yml +0 -8
- package/.github/ISSUE_TEMPLATE/feature_request.yml +0 -60
- package/.github/PULL_REQUEST_TEMPLATE/community-template.md +0 -29
- package/.github/workflows/ci.yml +0 -52
- package/.github/workflows/docker-and-deploy.yml +0 -132
- package/.github/workflows/merge-gate.yml +0 -233
- package/.opencode/plugins/entire.ts +0 -133
- package/.superset/config.json +0 -6
- package/.wts-config.json +0 -4
- package/.wts-setup.ts +0 -171
- package/CHANGELOG.md +0 -447
- package/CLAUDE.md +0 -521
- package/CONTRIBUTING.md +0 -315
- package/DEPLOYMENT.md +0 -622
- package/Dockerfile +0 -65
- package/Dockerfile.worker +0 -189
- package/MCP.md +0 -841
- package/UI.md +0 -40
- package/api-entrypoint.sh +0 -56
- 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/depot.json +0 -1
- package/docker-compose.example.yml +0 -350
- package/docker-compose.local.yml +0 -119
- package/docker-entrypoint.sh +0 -632
- package/docs-site/app/api/search/route.ts +0 -4
- package/docs-site/app/docs/[[...slug]]/page.tsx +0 -87
- package/docs-site/app/docs/layout.tsx +0 -12
- package/docs-site/app/globals.css +0 -24
- package/docs-site/app/layout.config.tsx +0 -34
- package/docs-site/app/layout.tsx +0 -119
- package/docs-site/app/llms-full.txt/route.ts +0 -11
- package/docs-site/app/llms.mdx/docs/[[...slug]]/route.ts +0 -24
- package/docs-site/app/llms.txt/route.ts +0 -8
- package/docs-site/app/page.tsx +0 -5
- package/docs-site/app/robots.ts +0 -13
- package/docs-site/app/sitemap.ts +0 -37
- package/docs-site/components/api-page.client.tsx +0 -4
- package/docs-site/components/api-page.tsx +0 -7
- package/docs-site/components/mdx/mermaid.tsx +0 -55
- package/docs-site/content/docs/(documentation)/architecture/agents.mdx +0 -117
- package/docs-site/content/docs/(documentation)/architecture/hooks.mdx +0 -77
- package/docs-site/content/docs/(documentation)/architecture/memory.mdx +0 -96
- package/docs-site/content/docs/(documentation)/architecture/meta.json +0 -4
- package/docs-site/content/docs/(documentation)/architecture/overview.mdx +0 -172
- package/docs-site/content/docs/(documentation)/concepts/epics.mdx +0 -98
- package/docs-site/content/docs/(documentation)/concepts/meta.json +0 -4
- package/docs-site/content/docs/(documentation)/concepts/scheduling.mdx +0 -136
- package/docs-site/content/docs/(documentation)/concepts/services.mdx +0 -104
- package/docs-site/content/docs/(documentation)/concepts/task-lifecycle.mdx +0 -148
- package/docs-site/content/docs/(documentation)/concepts/workflows.mdx +0 -209
- package/docs-site/content/docs/(documentation)/contributing.mdx +0 -158
- package/docs-site/content/docs/(documentation)/getting-started.mdx +0 -157
- package/docs-site/content/docs/(documentation)/guides/agentmail-integration.mdx +0 -79
- package/docs-site/content/docs/(documentation)/guides/deployment.mdx +0 -171
- package/docs-site/content/docs/(documentation)/guides/github-integration.mdx +0 -81
- package/docs-site/content/docs/(documentation)/guides/gitlab-integration.mdx +0 -93
- package/docs-site/content/docs/(documentation)/guides/linear-integration.mdx +0 -98
- package/docs-site/content/docs/(documentation)/guides/meta.json +0 -13
- package/docs-site/content/docs/(documentation)/guides/sentry-integration.mdx +0 -52
- package/docs-site/content/docs/(documentation)/guides/slack-integration.mdx +0 -179
- package/docs-site/content/docs/(documentation)/guides/x402-payments.mdx +0 -154
- package/docs-site/content/docs/(documentation)/index.mdx +0 -65
- package/docs-site/content/docs/(documentation)/meta.json +0 -19
- package/docs-site/content/docs/(documentation)/reference/cli.mdx +0 -241
- package/docs-site/content/docs/(documentation)/reference/environment-variables.mdx +0 -205
- package/docs-site/content/docs/(documentation)/reference/mcp-tools.mdx +0 -449
- package/docs-site/content/docs/(documentation)/reference/meta.json +0 -4
- package/docs-site/content/docs/api-reference/active-sessions.mdx +0 -9
- package/docs-site/content/docs/api-reference/agents.mdx +0 -9
- package/docs-site/content/docs/api-reference/channels.mdx +0 -9
- package/docs-site/content/docs/api-reference/config.mdx +0 -9
- package/docs-site/content/docs/api-reference/debug.mdx +0 -9
- package/docs-site/content/docs/api-reference/ecosystem.mdx +0 -9
- package/docs-site/content/docs/api-reference/epics.mdx +0 -9
- package/docs-site/content/docs/api-reference/index.mdx +0 -32
- package/docs-site/content/docs/api-reference/memory.mdx +0 -9
- package/docs-site/content/docs/api-reference/meta.json +0 -25
- package/docs-site/content/docs/api-reference/poll.mdx +0 -9
- package/docs-site/content/docs/api-reference/repos.mdx +0 -9
- package/docs-site/content/docs/api-reference/schedules.mdx +0 -9
- package/docs-site/content/docs/api-reference/session-data.mdx +0 -9
- package/docs-site/content/docs/api-reference/stats.mdx +0 -9
- package/docs-site/content/docs/api-reference/tasks.mdx +0 -9
- package/docs-site/content/docs/api-reference/trackers.mdx +0 -9
- package/docs-site/content/docs/api-reference/webhooks.mdx +0 -9
- package/docs-site/content/docs/api-reference/workflows.mdx +0 -9
- package/docs-site/content/docs/meta.json +0 -3
- package/docs-site/lib/get-llm-text.ts +0 -10
- package/docs-site/lib/openapi.ts +0 -23
- package/docs-site/lib/source.ts +0 -8
- package/docs-site/mdx-components.tsx +0 -13
- package/docs-site/next.config.mjs +0 -29
- package/docs-site/package.json +0 -35
- package/docs-site/pnpm-lock.yaml +0 -5407
- package/docs-site/postcss.config.mjs +0 -8
- package/docs-site/public/logo.png +0 -0
- package/docs-site/scripts/generate-docs.ts +0 -171
- package/docs-site/source.config.ts +0 -17
- package/docs-site/tsconfig.json +0 -46
- package/ecosystem.config.cjs +0 -66
- package/landing/next.config.ts +0 -14
- package/landing/package.json +0 -31
- package/landing/pnpm-lock.yaml +0 -1091
- package/landing/postcss.config.mjs +0 -8
- package/landing/public/apple-touch-icon.png +0 -0
- package/landing/public/favicon.ico +0 -0
- package/landing/public/logo.png +0 -0
- package/landing/public/og-image.png +0 -0
- package/landing/public/omghost-desplega.svg +0 -30
- package/landing/public/omghost-openfort.svg +0 -9
- package/landing/src/app/actions/waitlist.ts +0 -25
- package/landing/src/app/blog/openfort-hackathon/page.tsx +0 -863
- package/landing/src/app/blog/page.tsx +0 -162
- package/landing/src/app/blog/swarm-metrics/page.tsx +0 -685
- package/landing/src/app/examples/page.tsx +0 -174
- package/landing/src/app/examples/x402/page.tsx +0 -456
- package/landing/src/app/globals.css +0 -122
- package/landing/src/app/layout.tsx +0 -134
- package/landing/src/app/page.tsx +0 -27
- package/landing/src/app/robots.ts +0 -13
- package/landing/src/app/sitemap.ts +0 -44
- package/landing/src/components/architecture.tsx +0 -163
- package/landing/src/components/cta.tsx +0 -52
- package/landing/src/components/features.tsx +0 -160
- package/landing/src/components/footer.tsx +0 -100
- package/landing/src/components/hero.tsx +0 -217
- package/landing/src/components/how-it-works.tsx +0 -165
- package/landing/src/components/navbar.tsx +0 -147
- package/landing/src/components/waitlist.tsx +0 -110
- package/landing/src/components/why-choose.tsx +0 -149
- package/landing/src/components/workshops.tsx +0 -328
- package/landing/src/lib/utils.ts +0 -6
- package/landing/tsconfig.json +0 -41
- package/misc/transcripts/2026-03-09-pi-mono-e2e-verification.md +0 -154
- package/new-ui/CLAUDE.md +0 -92
- package/new-ui/README.md +0 -73
- package/new-ui/biome.json +0 -42
- package/new-ui/components.json +0 -21
- package/new-ui/index.html +0 -25
- package/new-ui/package.json +0 -49
- package/new-ui/pnpm-lock.yaml +0 -4845
- package/new-ui/public/logo.png +0 -0
- package/new-ui/src/api/client.ts +0 -814
- package/new-ui/src/api/hooks/index.ts +0 -64
- package/new-ui/src/api/hooks/use-agents.ts +0 -58
- package/new-ui/src/api/hooks/use-channels.ts +0 -115
- package/new-ui/src/api/hooks/use-config-api.ts +0 -46
- package/new-ui/src/api/hooks/use-costs.ts +0 -122
- package/new-ui/src/api/hooks/use-db-query.ts +0 -29
- package/new-ui/src/api/hooks/use-epics.ts +0 -75
- package/new-ui/src/api/hooks/use-repos.ts +0 -61
- package/new-ui/src/api/hooks/use-schedules.ts +0 -81
- package/new-ui/src/api/hooks/use-services.ts +0 -16
- package/new-ui/src/api/hooks/use-stats.ts +0 -27
- package/new-ui/src/api/hooks/use-tasks.ts +0 -89
- package/new-ui/src/api/hooks/use-workflows.ts +0 -109
- package/new-ui/src/api/types.ts +0 -549
- package/new-ui/src/app/App.tsx +0 -13
- package/new-ui/src/app/providers.tsx +0 -32
- package/new-ui/src/app/router.tsx +0 -52
- package/new-ui/src/components/layout/app-header.tsx +0 -47
- package/new-ui/src/components/layout/app-sidebar.tsx +0 -128
- package/new-ui/src/components/layout/breadcrumbs.tsx +0 -57
- package/new-ui/src/components/layout/config-guard.tsx +0 -22
- package/new-ui/src/components/layout/root-layout.tsx +0 -40
- package/new-ui/src/components/layout/swarm-switcher.tsx +0 -85
- package/new-ui/src/components/shared/command-menu.tsx +0 -131
- package/new-ui/src/components/shared/data-grid.tsx +0 -141
- package/new-ui/src/components/shared/empty-state.tsx +0 -24
- package/new-ui/src/components/shared/error-boundary.tsx +0 -72
- package/new-ui/src/components/shared/json-viewer.tsx +0 -47
- package/new-ui/src/components/shared/name-connection-modal.tsx +0 -99
- package/new-ui/src/components/shared/page-skeleton.tsx +0 -16
- package/new-ui/src/components/shared/session-log-viewer.tsx +0 -364
- package/new-ui/src/components/shared/stats-bar.tsx +0 -132
- package/new-ui/src/components/shared/status-badge.tsx +0 -131
- package/new-ui/src/components/shared/usage-summary.tsx +0 -179
- package/new-ui/src/components/ui/alert-dialog.tsx +0 -176
- package/new-ui/src/components/ui/alert.tsx +0 -60
- package/new-ui/src/components/ui/avatar.tsx +0 -96
- package/new-ui/src/components/ui/badge.tsx +0 -46
- package/new-ui/src/components/ui/button.tsx +0 -62
- package/new-ui/src/components/ui/card.tsx +0 -75
- package/new-ui/src/components/ui/command.tsx +0 -160
- package/new-ui/src/components/ui/dialog.tsx +0 -143
- package/new-ui/src/components/ui/dropdown-menu.tsx +0 -226
- package/new-ui/src/components/ui/input.tsx +0 -21
- package/new-ui/src/components/ui/label.tsx +0 -19
- package/new-ui/src/components/ui/progress.tsx +0 -26
- package/new-ui/src/components/ui/scroll-area.tsx +0 -54
- package/new-ui/src/components/ui/select.tsx +0 -175
- package/new-ui/src/components/ui/separator.tsx +0 -28
- package/new-ui/src/components/ui/sheet.tsx +0 -132
- package/new-ui/src/components/ui/sidebar.tsx +0 -691
- package/new-ui/src/components/ui/skeleton.tsx +0 -13
- package/new-ui/src/components/ui/sonner.tsx +0 -35
- package/new-ui/src/components/ui/switch.tsx +0 -33
- package/new-ui/src/components/ui/table.tsx +0 -92
- package/new-ui/src/components/ui/tabs.tsx +0 -79
- package/new-ui/src/components/ui/textarea.tsx +0 -18
- package/new-ui/src/components/ui/tooltip.tsx +0 -51
- package/new-ui/src/components/workflows/action-node.tsx +0 -53
- package/new-ui/src/components/workflows/condition-node.tsx +0 -50
- package/new-ui/src/components/workflows/graph-utils.ts +0 -124
- package/new-ui/src/components/workflows/json-tree.tsx +0 -189
- package/new-ui/src/components/workflows/node-styles.ts +0 -10
- package/new-ui/src/components/workflows/step-detail-sheet.tsx +0 -87
- package/new-ui/src/components/workflows/trigger-node.tsx +0 -41
- package/new-ui/src/components/workflows/workflow-graph.tsx +0 -65
- package/new-ui/src/hooks/use-auto-scroll.ts +0 -82
- package/new-ui/src/hooks/use-config.ts +0 -203
- package/new-ui/src/hooks/use-keyboard-shortcuts.ts +0 -41
- package/new-ui/src/hooks/use-mobile.ts +0 -19
- package/new-ui/src/hooks/use-theme.ts +0 -60
- package/new-ui/src/lib/config.ts +0 -188
- package/new-ui/src/lib/slugs.ts +0 -71
- package/new-ui/src/lib/utils.ts +0 -120
- package/new-ui/src/main.tsx +0 -11
- package/new-ui/src/pages/agents/[id]/page.tsx +0 -492
- package/new-ui/src/pages/agents/page.tsx +0 -134
- package/new-ui/src/pages/chat/page.tsx +0 -674
- package/new-ui/src/pages/config/page.tsx +0 -1109
- package/new-ui/src/pages/dashboard/page.tsx +0 -454
- package/new-ui/src/pages/debug/page.tsx +0 -275
- package/new-ui/src/pages/epics/[id]/page.tsx +0 -809
- package/new-ui/src/pages/epics/page.tsx +0 -321
- package/new-ui/src/pages/not-found/page.tsx +0 -18
- package/new-ui/src/pages/repos/page.tsx +0 -369
- package/new-ui/src/pages/schedules/[id]/page.tsx +0 -664
- package/new-ui/src/pages/schedules/page.tsx +0 -477
- package/new-ui/src/pages/services/page.tsx +0 -128
- package/new-ui/src/pages/tasks/[id]/page.tsx +0 -670
- package/new-ui/src/pages/tasks/page.tsx +0 -592
- package/new-ui/src/pages/usage/page.tsx +0 -195
- package/new-ui/src/pages/workflow-runs/[id]/page.tsx +0 -363
- package/new-ui/src/pages/workflows/[id]/page.tsx +0 -417
- package/new-ui/src/pages/workflows/page.tsx +0 -266
- package/new-ui/src/styles/ag-grid.css +0 -36
- package/new-ui/src/styles/globals.css +0 -213
- package/new-ui/test-results/.last-run.json +0 -4
- package/new-ui/tsconfig.app.json +0 -34
- package/new-ui/tsconfig.json +0 -4
- package/new-ui/tsconfig.node.json +0 -26
- package/new-ui/vercel.json +0 -4
- package/new-ui/vite.config.ts +0 -28
- package/plugin/README.md +0 -1
- package/plugin/build-pi-skills.ts +0 -233
- package/plugin/hooks/hooks.json +0 -71
- package/prek.toml +0 -75
- package/pyproject.toml +0 -9
- package/scripts/check-db-boundary.sh +0 -60
- package/scripts/e2e-docker-provider.ts +0 -820
- package/scripts/e2e-io-schemas-test.ts +0 -807
- package/scripts/e2e-provider-test.ts +0 -220
- package/scripts/e2e-workflow-redesign.sh +0 -229
- package/scripts/e2e-workflow-test.sh +0 -285
- package/scripts/e2e-workflow-test.ts +0 -857
- package/scripts/generate-mcp-docs.ts +0 -415
- package/scripts/generate-openapi.ts +0 -26
- package/scripts/measure-tool-tokens.ts +0 -118
- package/scripts/x402-e2e-test.ts +0 -195
- package/scripts/x402-test-server.ts +0 -236
- package/scripts/x402-testnet-e2e.ts +0 -668
- package/slack-manifest.json +0 -88
- package/templates-ui/README.md +0 -46
- package/templates-ui/components.json +0 -17
- package/templates-ui/eslint.config.mjs +0 -18
- package/templates-ui/next.config.ts +0 -7
- package/templates-ui/package.json +0 -35
- package/templates-ui/pnpm-lock.yaml +0 -4571
- package/templates-ui/postcss.config.mjs +0 -7
- package/templates-ui/public/file.svg +0 -1
- package/templates-ui/public/globe.svg +0 -1
- package/templates-ui/public/logo.png +0 -0
- package/templates-ui/public/next.svg +0 -1
- package/templates-ui/public/vercel.svg +0 -1
- package/templates-ui/public/window.svg +0 -1
- package/templates-ui/src/app/[category]/[name]/page.tsx +0 -89
- package/templates-ui/src/app/api/templates/[...slug]/route.ts +0 -52
- package/templates-ui/src/app/api/templates/route.ts +0 -18
- package/templates-ui/src/app/builder/page.tsx +0 -37
- package/templates-ui/src/app/globals.css +0 -94
- package/templates-ui/src/app/layout.tsx +0 -79
- package/templates-ui/src/app/page.tsx +0 -38
- package/templates-ui/src/app/robots.ts +0 -11
- package/templates-ui/src/app/sitemap.ts +0 -31
- package/templates-ui/src/components/compose-builder.tsx +0 -442
- package/templates-ui/src/components/compose-preview.tsx +0 -117
- package/templates-ui/src/components/file-preview.tsx +0 -77
- package/templates-ui/src/components/footer.tsx +0 -40
- package/templates-ui/src/components/header.tsx +0 -41
- package/templates-ui/src/components/template-card.tsx +0 -87
- package/templates-ui/src/components/template-detail.tsx +0 -125
- package/templates-ui/src/components/template-gallery.tsx +0 -263
- package/templates-ui/src/components/ui/badge.tsx +0 -36
- package/templates-ui/src/components/ui/button.tsx +0 -57
- package/templates-ui/src/components/ui/card.tsx +0 -76
- package/templates-ui/src/components/ui/separator.tsx +0 -31
- package/templates-ui/src/components/ui/tooltip.tsx +0 -32
- package/templates-ui/src/lib/compose-generator.ts +0 -241
- package/templates-ui/src/lib/templates.ts +0 -137
- package/templates-ui/src/lib/utils.ts +0 -6
- package/templates-ui/tsconfig.json +0 -34
- package/thoughts/research/2026-02-28-openfort-viem-x402-research.md +0 -679
- package/thoughts/research/2026-02-28-x402-payments-research.md +0 -686
- package/thoughts/researcher/plans/2026-02-20-agent-self-improvement-plan.md +0 -282
- package/thoughts/researcher/research/2026-02-20-agent-self-improvement.md +0 -492
- package/thoughts/shared/plans/.gitkeep +0 -0
- 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/plans/2026-02-26-mcp-tool-context-reduction.md +0 -282
- package/thoughts/shared/plans/2026-03-02-claude-context-mode-integration.md +0 -328
- package/thoughts/shared/plans/2026-03-02-code-level-heartbeat.md +0 -224
- 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/shared/research/2026-02-26-cliffy-mcp-tools.md +0 -159
- package/thoughts/shared/research/2026-03-03-database-migration-system-refactor.md +0 -337
- package/thoughts/swarm-researcher/plans/2026-02-23-openclaw-improvements-plan.md +0 -778
- package/thoughts/swarm-researcher/plans/2026-02-26-artifacts-localtunnel-plan.md +0 -1269
- package/thoughts/swarm-researcher/research/2026-02-23-openclaw-vs-agent-swarm-comparison.md +0 -411
- package/thoughts/swarm-researcher/research/2026-02-26-artifacts-localtunnel.md +0 -724
- package/thoughts/taras/brainstorms/2026-03-20-prompt-template-registry.md +0 -443
- package/thoughts/taras/brainstorms/2026-03-20-setup-cli-onboarding.md +0 -307
- 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/plans/2026-02-20-auto-improvement.md +0 -803
- package/thoughts/taras/plans/2026-02-20-env-management.md +0 -538
- package/thoughts/taras/plans/2026-02-20-memory-system.md +0 -882
- package/thoughts/taras/plans/2026-02-20-repos-knowledge.md +0 -806
- package/thoughts/taras/plans/2026-02-20-session-attach.md +0 -647
- package/thoughts/taras/plans/2026-02-20-worker-identity.md +0 -820
- package/thoughts/taras/plans/2026-02-25-feat-new-ui-visual-redesign-plan.md +0 -768
- package/thoughts/taras/plans/2026-03-04-fix-buildSystemPrompt-missing-fields.md +0 -77
- package/thoughts/taras/plans/2026-03-04-new-ui-missing-actions.md +0 -543
- package/thoughts/taras/plans/2026-03-06-one-time-scheduled-tasks.md +0 -373
- package/thoughts/taras/plans/2026-03-08-memory-self-improvement-enhancements.md +0 -512
- package/thoughts/taras/plans/2026-03-08-pi-mono-provider-implementation.md +0 -919
- package/thoughts/taras/plans/2026-03-09-templates-registry.md +0 -723
- package/thoughts/taras/plans/2026-03-10-task-working-directory.md +0 -371
- package/thoughts/taras/plans/2026-03-11-archil-per-agent-write-strategy.md +0 -621
- package/thoughts/taras/plans/2026-03-12-eliminate-inbox-route-to-tasks.md +0 -61
- package/thoughts/taras/plans/2026-03-12-slack-thread-followup-additive.md +0 -488
- package/thoughts/taras/plans/2026-03-13-slack-ai-improvements.md +0 -644
- package/thoughts/taras/plans/2026-03-16-route-wrapper-openapi.md +0 -636
- package/thoughts/taras/plans/2026-03-17-multi-api-config.md +0 -444
- package/thoughts/taras/plans/2026-03-18-agent-fs-integration.md +0 -591
- package/thoughts/taras/plans/2026-03-18-debug-db-explorer.md +0 -446
- package/thoughts/taras/plans/2026-03-18-workflow-redesign.md +0 -987
- package/thoughts/taras/plans/2026-03-19-compound-learnings.md +0 -403
- package/thoughts/taras/plans/2026-03-19-ticket-tracker-linear-integration.md +0 -860
- package/thoughts/taras/plans/2026-03-19-workflow-io-schemas-and-bugs.md +0 -899
- package/thoughts/taras/plans/2026-03-20-setup-cli-onboarding.md +0 -874
- package/thoughts/taras/plans/2026-03-20-workflow-structured-output-validation-workspace.md +0 -723
- 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/thoughts/taras/research/2026-02-19-agent-native-swarm-architecture.md +0 -390
- package/thoughts/taras/research/2026-02-19-swarm-gaps-implementation.md +0 -594
- package/thoughts/taras/research/2026-02-25-dashboard-ui-design-best-practices.md +0 -825
- package/thoughts/taras/research/2026-02-26-task-detail-page-redesign.md +0 -393
- package/thoughts/taras/research/2026-03-03-new-ui-missing-actions.md +0 -168
- package/thoughts/taras/research/2026-03-05-pi-mono-provider-research.md +0 -230
- package/thoughts/taras/research/2026-03-06-workflow-engine-design.md +0 -445
- package/thoughts/taras/research/2026-03-08-drive-loop-concept.md +0 -375
- package/thoughts/taras/research/2026-03-08-pi-mono-deep-dive.md +0 -869
- package/thoughts/taras/research/2026-03-09-templates-registry.md +0 -373
- package/thoughts/taras/research/2026-03-10-agent-working-directory.md +0 -223
- package/thoughts/taras/research/2026-03-10-configurable-event-prompts.md +0 -339
- package/thoughts/taras/research/2026-03-11-archil-production-setup.md +0 -181
- package/thoughts/taras/research/2026-03-11-archil-shared-disk-write-strategies.md +0 -437
- package/thoughts/taras/research/2026-03-13-slack-ai-features.md +0 -258
- package/thoughts/taras/research/2026-03-16-openapi-docs-generation.md +0 -335
- package/thoughts/taras/research/2026-03-16-route-wrapper-openapi.md +0 -670
- package/thoughts/taras/research/2026-03-16-slack-thread-followups-e2e.md +0 -54
- package/thoughts/taras/research/2026-03-18-agent-fs-integration.md +0 -558
- package/thoughts/taras/research/2026-03-18-linear-integration-finalization.md +0 -526
- package/thoughts/taras/research/2026-03-18-workflow-redesign.md +0 -797
- package/thoughts/taras/research/2026-03-19-workflow-node-io-schemas-and-bugs.md +0 -563
- package/thoughts/taras/research/2026-03-19-workflow-structured-output-validation-workspace.md +0 -486
- package/thoughts/taras/research/2026-03-20-prompt-template-registry.md +0 -469
- package/tsconfig.json +0 -37
|
@@ -1,797 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
date: 2026-03-18T14:30:00Z
|
|
3
|
-
topic: "Workflow Engine Redesign"
|
|
4
|
-
status: reviewed
|
|
5
|
-
researcher: taras+claude
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# Workflow Engine Redesign — Research
|
|
9
|
-
|
|
10
|
-
**Status**: v2 — incorporates Taras review feedback + Temporal research
|
|
11
|
-
**Prior art**: [Content-Agent Workflow Engine Mechanics](/Users/taras/Documents/code/content-agent/thoughts/shared/research/2026-03-18-workflow-engine-mechanics.md)
|
|
12
|
-
**Supersedes**: [2026-03-06-workflow-engine-design.md](./2026-03-06-workflow-engine-design.md) (original design that led to current implementation)
|
|
13
|
-
|
|
14
|
-
---
|
|
15
|
-
|
|
16
|
-
## 1. Motivation
|
|
17
|
-
|
|
18
|
-
The current workflow engine (shipped from the March 6 design) works but has architectural issues:
|
|
19
|
-
|
|
20
|
-
1. **Cyclical triggers** — Event-based triggers (task.created, task.completed) fire workflows that create tasks, creating infinite loops. Guard logic is fragile.
|
|
21
|
-
2. **No executor abstraction** — Node executors are scattered across files with no unified contract. Adding a new step type means touching engine code.
|
|
22
|
-
3. **No validation/quality gates** — No built-in way to validate step outputs, retry on failure, or gate workflow progression.
|
|
23
|
-
4. **No durability guarantees** — If the process crashes mid-workflow, recovery depends on detecting "stuck" runs after the fact.
|
|
24
|
-
5. **No versioning** — Workflow definitions are mutable JSON in the DB with no history.
|
|
25
|
-
6. **No workflow templates** — Can't share reusable workflow patterns (e.g., content-agent daily docs update, weekly blog post generator, QA weekly bug bash). Templates should be first-class: define once, instantiate many times with different inputs.
|
|
26
|
-
|
|
27
|
-
The goal: redesign the workflow engine to adopt the **executor registry pattern** from content-agent, add **checkpoint-based durability**, and clean up the execution model — while preserving DAG support and the multi-agent task delegation model.
|
|
28
|
-
|
|
29
|
-
---
|
|
30
|
-
|
|
31
|
-
## 2. Design Decisions (from Q&A with Taras)
|
|
32
|
-
|
|
33
|
-
| Decision | Choice | Rationale |
|
|
34
|
-
|----------|--------|-----------|
|
|
35
|
-
| **Core pattern** | Executor registry (class-based) + validation model | Clean `execute(input) → output`, quality gates, retry. Extensible via class inheritance. |
|
|
36
|
-
| **Execution model** | Directed graph with event-loop style executor | Idle until step ready → execute → checkpoint → next. Cycles allowed with max-iterations guard. See §4.8. |
|
|
37
|
-
| **Durability** | Checkpoint-based | Persist after each step, resume from last success. Aspire to deterministic replay later. |
|
|
38
|
-
| **Definitions** | Keep in database (JSON) | UI-friendly, API-driven. Add version history table. |
|
|
39
|
-
| **Triggers** | Simplify: webhook (HMAC) + schedule (via scheduleId ref). Manual always available (UI or agent MCP tool). | Event-based triggers deferred — too cyclical. |
|
|
40
|
-
| **Step types (initial)** | script, agent-task, raw-llm, vcs, property-match, code-match, send-message, validate | Start lean, extend via registry. |
|
|
41
|
-
| **LLM execution** | Agent tasks (primary) + raw AI SDK/OpenRouter (secondary) | Complex work = agent task. Simple structured ops = direct LLM call. |
|
|
42
|
-
| **Validation** | Executor `fn(output) → {pass, reasoning, confidence}` per-step | Reusable RetryPolicy type. Global final validation deferred. |
|
|
43
|
-
| **Context chain** | Node IDs (current style) | Machine-friendly, avoids naming conflicts in DAG. |
|
|
44
|
-
| **Schedules** | Triggers array on workflow, schedule ID reference to `scheduled_tasks` | SOT in schedules table, atomic removal. |
|
|
45
|
-
| **Schema format** | Nodes with `next` references, edges auto-generated for UI | Compact authoring, nodes are the key structure, edges purely visual. |
|
|
46
|
-
| **Migration** | Rip and replace | Clean break on `feat/workflow-redesign` branch. Nobody uses current workflows. |
|
|
47
|
-
| **Templates** | First-class workflow templates, instantiable with custom inputs | Enable reusable patterns across domains. |
|
|
48
|
-
|
|
49
|
-
**Future direction**: Agent-defined reusable "tools" as first-class citizens — think little lambdas that agents can define and then reference in workflow steps. Not in scope for this redesign but the class-based executor pattern should make this extensible.
|
|
50
|
-
|
|
51
|
-
---
|
|
52
|
-
|
|
53
|
-
## 3. Current System Inventory
|
|
54
|
-
|
|
55
|
-
### 3.1 What Exists Today
|
|
56
|
-
|
|
57
|
-
**Database tables** (from `003_workflows.sql`, `004_workflow_source.sql`):
|
|
58
|
-
- `workflows` — DAG definitions (JSON: `{nodes, edges}`)
|
|
59
|
-
- `workflow_runs` — Execution instances (status, context, error)
|
|
60
|
-
- `workflow_run_steps` — Per-node execution history
|
|
61
|
-
- `agent_tasks` extensions — `workflowRunId`, `workflowRunStepId` columns
|
|
62
|
-
|
|
63
|
-
**Engine** (`src/workflows/`):
|
|
64
|
-
- `engine.ts` — BFS DAG walker, `startWorkflowExecution()`, `walkGraph()`, `executeNode()`
|
|
65
|
-
- `resume.ts` — Event bus listeners for task.completed/failed/cancelled → resume waiting runs
|
|
66
|
-
- `recovery.ts` — Detect stuck runs (waiting + task already done), re-trigger resume
|
|
67
|
-
- `triggers.ts` — Match events → fire workflows
|
|
68
|
-
- `event-bus.ts` — Simple in-process EventEmitter
|
|
69
|
-
- `template.ts` — `{{path.to.value}}` interpolation
|
|
70
|
-
- `llm-provider.ts` — OpenRouter or Claude CLI for `llm-classify` node
|
|
71
|
-
- `index.ts` — `initWorkflows()` wires up event listeners
|
|
72
|
-
|
|
73
|
-
**Node executors** (`src/workflows/nodes/`):
|
|
74
|
-
- `create-task.ts` — Async: creates agent task, pauses workflow
|
|
75
|
-
- `delegate-to-agent.ts` — Async: assigns/offers task to agent
|
|
76
|
-
- `send-message.ts` — Instant: posts to channel
|
|
77
|
-
- `property-match.ts` — Instant: JSONPath condition evaluation
|
|
78
|
-
- `code-match.ts` — Instant: sandboxed JS evaluation
|
|
79
|
-
- `llm-classify.ts` — Instant (awaited): AI SDK structured output for classification
|
|
80
|
-
|
|
81
|
-
**Trigger nodes** (7 types): `trigger-new-task`, `trigger-task-completed`, `trigger-webhook`, `trigger-email`, `trigger-slack-message`, `trigger-github-event`, `trigger-gitlab-event`
|
|
82
|
-
|
|
83
|
-
**MCP tools** (`src/tools/workflows/`): 9 tools (CRUD + trigger + runs + retry)
|
|
84
|
-
**HTTP API** (`src/http/workflows.ts`): Full REST for workflows, runs, steps, retry, webhook trigger
|
|
85
|
-
**Tests**: ~2,500 lines across 9 test files + E2E script
|
|
86
|
-
|
|
87
|
-
### 3.2 What Gets Replaced
|
|
88
|
-
|
|
89
|
-
| Component | Action |
|
|
90
|
-
|-----------|--------|
|
|
91
|
-
| `src/workflows/engine.ts` | **Replace** — New engine with executor registry + checkpoint durability |
|
|
92
|
-
| `src/workflows/nodes/*` | **Replace** — Rewrite as executor classes registered in registry |
|
|
93
|
-
| `src/workflows/triggers.ts` | **Replace** — Simplify to webhook + schedule + manual |
|
|
94
|
-
| `src/workflows/resume.ts` | **Refactor** — Keep event-driven resume for async executors but integrate into new engine |
|
|
95
|
-
| `src/workflows/recovery.ts` | **Refactor** — Checkpoint-based recovery replaces stuck-run detection |
|
|
96
|
-
| `src/workflows/event-bus.ts` | **Keep** — Still needed for async task completion events |
|
|
97
|
-
| `src/workflows/template.ts` | **Keep** — Template interpolation is fine |
|
|
98
|
-
| `src/workflows/llm-provider.ts` | **Refactor** — Becomes part of `raw-llm` executor |
|
|
99
|
-
| DB schema | **Migrate** — New migration for schema changes |
|
|
100
|
-
| MCP tools | **Update** — Same tools, updated to new engine |
|
|
101
|
-
| HTTP API | **Update** — Same endpoints, updated to new engine |
|
|
102
|
-
| Tests | **Rewrite** — New tests for new engine |
|
|
103
|
-
|
|
104
|
-
---
|
|
105
|
-
|
|
106
|
-
## 4. Proposed Architecture
|
|
107
|
-
|
|
108
|
-
### 4.1 Executor Registry (Class-Based)
|
|
109
|
-
|
|
110
|
-
The core abstraction: every step is an **executor class** registered by type name. The class-based approach enables:
|
|
111
|
-
- Typed configs and outputs via Zod schemas
|
|
112
|
-
- Constructor-injected dependencies (DB, event bus, etc.)
|
|
113
|
-
- Extensibility through inheritance
|
|
114
|
-
- Future: agent-defined custom executors
|
|
115
|
-
|
|
116
|
-
```typescript
|
|
117
|
-
import { z, type ZodType } from "zod";
|
|
118
|
-
|
|
119
|
-
// ─── Shared Types ───────────────────────────────────────────
|
|
120
|
-
|
|
121
|
-
/** Reusable retry policy — used by validation, steps, etc. */
|
|
122
|
-
const RetryPolicySchema = z.object({
|
|
123
|
-
maxRetries: z.number().int().min(0).default(3),
|
|
124
|
-
strategy: z.enum(["exponential", "static", "linear"]).default("exponential"),
|
|
125
|
-
baseDelayMs: z.number().int().min(0).default(1000),
|
|
126
|
-
maxDelayMs: z.number().int().min(0).default(60000),
|
|
127
|
-
});
|
|
128
|
-
type RetryPolicy = z.infer<typeof RetryPolicySchema>;
|
|
129
|
-
|
|
130
|
-
/** Execution metadata passed to every executor */
|
|
131
|
-
const ExecutorMetaSchema = z.object({
|
|
132
|
-
runId: z.string().uuid(),
|
|
133
|
-
stepId: z.string().uuid(),
|
|
134
|
-
nodeId: z.string(),
|
|
135
|
-
workflowId: z.string().uuid(),
|
|
136
|
-
dryRun: z.boolean().default(false),
|
|
137
|
-
});
|
|
138
|
-
type ExecutorMeta = z.infer<typeof ExecutorMetaSchema>;
|
|
139
|
-
|
|
140
|
-
// ─── Executor Base Class ────────────────────────────────────
|
|
141
|
-
|
|
142
|
-
abstract class BaseExecutor<
|
|
143
|
-
TConfig extends ZodType = ZodType,
|
|
144
|
-
TOutput extends ZodType = ZodType,
|
|
145
|
-
> {
|
|
146
|
-
abstract readonly type: string;
|
|
147
|
-
abstract readonly mode: "instant" | "async";
|
|
148
|
-
abstract readonly configSchema: TConfig;
|
|
149
|
-
abstract readonly outputSchema: TOutput;
|
|
150
|
-
|
|
151
|
-
/** Optional retry policy — override per executor type */
|
|
152
|
-
readonly retryPolicy?: RetryPolicy;
|
|
153
|
-
|
|
154
|
-
constructor(protected readonly deps: ExecutorDependencies) {}
|
|
155
|
-
|
|
156
|
-
/** Validate config, execute, validate output — catches Zod errors at both boundaries */
|
|
157
|
-
async run(input: ExecutorInput): Promise<ExecutorResult<z.infer<TOutput>>> {
|
|
158
|
-
// Validate input config
|
|
159
|
-
const configResult = this.configSchema.safeParse(input.config);
|
|
160
|
-
if (!configResult.success) {
|
|
161
|
-
return { status: "failed", error: `Input validation failed: ${configResult.error.message}` };
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
const result = await this.execute(configResult.data, input.context, input.meta);
|
|
165
|
-
|
|
166
|
-
// Validate output
|
|
167
|
-
if (result.status === "success" && result.output !== undefined) {
|
|
168
|
-
const outputResult = this.outputSchema.safeParse(result.output);
|
|
169
|
-
if (!outputResult.success) {
|
|
170
|
-
return { status: "failed", error: `Output validation failed: ${outputResult.error.message}` };
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
return result;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/** Implement this in each executor */
|
|
177
|
-
protected abstract execute(
|
|
178
|
-
config: z.infer<TConfig>,
|
|
179
|
-
context: Readonly<Record<string, unknown>>,
|
|
180
|
-
meta: ExecutorMeta,
|
|
181
|
-
): Promise<ExecutorResult<z.infer<TOutput>>>;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
interface ExecutorDependencies {
|
|
185
|
-
db: typeof import("../be/db");
|
|
186
|
-
eventBus: WorkflowEventBus;
|
|
187
|
-
interpolate: (template: string, ctx: Record<string, unknown>) => string;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
interface ExecutorInput {
|
|
191
|
-
config: Record<string, unknown>;
|
|
192
|
-
context: Readonly<Record<string, unknown>>;
|
|
193
|
-
meta: ExecutorMeta;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
interface ExecutorResult<TOutput = unknown> {
|
|
197
|
-
status: "success" | "failed" | "skipped";
|
|
198
|
-
output?: TOutput;
|
|
199
|
-
nextPort?: string; // default: "default"
|
|
200
|
-
error?: string;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/** Async executors signal the engine to pause */
|
|
204
|
-
interface AsyncExecutorResult<TOutput = unknown> extends ExecutorResult<TOutput> {
|
|
205
|
-
async: true;
|
|
206
|
-
waitFor: string; // e.g., "task.completed"
|
|
207
|
-
correlationId: string; // e.g., taskId
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// ─── Registry ───────────────────────────────────────────────
|
|
211
|
-
|
|
212
|
-
class ExecutorRegistry {
|
|
213
|
-
private executors = new Map<string, BaseExecutor>();
|
|
214
|
-
|
|
215
|
-
register(executor: BaseExecutor): void {
|
|
216
|
-
this.executors.set(executor.type, executor);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
get(type: string): BaseExecutor {
|
|
220
|
-
const executor = this.executors.get(type);
|
|
221
|
-
if (!executor) throw new Error(`Unknown executor type: ${type}`);
|
|
222
|
-
return executor;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
has(type: string): boolean {
|
|
226
|
-
return this.executors.has(type);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
types(): string[] {
|
|
230
|
-
return [...this.executors.keys()];
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
**Example: notify executor implementation**
|
|
236
|
-
|
|
237
|
-
```typescript
|
|
238
|
-
class NotifyExecutor extends BaseExecutor<typeof NotifyConfigSchema, typeof NotifyOutputSchema> {
|
|
239
|
-
readonly type = "notify";
|
|
240
|
-
readonly mode = "instant" as const;
|
|
241
|
-
|
|
242
|
-
readonly configSchema = z.object({
|
|
243
|
-
channel: z.enum(["swarm", "slack", "email"]),
|
|
244
|
-
target: z.string().optional(),
|
|
245
|
-
template: z.string(),
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
readonly outputSchema = z.object({
|
|
249
|
-
sent: z.boolean(),
|
|
250
|
-
messageId: z.string().optional(),
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
protected async execute(
|
|
254
|
-
config: z.infer<typeof this.configSchema>,
|
|
255
|
-
context: Readonly<Record<string, unknown>>,
|
|
256
|
-
meta: ExecutorMeta,
|
|
257
|
-
): Promise<ExecutorResult<z.infer<typeof this.outputSchema>>> {
|
|
258
|
-
const message = this.deps.interpolate(config.template, context);
|
|
259
|
-
|
|
260
|
-
if (meta.dryRun) {
|
|
261
|
-
return { status: "success", output: { sent: false } };
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
switch (config.channel) {
|
|
265
|
-
case "swarm":
|
|
266
|
-
const msgId = this.deps.db.postMessage(config.target ?? "general", message);
|
|
267
|
-
return { status: "success", output: { sent: true, messageId: msgId } };
|
|
268
|
-
case "slack":
|
|
269
|
-
// ... slack posting logic
|
|
270
|
-
case "email":
|
|
271
|
-
// ... email sending logic
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
// Registration
|
|
277
|
-
registry.register(new NotifyExecutor(deps));
|
|
278
|
-
```
|
|
279
|
-
|
|
280
|
-
### 4.2 Initial Executor Set
|
|
281
|
-
|
|
282
|
-
| Executor | Type | Mode | What it does |
|
|
283
|
-
|----------|------|------|-------------|
|
|
284
|
-
| **script** | `script` | Instant | Run bash/TS(bun)/Python script. Config: `{runtime, script, args, timeout, cwd?, workerId? (future)}`. Scripts can be scoped to run on a specific worker machine via `workerId` (future — dispatch mechanism TBD). |
|
|
285
|
-
| **agent-task** | `agent-task` | **Async** | Create task for worker agent. Config: `{template, agentId?, tags?, priority?}`. Pauses workflow until task completes. |
|
|
286
|
-
| **raw-llm** | `raw-llm` | Instant | Direct AI SDK + OpenRouter call. Config: `{prompt, model?, schema?}`. Returns structured output via Zod. |
|
|
287
|
-
| **vcs** | `vcs` | Instant | Version control operations. Config: `{action: "create-issue" \| "create-pr" \| "comment", provider: "github" \| "gitlab", repo, ...}`. |
|
|
288
|
-
| **property-match** | `property-match` | Instant | JSONPath condition evaluation. Returns `nextPort: "true" \| "false"`. |
|
|
289
|
-
| **code-match** | `code-match` | Instant | Sandboxed JS evaluation. Returns `nextPort` based on result. |
|
|
290
|
-
| **notify** | `notify` | Instant | Multi-channel notification. Config: `{channel: "swarm" \| "slack" \| "email", target?, template}`. |
|
|
291
|
-
| **validate** | `validate` | Instant | Quality gate. Config: `{targetNodeId, prompt?, schema?}`. Returns `{pass, reasoning, confidence}`. |
|
|
292
|
-
|
|
293
|
-
### 4.3 Validation System
|
|
294
|
-
|
|
295
|
-
Validation is an executor with a special relationship to the engine.
|
|
296
|
-
|
|
297
|
-
```typescript
|
|
298
|
-
const ValidationResultSchema = z.object({
|
|
299
|
-
pass: z.boolean(),
|
|
300
|
-
reasoning: z.string(),
|
|
301
|
-
confidence: z.number().min(0).max(1),
|
|
302
|
-
});
|
|
303
|
-
type ValidationResult = z.infer<typeof ValidationResultSchema>;
|
|
304
|
-
|
|
305
|
-
const StepValidationConfigSchema = z.object({
|
|
306
|
-
/** Executor type for validation (default: "validate") */
|
|
307
|
-
executor: z.string().default("validate"),
|
|
308
|
-
/** Config passed to the validation executor */
|
|
309
|
-
config: z.record(z.unknown()),
|
|
310
|
-
/** If true, a failed validation halts the workflow */
|
|
311
|
-
mustPass: z.boolean().default(false),
|
|
312
|
-
/** Retry policy on validation failure */
|
|
313
|
-
retry: RetryPolicySchema.optional(),
|
|
314
|
-
});
|
|
315
|
-
type StepValidationConfig = z.infer<typeof StepValidationConfigSchema>;
|
|
316
|
-
```
|
|
317
|
-
|
|
318
|
-
Per-step: any node can have a `validation` field. After the step executes, the engine runs the validation executor. If `retry` is set, re-runs the step with `{previousOutput, validationResult}` injected into context, following the retry policy (exponential, static, or linear backoff).
|
|
319
|
-
|
|
320
|
-
### 4.4 Checkpoint-Based Durability
|
|
321
|
-
|
|
322
|
-
After each step completes:
|
|
323
|
-
1. Write step result to `workflow_run_steps`
|
|
324
|
-
2. Write accumulated context to `workflow_runs.context`
|
|
325
|
-
3. Both in a single SQLite transaction (atomic checkpoint)
|
|
326
|
-
|
|
327
|
-
Key features:
|
|
328
|
-
- **Step-level retry** — Each executor can declare a `RetryPolicy`. The engine handles retry scheduling.
|
|
329
|
-
- **Idempotency keys** — Steps get a deterministic key `${runId}:${nodeId}` to prevent double execution.
|
|
330
|
-
- **Context snapshots** — Context written atomically with step completion.
|
|
331
|
-
- **Recovery on startup** — Scan for `status: 'running'` runs, find last completed step, resume from successors.
|
|
332
|
-
- **All JSON parsed via Zod** — No raw `JSON.parse`. Every DB read validates through schemas.
|
|
333
|
-
|
|
334
|
-
```typescript
|
|
335
|
-
// Recovery — Zod-validated, type-safe
|
|
336
|
-
async function recoverIncompleteRuns(): Promise<void> {
|
|
337
|
-
const incompleteRuns = db.getWorkflowRunsByStatus(["running", "waiting"]);
|
|
338
|
-
|
|
339
|
-
for (const run of incompleteRuns) {
|
|
340
|
-
const completedStepNodeIds = db.getCompletedStepNodeIds(run.id);
|
|
341
|
-
const context = WorkflowContextSchema.parse(JSON.parse(run.context));
|
|
342
|
-
const def = WorkflowDefinitionSchema.parse(JSON.parse(run.workflow.definition));
|
|
343
|
-
|
|
344
|
-
const readyNodes = findReadyNodes(def, completedStepNodeIds);
|
|
345
|
-
|
|
346
|
-
if (readyNodes.length > 0) {
|
|
347
|
-
await walkGraph(def, run.id, context, readyNodes);
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
```
|
|
352
|
-
|
|
353
|
-
**Retry with backoff** — Applied per-step when execution fails:
|
|
354
|
-
|
|
355
|
-
```typescript
|
|
356
|
-
function calculateDelay(policy: RetryPolicy, attempt: number): number {
|
|
357
|
-
switch (policy.strategy) {
|
|
358
|
-
case "exponential": {
|
|
359
|
-
const delay = Math.min(policy.baseDelayMs * 2 ** (attempt - 1), policy.maxDelayMs);
|
|
360
|
-
return Math.floor(Math.random() * delay); // full jitter
|
|
361
|
-
}
|
|
362
|
-
case "linear":
|
|
363
|
-
return Math.min(policy.baseDelayMs * attempt, policy.maxDelayMs);
|
|
364
|
-
case "static":
|
|
365
|
-
return policy.baseDelayMs;
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
```
|
|
369
|
-
|
|
370
|
-
A retry poller runs on `setInterval` (e.g., every 5s), picks up failed steps past their `nextRetryAt`, and re-executes them. This same poller mechanism doubles as the executor for durable timer nodes (future).
|
|
371
|
-
|
|
372
|
-
### 4.5 Workflow Definition Schema
|
|
373
|
-
|
|
374
|
-
**Decision: Nodes with `next` references (Option B)**. Edges are auto-generated for UI rendering.
|
|
375
|
-
|
|
376
|
-
Nodes are the canonical structure. When `next` is absent → the workflow terminates at that node. No special "input" or "output" node types — any node can be an entry point (no incoming references) or a terminal (no `next`).
|
|
377
|
-
|
|
378
|
-
```typescript
|
|
379
|
-
const WorkflowNodeSchema = z.object({
|
|
380
|
-
id: z.string(),
|
|
381
|
-
type: z.string(), // executor type name
|
|
382
|
-
label: z.string().optional(),
|
|
383
|
-
config: z.record(z.unknown()),
|
|
384
|
-
/** Next node(s) — string for single, object for port-based branching */
|
|
385
|
-
next: z.union([
|
|
386
|
-
z.string(), // single next: "nodeId"
|
|
387
|
-
z.record(z.string(), z.string()), // port-based: { "true": "n3", "false": "n4" }
|
|
388
|
-
]).optional(),
|
|
389
|
-
/** Per-step validation config */
|
|
390
|
-
validation: StepValidationConfigSchema.optional(),
|
|
391
|
-
/** Per-step retry policy (overrides executor default) */
|
|
392
|
-
retry: RetryPolicySchema.optional(),
|
|
393
|
-
});
|
|
394
|
-
type WorkflowNode = z.infer<typeof WorkflowNodeSchema>;
|
|
395
|
-
|
|
396
|
-
const WorkflowDefinitionSchema = z.object({
|
|
397
|
-
nodes: z.array(WorkflowNodeSchema).min(1),
|
|
398
|
-
});
|
|
399
|
-
type WorkflowDefinition = z.infer<typeof WorkflowDefinitionSchema>;
|
|
400
|
-
|
|
401
|
-
/** Auto-generate edges from `next` references — for UI graph rendering */
|
|
402
|
-
function generateEdges(def: WorkflowDefinition): WorkflowEdge[] {
|
|
403
|
-
const edges: WorkflowEdge[] = [];
|
|
404
|
-
for (const node of def.nodes) {
|
|
405
|
-
if (!node.next) continue;
|
|
406
|
-
if (typeof node.next === "string") {
|
|
407
|
-
edges.push({ id: `${node.id}→${node.next}`, source: node.id, target: node.next, sourcePort: "default" });
|
|
408
|
-
} else {
|
|
409
|
-
for (const [port, targetId] of Object.entries(node.next)) {
|
|
410
|
-
edges.push({ id: `${node.id}→${targetId}:${port}`, source: node.id, target: targetId, sourcePort: port });
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
return edges;
|
|
415
|
-
}
|
|
416
|
-
```
|
|
417
|
-
|
|
418
|
-
**Consistency validation** — Before a workflow can run, validate:
|
|
419
|
-
1. All `next` references point to existing node IDs
|
|
420
|
-
2. Exactly **one entry node** (a node with no incoming `next` references). Multiple triggers can invoke the same workflow, but execution always starts at the single entry node. The trigger payload is injected into `context.trigger` before the entry node runs.
|
|
421
|
-
3. No orphaned nodes (every non-entry node must be reachable from the entry node)
|
|
422
|
-
4. Cycles are explicitly allowed — the graph is a **directed graph** (not strictly a DAG). The engine must enforce a `maxIterations` guard per node (default: 100) to prevent infinite loops. When a node exceeds `maxIterations`, the run fails with `error: "max iterations exceeded on node <id>"`
|
|
423
|
-
|
|
424
|
-
**Example: linear workflow**
|
|
425
|
-
```json
|
|
426
|
-
{
|
|
427
|
-
"nodes": [
|
|
428
|
-
{ "id": "extract", "type": "script", "config": { "runtime": "bash", "script": "scripts/extract.sh" }, "next": "check" },
|
|
429
|
-
{ "id": "check", "type": "property-match", "config": { "property": "{{extract.exitCode}}", "operator": "eq", "value": 0 }, "next": { "true": "process", "false": "notify-fail" } },
|
|
430
|
-
{ "id": "process", "type": "agent-task", "config": { "template": "Process: {{extract.output}}" } },
|
|
431
|
-
{ "id": "notify-fail", "type": "notify", "config": { "channel": "slack", "template": "Extract failed: {{extract.error}}" } }
|
|
432
|
-
]
|
|
433
|
-
}
|
|
434
|
-
```
|
|
435
|
-
|
|
436
|
-
> **Convention**: Nodes without a `next` field are **terminal nodes** — execution ends when they complete. In this example, both `process` and `notify-fail` are terminal nodes (the workflow ends at whichever branch is taken).
|
|
437
|
-
|
|
438
|
-
### 4.6 Trigger System
|
|
439
|
-
|
|
440
|
-
Workflows have a `triggers` array in their definition. Each trigger is typed.
|
|
441
|
-
|
|
442
|
-
> **Note**: All workflows can always be triggered manually (via `POST /api/workflows/:id/trigger` from the UI or an agent using the `trigger-workflow` MCP tool). The `triggers` array only declares **additional** trigger mechanisms beyond manual. If `triggers` is empty or absent, the workflow is manual-only.
|
|
443
|
-
|
|
444
|
-
```typescript
|
|
445
|
-
const TriggerConfigSchema = z.discriminatedUnion("type", [
|
|
446
|
-
z.object({
|
|
447
|
-
type: z.literal("webhook"),
|
|
448
|
-
hmacSecret: z.string().optional(), // HMAC-SHA256 signature verification
|
|
449
|
-
hmacHeader: z.string().default("X-Hub-Signature-256"),
|
|
450
|
-
}),
|
|
451
|
-
z.object({
|
|
452
|
-
type: z.literal("schedule"),
|
|
453
|
-
scheduleId: z.string().uuid(), // Reference to scheduled_tasks row
|
|
454
|
-
}),
|
|
455
|
-
]);
|
|
456
|
-
type TriggerConfig = z.infer<typeof TriggerConfigSchema>;
|
|
457
|
-
```
|
|
458
|
-
|
|
459
|
-
**Manual trigger** (always available): `POST /api/workflows/:id/trigger` — requires API auth (Bearer token). Callable from the dashboard UI or by agents via the `trigger-workflow` MCP tool. Payload passed as `context.trigger`.
|
|
460
|
-
|
|
461
|
-
**Webhook trigger**: `POST /api/webhooks/:workflowId` — optionally verified via HMAC-SHA256 signature. The workflow's `hmacSecret` is used to verify the `X-Hub-Signature-256` header (or custom header).
|
|
462
|
-
|
|
463
|
-
**Schedule trigger**: The workflow references a `scheduleId` from the existing `scheduled_tasks` table. The schedule's SOT is in `scheduled_tasks` — the workflow just links to it. Creating/removing a schedule atomically updates both the `scheduled_tasks` row and the workflow's `triggers` array.
|
|
464
|
-
|
|
465
|
-
Event-based triggers (task.created, github.*, slack.*) deferred to a later phase.
|
|
466
|
-
|
|
467
|
-
### 4.7 Version History
|
|
468
|
-
|
|
469
|
-
Simple JSON snapshot table:
|
|
470
|
-
|
|
471
|
-
```sql
|
|
472
|
-
CREATE TABLE IF NOT EXISTS workflow_versions (
|
|
473
|
-
id TEXT PRIMARY KEY,
|
|
474
|
-
workflowId TEXT NOT NULL REFERENCES workflows(id) ON DELETE CASCADE,
|
|
475
|
-
version INTEGER NOT NULL,
|
|
476
|
-
snapshot TEXT NOT NULL, -- JSON: WorkflowSnapshot (see below)
|
|
477
|
-
changedByAgentId TEXT,
|
|
478
|
-
createdAt TEXT DEFAULT (datetime('now')),
|
|
479
|
-
UNIQUE(workflowId, version)
|
|
480
|
-
);
|
|
481
|
-
```
|
|
482
|
-
|
|
483
|
-
**Snapshot contents** — the `snapshot` column stores the full workflow record as it existed before the update:
|
|
484
|
-
|
|
485
|
-
```typescript
|
|
486
|
-
const WorkflowSnapshotSchema = z.object({
|
|
487
|
-
name: z.string(),
|
|
488
|
-
description: z.string().optional(),
|
|
489
|
-
definition: WorkflowDefinitionSchema, // { nodes: [...] }
|
|
490
|
-
triggers: z.array(TriggerConfigSchema),
|
|
491
|
-
cooldown: CooldownConfigSchema.optional(),
|
|
492
|
-
input: z.record(InputValueSchema).optional(),
|
|
493
|
-
enabled: z.boolean(),
|
|
494
|
-
});
|
|
495
|
-
```
|
|
496
|
-
|
|
497
|
-
On every `UPDATE` to a workflow, insert a version record with the **previous** state. The current state is always in the `workflows` table.
|
|
498
|
-
|
|
499
|
-
### 4.8 Execution Model (Event-Loop Style)
|
|
500
|
-
|
|
501
|
-
The workflow executor runs **in-process in the API server**. It follows an event-loop approach:
|
|
502
|
-
|
|
503
|
-
```
|
|
504
|
-
┌─────────────────────────────────────────────────────────────┐
|
|
505
|
-
│ WORKFLOW EXECUTOR (in API process) │
|
|
506
|
-
│ │
|
|
507
|
-
│ State: IDLE │
|
|
508
|
-
│ ┌────────────────────────────────────────────────────────┐ │
|
|
509
|
-
│ │ Event arrives (trigger, task completion, retry timer) │ │
|
|
510
|
-
│ └──────────────┬─────────────────────────────────────────┘ │
|
|
511
|
-
│ ▼ │
|
|
512
|
-
│ ┌────────────────────────────────────────────────────────┐ │
|
|
513
|
-
│ │ Find ready steps (predecessors all completed) │ │
|
|
514
|
-
│ │ For each ready step: │ │
|
|
515
|
-
│ │ │ │
|
|
516
|
-
│ │ ┌─ Instant (script, llm, condition, notify, vcs) ──┐ │ │
|
|
517
|
-
│ │ │ Execute in-process │ │ │
|
|
518
|
-
│ │ │ Checkpoint result to DB (atomic transaction) │ │ │
|
|
519
|
-
│ │ │ Add output to context │ │ │
|
|
520
|
-
│ │ │ Find next ready steps → continue loop │ │ │
|
|
521
|
-
│ │ └────────────────────────────────────────────────────┘ │ │
|
|
522
|
-
│ │ │ │
|
|
523
|
-
│ │ ┌─ Async (agent-task) ───────────────────────────────┐ │ │
|
|
524
|
-
│ │ │ Create task, store correlation ID │ │ │
|
|
525
|
-
│ │ │ Mark step as "waiting", checkpoint to DB │ │ │
|
|
526
|
-
│ │ │ → Return to IDLE (don't block) │ │ │
|
|
527
|
-
│ │ │ → When task completes, event bus fires │ │ │
|
|
528
|
-
│ │ │ → Executor wakes up, resumes from next step │ │ │
|
|
529
|
-
│ │ └────────────────────────────────────────────────────┘ │ │
|
|
530
|
-
│ │ │ │
|
|
531
|
-
│ │ When no more ready steps → workflow complete │ │
|
|
532
|
-
│ └────────────────────────────────────────────────────────┘ │
|
|
533
|
-
│ │
|
|
534
|
-
│ Retry Poller (setInterval ~5s) │
|
|
535
|
-
│ ┌────────────────────────────────────────────────────────┐ │
|
|
536
|
-
│ │ Query: steps WHERE status='failed' AND nextRetryAt≤now│ │
|
|
537
|
-
│ │ Re-execute each via the same loop │ │
|
|
538
|
-
│ └────────────────────────────────────────────────────────┘ │
|
|
539
|
-
│ │
|
|
540
|
-
│ Startup Recovery │
|
|
541
|
-
│ ┌────────────────────────────────────────────────────────┐ │
|
|
542
|
-
│ │ Find runs with status='running' but no active steps │ │
|
|
543
|
-
│ │ Resume from last checkpoint │ │
|
|
544
|
-
│ └────────────────────────────────────────────────────────┘ │
|
|
545
|
-
└─────────────────────────────────────────────────────────────┘
|
|
546
|
-
```
|
|
547
|
-
|
|
548
|
-
> **Event-based execution**: Yes — the executor loop is purely event-driven. It only moves forward when an event arrives (trigger invocation, task completion callback, or retry poller tick). Between events, the executor is idle and holds no resources.
|
|
549
|
-
|
|
550
|
-
> **Execution timeout**: Each instant executor should declare a `timeoutMs` in its config (e.g., script executor defaults to 30s, raw-llm to 60s). The engine wraps `execute()` in a `Promise.race` with a timeout. If the timeout fires, the step is marked `failed` with `error: “timeout”` and follows the retry policy. For async executors (agent-task), the timeout is handled by the task system itself (task-level TTL), not the workflow engine.
|
|
551
|
-
|
|
552
|
-
**Key principle**: The executor is **never blocked**. Instant steps execute and immediately checkpoint. Async steps mark as waiting and return control. The event bus / retry poller re-enters the loop when work becomes available.
|
|
553
|
-
|
|
554
|
-
**Parallel step execution**: When the graph has nodes whose predecessors are all completed, they can execute concurrently via `Promise.all()`. This is a natural consequence of the "find ready steps" approach.
|
|
555
|
-
|
|
556
|
-
**Where things run**:
|
|
557
|
-
- The executor loop itself runs in the API server process
|
|
558
|
-
- Instant steps (script, LLM, conditions) run in-process or as subprocesses
|
|
559
|
-
- Async steps (agent-task) create tasks that run on worker machines
|
|
560
|
-
- Scripts with a `workerId` config are dispatched to that specific worker (future: via task with script payload)
|
|
561
|
-
|
|
562
|
-
### 4.9 Input Resolution
|
|
563
|
-
|
|
564
|
-
Workflow-level `input` supports variable resolution:
|
|
565
|
-
|
|
566
|
-
```typescript
|
|
567
|
-
const EnvVarSchema = z.string().regex(/^\$\{.+\}$/); // env var: ${MY_VAR}
|
|
568
|
-
const SecretRefSchema = z.string().regex(/^secret\..+$/); // swarm secret: secret.OPENAI_KEY
|
|
569
|
-
const LiteralSchema = z.string(); // literal value
|
|
570
|
-
|
|
571
|
-
const InputValueSchema = z.union([EnvVarSchema, SecretRefSchema, LiteralSchema]);
|
|
572
|
-
```
|
|
573
|
-
|
|
574
|
-
Resolution at workflow start:
|
|
575
|
-
- `${ENV_VAR}` → `process.env.ENV_VAR`
|
|
576
|
-
- `secret.NAME` → fetched from swarm secrets store
|
|
577
|
-
- Literal strings → passed through
|
|
578
|
-
|
|
579
|
-
### 4.10 Workflow Templates
|
|
580
|
-
|
|
581
|
-
Templates are workflow definitions designed for reuse:
|
|
582
|
-
|
|
583
|
-
```typescript
|
|
584
|
-
const WorkflowTemplateSchema = z.object({
|
|
585
|
-
id: z.string().uuid(),
|
|
586
|
-
name: z.string(),
|
|
587
|
-
description: z.string(),
|
|
588
|
-
category: z.string(), // e.g., "content", "qa", "devops"
|
|
589
|
-
/** Template variables — must be provided when instantiating */
|
|
590
|
-
variables: z.array(z.object({
|
|
591
|
-
name: z.string(),
|
|
592
|
-
description: z.string(),
|
|
593
|
-
type: z.enum(["string", "number", "boolean"]),
|
|
594
|
-
default: z.unknown().optional(),
|
|
595
|
-
required: z.boolean().default(true),
|
|
596
|
-
})),
|
|
597
|
-
/** The workflow definition with {{variable}} placeholders */
|
|
598
|
-
definition: WorkflowDefinitionSchema,
|
|
599
|
-
});
|
|
600
|
-
```
|
|
601
|
-
|
|
602
|
-
**Examples**:
|
|
603
|
-
- "Daily Blog Generator" template — variables: `{repo, branch, topic_source}`
|
|
604
|
-
- "Weekly QA Bug Bash" template — variables: `{test_suite, notify_channel}`
|
|
605
|
-
- "PR Review Pipeline" template — variables: `{repo, reviewers, auto_merge}`
|
|
606
|
-
|
|
607
|
-
Templates can be stored alongside regular workflows or in the existing templates registry.
|
|
608
|
-
|
|
609
|
-
### 4.11 Cooldown System
|
|
610
|
-
|
|
611
|
-
Simple pre-execution check:
|
|
612
|
-
|
|
613
|
-
```typescript
|
|
614
|
-
const CooldownConfigSchema = z.object({
|
|
615
|
-
hours: z.number().min(0),
|
|
616
|
-
});
|
|
617
|
-
|
|
618
|
-
/** Check before starting a workflow */
|
|
619
|
-
async function shouldSkipCooldown(workflowId: string, cooldown: CooldownConfig): Promise<boolean> {
|
|
620
|
-
const lastSuccess = db.getLastSuccessfulRun(workflowId);
|
|
621
|
-
if (!lastSuccess) return false;
|
|
622
|
-
const hoursSince = (Date.now() - new Date(lastSuccess.finishedAt).getTime()) / 3600000;
|
|
623
|
-
return hoursSince < cooldown.hours;
|
|
624
|
-
}
|
|
625
|
-
```
|
|
626
|
-
|
|
627
|
-
Defined at workflow level: `cooldown: { hours: 24 }`. Checked before execution starts. If within cooldown, the run is skipped with `status: "skipped"`, `error: "cooldown"`.
|
|
628
|
-
|
|
629
|
-
---
|
|
630
|
-
|
|
631
|
-
## 5. Comparison: Content-Agent vs Proposed Design
|
|
632
|
-
|
|
633
|
-
| Aspect | Content-Agent | Proposed Agent-Swarm |
|
|
634
|
-
|--------|--------------|---------------------|
|
|
635
|
-
| Format | YAML files | JSON in DB + version history |
|
|
636
|
-
| Execution | Linear sequential | Directed graph (event-loop BFS walk, supports loops + branches) |
|
|
637
|
-
| Executors | 4 (prompt, bash, git_pr, imgflip) | 8 initial (script, agent-task, raw-llm, vcs, property-match, code-match, notify, validate) |
|
|
638
|
-
| Registry | Hardcoded if/elif | Class-based `ExecutorRegistry` with typed config/output (Zod) |
|
|
639
|
-
| Context | `steps.<name>.output` | `ctx[nodeId]` |
|
|
640
|
-
| Async | None — runs to completion | Async executors pause/resume via event bus |
|
|
641
|
-
| Validation | Litmus test config block + retry | Validation executor with reusable `RetryPolicy` type |
|
|
642
|
-
| Durability | None (re-run from start) | Checkpoint-based (resume from last completed step) |
|
|
643
|
-
| Scheduling | Inline YAML, APScheduler | `triggers[]` with `scheduleId` ref to `scheduled_tasks` |
|
|
644
|
-
| Cooldowns | Built-in per-workflow | Pre-execution check, workflow-level config |
|
|
645
|
-
| Triggers | Cron + CLI + programmatic | Manual always available (UI/agent) + webhook (HMAC) + schedule |
|
|
646
|
-
| Templates | YAML files = implicit templates | First-class template system with variables |
|
|
647
|
-
| Input resolution | `${ENV_VAR}`, `data/file.json` | `${ENV_VAR}`, `secret.NAME` |
|
|
648
|
-
| Typing | Python, untyped | Full Zod schemas for config, output, context |
|
|
649
|
-
|
|
650
|
-
---
|
|
651
|
-
|
|
652
|
-
## 6. Resolved Questions
|
|
653
|
-
|
|
654
|
-
| # | Question | Resolution |
|
|
655
|
-
|---|----------|-----------|
|
|
656
|
-
| 6.1 | Schema format | **Nodes with `next`**, edges auto-generated for UI. Support loops + branches. No special input/output nodes. |
|
|
657
|
-
| 6.2 | Cooldowns | **Yes** — part of initial execution logic, pre-execution check. |
|
|
658
|
-
| 6.3 | Input resolution | **Yes** — `${ENV_VAR}` and `secret.NAME` supported. |
|
|
659
|
-
| 6.4 | Retry policy | **Reusable `RetryPolicy` type** with strategy: exponential/static/linear. Used by validation and step-level retry. |
|
|
660
|
-
| 6.5 | Parallel execution | **Yes** — natural consequence of "find ready steps" in directed graph. Nodes with all predecessors completed run via `Promise.all()`. |
|
|
661
|
-
| 6.6 | Global final validation | **Deferred** — not in initial scope. |
|
|
662
|
-
| 6.7 | Existing workflow migration | **Not needed** — nobody uses current workflows. Clean slate. |
|
|
663
|
-
|
|
664
|
-
---
|
|
665
|
-
|
|
666
|
-
## 7. Key Files to Change
|
|
667
|
-
|
|
668
|
-
```
|
|
669
|
-
src/workflows/
|
|
670
|
-
engine.ts → Full rewrite (executor registry + checkpoint + event-loop)
|
|
671
|
-
nodes/ → Delete, replaced by executors/
|
|
672
|
-
resume.ts → Refactor for new engine
|
|
673
|
-
recovery.ts → Replace with checkpoint-based recovery
|
|
674
|
-
triggers.ts → Simplify to webhook + schedule + manual
|
|
675
|
-
event-bus.ts → Keep
|
|
676
|
-
template.ts → Keep, add secret.NAME support
|
|
677
|
-
llm-provider.ts → Move into raw-llm executor
|
|
678
|
-
index.ts → Update initialization
|
|
679
|
-
|
|
680
|
-
src/be/
|
|
681
|
-
migrations/
|
|
682
|
-
NNN_workflow_redesign.sql → Schema changes (version history, retry columns, triggers)
|
|
683
|
-
db.ts → Update workflow query functions
|
|
684
|
-
|
|
685
|
-
src/tools/workflows/ → Update MCP tools for new types
|
|
686
|
-
src/http/workflows.ts → Update HTTP handlers, HMAC verification
|
|
687
|
-
src/types.ts → Update workflow type definitions (Zod schemas)
|
|
688
|
-
|
|
689
|
-
NEW files:
|
|
690
|
-
src/workflows/executors/ → Executor implementations
|
|
691
|
-
base.ts → BaseExecutor class
|
|
692
|
-
registry.ts → ExecutorRegistry
|
|
693
|
-
script.ts → Script executor (bash/ts/python)
|
|
694
|
-
agent-task.ts → Async task creation executor
|
|
695
|
-
raw-llm.ts → Direct LLM call executor
|
|
696
|
-
vcs.ts → Version control executor (GitHub/GitLab)
|
|
697
|
-
notify.ts → Multi-channel notification executor
|
|
698
|
-
validate.ts → Validation executor
|
|
699
|
-
src/workflows/checkpoint.ts → Checkpoint persistence logic
|
|
700
|
-
src/workflows/version.ts → Version history logic
|
|
701
|
-
src/workflows/retry-poller.ts → Retry poller (setInterval)
|
|
702
|
-
src/workflows/validation.ts → Validation engine (wraps validate executor)
|
|
703
|
-
src/workflows/templates.ts → Template instantiation logic
|
|
704
|
-
```
|
|
705
|
-
|
|
706
|
-
---
|
|
707
|
-
|
|
708
|
-
## 8. Temporal-Like Patterns — Research Findings
|
|
709
|
-
|
|
710
|
-
Background research investigated Temporal, Inngest, Restate, DBOS, Hatchet, @hazeljs/flow, and a SQLite-based proof-of-concept. Key findings:
|
|
711
|
-
|
|
712
|
-
### 8.1 We Don't Need Full Temporal
|
|
713
|
-
|
|
714
|
-
Our workflows are **declarative directed graphs**, not arbitrary code. The graph structure itself is the replay mechanism — we don't need deterministic replay. What we need is **step result memoization** (skip completed steps on resume).
|
|
715
|
-
|
|
716
|
-
### 8.2 Minimum Viable Durable Execution
|
|
717
|
-
|
|
718
|
-
The existing `walkGraph` (currently named `walkDag` — to be renamed) + `workflow_run_steps` is **80% there**. Four concrete additions:
|
|
719
|
-
|
|
720
|
-
1. **Context checkpoint after every step** (currently only done on `waiting` — also do it on each `completed`)
|
|
721
|
-
2. **Retry columns** on `workflow_run_steps`: `retryCount`, `maxRetries`, `nextRetryAt` + a poller
|
|
722
|
-
3. **Step memoization** in `walkGraph`: before executing, check if step already completed → skip and inject stored output
|
|
723
|
-
4. **Startup recovery**: extend to handle "process crashed mid-walk" (runs in `running` status with no active steps)
|
|
724
|
-
|
|
725
|
-
### 8.3 DB Schema Additions
|
|
726
|
-
|
|
727
|
-
```sql
|
|
728
|
-
ALTER TABLE workflow_run_steps ADD COLUMN retryCount INTEGER NOT NULL DEFAULT 0;
|
|
729
|
-
ALTER TABLE workflow_run_steps ADD COLUMN maxRetries INTEGER NOT NULL DEFAULT 3;
|
|
730
|
-
ALTER TABLE workflow_run_steps ADD COLUMN nextRetryAt TEXT; -- ISO timestamp
|
|
731
|
-
ALTER TABLE workflow_run_steps ADD COLUMN idempotencyKey TEXT; -- runId:nodeId
|
|
732
|
-
|
|
733
|
-
CREATE INDEX IF NOT EXISTS idx_wrs_retry
|
|
734
|
-
ON workflow_run_steps(status, nextRetryAt)
|
|
735
|
-
WHERE status = 'failed' AND nextRetryAt IS NOT NULL;
|
|
736
|
-
```
|
|
737
|
-
|
|
738
|
-
### 8.4 Signals, Timers, Child Workflows
|
|
739
|
-
|
|
740
|
-
- **Signals**: Already implemented via `status: 'waiting'` + event bus resume. No changes needed.
|
|
741
|
-
- **Timers**: A future `timer` node type that writes `nextRetryAt` and returns `mode: "async"`. The retry poller doubles as the timer executor.
|
|
742
|
-
- **Child workflows**: A node type that calls `startWorkflowExecution()` for another workflow and returns `mode: "async"`. Store `parentRunId`/`parentStepId` for the callback.
|
|
743
|
-
|
|
744
|
-
### 8.5 What We Explicitly Skip
|
|
745
|
-
|
|
746
|
-
- **Full deterministic replay** — graphs are declarative, structure = replay
|
|
747
|
-
- **Event sourcing** — step log is sufficient
|
|
748
|
-
- **Separate worker processes** — single-process SQLite
|
|
749
|
-
- **Distributed locking** — SQLite WAL mode, no contention
|
|
750
|
-
- **External dependencies** — no Temporal, Inngest, or other infra. Pure bun:sqlite.
|
|
751
|
-
|
|
752
|
-
### 8.6 Idempotency Gap
|
|
753
|
-
|
|
754
|
-
If a `create-task` step succeeds but the process crashes before recording the step as `completed`, retrying will create a duplicate task.
|
|
755
|
-
|
|
756
|
-
**Mitigation**: The engine enforces idempotency at the executor level. Before executing any step, the engine checks for an existing completed step with the same idempotency key (`${runId}:${nodeId}`). If found, it skips execution and injects the stored output into context. For async executors specifically, the executor itself must also check — e.g., the `agent-task` executor queries for an existing task with the matching idempotency key before creating a new one. This is a **mandatory contract** for all async executor implementations.
|
|
757
|
-
|
|
758
|
-
---
|
|
759
|
-
|
|
760
|
-
## 9. Next Steps
|
|
761
|
-
|
|
762
|
-
1. ~~Finalize schema format~~ → **Decided: nodes with `next`, edges auto-generated**
|
|
763
|
-
2. ~~Finalize open questions~~ → **All resolved (§6)**
|
|
764
|
-
3. **Write implementation plan** — phase by phase with verification at each step
|
|
765
|
-
4. **Implement** — rip-and-replace on this branch
|
|
766
|
-
|
|
767
|
-
---
|
|
768
|
-
|
|
769
|
-
## Review Errata
|
|
770
|
-
|
|
771
|
-
_Reviewed: 2026-03-18 by Claude (automated review + codebase verification)_
|
|
772
|
-
_File review: 2026-03-18 by Taras (14 inline comments processed)_
|
|
773
|
-
|
|
774
|
-
### Pending
|
|
775
|
-
|
|
776
|
-
- [ ] **Code References table** — Add a structured table of key `file:line` references for the implementer. To be added during plan creation.
|
|
777
|
-
|
|
778
|
-
### Resolved
|
|
779
|
-
|
|
780
|
-
- [x] **DAG → directed graph terminology** — Standardized to "directed graph" throughout. Renamed `walkDag` → `walkGraph`. Added `maxIterations` guard (default: 100) to §4.5.
|
|
781
|
-
- [x] **Missing Research Question/Summary** — Dismissed by Taras: motivation section is sufficient.
|
|
782
|
-
- [x] **Unhandled ZodError in `BaseExecutor.run()`** — Updated pseudocode to use `safeParse()` for both input config and output validation.
|
|
783
|
-
- [x] **Single entry node constraint** — Added to §4.5: exactly one entry node, trigger payload in `context.trigger`.
|
|
784
|
-
- [x] **script executor `workerId`** — Marked as "(future)" in config and description.
|
|
785
|
-
- [x] **InputValueSchema invalid Zod** — Fixed to use `z.string().regex()`.
|
|
786
|
-
- [x] **Idempotency gap** — Resolved in §8.6 with engine-level memoization + mandatory async executor idempotency key check.
|
|
787
|
-
- [x] **Webhook HMAC upgrade** — Dismissed: nobody uses current workflows, clean slate.
|
|
788
|
-
- [x] **Manual trigger redundancy** — Removed from `TriggerConfigSchema`. All workflows always manually triggerable (UI or agent MCP tool).
|
|
789
|
-
- [x] **Executor implementation example** — Added notify executor example after §4.1.
|
|
790
|
-
- [x] **"No next = end" clarification** — Added convention note after linear workflow example.
|
|
791
|
-
- [x] **Snapshot type** — Added `WorkflowSnapshotSchema` type definition to §4.7.
|
|
792
|
-
- [x] **Execution timeout** — Added to §4.8: `Promise.race` for instant executors, task-level TTL for async.
|
|
793
|
-
- [x] **Event-based executor loop** — Confirmed in §4.8: purely event-driven, idle between events.
|
|
794
|
-
- [x] Frontmatter field `author` → `researcher` — auto-fixed
|
|
795
|
-
- [x] Test count corrected to ~2,500 lines across 9 test files — auto-fixed
|
|
796
|
-
- [x] All prior art, tables, files, tools verified against codebase
|
|
797
|
-
- [x] Context checkpoint gap claim (§8.2) verified accurate
|