@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
package/src/be/db.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Database } from "bun:sqlite";
|
|
2
|
+
import { configureDbResolver } from "../prompts/resolver";
|
|
2
3
|
import type {
|
|
3
4
|
ActiveSession,
|
|
4
5
|
Agent,
|
|
@@ -7,6 +8,7 @@ import type {
|
|
|
7
8
|
AgentMemory,
|
|
8
9
|
AgentMemoryScope,
|
|
9
10
|
AgentMemorySource,
|
|
11
|
+
AgentSkill,
|
|
10
12
|
AgentStatus,
|
|
11
13
|
AgentTask,
|
|
12
14
|
AgentTaskSource,
|
|
@@ -24,11 +26,17 @@ import type {
|
|
|
24
26
|
InboxMessage,
|
|
25
27
|
InboxMessageStatus,
|
|
26
28
|
InputValue,
|
|
29
|
+
PromptTemplate,
|
|
30
|
+
PromptTemplateHistory,
|
|
27
31
|
ScheduledTask,
|
|
28
32
|
Service,
|
|
29
33
|
ServiceStatus,
|
|
30
34
|
SessionCost,
|
|
31
35
|
SessionLog,
|
|
36
|
+
Skill,
|
|
37
|
+
SkillScope,
|
|
38
|
+
SkillType,
|
|
39
|
+
SkillWithInstallInfo,
|
|
32
40
|
SwarmConfig,
|
|
33
41
|
SwarmRepo,
|
|
34
42
|
TriggerConfig,
|
|
@@ -44,6 +52,7 @@ import type {
|
|
|
44
52
|
WorkflowVersion,
|
|
45
53
|
} from "../types";
|
|
46
54
|
import { runMigrations } from "./migrations/runner";
|
|
55
|
+
import { seedDefaultTemplates } from "./seed";
|
|
47
56
|
|
|
48
57
|
let db: Database | null = null;
|
|
49
58
|
|
|
@@ -55,9 +64,7 @@ export function initDb(dbPath = "./agent-swarm-db.sqlite"): Database {
|
|
|
55
64
|
db = new Database(dbPath, { create: true });
|
|
56
65
|
console.log(`Database initialized at ${dbPath}`);
|
|
57
66
|
|
|
58
|
-
// Capture in local const for TypeScript (db is guaranteed non-null here)
|
|
59
67
|
const database = db;
|
|
60
|
-
|
|
61
68
|
database.run("PRAGMA journal_mode = WAL;");
|
|
62
69
|
database.run("PRAGMA foreign_keys = ON;");
|
|
63
70
|
|
|
@@ -187,6 +194,12 @@ export function initDb(dbPath = "./agent-swarm-db.sqlite"): Database {
|
|
|
187
194
|
// Backfill: Seed v1 for existing agents that don't have any context versions yet
|
|
188
195
|
seedContextVersions();
|
|
189
196
|
|
|
197
|
+
// Inject DB resolver into the prompt template resolver (DI to avoid worker/API boundary violation)
|
|
198
|
+
configureDbResolver(resolvePromptTemplate);
|
|
199
|
+
|
|
200
|
+
// Seed default prompt templates from the in-memory code registry
|
|
201
|
+
seedDefaultTemplates();
|
|
202
|
+
|
|
190
203
|
return db;
|
|
191
204
|
}
|
|
192
205
|
|
|
@@ -2153,6 +2166,16 @@ export function getUnassignedTasksCount(): number {
|
|
|
2153
2166
|
return result?.count ?? 0;
|
|
2154
2167
|
}
|
|
2155
2168
|
|
|
2169
|
+
/** Get unassigned task IDs, ordered by priority (highest first) then creation time */
|
|
2170
|
+
export function getUnassignedTaskIds(limit = 10): string[] {
|
|
2171
|
+
const rows = getDb()
|
|
2172
|
+
.prepare<{ id: string }, [number]>(
|
|
2173
|
+
"SELECT id FROM agent_tasks WHERE status = 'unassigned' ORDER BY priority DESC, createdAt ASC LIMIT ?",
|
|
2174
|
+
)
|
|
2175
|
+
.all(limit);
|
|
2176
|
+
return rows.map((r) => r.id);
|
|
2177
|
+
}
|
|
2178
|
+
|
|
2156
2179
|
// ============================================================================
|
|
2157
2180
|
// Dependency Checking
|
|
2158
2181
|
// ============================================================================
|
|
@@ -5430,6 +5453,7 @@ export function insertActiveSession(session: {
|
|
|
5430
5453
|
triggerType: string;
|
|
5431
5454
|
inboxMessageId?: string;
|
|
5432
5455
|
taskDescription?: string;
|
|
5456
|
+
runnerSessionId?: string;
|
|
5433
5457
|
}): ActiveSession {
|
|
5434
5458
|
const id = crypto.randomUUID();
|
|
5435
5459
|
const now = new Date().toISOString();
|
|
@@ -5437,10 +5461,20 @@ export function insertActiveSession(session: {
|
|
|
5437
5461
|
const row = getDb()
|
|
5438
5462
|
.prepare<
|
|
5439
5463
|
ActiveSession,
|
|
5440
|
-
[
|
|
5464
|
+
[
|
|
5465
|
+
string,
|
|
5466
|
+
string,
|
|
5467
|
+
string | null,
|
|
5468
|
+
string,
|
|
5469
|
+
string | null,
|
|
5470
|
+
string | null,
|
|
5471
|
+
string | null,
|
|
5472
|
+
string,
|
|
5473
|
+
string,
|
|
5474
|
+
]
|
|
5441
5475
|
>(
|
|
5442
|
-
`INSERT INTO active_sessions (id, agentId, taskId, triggerType, inboxMessageId, taskDescription, startedAt, lastHeartbeatAt)
|
|
5443
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
5476
|
+
`INSERT INTO active_sessions (id, agentId, taskId, triggerType, inboxMessageId, taskDescription, runnerSessionId, startedAt, lastHeartbeatAt)
|
|
5477
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
5444
5478
|
RETURNING *`,
|
|
5445
5479
|
)
|
|
5446
5480
|
.get(
|
|
@@ -5450,6 +5484,7 @@ export function insertActiveSession(session: {
|
|
|
5450
5484
|
session.triggerType,
|
|
5451
5485
|
session.inboxMessageId ?? null,
|
|
5452
5486
|
session.taskDescription ?? null,
|
|
5487
|
+
session.runnerSessionId ?? null,
|
|
5453
5488
|
now,
|
|
5454
5489
|
now,
|
|
5455
5490
|
);
|
|
@@ -5502,6 +5537,30 @@ export function cleanupAgentSessions(agentId: string): number {
|
|
|
5502
5537
|
return result.changes;
|
|
5503
5538
|
}
|
|
5504
5539
|
|
|
5540
|
+
/** Update providerSessionId on an active session identified by taskId */
|
|
5541
|
+
export function updateActiveSessionProviderSessionId(
|
|
5542
|
+
taskId: string,
|
|
5543
|
+
providerSessionId: string,
|
|
5544
|
+
): boolean {
|
|
5545
|
+
const result = getDb()
|
|
5546
|
+
.prepare("UPDATE active_sessions SET providerSessionId = ? WHERE taskId = ?")
|
|
5547
|
+
.run(providerSessionId, taskId);
|
|
5548
|
+
return result.changes > 0;
|
|
5549
|
+
}
|
|
5550
|
+
|
|
5551
|
+
/**
|
|
5552
|
+
* Reassociate session logs from a runner session to a real task ID.
|
|
5553
|
+
* Used when a pool task is claimed — logs were stored under a random UUID,
|
|
5554
|
+
* this updates them to use the real task ID.
|
|
5555
|
+
* Idempotent — safe to call multiple times.
|
|
5556
|
+
*/
|
|
5557
|
+
export function reassociateSessionLogs(runnerSessionId: string, realTaskId: string): number {
|
|
5558
|
+
const result = getDb()
|
|
5559
|
+
.prepare("UPDATE session_logs SET taskId = ? WHERE sessionId = ? AND taskId != ?")
|
|
5560
|
+
.run(realTaskId, runnerSessionId, realTaskId);
|
|
5561
|
+
return result.changes;
|
|
5562
|
+
}
|
|
5563
|
+
|
|
5505
5564
|
// ============================================================================
|
|
5506
5565
|
// Heartbeat / Triage Query Functions
|
|
5507
5566
|
// ============================================================================
|
|
@@ -5571,6 +5630,8 @@ type WorkflowRow = {
|
|
|
5571
5630
|
cooldown: string | null;
|
|
5572
5631
|
input: string | null;
|
|
5573
5632
|
triggerSchema: string | null;
|
|
5633
|
+
dir: string | null;
|
|
5634
|
+
vcs_repo: string | null;
|
|
5574
5635
|
createdByAgentId: string | null;
|
|
5575
5636
|
createdAt: string;
|
|
5576
5637
|
lastUpdatedAt: string;
|
|
@@ -5589,6 +5650,8 @@ function rowToWorkflow(row: WorkflowRow): Workflow {
|
|
|
5589
5650
|
triggerSchema: row.triggerSchema
|
|
5590
5651
|
? (JSON.parse(row.triggerSchema) as Record<string, unknown>)
|
|
5591
5652
|
: undefined,
|
|
5653
|
+
dir: row.dir ?? undefined,
|
|
5654
|
+
vcsRepo: row.vcs_repo ?? undefined,
|
|
5592
5655
|
createdByAgentId: row.createdByAgentId ?? undefined,
|
|
5593
5656
|
createdAt: row.createdAt,
|
|
5594
5657
|
lastUpdatedAt: row.lastUpdatedAt,
|
|
@@ -5603,6 +5666,8 @@ export function createWorkflow(data: {
|
|
|
5603
5666
|
cooldown?: CooldownConfig;
|
|
5604
5667
|
input?: Record<string, InputValue>;
|
|
5605
5668
|
triggerSchema?: Record<string, unknown>;
|
|
5669
|
+
dir?: string;
|
|
5670
|
+
vcsRepo?: string;
|
|
5606
5671
|
createdByAgentId?: string;
|
|
5607
5672
|
}): Workflow {
|
|
5608
5673
|
const id = crypto.randomUUID();
|
|
@@ -5619,10 +5684,12 @@ export function createWorkflow(data: {
|
|
|
5619
5684
|
string | null,
|
|
5620
5685
|
string | null,
|
|
5621
5686
|
string | null,
|
|
5687
|
+
string | null,
|
|
5688
|
+
string | null,
|
|
5622
5689
|
]
|
|
5623
5690
|
>(
|
|
5624
|
-
`INSERT INTO workflows (id, name, description, definition, triggers, cooldown, input, triggerSchema, createdByAgentId)
|
|
5625
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING *`,
|
|
5691
|
+
`INSERT INTO workflows (id, name, description, definition, triggers, cooldown, input, triggerSchema, dir, vcs_repo, createdByAgentId)
|
|
5692
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING *`,
|
|
5626
5693
|
)
|
|
5627
5694
|
.get(
|
|
5628
5695
|
id,
|
|
@@ -5633,6 +5700,8 @@ export function createWorkflow(data: {
|
|
|
5633
5700
|
data.cooldown ? JSON.stringify(data.cooldown) : null,
|
|
5634
5701
|
data.input ? JSON.stringify(data.input) : null,
|
|
5635
5702
|
data.triggerSchema ? JSON.stringify(data.triggerSchema) : null,
|
|
5703
|
+
data.dir ?? null,
|
|
5704
|
+
data.vcsRepo ?? null,
|
|
5636
5705
|
data.createdByAgentId ?? null,
|
|
5637
5706
|
);
|
|
5638
5707
|
if (!row) throw new Error("Failed to create workflow");
|
|
@@ -5671,6 +5740,8 @@ export function updateWorkflow(
|
|
|
5671
5740
|
cooldown?: CooldownConfig | null;
|
|
5672
5741
|
input?: Record<string, InputValue> | null;
|
|
5673
5742
|
triggerSchema?: Record<string, unknown> | null;
|
|
5743
|
+
dir?: string | null;
|
|
5744
|
+
vcsRepo?: string | null;
|
|
5674
5745
|
},
|
|
5675
5746
|
): Workflow | null {
|
|
5676
5747
|
const updates: string[] = [];
|
|
@@ -5707,6 +5778,14 @@ export function updateWorkflow(
|
|
|
5707
5778
|
updates.push("triggerSchema = ?");
|
|
5708
5779
|
params.push(data.triggerSchema ? JSON.stringify(data.triggerSchema) : null);
|
|
5709
5780
|
}
|
|
5781
|
+
if (data.dir !== undefined) {
|
|
5782
|
+
updates.push("dir = ?");
|
|
5783
|
+
params.push(data.dir ?? null);
|
|
5784
|
+
}
|
|
5785
|
+
if (data.vcsRepo !== undefined) {
|
|
5786
|
+
updates.push("vcs_repo = ?");
|
|
5787
|
+
params.push(data.vcsRepo ?? null);
|
|
5788
|
+
}
|
|
5710
5789
|
if (updates.length === 0) return getWorkflow(id);
|
|
5711
5790
|
updates.push("lastUpdatedAt = ?");
|
|
5712
5791
|
params.push(new Date().toISOString());
|
|
@@ -5739,6 +5818,22 @@ export function deleteWorkflow(id: string): boolean {
|
|
|
5739
5818
|
return result.changes > 0;
|
|
5740
5819
|
}
|
|
5741
5820
|
|
|
5821
|
+
/**
|
|
5822
|
+
* Find enabled workflows that have a schedule trigger matching the given scheduleId.
|
|
5823
|
+
* Uses SQLite JSON functions to query into the triggers JSON array.
|
|
5824
|
+
*/
|
|
5825
|
+
export function getWorkflowsByScheduleId(scheduleId: string): Workflow[] {
|
|
5826
|
+
const rows = getDb()
|
|
5827
|
+
.prepare<WorkflowRow, [string]>(
|
|
5828
|
+
`SELECT w.* FROM workflows w, json_each(w.triggers) AS t
|
|
5829
|
+
WHERE w.enabled = 1
|
|
5830
|
+
AND json_extract(t.value, '$.type') = 'schedule'
|
|
5831
|
+
AND json_extract(t.value, '$.scheduleId') = ?`,
|
|
5832
|
+
)
|
|
5833
|
+
.all(scheduleId);
|
|
5834
|
+
return rows.map(rowToWorkflow);
|
|
5835
|
+
}
|
|
5836
|
+
|
|
5742
5837
|
// ============================================================================
|
|
5743
5838
|
// Workflow Run CRUD
|
|
5744
5839
|
// ============================================================================
|
|
@@ -6138,3 +6233,1134 @@ export function getWorkflowVersion(workflowId: string, version: number): Workflo
|
|
|
6138
6233
|
.get(workflowId, version);
|
|
6139
6234
|
return row ? rowToWorkflowVersion(row) : null;
|
|
6140
6235
|
}
|
|
6236
|
+
|
|
6237
|
+
// ============================================================================
|
|
6238
|
+
// Prompt Template Operations
|
|
6239
|
+
// ============================================================================
|
|
6240
|
+
|
|
6241
|
+
type PromptTemplateRow = {
|
|
6242
|
+
id: string;
|
|
6243
|
+
eventType: string;
|
|
6244
|
+
scope: string;
|
|
6245
|
+
scopeId: string | null;
|
|
6246
|
+
state: string;
|
|
6247
|
+
body: string;
|
|
6248
|
+
isDefault: number; // SQLite boolean
|
|
6249
|
+
version: number;
|
|
6250
|
+
createdBy: string | null;
|
|
6251
|
+
createdAt: string;
|
|
6252
|
+
updatedAt: string;
|
|
6253
|
+
};
|
|
6254
|
+
|
|
6255
|
+
type PromptTemplateHistoryRow = {
|
|
6256
|
+
id: string;
|
|
6257
|
+
templateId: string;
|
|
6258
|
+
version: number;
|
|
6259
|
+
body: string;
|
|
6260
|
+
state: string;
|
|
6261
|
+
changedBy: string | null;
|
|
6262
|
+
changedAt: string;
|
|
6263
|
+
changeReason: string | null;
|
|
6264
|
+
};
|
|
6265
|
+
|
|
6266
|
+
function rowToPromptTemplate(row: PromptTemplateRow): PromptTemplate {
|
|
6267
|
+
return {
|
|
6268
|
+
id: row.id,
|
|
6269
|
+
eventType: row.eventType,
|
|
6270
|
+
scope: row.scope as "global" | "agent" | "repo",
|
|
6271
|
+
scopeId: row.scopeId ?? null,
|
|
6272
|
+
state: row.state as "enabled" | "default_prompt_fallback" | "skip_event",
|
|
6273
|
+
body: row.body,
|
|
6274
|
+
isDefault: row.isDefault === 1,
|
|
6275
|
+
version: row.version,
|
|
6276
|
+
createdBy: row.createdBy ?? null,
|
|
6277
|
+
createdAt: row.createdAt,
|
|
6278
|
+
updatedAt: row.updatedAt,
|
|
6279
|
+
};
|
|
6280
|
+
}
|
|
6281
|
+
|
|
6282
|
+
function rowToPromptTemplateHistory(row: PromptTemplateHistoryRow): PromptTemplateHistory {
|
|
6283
|
+
return {
|
|
6284
|
+
id: row.id,
|
|
6285
|
+
templateId: row.templateId,
|
|
6286
|
+
version: row.version,
|
|
6287
|
+
body: row.body,
|
|
6288
|
+
state: row.state,
|
|
6289
|
+
changedBy: row.changedBy ?? null,
|
|
6290
|
+
changedAt: row.changedAt,
|
|
6291
|
+
changeReason: row.changeReason ?? null,
|
|
6292
|
+
};
|
|
6293
|
+
}
|
|
6294
|
+
|
|
6295
|
+
/**
|
|
6296
|
+
* List prompt templates with optional filters.
|
|
6297
|
+
*/
|
|
6298
|
+
export function getPromptTemplates(filters?: {
|
|
6299
|
+
eventType?: string;
|
|
6300
|
+
scope?: string;
|
|
6301
|
+
scopeId?: string;
|
|
6302
|
+
isDefault?: boolean;
|
|
6303
|
+
}): PromptTemplate[] {
|
|
6304
|
+
const conditions: string[] = [];
|
|
6305
|
+
const params: (string | number)[] = [];
|
|
6306
|
+
|
|
6307
|
+
if (filters?.eventType) {
|
|
6308
|
+
conditions.push("eventType = ?");
|
|
6309
|
+
params.push(filters.eventType);
|
|
6310
|
+
}
|
|
6311
|
+
if (filters?.scope) {
|
|
6312
|
+
conditions.push("scope = ?");
|
|
6313
|
+
params.push(filters.scope);
|
|
6314
|
+
}
|
|
6315
|
+
if (filters?.scopeId) {
|
|
6316
|
+
conditions.push("scopeId = ?");
|
|
6317
|
+
params.push(filters.scopeId);
|
|
6318
|
+
}
|
|
6319
|
+
if (filters?.isDefault !== undefined) {
|
|
6320
|
+
conditions.push("isDefault = ?");
|
|
6321
|
+
params.push(filters.isDefault ? 1 : 0);
|
|
6322
|
+
}
|
|
6323
|
+
|
|
6324
|
+
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
6325
|
+
const query = `SELECT * FROM prompt_templates ${whereClause} ORDER BY eventType ASC`;
|
|
6326
|
+
|
|
6327
|
+
return getDb()
|
|
6328
|
+
.prepare<PromptTemplateRow, (string | number)[]>(query)
|
|
6329
|
+
.all(...params)
|
|
6330
|
+
.map(rowToPromptTemplate);
|
|
6331
|
+
}
|
|
6332
|
+
|
|
6333
|
+
/**
|
|
6334
|
+
* Get a single prompt template by ID.
|
|
6335
|
+
*/
|
|
6336
|
+
export function getPromptTemplateById(id: string): PromptTemplate | null {
|
|
6337
|
+
const row = getDb()
|
|
6338
|
+
.prepare<PromptTemplateRow, [string]>("SELECT * FROM prompt_templates WHERE id = ?")
|
|
6339
|
+
.get(id);
|
|
6340
|
+
return row ? rowToPromptTemplate(row) : null;
|
|
6341
|
+
}
|
|
6342
|
+
|
|
6343
|
+
/**
|
|
6344
|
+
* Upsert a prompt template. Inserts or updates by (eventType, scope, scopeId) unique constraint.
|
|
6345
|
+
* Creates a history entry on both insert and update.
|
|
6346
|
+
*/
|
|
6347
|
+
export function upsertPromptTemplate(data: {
|
|
6348
|
+
eventType: string;
|
|
6349
|
+
scope: "global" | "agent" | "repo";
|
|
6350
|
+
scopeId?: string | null;
|
|
6351
|
+
state?: "enabled" | "default_prompt_fallback" | "skip_event";
|
|
6352
|
+
body: string;
|
|
6353
|
+
createdBy?: string | null;
|
|
6354
|
+
changedBy?: string | null;
|
|
6355
|
+
changeReason?: string | null;
|
|
6356
|
+
isDefault?: boolean;
|
|
6357
|
+
}): PromptTemplate {
|
|
6358
|
+
const now = new Date().toISOString();
|
|
6359
|
+
const scopeId = data.scope === "global" ? null : (data.scopeId ?? null);
|
|
6360
|
+
const state = data.state ?? "enabled";
|
|
6361
|
+
const createdBy = data.createdBy ?? data.changedBy ?? null;
|
|
6362
|
+
const changedBy = data.changedBy ?? data.createdBy ?? null;
|
|
6363
|
+
const changeReason = data.changeReason ?? null;
|
|
6364
|
+
|
|
6365
|
+
// Manual check for existing entry because SQLite's UNIQUE constraint
|
|
6366
|
+
// treats NULL != NULL, so ON CONFLICT never fires when scopeId is NULL (global scope).
|
|
6367
|
+
const existing =
|
|
6368
|
+
scopeId === null
|
|
6369
|
+
? getDb()
|
|
6370
|
+
.prepare<PromptTemplateRow, [string, string]>(
|
|
6371
|
+
"SELECT * FROM prompt_templates WHERE eventType = ? AND scope = ? AND scopeId IS NULL",
|
|
6372
|
+
)
|
|
6373
|
+
.get(data.eventType, data.scope)
|
|
6374
|
+
: getDb()
|
|
6375
|
+
.prepare<PromptTemplateRow, [string, string, string]>(
|
|
6376
|
+
"SELECT * FROM prompt_templates WHERE eventType = ? AND scope = ? AND scopeId = ?",
|
|
6377
|
+
)
|
|
6378
|
+
.get(data.eventType, data.scope, scopeId);
|
|
6379
|
+
|
|
6380
|
+
let row: PromptTemplateRow | null;
|
|
6381
|
+
|
|
6382
|
+
if (existing) {
|
|
6383
|
+
// If upserting at global scope and existing record has isDefault=true, flip it to false
|
|
6384
|
+
const newIsDefault =
|
|
6385
|
+
data.scope === "global" && existing.isDefault === 1 ? 0 : existing.isDefault;
|
|
6386
|
+
const newVersion = existing.version + 1;
|
|
6387
|
+
|
|
6388
|
+
row = getDb()
|
|
6389
|
+
.prepare<PromptTemplateRow, [string, string, number, number, string, string]>(
|
|
6390
|
+
`UPDATE prompt_templates SET body = ?, state = ?, isDefault = ?, version = ?, updatedAt = ?
|
|
6391
|
+
WHERE id = ? RETURNING *`,
|
|
6392
|
+
)
|
|
6393
|
+
.get(data.body, state, newIsDefault, newVersion, now, existing.id);
|
|
6394
|
+
|
|
6395
|
+
// Create history entry for the update
|
|
6396
|
+
getDb()
|
|
6397
|
+
.prepare(
|
|
6398
|
+
`INSERT INTO prompt_template_history (id, templateId, version, body, state, changedBy, changedAt, changeReason)
|
|
6399
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
6400
|
+
)
|
|
6401
|
+
.run(
|
|
6402
|
+
crypto.randomUUID(),
|
|
6403
|
+
existing.id,
|
|
6404
|
+
newVersion,
|
|
6405
|
+
data.body,
|
|
6406
|
+
state,
|
|
6407
|
+
changedBy,
|
|
6408
|
+
now,
|
|
6409
|
+
changeReason,
|
|
6410
|
+
);
|
|
6411
|
+
} else {
|
|
6412
|
+
const id = crypto.randomUUID();
|
|
6413
|
+
row = getDb()
|
|
6414
|
+
.prepare<
|
|
6415
|
+
PromptTemplateRow,
|
|
6416
|
+
[
|
|
6417
|
+
string,
|
|
6418
|
+
string,
|
|
6419
|
+
string,
|
|
6420
|
+
string | null,
|
|
6421
|
+
string,
|
|
6422
|
+
string,
|
|
6423
|
+
number,
|
|
6424
|
+
number,
|
|
6425
|
+
string | null,
|
|
6426
|
+
string,
|
|
6427
|
+
string,
|
|
6428
|
+
]
|
|
6429
|
+
>(
|
|
6430
|
+
`INSERT INTO prompt_templates (id, eventType, scope, scopeId, state, body, isDefault, version, createdBy, createdAt, updatedAt)
|
|
6431
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING *`,
|
|
6432
|
+
)
|
|
6433
|
+
.get(
|
|
6434
|
+
id,
|
|
6435
|
+
data.eventType,
|
|
6436
|
+
data.scope,
|
|
6437
|
+
scopeId,
|
|
6438
|
+
state,
|
|
6439
|
+
data.body,
|
|
6440
|
+
data.isDefault ? 1 : 0,
|
|
6441
|
+
1,
|
|
6442
|
+
createdBy,
|
|
6443
|
+
now,
|
|
6444
|
+
now,
|
|
6445
|
+
);
|
|
6446
|
+
|
|
6447
|
+
// Create history entry for the insert
|
|
6448
|
+
getDb()
|
|
6449
|
+
.prepare(
|
|
6450
|
+
`INSERT INTO prompt_template_history (id, templateId, version, body, state, changedBy, changedAt, changeReason)
|
|
6451
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
6452
|
+
)
|
|
6453
|
+
.run(
|
|
6454
|
+
crypto.randomUUID(),
|
|
6455
|
+
id,
|
|
6456
|
+
1,
|
|
6457
|
+
data.body,
|
|
6458
|
+
state,
|
|
6459
|
+
changedBy,
|
|
6460
|
+
now,
|
|
6461
|
+
changeReason ?? "Initial creation",
|
|
6462
|
+
);
|
|
6463
|
+
}
|
|
6464
|
+
|
|
6465
|
+
if (!row) throw new Error("Failed to upsert prompt template");
|
|
6466
|
+
return rowToPromptTemplate(row);
|
|
6467
|
+
}
|
|
6468
|
+
|
|
6469
|
+
/**
|
|
6470
|
+
* Delete a prompt template by ID. Guards against deleting default templates.
|
|
6471
|
+
* Does NOT delete history rows (intentional for audit trail).
|
|
6472
|
+
*/
|
|
6473
|
+
export function deletePromptTemplate(id: string): boolean {
|
|
6474
|
+
const existing = getDb()
|
|
6475
|
+
.prepare<PromptTemplateRow, [string]>("SELECT * FROM prompt_templates WHERE id = ?")
|
|
6476
|
+
.get(id);
|
|
6477
|
+
|
|
6478
|
+
if (!existing) return false;
|
|
6479
|
+
if (existing.isDefault === 1) {
|
|
6480
|
+
throw new Error(
|
|
6481
|
+
"Cannot delete a default prompt template. Use resetPromptTemplateToDefault instead.",
|
|
6482
|
+
);
|
|
6483
|
+
}
|
|
6484
|
+
|
|
6485
|
+
const result = getDb().run("DELETE FROM prompt_templates WHERE id = ?", [id]);
|
|
6486
|
+
return result.changes > 0;
|
|
6487
|
+
}
|
|
6488
|
+
|
|
6489
|
+
/**
|
|
6490
|
+
* Reset a prompt template to its default state.
|
|
6491
|
+
* Sets body to defaultBody, isDefault=true, state='enabled', bumps version.
|
|
6492
|
+
*/
|
|
6493
|
+
export function resetPromptTemplateToDefault(id: string, defaultBody: string): PromptTemplate {
|
|
6494
|
+
const now = new Date().toISOString();
|
|
6495
|
+
const existing = getDb()
|
|
6496
|
+
.prepare<PromptTemplateRow, [string]>("SELECT * FROM prompt_templates WHERE id = ?")
|
|
6497
|
+
.get(id);
|
|
6498
|
+
|
|
6499
|
+
if (!existing) throw new Error(`Prompt template ${id} not found`);
|
|
6500
|
+
|
|
6501
|
+
const newVersion = existing.version + 1;
|
|
6502
|
+
|
|
6503
|
+
const row = getDb()
|
|
6504
|
+
.prepare<PromptTemplateRow, [string, number, string, string]>(
|
|
6505
|
+
`UPDATE prompt_templates SET body = ?, state = 'enabled', isDefault = 1, version = ?, updatedAt = ?
|
|
6506
|
+
WHERE id = ? RETURNING *`,
|
|
6507
|
+
)
|
|
6508
|
+
.get(defaultBody, newVersion, now, id);
|
|
6509
|
+
|
|
6510
|
+
if (!row) throw new Error("Failed to reset prompt template to default");
|
|
6511
|
+
|
|
6512
|
+
// Create history entry
|
|
6513
|
+
getDb()
|
|
6514
|
+
.prepare(
|
|
6515
|
+
`INSERT INTO prompt_template_history (id, templateId, version, body, state, changedBy, changedAt, changeReason)
|
|
6516
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
6517
|
+
)
|
|
6518
|
+
.run(
|
|
6519
|
+
crypto.randomUUID(),
|
|
6520
|
+
id,
|
|
6521
|
+
newVersion,
|
|
6522
|
+
defaultBody,
|
|
6523
|
+
"enabled",
|
|
6524
|
+
null,
|
|
6525
|
+
now,
|
|
6526
|
+
"Reset to default",
|
|
6527
|
+
);
|
|
6528
|
+
|
|
6529
|
+
return rowToPromptTemplate(row);
|
|
6530
|
+
}
|
|
6531
|
+
|
|
6532
|
+
/**
|
|
6533
|
+
* Get version history for a prompt template, ordered by version DESC.
|
|
6534
|
+
*/
|
|
6535
|
+
export function getPromptTemplateHistory(templateId: string): PromptTemplateHistory[] {
|
|
6536
|
+
return getDb()
|
|
6537
|
+
.prepare<PromptTemplateHistoryRow, [string]>(
|
|
6538
|
+
"SELECT * FROM prompt_template_history WHERE templateId = ? ORDER BY version DESC",
|
|
6539
|
+
)
|
|
6540
|
+
.all(templateId)
|
|
6541
|
+
.map(rowToPromptTemplateHistory);
|
|
6542
|
+
}
|
|
6543
|
+
|
|
6544
|
+
/**
|
|
6545
|
+
* Resolve the best prompt template for a given eventType using scope precedence.
|
|
6546
|
+
*
|
|
6547
|
+
* Two-pass resolution:
|
|
6548
|
+
* Pass 1 (exact match): Try exact eventType at agent → repo → global scope.
|
|
6549
|
+
* Pass 2 (wildcard): Generate wildcards from eventType (e.g. "github.pull_request.*", "github.*")
|
|
6550
|
+
* and try each at agent → repo → global scope.
|
|
6551
|
+
*
|
|
6552
|
+
* Exact match at ANY scope always beats wildcard at ANY scope.
|
|
6553
|
+
*
|
|
6554
|
+
* State behavior:
|
|
6555
|
+
* - 'enabled': return the template
|
|
6556
|
+
* - 'skip_event': return { skip: true }
|
|
6557
|
+
* - 'default_prompt_fallback': continue to next scope level
|
|
6558
|
+
*/
|
|
6559
|
+
export function resolvePromptTemplate(
|
|
6560
|
+
eventType: string,
|
|
6561
|
+
agentId?: string,
|
|
6562
|
+
repoId?: string,
|
|
6563
|
+
): { template: PromptTemplate } | { skip: true } | null {
|
|
6564
|
+
// Helper to look up a template at a specific scope
|
|
6565
|
+
const lookupAtScope = (
|
|
6566
|
+
et: string,
|
|
6567
|
+
scope: "global" | "agent" | "repo",
|
|
6568
|
+
scopeId: string | null,
|
|
6569
|
+
): PromptTemplateRow | undefined => {
|
|
6570
|
+
if (scopeId === null) {
|
|
6571
|
+
return (
|
|
6572
|
+
getDb()
|
|
6573
|
+
.prepare<PromptTemplateRow, [string, string]>(
|
|
6574
|
+
"SELECT * FROM prompt_templates WHERE eventType = ? AND scope = ? AND scopeId IS NULL",
|
|
6575
|
+
)
|
|
6576
|
+
.get(et, scope) ?? undefined
|
|
6577
|
+
);
|
|
6578
|
+
}
|
|
6579
|
+
return (
|
|
6580
|
+
getDb()
|
|
6581
|
+
.prepare<PromptTemplateRow, [string, string, string]>(
|
|
6582
|
+
"SELECT * FROM prompt_templates WHERE eventType = ? AND scope = ? AND scopeId = ?",
|
|
6583
|
+
)
|
|
6584
|
+
.get(et, scope, scopeId) ?? undefined
|
|
6585
|
+
);
|
|
6586
|
+
};
|
|
6587
|
+
|
|
6588
|
+
// Try resolution at the scope chain for a given eventType string
|
|
6589
|
+
const tryResolve = (et: string): { template: PromptTemplate } | { skip: true } | "continue" => {
|
|
6590
|
+
// Build scope chain: agent → repo → global
|
|
6591
|
+
const scopeChain: Array<{ scope: "global" | "agent" | "repo"; scopeId: string | null }> = [];
|
|
6592
|
+
if (agentId) scopeChain.push({ scope: "agent", scopeId: agentId });
|
|
6593
|
+
if (repoId) scopeChain.push({ scope: "repo", scopeId: repoId });
|
|
6594
|
+
scopeChain.push({ scope: "global", scopeId: null });
|
|
6595
|
+
|
|
6596
|
+
for (const { scope, scopeId } of scopeChain) {
|
|
6597
|
+
const row = lookupAtScope(et, scope, scopeId);
|
|
6598
|
+
if (!row) continue;
|
|
6599
|
+
|
|
6600
|
+
if (row.state === "enabled") {
|
|
6601
|
+
return { template: rowToPromptTemplate(row) };
|
|
6602
|
+
}
|
|
6603
|
+
if (row.state === "skip_event") {
|
|
6604
|
+
return { skip: true };
|
|
6605
|
+
}
|
|
6606
|
+
// default_prompt_fallback: continue to next scope
|
|
6607
|
+
}
|
|
6608
|
+
|
|
6609
|
+
return "continue";
|
|
6610
|
+
};
|
|
6611
|
+
|
|
6612
|
+
// Pass 1: exact match
|
|
6613
|
+
const exactResult = tryResolve(eventType);
|
|
6614
|
+
if (exactResult !== "continue") return exactResult;
|
|
6615
|
+
|
|
6616
|
+
// Pass 2: wildcard matching
|
|
6617
|
+
// e.g. "github.pull_request.review_submitted" → ["github.pull_request.*", "github.*"]
|
|
6618
|
+
const parts = eventType.split(".");
|
|
6619
|
+
const wildcards: string[] = [];
|
|
6620
|
+
for (let i = parts.length - 1; i >= 1; i--) {
|
|
6621
|
+
wildcards.push(`${parts.slice(0, i).join(".")}.*`);
|
|
6622
|
+
}
|
|
6623
|
+
|
|
6624
|
+
for (const wildcard of wildcards) {
|
|
6625
|
+
const wildcardResult = tryResolve(wildcard);
|
|
6626
|
+
if (wildcardResult !== "continue") return wildcardResult;
|
|
6627
|
+
}
|
|
6628
|
+
|
|
6629
|
+
return null;
|
|
6630
|
+
}
|
|
6631
|
+
|
|
6632
|
+
/**
|
|
6633
|
+
* Checkout a prompt template to a specific version from history.
|
|
6634
|
+
* Copies body and state from the history entry into the live record, bumps version.
|
|
6635
|
+
*/
|
|
6636
|
+
export function checkoutPromptTemplate(id: string, targetVersion: number): PromptTemplate {
|
|
6637
|
+
const now = new Date().toISOString();
|
|
6638
|
+
|
|
6639
|
+
const existing = getDb()
|
|
6640
|
+
.prepare<PromptTemplateRow, [string]>("SELECT * FROM prompt_templates WHERE id = ?")
|
|
6641
|
+
.get(id);
|
|
6642
|
+
if (!existing) throw new Error(`Prompt template ${id} not found`);
|
|
6643
|
+
|
|
6644
|
+
const historyEntry = getDb()
|
|
6645
|
+
.prepare<PromptTemplateHistoryRow, [string, number]>(
|
|
6646
|
+
"SELECT * FROM prompt_template_history WHERE templateId = ? AND version = ?",
|
|
6647
|
+
)
|
|
6648
|
+
.get(id, targetVersion);
|
|
6649
|
+
if (!historyEntry)
|
|
6650
|
+
throw new Error(`No history entry at version ${targetVersion} for template ${id}`);
|
|
6651
|
+
|
|
6652
|
+
const newVersion = existing.version + 1;
|
|
6653
|
+
|
|
6654
|
+
const row = getDb()
|
|
6655
|
+
.prepare<PromptTemplateRow, [string, string, number, string, string]>(
|
|
6656
|
+
`UPDATE prompt_templates SET body = ?, state = ?, version = ?, updatedAt = ?
|
|
6657
|
+
WHERE id = ? RETURNING *`,
|
|
6658
|
+
)
|
|
6659
|
+
.get(historyEntry.body, historyEntry.state, newVersion, now, id);
|
|
6660
|
+
|
|
6661
|
+
if (!row) throw new Error("Failed to checkout prompt template");
|
|
6662
|
+
|
|
6663
|
+
// Create history entry for the checkout
|
|
6664
|
+
getDb()
|
|
6665
|
+
.prepare(
|
|
6666
|
+
`INSERT INTO prompt_template_history (id, templateId, version, body, state, changedBy, changedAt, changeReason)
|
|
6667
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
6668
|
+
)
|
|
6669
|
+
.run(
|
|
6670
|
+
crypto.randomUUID(),
|
|
6671
|
+
id,
|
|
6672
|
+
newVersion,
|
|
6673
|
+
historyEntry.body,
|
|
6674
|
+
historyEntry.state,
|
|
6675
|
+
null,
|
|
6676
|
+
now,
|
|
6677
|
+
`Checked out from version ${targetVersion}`,
|
|
6678
|
+
);
|
|
6679
|
+
|
|
6680
|
+
return rowToPromptTemplate(row);
|
|
6681
|
+
}
|
|
6682
|
+
|
|
6683
|
+
// ─── Channel Activity Cursors ─────────────────────────────────────────────────
|
|
6684
|
+
|
|
6685
|
+
type ChannelActivityCursorRow = {
|
|
6686
|
+
channelId: string;
|
|
6687
|
+
lastSeenTs: string;
|
|
6688
|
+
updatedAt: string;
|
|
6689
|
+
};
|
|
6690
|
+
|
|
6691
|
+
export interface ChannelActivityCursor {
|
|
6692
|
+
channelId: string;
|
|
6693
|
+
lastSeenTs: string;
|
|
6694
|
+
updatedAt: string;
|
|
6695
|
+
}
|
|
6696
|
+
|
|
6697
|
+
function rowToChannelActivityCursor(row: ChannelActivityCursorRow): ChannelActivityCursor {
|
|
6698
|
+
return {
|
|
6699
|
+
channelId: row.channelId,
|
|
6700
|
+
lastSeenTs: row.lastSeenTs,
|
|
6701
|
+
updatedAt: row.updatedAt,
|
|
6702
|
+
};
|
|
6703
|
+
}
|
|
6704
|
+
|
|
6705
|
+
export function getAllChannelActivityCursors(): ChannelActivityCursor[] {
|
|
6706
|
+
return getDb()
|
|
6707
|
+
.prepare<ChannelActivityCursorRow, []>("SELECT * FROM channel_activity_cursors")
|
|
6708
|
+
.all()
|
|
6709
|
+
.map(rowToChannelActivityCursor);
|
|
6710
|
+
}
|
|
6711
|
+
|
|
6712
|
+
export function getChannelActivityCursor(channelId: string): ChannelActivityCursor | null {
|
|
6713
|
+
const row = getDb()
|
|
6714
|
+
.prepare<ChannelActivityCursorRow, [string]>(
|
|
6715
|
+
"SELECT * FROM channel_activity_cursors WHERE channelId = ?",
|
|
6716
|
+
)
|
|
6717
|
+
.get(channelId);
|
|
6718
|
+
return row ? rowToChannelActivityCursor(row) : null;
|
|
6719
|
+
}
|
|
6720
|
+
|
|
6721
|
+
export function upsertChannelActivityCursor(channelId: string, lastSeenTs: string): void {
|
|
6722
|
+
getDb()
|
|
6723
|
+
.prepare(
|
|
6724
|
+
`INSERT INTO channel_activity_cursors (channelId, lastSeenTs, updatedAt)
|
|
6725
|
+
VALUES (?, ?, datetime('now'))
|
|
6726
|
+
ON CONFLICT(channelId) DO UPDATE SET lastSeenTs = excluded.lastSeenTs, updatedAt = excluded.updatedAt`,
|
|
6727
|
+
)
|
|
6728
|
+
.run(channelId, lastSeenTs);
|
|
6729
|
+
}
|
|
6730
|
+
|
|
6731
|
+
// ============================================================================
|
|
6732
|
+
// Approval Requests
|
|
6733
|
+
// ============================================================================
|
|
6734
|
+
|
|
6735
|
+
export interface ApprovalRequest {
|
|
6736
|
+
id: string;
|
|
6737
|
+
title: string;
|
|
6738
|
+
questions: unknown[];
|
|
6739
|
+
workflowRunId: string | null;
|
|
6740
|
+
workflowRunStepId: string | null;
|
|
6741
|
+
sourceTaskId: string | null;
|
|
6742
|
+
approvers: unknown;
|
|
6743
|
+
status: "pending" | "approved" | "rejected" | "timeout";
|
|
6744
|
+
responses: unknown | null;
|
|
6745
|
+
resolvedBy: string | null;
|
|
6746
|
+
resolvedAt: string | null;
|
|
6747
|
+
timeoutSeconds: number | null;
|
|
6748
|
+
expiresAt: string | null;
|
|
6749
|
+
notificationChannels: unknown[] | null;
|
|
6750
|
+
createdAt: string;
|
|
6751
|
+
updatedAt: string;
|
|
6752
|
+
}
|
|
6753
|
+
|
|
6754
|
+
interface ApprovalRequestRow {
|
|
6755
|
+
id: string;
|
|
6756
|
+
title: string;
|
|
6757
|
+
questions: string;
|
|
6758
|
+
workflowRunId: string | null;
|
|
6759
|
+
workflowRunStepId: string | null;
|
|
6760
|
+
sourceTaskId: string | null;
|
|
6761
|
+
approvers: string;
|
|
6762
|
+
status: string;
|
|
6763
|
+
responses: string | null;
|
|
6764
|
+
resolvedBy: string | null;
|
|
6765
|
+
resolvedAt: string | null;
|
|
6766
|
+
timeoutSeconds: number | null;
|
|
6767
|
+
expiresAt: string | null;
|
|
6768
|
+
notificationChannels: string | null;
|
|
6769
|
+
createdAt: string;
|
|
6770
|
+
updatedAt: string;
|
|
6771
|
+
}
|
|
6772
|
+
|
|
6773
|
+
function rowToApprovalRequest(row: ApprovalRequestRow): ApprovalRequest {
|
|
6774
|
+
return {
|
|
6775
|
+
id: row.id,
|
|
6776
|
+
title: row.title,
|
|
6777
|
+
questions: JSON.parse(row.questions),
|
|
6778
|
+
workflowRunId: row.workflowRunId,
|
|
6779
|
+
workflowRunStepId: row.workflowRunStepId,
|
|
6780
|
+
sourceTaskId: row.sourceTaskId,
|
|
6781
|
+
approvers: JSON.parse(row.approvers),
|
|
6782
|
+
status: row.status as ApprovalRequest["status"],
|
|
6783
|
+
responses: row.responses ? JSON.parse(row.responses) : null,
|
|
6784
|
+
resolvedBy: row.resolvedBy,
|
|
6785
|
+
resolvedAt: row.resolvedAt,
|
|
6786
|
+
timeoutSeconds: row.timeoutSeconds,
|
|
6787
|
+
expiresAt: row.expiresAt,
|
|
6788
|
+
notificationChannels: row.notificationChannels ? JSON.parse(row.notificationChannels) : null,
|
|
6789
|
+
createdAt: row.createdAt,
|
|
6790
|
+
updatedAt: row.updatedAt,
|
|
6791
|
+
};
|
|
6792
|
+
}
|
|
6793
|
+
|
|
6794
|
+
export function createApprovalRequest(data: {
|
|
6795
|
+
id: string;
|
|
6796
|
+
title: string;
|
|
6797
|
+
questions: unknown[];
|
|
6798
|
+
approvers: unknown;
|
|
6799
|
+
workflowRunId?: string;
|
|
6800
|
+
workflowRunStepId?: string;
|
|
6801
|
+
sourceTaskId?: string;
|
|
6802
|
+
timeoutSeconds?: number;
|
|
6803
|
+
notificationChannels?: unknown[];
|
|
6804
|
+
}): ApprovalRequest {
|
|
6805
|
+
const now = new Date().toISOString();
|
|
6806
|
+
const expiresAt = data.timeoutSeconds
|
|
6807
|
+
? new Date(Date.now() + data.timeoutSeconds * 1000).toISOString()
|
|
6808
|
+
: null;
|
|
6809
|
+
|
|
6810
|
+
const row = getDb()
|
|
6811
|
+
.prepare<
|
|
6812
|
+
ApprovalRequestRow,
|
|
6813
|
+
[
|
|
6814
|
+
string,
|
|
6815
|
+
string,
|
|
6816
|
+
string,
|
|
6817
|
+
string | null,
|
|
6818
|
+
string | null,
|
|
6819
|
+
string | null,
|
|
6820
|
+
string,
|
|
6821
|
+
number | null,
|
|
6822
|
+
string | null,
|
|
6823
|
+
string | null,
|
|
6824
|
+
string,
|
|
6825
|
+
string,
|
|
6826
|
+
]
|
|
6827
|
+
>(
|
|
6828
|
+
`INSERT INTO approval_requests (id, title, questions, workflowRunId, workflowRunStepId, sourceTaskId, approvers, timeoutSeconds, expiresAt, notificationChannels, createdAt, updatedAt)
|
|
6829
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
6830
|
+
RETURNING *`,
|
|
6831
|
+
)
|
|
6832
|
+
.get(
|
|
6833
|
+
data.id,
|
|
6834
|
+
data.title,
|
|
6835
|
+
JSON.stringify(data.questions),
|
|
6836
|
+
data.workflowRunId ?? null,
|
|
6837
|
+
data.workflowRunStepId ?? null,
|
|
6838
|
+
data.sourceTaskId ?? null,
|
|
6839
|
+
JSON.stringify(data.approvers),
|
|
6840
|
+
data.timeoutSeconds ?? null,
|
|
6841
|
+
expiresAt,
|
|
6842
|
+
data.notificationChannels ? JSON.stringify(data.notificationChannels) : null,
|
|
6843
|
+
now,
|
|
6844
|
+
now,
|
|
6845
|
+
);
|
|
6846
|
+
|
|
6847
|
+
return rowToApprovalRequest(row!);
|
|
6848
|
+
}
|
|
6849
|
+
|
|
6850
|
+
export function getApprovalRequestById(id: string): ApprovalRequest | null {
|
|
6851
|
+
const row = getDb()
|
|
6852
|
+
.prepare<ApprovalRequestRow, [string]>("SELECT * FROM approval_requests WHERE id = ?")
|
|
6853
|
+
.get(id);
|
|
6854
|
+
return row ? rowToApprovalRequest(row) : null;
|
|
6855
|
+
}
|
|
6856
|
+
|
|
6857
|
+
export function resolveApprovalRequest(
|
|
6858
|
+
id: string,
|
|
6859
|
+
data: {
|
|
6860
|
+
status: "approved" | "rejected" | "timeout";
|
|
6861
|
+
responses?: unknown;
|
|
6862
|
+
resolvedBy?: string;
|
|
6863
|
+
},
|
|
6864
|
+
): ApprovalRequest | null {
|
|
6865
|
+
const now = new Date().toISOString();
|
|
6866
|
+
const row = getDb()
|
|
6867
|
+
.prepare<ApprovalRequestRow, [string, string | null, string | null, string, string, string]>(
|
|
6868
|
+
`UPDATE approval_requests
|
|
6869
|
+
SET status = ?, responses = ?, resolvedBy = ?, resolvedAt = ?, updatedAt = ?
|
|
6870
|
+
WHERE id = ? AND status = 'pending'
|
|
6871
|
+
RETURNING *`,
|
|
6872
|
+
)
|
|
6873
|
+
.get(
|
|
6874
|
+
data.status,
|
|
6875
|
+
data.responses ? JSON.stringify(data.responses) : null,
|
|
6876
|
+
data.resolvedBy ?? null,
|
|
6877
|
+
now,
|
|
6878
|
+
now,
|
|
6879
|
+
id,
|
|
6880
|
+
);
|
|
6881
|
+
return row ? rowToApprovalRequest(row) : null;
|
|
6882
|
+
}
|
|
6883
|
+
|
|
6884
|
+
export function listApprovalRequests(filters?: {
|
|
6885
|
+
status?: string;
|
|
6886
|
+
workflowRunId?: string;
|
|
6887
|
+
limit?: number;
|
|
6888
|
+
}): ApprovalRequest[] {
|
|
6889
|
+
const conditions: string[] = [];
|
|
6890
|
+
const params: (string | number)[] = [];
|
|
6891
|
+
|
|
6892
|
+
if (filters?.status) {
|
|
6893
|
+
conditions.push("status = ?");
|
|
6894
|
+
params.push(filters.status);
|
|
6895
|
+
}
|
|
6896
|
+
if (filters?.workflowRunId) {
|
|
6897
|
+
conditions.push("workflowRunId = ?");
|
|
6898
|
+
params.push(filters.workflowRunId);
|
|
6899
|
+
}
|
|
6900
|
+
|
|
6901
|
+
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
6902
|
+
const limit = filters?.limit ?? 100;
|
|
6903
|
+
params.push(limit);
|
|
6904
|
+
|
|
6905
|
+
const stmt = getDb().prepare(
|
|
6906
|
+
`SELECT * FROM approval_requests ${where} ORDER BY createdAt DESC LIMIT ?`,
|
|
6907
|
+
);
|
|
6908
|
+
const rows = stmt.all(...params) as ApprovalRequestRow[];
|
|
6909
|
+
|
|
6910
|
+
return rows.map(rowToApprovalRequest);
|
|
6911
|
+
}
|
|
6912
|
+
|
|
6913
|
+
export interface StuckApprovalRun {
|
|
6914
|
+
runId: string;
|
|
6915
|
+
stepId: string;
|
|
6916
|
+
nodeId: string;
|
|
6917
|
+
workflowId: string;
|
|
6918
|
+
approvalId: string;
|
|
6919
|
+
approvalStatus: string;
|
|
6920
|
+
approvalResponses: string | null;
|
|
6921
|
+
expiresAt: string | null;
|
|
6922
|
+
}
|
|
6923
|
+
|
|
6924
|
+
export function getStuckApprovalRuns(): StuckApprovalRun[] {
|
|
6925
|
+
return getDb()
|
|
6926
|
+
.prepare<StuckApprovalRun, []>(
|
|
6927
|
+
`SELECT
|
|
6928
|
+
wr.id as runId,
|
|
6929
|
+
wrs.id as stepId,
|
|
6930
|
+
wrs.nodeId,
|
|
6931
|
+
wr.workflowId,
|
|
6932
|
+
ar.id as approvalId,
|
|
6933
|
+
ar.status as approvalStatus,
|
|
6934
|
+
ar.responses as approvalResponses,
|
|
6935
|
+
ar.expiresAt
|
|
6936
|
+
FROM workflow_runs wr
|
|
6937
|
+
JOIN workflow_run_steps wrs ON wrs.runId = wr.id AND wrs.status = 'waiting'
|
|
6938
|
+
JOIN approval_requests ar ON ar.workflowRunStepId = wrs.id
|
|
6939
|
+
WHERE wr.status = 'waiting'
|
|
6940
|
+
AND (ar.status IN ('approved', 'rejected', 'timeout')
|
|
6941
|
+
OR (ar.status = 'pending' AND ar.expiresAt IS NOT NULL AND ar.expiresAt < datetime('now')))`,
|
|
6942
|
+
)
|
|
6943
|
+
.all();
|
|
6944
|
+
}
|
|
6945
|
+
|
|
6946
|
+
export function getApprovalRequestByStepId(stepId: string): ApprovalRequest | null {
|
|
6947
|
+
const row = getDb()
|
|
6948
|
+
.prepare<ApprovalRequestRow, [string]>(
|
|
6949
|
+
"SELECT * FROM approval_requests WHERE workflowRunStepId = ?",
|
|
6950
|
+
)
|
|
6951
|
+
.get(stepId);
|
|
6952
|
+
return row ? rowToApprovalRequest(row) : null;
|
|
6953
|
+
}
|
|
6954
|
+
|
|
6955
|
+
// TODO: Wire into a periodic cron/sweep to auto-timeout expired approval requests (Phase 2)
|
|
6956
|
+
export function getExpiredPendingApprovals(): ApprovalRequest[] {
|
|
6957
|
+
const rows = getDb()
|
|
6958
|
+
.prepare<ApprovalRequestRow, []>(
|
|
6959
|
+
`SELECT * FROM approval_requests
|
|
6960
|
+
WHERE status = 'pending'
|
|
6961
|
+
AND expiresAt IS NOT NULL
|
|
6962
|
+
AND expiresAt < datetime('now')`,
|
|
6963
|
+
)
|
|
6964
|
+
.all();
|
|
6965
|
+
return rows.map(rowToApprovalRequest);
|
|
6966
|
+
}
|
|
6967
|
+
|
|
6968
|
+
// ============================================================================
|
|
6969
|
+
// Skills
|
|
6970
|
+
// ============================================================================
|
|
6971
|
+
|
|
6972
|
+
type SkillRow = {
|
|
6973
|
+
id: string;
|
|
6974
|
+
name: string;
|
|
6975
|
+
description: string;
|
|
6976
|
+
content: string;
|
|
6977
|
+
type: string;
|
|
6978
|
+
scope: string;
|
|
6979
|
+
ownerAgentId: string | null;
|
|
6980
|
+
sourceUrl: string | null;
|
|
6981
|
+
sourceRepo: string | null;
|
|
6982
|
+
sourcePath: string | null;
|
|
6983
|
+
sourceBranch: string;
|
|
6984
|
+
sourceHash: string | null;
|
|
6985
|
+
isComplex: number;
|
|
6986
|
+
allowedTools: string | null;
|
|
6987
|
+
model: string | null;
|
|
6988
|
+
effort: string | null;
|
|
6989
|
+
context: string | null;
|
|
6990
|
+
agent: string | null;
|
|
6991
|
+
disableModelInvocation: number;
|
|
6992
|
+
userInvocable: number;
|
|
6993
|
+
version: number;
|
|
6994
|
+
isEnabled: number;
|
|
6995
|
+
createdAt: string;
|
|
6996
|
+
lastUpdatedAt: string;
|
|
6997
|
+
lastFetchedAt: string | null;
|
|
6998
|
+
};
|
|
6999
|
+
|
|
7000
|
+
function rowToSkill(row: SkillRow): Skill {
|
|
7001
|
+
return {
|
|
7002
|
+
id: row.id,
|
|
7003
|
+
name: row.name,
|
|
7004
|
+
description: row.description,
|
|
7005
|
+
content: row.content,
|
|
7006
|
+
type: row.type as SkillType,
|
|
7007
|
+
scope: row.scope as SkillScope,
|
|
7008
|
+
ownerAgentId: row.ownerAgentId,
|
|
7009
|
+
sourceUrl: row.sourceUrl,
|
|
7010
|
+
sourceRepo: row.sourceRepo,
|
|
7011
|
+
sourcePath: row.sourcePath,
|
|
7012
|
+
sourceBranch: row.sourceBranch,
|
|
7013
|
+
sourceHash: row.sourceHash,
|
|
7014
|
+
isComplex: row.isComplex === 1,
|
|
7015
|
+
allowedTools: row.allowedTools,
|
|
7016
|
+
model: row.model,
|
|
7017
|
+
effort: row.effort,
|
|
7018
|
+
context: row.context,
|
|
7019
|
+
agent: row.agent,
|
|
7020
|
+
disableModelInvocation: row.disableModelInvocation === 1,
|
|
7021
|
+
userInvocable: row.userInvocable === 1,
|
|
7022
|
+
version: row.version,
|
|
7023
|
+
isEnabled: row.isEnabled === 1,
|
|
7024
|
+
createdAt: row.createdAt,
|
|
7025
|
+
lastUpdatedAt: row.lastUpdatedAt,
|
|
7026
|
+
lastFetchedAt: row.lastFetchedAt,
|
|
7027
|
+
};
|
|
7028
|
+
}
|
|
7029
|
+
|
|
7030
|
+
type AgentSkillRow = {
|
|
7031
|
+
id: string;
|
|
7032
|
+
agentId: string;
|
|
7033
|
+
skillId: string;
|
|
7034
|
+
isActive: number;
|
|
7035
|
+
installedAt: string;
|
|
7036
|
+
};
|
|
7037
|
+
|
|
7038
|
+
function rowToAgentSkill(row: AgentSkillRow): AgentSkill {
|
|
7039
|
+
return {
|
|
7040
|
+
id: row.id,
|
|
7041
|
+
agentId: row.agentId,
|
|
7042
|
+
skillId: row.skillId,
|
|
7043
|
+
isActive: row.isActive === 1,
|
|
7044
|
+
installedAt: row.installedAt,
|
|
7045
|
+
};
|
|
7046
|
+
}
|
|
7047
|
+
|
|
7048
|
+
type SkillWithInstallRow = SkillRow & { isActive: number; installedAt: string };
|
|
7049
|
+
|
|
7050
|
+
function rowToSkillWithInstall(row: SkillWithInstallRow): SkillWithInstallInfo {
|
|
7051
|
+
return {
|
|
7052
|
+
...rowToSkill(row),
|
|
7053
|
+
isActive: row.isActive === 1,
|
|
7054
|
+
installedAt: row.installedAt,
|
|
7055
|
+
};
|
|
7056
|
+
}
|
|
7057
|
+
|
|
7058
|
+
export interface SkillInsert {
|
|
7059
|
+
name: string;
|
|
7060
|
+
description: string;
|
|
7061
|
+
content: string;
|
|
7062
|
+
type?: SkillType;
|
|
7063
|
+
scope?: SkillScope;
|
|
7064
|
+
ownerAgentId?: string;
|
|
7065
|
+
sourceUrl?: string;
|
|
7066
|
+
sourceRepo?: string;
|
|
7067
|
+
sourcePath?: string;
|
|
7068
|
+
sourceBranch?: string;
|
|
7069
|
+
sourceHash?: string;
|
|
7070
|
+
isComplex?: boolean;
|
|
7071
|
+
allowedTools?: string;
|
|
7072
|
+
model?: string;
|
|
7073
|
+
effort?: string;
|
|
7074
|
+
context?: string;
|
|
7075
|
+
agent?: string;
|
|
7076
|
+
disableModelInvocation?: boolean;
|
|
7077
|
+
userInvocable?: boolean;
|
|
7078
|
+
}
|
|
7079
|
+
|
|
7080
|
+
export function createSkill(data: SkillInsert): Skill {
|
|
7081
|
+
const id = crypto.randomUUID();
|
|
7082
|
+
const now = new Date().toISOString();
|
|
7083
|
+
|
|
7084
|
+
const row = getDb()
|
|
7085
|
+
.prepare<SkillRow, (string | number | null)[]>(
|
|
7086
|
+
`INSERT INTO skills (
|
|
7087
|
+
id, name, description, content, type, scope, ownerAgentId,
|
|
7088
|
+
sourceUrl, sourceRepo, sourcePath, sourceBranch, sourceHash, isComplex,
|
|
7089
|
+
allowedTools, model, effort, context, agent, disableModelInvocation, userInvocable,
|
|
7090
|
+
version, isEnabled, createdAt, lastUpdatedAt
|
|
7091
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, 1, ?, ?) RETURNING *`,
|
|
7092
|
+
)
|
|
7093
|
+
.get(
|
|
7094
|
+
id,
|
|
7095
|
+
data.name,
|
|
7096
|
+
data.description,
|
|
7097
|
+
data.content,
|
|
7098
|
+
data.type ?? "personal",
|
|
7099
|
+
data.scope ?? "agent",
|
|
7100
|
+
data.ownerAgentId ?? null,
|
|
7101
|
+
data.sourceUrl ?? null,
|
|
7102
|
+
data.sourceRepo ?? null,
|
|
7103
|
+
data.sourcePath ?? null,
|
|
7104
|
+
data.sourceBranch ?? "main",
|
|
7105
|
+
data.sourceHash ?? null,
|
|
7106
|
+
data.isComplex ? 1 : 0,
|
|
7107
|
+
data.allowedTools ?? null,
|
|
7108
|
+
data.model ?? null,
|
|
7109
|
+
data.effort ?? null,
|
|
7110
|
+
data.context ?? null,
|
|
7111
|
+
data.agent ?? null,
|
|
7112
|
+
data.disableModelInvocation ? 1 : 0,
|
|
7113
|
+
data.userInvocable === false ? 0 : 1,
|
|
7114
|
+
now,
|
|
7115
|
+
now,
|
|
7116
|
+
);
|
|
7117
|
+
|
|
7118
|
+
if (!row) throw new Error("Failed to create skill");
|
|
7119
|
+
return rowToSkill(row);
|
|
7120
|
+
}
|
|
7121
|
+
|
|
7122
|
+
export function updateSkill(
|
|
7123
|
+
id: string,
|
|
7124
|
+
updates: Partial<SkillInsert> & { isEnabled?: boolean; lastFetchedAt?: string },
|
|
7125
|
+
): Skill | null {
|
|
7126
|
+
const existing = getSkillById(id);
|
|
7127
|
+
if (!existing) return null;
|
|
7128
|
+
|
|
7129
|
+
const now = new Date().toISOString();
|
|
7130
|
+
const sets: string[] = ["lastUpdatedAt = ?"];
|
|
7131
|
+
const params: (string | number | null)[] = [now];
|
|
7132
|
+
|
|
7133
|
+
if (updates.name !== undefined) {
|
|
7134
|
+
sets.push("name = ?");
|
|
7135
|
+
params.push(updates.name);
|
|
7136
|
+
}
|
|
7137
|
+
if (updates.description !== undefined) {
|
|
7138
|
+
sets.push("description = ?");
|
|
7139
|
+
params.push(updates.description);
|
|
7140
|
+
}
|
|
7141
|
+
if (updates.content !== undefined) {
|
|
7142
|
+
sets.push("content = ?");
|
|
7143
|
+
params.push(updates.content);
|
|
7144
|
+
}
|
|
7145
|
+
if (updates.scope !== undefined) {
|
|
7146
|
+
sets.push("scope = ?");
|
|
7147
|
+
params.push(updates.scope);
|
|
7148
|
+
}
|
|
7149
|
+
if (updates.isEnabled !== undefined) {
|
|
7150
|
+
sets.push("isEnabled = ?");
|
|
7151
|
+
params.push(updates.isEnabled ? 1 : 0);
|
|
7152
|
+
}
|
|
7153
|
+
if (updates.allowedTools !== undefined) {
|
|
7154
|
+
sets.push("allowedTools = ?");
|
|
7155
|
+
params.push(updates.allowedTools ?? null);
|
|
7156
|
+
}
|
|
7157
|
+
if (updates.model !== undefined) {
|
|
7158
|
+
sets.push("model = ?");
|
|
7159
|
+
params.push(updates.model ?? null);
|
|
7160
|
+
}
|
|
7161
|
+
if (updates.effort !== undefined) {
|
|
7162
|
+
sets.push("effort = ?");
|
|
7163
|
+
params.push(updates.effort ?? null);
|
|
7164
|
+
}
|
|
7165
|
+
if (updates.context !== undefined) {
|
|
7166
|
+
sets.push("context = ?");
|
|
7167
|
+
params.push(updates.context ?? null);
|
|
7168
|
+
}
|
|
7169
|
+
if (updates.agent !== undefined) {
|
|
7170
|
+
sets.push("agent = ?");
|
|
7171
|
+
params.push(updates.agent ?? null);
|
|
7172
|
+
}
|
|
7173
|
+
if (updates.disableModelInvocation !== undefined) {
|
|
7174
|
+
sets.push("disableModelInvocation = ?");
|
|
7175
|
+
params.push(updates.disableModelInvocation ? 1 : 0);
|
|
7176
|
+
}
|
|
7177
|
+
if (updates.userInvocable !== undefined) {
|
|
7178
|
+
sets.push("userInvocable = ?");
|
|
7179
|
+
params.push(updates.userInvocable ? 1 : 0);
|
|
7180
|
+
}
|
|
7181
|
+
if (updates.sourceUrl !== undefined) {
|
|
7182
|
+
sets.push("sourceUrl = ?");
|
|
7183
|
+
params.push(updates.sourceUrl ?? null);
|
|
7184
|
+
}
|
|
7185
|
+
if (updates.sourceRepo !== undefined) {
|
|
7186
|
+
sets.push("sourceRepo = ?");
|
|
7187
|
+
params.push(updates.sourceRepo ?? null);
|
|
7188
|
+
}
|
|
7189
|
+
if (updates.sourcePath !== undefined) {
|
|
7190
|
+
sets.push("sourcePath = ?");
|
|
7191
|
+
params.push(updates.sourcePath ?? null);
|
|
7192
|
+
}
|
|
7193
|
+
if (updates.sourceBranch !== undefined) {
|
|
7194
|
+
sets.push("sourceBranch = ?");
|
|
7195
|
+
params.push(updates.sourceBranch ?? "main");
|
|
7196
|
+
}
|
|
7197
|
+
if (updates.sourceHash !== undefined) {
|
|
7198
|
+
sets.push("sourceHash = ?");
|
|
7199
|
+
params.push(updates.sourceHash ?? null);
|
|
7200
|
+
}
|
|
7201
|
+
if (updates.isComplex !== undefined) {
|
|
7202
|
+
sets.push("isComplex = ?");
|
|
7203
|
+
params.push(updates.isComplex ? 1 : 0);
|
|
7204
|
+
}
|
|
7205
|
+
if (updates.lastFetchedAt !== undefined) {
|
|
7206
|
+
sets.push("lastFetchedAt = ?");
|
|
7207
|
+
params.push(updates.lastFetchedAt);
|
|
7208
|
+
}
|
|
7209
|
+
|
|
7210
|
+
// Bump version when content changes
|
|
7211
|
+
if (updates.content !== undefined) {
|
|
7212
|
+
sets.push("version = version + 1");
|
|
7213
|
+
}
|
|
7214
|
+
|
|
7215
|
+
params.push(id);
|
|
7216
|
+
const row = getDb()
|
|
7217
|
+
.prepare<SkillRow, (string | number | null)[]>(
|
|
7218
|
+
`UPDATE skills SET ${sets.join(", ")} WHERE id = ? RETURNING *`,
|
|
7219
|
+
)
|
|
7220
|
+
.get(...params);
|
|
7221
|
+
|
|
7222
|
+
return row ? rowToSkill(row) : null;
|
|
7223
|
+
}
|
|
7224
|
+
|
|
7225
|
+
export function deleteSkill(id: string): boolean {
|
|
7226
|
+
const result = getDb().prepare("DELETE FROM skills WHERE id = ?").run(id);
|
|
7227
|
+
return result.changes > 0;
|
|
7228
|
+
}
|
|
7229
|
+
|
|
7230
|
+
export function getSkillById(id: string): Skill | null {
|
|
7231
|
+
const row = getDb().prepare<SkillRow, [string]>("SELECT * FROM skills WHERE id = ?").get(id);
|
|
7232
|
+
return row ? rowToSkill(row) : null;
|
|
7233
|
+
}
|
|
7234
|
+
|
|
7235
|
+
export function getSkillByName(
|
|
7236
|
+
name: string,
|
|
7237
|
+
scope: SkillScope,
|
|
7238
|
+
ownerAgentId?: string,
|
|
7239
|
+
): Skill | null {
|
|
7240
|
+
const row = getDb()
|
|
7241
|
+
.prepare<SkillRow, [string, string, string]>(
|
|
7242
|
+
"SELECT * FROM skills WHERE name = ? AND scope = ? AND COALESCE(ownerAgentId, '') = ?",
|
|
7243
|
+
)
|
|
7244
|
+
.get(name, scope, ownerAgentId ?? "");
|
|
7245
|
+
return row ? rowToSkill(row) : null;
|
|
7246
|
+
}
|
|
7247
|
+
|
|
7248
|
+
export interface SkillFilters {
|
|
7249
|
+
type?: SkillType;
|
|
7250
|
+
scope?: SkillScope;
|
|
7251
|
+
ownerAgentId?: string;
|
|
7252
|
+
isEnabled?: boolean;
|
|
7253
|
+
search?: string;
|
|
7254
|
+
limit?: number;
|
|
7255
|
+
includeContent?: boolean;
|
|
7256
|
+
}
|
|
7257
|
+
|
|
7258
|
+
export function listSkills(filters?: SkillFilters): Skill[] {
|
|
7259
|
+
const columns =
|
|
7260
|
+
filters?.includeContent === false
|
|
7261
|
+
? "id, name, description, type, scope, ownerAgentId, sourceUrl, sourceRepo, sourcePath, sourceBranch, sourceHash, isComplex, allowedTools, model, effort, context, agent, disableModelInvocation, userInvocable, version, isEnabled, createdAt, lastUpdatedAt, lastFetchedAt, '' as content"
|
|
7262
|
+
: "*";
|
|
7263
|
+
let query = `SELECT ${columns} FROM skills WHERE 1=1`;
|
|
7264
|
+
const params: (string | number)[] = [];
|
|
7265
|
+
|
|
7266
|
+
if (filters?.type) {
|
|
7267
|
+
query += " AND type = ?";
|
|
7268
|
+
params.push(filters.type);
|
|
7269
|
+
}
|
|
7270
|
+
if (filters?.scope) {
|
|
7271
|
+
query += " AND scope = ?";
|
|
7272
|
+
params.push(filters.scope);
|
|
7273
|
+
}
|
|
7274
|
+
if (filters?.ownerAgentId) {
|
|
7275
|
+
query += " AND ownerAgentId = ?";
|
|
7276
|
+
params.push(filters.ownerAgentId);
|
|
7277
|
+
}
|
|
7278
|
+
if (filters?.isEnabled !== undefined) {
|
|
7279
|
+
query += " AND isEnabled = ?";
|
|
7280
|
+
params.push(filters.isEnabled ? 1 : 0);
|
|
7281
|
+
}
|
|
7282
|
+
if (filters?.search) {
|
|
7283
|
+
query += " AND (name LIKE ? OR description LIKE ?)";
|
|
7284
|
+
const term = `%${filters.search}%`;
|
|
7285
|
+
params.push(term, term);
|
|
7286
|
+
}
|
|
7287
|
+
|
|
7288
|
+
query += " ORDER BY name ASC";
|
|
7289
|
+
|
|
7290
|
+
if (filters?.limit) {
|
|
7291
|
+
query += " LIMIT ?";
|
|
7292
|
+
params.push(filters.limit);
|
|
7293
|
+
}
|
|
7294
|
+
|
|
7295
|
+
return getDb()
|
|
7296
|
+
.prepare<SkillRow, (string | number)[]>(query)
|
|
7297
|
+
.all(...params)
|
|
7298
|
+
.map(rowToSkill);
|
|
7299
|
+
}
|
|
7300
|
+
|
|
7301
|
+
export function searchSkills(query: string, limit = 20): Skill[] {
|
|
7302
|
+
const term = `%${query}%`;
|
|
7303
|
+
return getDb()
|
|
7304
|
+
.prepare<SkillRow, [string, string, number]>(
|
|
7305
|
+
"SELECT * FROM skills WHERE (name LIKE ? OR description LIKE ?) AND isEnabled = 1 ORDER BY name ASC LIMIT ?",
|
|
7306
|
+
)
|
|
7307
|
+
.all(term, term, limit)
|
|
7308
|
+
.map(rowToSkill);
|
|
7309
|
+
}
|
|
7310
|
+
|
|
7311
|
+
export function installSkill(agentId: string, skillId: string): AgentSkill {
|
|
7312
|
+
const id = crypto.randomUUID();
|
|
7313
|
+
const now = new Date().toISOString();
|
|
7314
|
+
|
|
7315
|
+
const row = getDb()
|
|
7316
|
+
.prepare<AgentSkillRow, [string, string, string, string]>(
|
|
7317
|
+
`INSERT INTO agent_skills (id, agentId, skillId, isActive, installedAt)
|
|
7318
|
+
VALUES (?, ?, ?, 1, ?)
|
|
7319
|
+
ON CONFLICT(agentId, skillId) DO UPDATE SET isActive = 1
|
|
7320
|
+
RETURNING *`,
|
|
7321
|
+
)
|
|
7322
|
+
.get(id, agentId, skillId, now);
|
|
7323
|
+
|
|
7324
|
+
if (!row) throw new Error("Failed to install skill");
|
|
7325
|
+
return rowToAgentSkill(row);
|
|
7326
|
+
}
|
|
7327
|
+
|
|
7328
|
+
export function uninstallSkill(agentId: string, skillId: string): boolean {
|
|
7329
|
+
const result = getDb()
|
|
7330
|
+
.prepare("DELETE FROM agent_skills WHERE agentId = ? AND skillId = ?")
|
|
7331
|
+
.run(agentId, skillId);
|
|
7332
|
+
return result.changes > 0;
|
|
7333
|
+
}
|
|
7334
|
+
|
|
7335
|
+
export function getAgentSkills(agentId: string, activeOnly = true): SkillWithInstallInfo[] {
|
|
7336
|
+
const query = `
|
|
7337
|
+
SELECT s.*, as2.isActive, as2.installedAt
|
|
7338
|
+
FROM skills s
|
|
7339
|
+
JOIN agent_skills as2 ON s.id = as2.skillId
|
|
7340
|
+
WHERE as2.agentId = ?
|
|
7341
|
+
${activeOnly ? "AND as2.isActive = 1" : ""}
|
|
7342
|
+
AND s.isEnabled = 1
|
|
7343
|
+
ORDER BY
|
|
7344
|
+
CASE WHEN s.type = 'personal' THEN 0 ELSE 1 END,
|
|
7345
|
+
s.name
|
|
7346
|
+
`;
|
|
7347
|
+
|
|
7348
|
+
const rows = getDb().prepare<SkillWithInstallRow, [string]>(query).all(agentId);
|
|
7349
|
+
|
|
7350
|
+
// Deduplicate by name — personal skills take precedence (already sorted first)
|
|
7351
|
+
const seen = new Set<string>();
|
|
7352
|
+
return rows
|
|
7353
|
+
.filter((r) => {
|
|
7354
|
+
if (seen.has(r.name)) return false;
|
|
7355
|
+
seen.add(r.name);
|
|
7356
|
+
return true;
|
|
7357
|
+
})
|
|
7358
|
+
.map(rowToSkillWithInstall);
|
|
7359
|
+
}
|
|
7360
|
+
|
|
7361
|
+
export function toggleAgentSkill(agentId: string, skillId: string, isActive: boolean): boolean {
|
|
7362
|
+
const result = getDb()
|
|
7363
|
+
.prepare("UPDATE agent_skills SET isActive = ? WHERE agentId = ? AND skillId = ?")
|
|
7364
|
+
.run(isActive ? 1 : 0, agentId, skillId);
|
|
7365
|
+
return result.changes > 0;
|
|
7366
|
+
}
|