@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
|
@@ -731,8 +731,8 @@ describe("artifact CLI command", () => {
|
|
|
731
731
|
// ─── Base prompt tests ──────────────────────────────────────────────────
|
|
732
732
|
|
|
733
733
|
describe("base prompt artifacts mention", () => {
|
|
734
|
-
test("includes artifacts mention when capability is set", () => {
|
|
735
|
-
const prompt = getBasePrompt({
|
|
734
|
+
test("includes artifacts mention when capability is set", async () => {
|
|
735
|
+
const prompt = await getBasePrompt({
|
|
736
736
|
role: "worker",
|
|
737
737
|
agentId: "test-agent-id",
|
|
738
738
|
swarmUrl: "https://test.swarm.example.com",
|
|
@@ -743,8 +743,8 @@ describe("base prompt artifacts mention", () => {
|
|
|
743
743
|
expect(prompt).toContain("/workspace/personal/artifacts/");
|
|
744
744
|
});
|
|
745
745
|
|
|
746
|
-
test("excludes artifacts mention without capability", () => {
|
|
747
|
-
const prompt = getBasePrompt({
|
|
746
|
+
test("excludes artifacts mention without capability", async () => {
|
|
747
|
+
const prompt = await getBasePrompt({
|
|
748
748
|
role: "worker",
|
|
749
749
|
agentId: "test-agent-id",
|
|
750
750
|
swarmUrl: "https://test.swarm.example.com",
|
|
@@ -754,8 +754,8 @@ describe("base prompt artifacts mention", () => {
|
|
|
754
754
|
expect(prompt).not.toContain("/artifacts");
|
|
755
755
|
});
|
|
756
756
|
|
|
757
|
-
test("includes artifacts mention when no capabilities specified (default)", () => {
|
|
758
|
-
const prompt = getBasePrompt({
|
|
757
|
+
test("includes artifacts mention when no capabilities specified (default)", async () => {
|
|
758
|
+
const prompt = await getBasePrompt({
|
|
759
759
|
role: "worker",
|
|
760
760
|
agentId: "test-agent-id",
|
|
761
761
|
swarmUrl: "https://test.swarm.example.com",
|
|
@@ -764,8 +764,8 @@ describe("base prompt artifacts mention", () => {
|
|
|
764
764
|
expect(prompt).toContain("/artifacts");
|
|
765
765
|
});
|
|
766
766
|
|
|
767
|
-
test("does NOT inline full artifact docs (those go in the skill)", () => {
|
|
768
|
-
const prompt = getBasePrompt({
|
|
767
|
+
test("does NOT inline full artifact docs (those go in the skill)", async () => {
|
|
768
|
+
const prompt = await getBasePrompt({
|
|
769
769
|
role: "worker",
|
|
770
770
|
agentId: "test-agent-id",
|
|
771
771
|
swarmUrl: "https://test.swarm.example.com",
|
|
@@ -776,8 +776,8 @@ describe("base prompt artifacts mention", () => {
|
|
|
776
776
|
expect(prompt).not.toContain("SwarmSDK");
|
|
777
777
|
});
|
|
778
778
|
|
|
779
|
-
test("mentions localtunnel concept", () => {
|
|
780
|
-
const prompt = getBasePrompt({
|
|
779
|
+
test("mentions localtunnel concept", async () => {
|
|
780
|
+
const prompt = await getBasePrompt({
|
|
781
781
|
role: "worker",
|
|
782
782
|
agentId: "test-agent-id",
|
|
783
783
|
swarmUrl: "https://test.swarm.example.com",
|
|
@@ -787,8 +787,8 @@ describe("base prompt artifacts mention", () => {
|
|
|
787
787
|
expect(prompt).toContain("localtunnel");
|
|
788
788
|
});
|
|
789
789
|
|
|
790
|
-
test("mentions artifact storage path", () => {
|
|
791
|
-
const prompt = getBasePrompt({
|
|
790
|
+
test("mentions artifact storage path", async () => {
|
|
791
|
+
const prompt = await getBasePrompt({
|
|
792
792
|
role: "worker",
|
|
793
793
|
agentId: "test-agent-id",
|
|
794
794
|
swarmUrl: "https://test.swarm.example.com",
|
|
@@ -12,25 +12,25 @@ const minimalArgs: BasePromptArgs = {
|
|
|
12
12
|
// Basic fields
|
|
13
13
|
// ---------------------------------------------------------------------------
|
|
14
14
|
describe("getBasePrompt — basic fields", () => {
|
|
15
|
-
test("includes role and agentId", () => {
|
|
16
|
-
const result = getBasePrompt(minimalArgs);
|
|
15
|
+
test("includes role and agentId", async () => {
|
|
16
|
+
const result = await getBasePrompt(minimalArgs);
|
|
17
17
|
expect(result).toContain("worker");
|
|
18
18
|
expect(result).toContain("agent-abc-123");
|
|
19
19
|
});
|
|
20
20
|
|
|
21
|
-
test("lead role gets lead prompt", () => {
|
|
22
|
-
const result = getBasePrompt({ ...minimalArgs, role: "lead" });
|
|
21
|
+
test("lead role gets lead prompt", async () => {
|
|
22
|
+
const result = await getBasePrompt({ ...minimalArgs, role: "lead" });
|
|
23
23
|
expect(result).toContain("lead agent");
|
|
24
24
|
expect(result).toContain("coordinator");
|
|
25
25
|
});
|
|
26
26
|
|
|
27
|
-
test("worker role gets worker prompt", () => {
|
|
28
|
-
const result = getBasePrompt(minimalArgs);
|
|
27
|
+
test("worker role gets worker prompt", async () => {
|
|
28
|
+
const result = await getBasePrompt(minimalArgs);
|
|
29
29
|
expect(result).toContain("worker agent");
|
|
30
30
|
});
|
|
31
31
|
|
|
32
|
-
test("includes swarmUrl and agentId in services section", () => {
|
|
33
|
-
const result = getBasePrompt(minimalArgs);
|
|
32
|
+
test("includes swarmUrl and agentId in services section", async () => {
|
|
33
|
+
const result = await getBasePrompt(minimalArgs);
|
|
34
34
|
expect(result).toContain("swarm.example.com");
|
|
35
35
|
expect(result).toContain(`https://agent-abc-123.swarm.example.com`);
|
|
36
36
|
});
|
|
@@ -40,13 +40,13 @@ describe("getBasePrompt — basic fields", () => {
|
|
|
40
40
|
// Identity fields (name, description, soulMd, identityMd)
|
|
41
41
|
// ---------------------------------------------------------------------------
|
|
42
42
|
describe("getBasePrompt — identity fields", () => {
|
|
43
|
-
test("includes name when provided", () => {
|
|
44
|
-
const result = getBasePrompt({ ...minimalArgs, name: "TestAgent" });
|
|
43
|
+
test("includes name when provided", async () => {
|
|
44
|
+
const result = await getBasePrompt({ ...minimalArgs, name: "TestAgent" });
|
|
45
45
|
expect(result).toContain("**Name:** TestAgent");
|
|
46
46
|
});
|
|
47
47
|
|
|
48
|
-
test("includes description when name provided", () => {
|
|
49
|
-
const result = getBasePrompt({
|
|
48
|
+
test("includes description when name provided", async () => {
|
|
49
|
+
const result = await getBasePrompt({
|
|
50
50
|
...minimalArgs,
|
|
51
51
|
name: "TestAgent",
|
|
52
52
|
description: "A helpful agent",
|
|
@@ -54,16 +54,16 @@ describe("getBasePrompt — identity fields", () => {
|
|
|
54
54
|
expect(result).toContain("**Description:** A helpful agent");
|
|
55
55
|
});
|
|
56
56
|
|
|
57
|
-
test("does not include description without name", () => {
|
|
58
|
-
const result = getBasePrompt({
|
|
57
|
+
test("does not include description without name", async () => {
|
|
58
|
+
const result = await getBasePrompt({
|
|
59
59
|
...minimalArgs,
|
|
60
60
|
description: "A helpful agent",
|
|
61
61
|
});
|
|
62
62
|
expect(result).not.toContain("**Description:**");
|
|
63
63
|
});
|
|
64
64
|
|
|
65
|
-
test("includes soulMd content", () => {
|
|
66
|
-
const result = getBasePrompt({
|
|
65
|
+
test("includes soulMd content", async () => {
|
|
66
|
+
const result = await getBasePrompt({
|
|
67
67
|
...minimalArgs,
|
|
68
68
|
soulMd: "I am a creative soul.",
|
|
69
69
|
});
|
|
@@ -71,8 +71,8 @@ describe("getBasePrompt — identity fields", () => {
|
|
|
71
71
|
expect(result).toContain("I am a creative soul.");
|
|
72
72
|
});
|
|
73
73
|
|
|
74
|
-
test("includes identityMd content", () => {
|
|
75
|
-
const result = getBasePrompt({
|
|
74
|
+
test("includes identityMd content", async () => {
|
|
75
|
+
const result = await getBasePrompt({
|
|
76
76
|
...minimalArgs,
|
|
77
77
|
identityMd: "Identity content here.",
|
|
78
78
|
});
|
|
@@ -80,8 +80,8 @@ describe("getBasePrompt — identity fields", () => {
|
|
|
80
80
|
expect(result).toContain("Identity content here.");
|
|
81
81
|
});
|
|
82
82
|
|
|
83
|
-
test("no identity section when none provided", () => {
|
|
84
|
-
const result = getBasePrompt(minimalArgs);
|
|
83
|
+
test("no identity section when none provided", async () => {
|
|
84
|
+
const result = await getBasePrompt(minimalArgs);
|
|
85
85
|
expect(result).not.toContain("## Your Identity");
|
|
86
86
|
});
|
|
87
87
|
});
|
|
@@ -90,8 +90,8 @@ describe("getBasePrompt — identity fields", () => {
|
|
|
90
90
|
// claudeMd and toolsMd injection
|
|
91
91
|
// ---------------------------------------------------------------------------
|
|
92
92
|
describe("getBasePrompt — claudeMd and toolsMd injection", () => {
|
|
93
|
-
test("includes claudeMd under Agent Instructions", () => {
|
|
94
|
-
const result = getBasePrompt({
|
|
93
|
+
test("includes claudeMd under Agent Instructions", async () => {
|
|
94
|
+
const result = await getBasePrompt({
|
|
95
95
|
...minimalArgs,
|
|
96
96
|
claudeMd: "Follow these rules.",
|
|
97
97
|
});
|
|
@@ -99,8 +99,8 @@ describe("getBasePrompt — claudeMd and toolsMd injection", () => {
|
|
|
99
99
|
expect(result).toContain("Follow these rules.");
|
|
100
100
|
});
|
|
101
101
|
|
|
102
|
-
test("includes toolsMd under Tools & Capabilities", () => {
|
|
103
|
-
const result = getBasePrompt({
|
|
102
|
+
test("includes toolsMd under Tools & Capabilities", async () => {
|
|
103
|
+
const result = await getBasePrompt({
|
|
104
104
|
...minimalArgs,
|
|
105
105
|
toolsMd: "You can use curl.",
|
|
106
106
|
});
|
|
@@ -108,8 +108,8 @@ describe("getBasePrompt — claudeMd and toolsMd injection", () => {
|
|
|
108
108
|
expect(result).toContain("You can use curl.");
|
|
109
109
|
});
|
|
110
110
|
|
|
111
|
-
test("both claudeMd and toolsMd coexist", () => {
|
|
112
|
-
const result = getBasePrompt({
|
|
111
|
+
test("both claudeMd and toolsMd coexist", async () => {
|
|
112
|
+
const result = await getBasePrompt({
|
|
113
113
|
...minimalArgs,
|
|
114
114
|
claudeMd: "Agent instructions content",
|
|
115
115
|
toolsMd: "Tools content",
|
|
@@ -120,8 +120,8 @@ describe("getBasePrompt — claudeMd and toolsMd injection", () => {
|
|
|
120
120
|
expect(result).toContain("Tools content");
|
|
121
121
|
});
|
|
122
122
|
|
|
123
|
-
test("neither present when not provided", () => {
|
|
124
|
-
const result = getBasePrompt(minimalArgs);
|
|
123
|
+
test("neither present when not provided", async () => {
|
|
124
|
+
const result = await getBasePrompt(minimalArgs);
|
|
125
125
|
expect(result).not.toContain("## Agent Instructions");
|
|
126
126
|
expect(result).not.toContain("## Your Tools & Capabilities");
|
|
127
127
|
});
|
|
@@ -131,8 +131,8 @@ describe("getBasePrompt — claudeMd and toolsMd injection", () => {
|
|
|
131
131
|
// repoContext
|
|
132
132
|
// ---------------------------------------------------------------------------
|
|
133
133
|
describe("getBasePrompt — repoContext", () => {
|
|
134
|
-
test("includes repo claudeMd with clone path", () => {
|
|
135
|
-
const result = getBasePrompt({
|
|
134
|
+
test("includes repo claudeMd with clone path", async () => {
|
|
135
|
+
const result = await getBasePrompt({
|
|
136
136
|
...minimalArgs,
|
|
137
137
|
repoContext: {
|
|
138
138
|
claudeMd: "Repo-specific rules here.",
|
|
@@ -144,8 +144,8 @@ describe("getBasePrompt — repoContext", () => {
|
|
|
144
144
|
expect(result).toContain("Repo-specific rules here.");
|
|
145
145
|
});
|
|
146
146
|
|
|
147
|
-
test("shows warning when provided", () => {
|
|
148
|
-
const result = getBasePrompt({
|
|
147
|
+
test("shows warning when provided", async () => {
|
|
148
|
+
const result = await getBasePrompt({
|
|
149
149
|
...minimalArgs,
|
|
150
150
|
repoContext: {
|
|
151
151
|
claudeMd: "Rules",
|
|
@@ -156,8 +156,8 @@ describe("getBasePrompt — repoContext", () => {
|
|
|
156
156
|
expect(result).toContain("WARNING: Repo is stale");
|
|
157
157
|
});
|
|
158
158
|
|
|
159
|
-
test("shows 'no CLAUDE.md' message when claudeMd is null and no warning", () => {
|
|
160
|
-
const result = getBasePrompt({
|
|
159
|
+
test("shows 'no CLAUDE.md' message when claudeMd is null and no warning", async () => {
|
|
160
|
+
const result = await getBasePrompt({
|
|
161
161
|
...minimalArgs,
|
|
162
162
|
repoContext: {
|
|
163
163
|
claudeMd: null,
|
|
@@ -172,21 +172,21 @@ describe("getBasePrompt — repoContext", () => {
|
|
|
172
172
|
// Capabilities
|
|
173
173
|
// ---------------------------------------------------------------------------
|
|
174
174
|
describe("getBasePrompt — capabilities", () => {
|
|
175
|
-
test("services section included by default", () => {
|
|
176
|
-
const result = getBasePrompt(minimalArgs);
|
|
175
|
+
test("services section included by default", async () => {
|
|
176
|
+
const result = await getBasePrompt(minimalArgs);
|
|
177
177
|
expect(result).toContain("Service Registry");
|
|
178
178
|
});
|
|
179
179
|
|
|
180
|
-
test("services section excluded when capabilities don't include services", () => {
|
|
181
|
-
const result = getBasePrompt({
|
|
180
|
+
test("services section excluded when capabilities don't include services", async () => {
|
|
181
|
+
const result = await getBasePrompt({
|
|
182
182
|
...minimalArgs,
|
|
183
183
|
capabilities: ["artifacts"],
|
|
184
184
|
});
|
|
185
185
|
expect(result).not.toContain("Service Registry");
|
|
186
186
|
});
|
|
187
187
|
|
|
188
|
-
test("capabilities list rendered", () => {
|
|
189
|
-
const result = getBasePrompt({
|
|
188
|
+
test("capabilities list rendered", async () => {
|
|
189
|
+
const result = await getBasePrompt({
|
|
190
190
|
...minimalArgs,
|
|
191
191
|
capabilities: ["services", "artifacts"],
|
|
192
192
|
});
|
|
@@ -202,8 +202,8 @@ describe("getBasePrompt — capabilities", () => {
|
|
|
202
202
|
describe("getBasePrompt — truncation", () => {
|
|
203
203
|
const bigString = (n: number) => "x".repeat(n);
|
|
204
204
|
|
|
205
|
-
test("claudeMd truncated when exceeding per-section limit (20k chars)", () => {
|
|
206
|
-
const result = getBasePrompt({
|
|
205
|
+
test("claudeMd truncated when exceeding per-section limit (20k chars)", async () => {
|
|
206
|
+
const result = await getBasePrompt({
|
|
207
207
|
...minimalArgs,
|
|
208
208
|
claudeMd: bigString(25_000),
|
|
209
209
|
});
|
|
@@ -212,8 +212,8 @@ describe("getBasePrompt — truncation", () => {
|
|
|
212
212
|
expect(result).not.toContain(bigString(25_000));
|
|
213
213
|
});
|
|
214
214
|
|
|
215
|
-
test("toolsMd truncated when exceeding per-section limit", () => {
|
|
216
|
-
const result = getBasePrompt({
|
|
215
|
+
test("toolsMd truncated when exceeding per-section limit", async () => {
|
|
216
|
+
const result = await getBasePrompt({
|
|
217
217
|
...minimalArgs,
|
|
218
218
|
toolsMd: bigString(25_000),
|
|
219
219
|
});
|
|
@@ -221,18 +221,18 @@ describe("getBasePrompt — truncation", () => {
|
|
|
221
221
|
expect(result).not.toContain(bigString(25_000));
|
|
222
222
|
});
|
|
223
223
|
|
|
224
|
-
test("total budget respected — tools truncated before claudeMd", () => {
|
|
224
|
+
test("total budget respected — tools truncated before claudeMd", async () => {
|
|
225
225
|
// Use soulMd to eat up most of the 150k total budget so that
|
|
226
226
|
// truncatable sections (claudeMd, toolsMd) must compete for the remainder.
|
|
227
227
|
// soulMd is part of `prompt` which counts toward protectedLength.
|
|
228
|
-
const baseResult = getBasePrompt(minimalArgs);
|
|
228
|
+
const baseResult = await getBasePrompt(minimalArgs);
|
|
229
229
|
const staticLength = baseResult.length; // ~12-13k for static content
|
|
230
230
|
|
|
231
231
|
// Leave exactly enough budget for claudeMd but not toolsMd.
|
|
232
232
|
// Total budget = 150k - protectedLength.
|
|
233
233
|
// We want: protectedLength ≈ 150k - 18k = 132k, so claudeMd (15k) fits but toolsMd doesn't.
|
|
234
234
|
const soulSize = 132_000 - staticLength;
|
|
235
|
-
const result = getBasePrompt({
|
|
235
|
+
const result = await getBasePrompt({
|
|
236
236
|
...minimalArgs,
|
|
237
237
|
soulMd: bigString(Math.max(0, soulSize)),
|
|
238
238
|
claudeMd: bigString(15_000),
|
|
@@ -248,9 +248,9 @@ describe("getBasePrompt — truncation", () => {
|
|
|
248
248
|
expect(hasToolsTruncation || !hasToolsHeader).toBe(true);
|
|
249
249
|
});
|
|
250
250
|
|
|
251
|
-
test("repo context never truncated", () => {
|
|
251
|
+
test("repo context never truncated", async () => {
|
|
252
252
|
const hugeRepoClaudeMd = bigString(30_000);
|
|
253
|
-
const result = getBasePrompt({
|
|
253
|
+
const result = await getBasePrompt({
|
|
254
254
|
...minimalArgs,
|
|
255
255
|
repoContext: {
|
|
256
256
|
claudeMd: hugeRepoClaudeMd,
|
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
import { afterAll, beforeAll, describe, expect, mock, setDefaultTimeout, test } from "bun:test";
|
|
2
|
+
import { unlinkSync } from "node:fs";
|
|
3
|
+
import { createServer as createHttpServer, type Server } from "node:http";
|
|
4
|
+
|
|
5
|
+
// Migrations + mock.module + @slack/bolt resolution can take >5s on cold start
|
|
6
|
+
setDefaultTimeout(30_000);
|
|
7
|
+
|
|
8
|
+
// ─── Mock Slack client (must be before channel-activity import) ─────────────
|
|
9
|
+
|
|
10
|
+
// Mutable mock implementation — changed per test via reassignment
|
|
11
|
+
let historyImpl: (args: Record<string, unknown>) => Promise<{
|
|
12
|
+
messages?: Array<Record<string, unknown>>;
|
|
13
|
+
}> = () => Promise.resolve({ messages: [] });
|
|
14
|
+
|
|
15
|
+
mock.module("../slack/app", () => ({
|
|
16
|
+
getSlackApp: () => ({
|
|
17
|
+
client: {
|
|
18
|
+
auth: { test: () => Promise.resolve({ user_id: "UBOT123" }) },
|
|
19
|
+
conversations: {
|
|
20
|
+
history: (args: Record<string, unknown>) => historyImpl(args),
|
|
21
|
+
list: () =>
|
|
22
|
+
Promise.resolve({
|
|
23
|
+
channels: [
|
|
24
|
+
{ id: "C001", name: "general", is_member: true },
|
|
25
|
+
{ id: "C002", name: "random", is_member: true },
|
|
26
|
+
{ id: "C003", name: "archived", is_member: false },
|
|
27
|
+
],
|
|
28
|
+
response_metadata: {},
|
|
29
|
+
}),
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
}),
|
|
33
|
+
}));
|
|
34
|
+
|
|
35
|
+
import {
|
|
36
|
+
closeDb,
|
|
37
|
+
getAllChannelActivityCursors,
|
|
38
|
+
getChannelActivityCursor,
|
|
39
|
+
initDb,
|
|
40
|
+
upsertChannelActivityCursor,
|
|
41
|
+
} from "../be/db";
|
|
42
|
+
import { fetchChannelActivity } from "../slack/channel-activity";
|
|
43
|
+
|
|
44
|
+
const TEST_DB_PATH = `./test-channel-activity-${process.pid}.sqlite`;
|
|
45
|
+
|
|
46
|
+
beforeAll(() => {
|
|
47
|
+
initDb(TEST_DB_PATH);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
afterAll(() => {
|
|
51
|
+
closeDb();
|
|
52
|
+
for (const suffix of ["", "-wal", "-shm"]) {
|
|
53
|
+
try {
|
|
54
|
+
unlinkSync(`${TEST_DB_PATH}${suffix}`);
|
|
55
|
+
} catch {
|
|
56
|
+
// ignore
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// ─── DB Functions ──────────────────────────────────────────────────────────────
|
|
62
|
+
|
|
63
|
+
describe("Channel Activity Cursors — DB functions", () => {
|
|
64
|
+
test("getChannelActivityCursor returns null for non-existent channel", () => {
|
|
65
|
+
const cursor = getChannelActivityCursor("C_NONEXISTENT");
|
|
66
|
+
expect(cursor).toBeNull();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("getAllChannelActivityCursors returns an array", () => {
|
|
70
|
+
const cursors = getAllChannelActivityCursors();
|
|
71
|
+
expect(Array.isArray(cursors)).toBe(true);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("upsertChannelActivityCursor inserts a new cursor", () => {
|
|
75
|
+
upsertChannelActivityCursor("C_INSERT_TEST", "1711111111.000001");
|
|
76
|
+
const cursor = getChannelActivityCursor("C_INSERT_TEST");
|
|
77
|
+
expect(cursor).not.toBeNull();
|
|
78
|
+
expect(cursor!.channelId).toBe("C_INSERT_TEST");
|
|
79
|
+
expect(cursor!.lastSeenTs).toBe("1711111111.000001");
|
|
80
|
+
expect(cursor!.updatedAt).toBeTruthy();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("upsertChannelActivityCursor updates existing cursor", () => {
|
|
84
|
+
upsertChannelActivityCursor("C_UPDATE_TEST", "1711111111.000001");
|
|
85
|
+
upsertChannelActivityCursor("C_UPDATE_TEST", "1711111111.000099");
|
|
86
|
+
const after = getChannelActivityCursor("C_UPDATE_TEST");
|
|
87
|
+
expect(after!.lastSeenTs).toBe("1711111111.000099");
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("getAllChannelActivityCursors returns all inserted cursors", () => {
|
|
91
|
+
upsertChannelActivityCursor("C_ALL_1", "1711111111.000001");
|
|
92
|
+
upsertChannelActivityCursor("C_ALL_2", "1711111111.000002");
|
|
93
|
+
const ids = getAllChannelActivityCursors().map((c) => c.channelId);
|
|
94
|
+
expect(ids).toContain("C_ALL_1");
|
|
95
|
+
expect(ids).toContain("C_ALL_2");
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test("channelId is primary key — no duplicates", () => {
|
|
99
|
+
upsertChannelActivityCursor("C_PK_TEST", "1711111111.000001");
|
|
100
|
+
upsertChannelActivityCursor("C_PK_TEST", "1711111111.000002");
|
|
101
|
+
upsertChannelActivityCursor("C_PK_TEST", "1711111111.000003");
|
|
102
|
+
const cursors = getAllChannelActivityCursors().filter((c) => c.channelId === "C_PK_TEST");
|
|
103
|
+
expect(cursors.length).toBe(1);
|
|
104
|
+
expect(cursors[0].lastSeenTs).toBe("1711111111.000003");
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// ─── Cursor Commit Endpoint ─────────────────────────────────────────────────
|
|
109
|
+
|
|
110
|
+
describe("Channel Activity — cursor commit endpoint", () => {
|
|
111
|
+
let server: Server;
|
|
112
|
+
const TEST_PORT = 13099;
|
|
113
|
+
|
|
114
|
+
beforeAll(async () => {
|
|
115
|
+
server = createHttpServer(async (req, res) => {
|
|
116
|
+
if (req.method === "POST" && req.url === "/api/channel-activity/commit-cursors") {
|
|
117
|
+
const body = await new Promise<string>((resolve) => {
|
|
118
|
+
let data = "";
|
|
119
|
+
req.on("data", (chunk: Buffer) => {
|
|
120
|
+
data += chunk.toString();
|
|
121
|
+
});
|
|
122
|
+
req.on("end", () => resolve(data));
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
const parsed = JSON.parse(body) as {
|
|
127
|
+
cursorUpdates?: Array<{ channelId: string; ts: string }>;
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
if (!parsed.cursorUpdates || !Array.isArray(parsed.cursorUpdates)) {
|
|
131
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
132
|
+
res.end(JSON.stringify({ error: "Missing cursorUpdates array" }));
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
for (const { channelId, ts } of parsed.cursorUpdates) {
|
|
137
|
+
if (channelId && ts) {
|
|
138
|
+
upsertChannelActivityCursor(channelId, ts);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
143
|
+
res.end(JSON.stringify({ success: true, committed: parsed.cursorUpdates.length }));
|
|
144
|
+
} catch (err) {
|
|
145
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
146
|
+
res.end(JSON.stringify({ error: `Invalid request: ${err}` }));
|
|
147
|
+
}
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
res.writeHead(404);
|
|
152
|
+
res.end("Not Found");
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
await new Promise<void>((resolve) => {
|
|
156
|
+
server.listen(TEST_PORT, () => resolve());
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
afterAll(() => {
|
|
161
|
+
server.close();
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
test("commits cursor updates successfully", async () => {
|
|
165
|
+
const resp = await fetch(`http://localhost:${TEST_PORT}/api/channel-activity/commit-cursors`, {
|
|
166
|
+
method: "POST",
|
|
167
|
+
headers: { "Content-Type": "application/json" },
|
|
168
|
+
body: JSON.stringify({
|
|
169
|
+
cursorUpdates: [
|
|
170
|
+
{ channelId: "C_COMMIT_1", ts: "1711222222.000001" },
|
|
171
|
+
{ channelId: "C_COMMIT_2", ts: "1711222222.000002" },
|
|
172
|
+
],
|
|
173
|
+
}),
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
expect(resp.status).toBe(200);
|
|
177
|
+
const data = (await resp.json()) as { success: boolean; committed: number };
|
|
178
|
+
expect(data.success).toBe(true);
|
|
179
|
+
expect(data.committed).toBe(2);
|
|
180
|
+
|
|
181
|
+
expect(getChannelActivityCursor("C_COMMIT_1")!.lastSeenTs).toBe("1711222222.000001");
|
|
182
|
+
expect(getChannelActivityCursor("C_COMMIT_2")!.lastSeenTs).toBe("1711222222.000002");
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
test("rejects request without cursorUpdates array", async () => {
|
|
186
|
+
const resp = await fetch(`http://localhost:${TEST_PORT}/api/channel-activity/commit-cursors`, {
|
|
187
|
+
method: "POST",
|
|
188
|
+
headers: { "Content-Type": "application/json" },
|
|
189
|
+
body: JSON.stringify({ foo: "bar" }),
|
|
190
|
+
});
|
|
191
|
+
expect(resp.status).toBe(400);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
test("rejects invalid JSON body", async () => {
|
|
195
|
+
const resp = await fetch(`http://localhost:${TEST_PORT}/api/channel-activity/commit-cursors`, {
|
|
196
|
+
method: "POST",
|
|
197
|
+
headers: { "Content-Type": "application/json" },
|
|
198
|
+
body: "not json",
|
|
199
|
+
});
|
|
200
|
+
expect(resp.status).toBe(400);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
test("skips entries with missing channelId or ts", async () => {
|
|
204
|
+
const resp = await fetch(`http://localhost:${TEST_PORT}/api/channel-activity/commit-cursors`, {
|
|
205
|
+
method: "POST",
|
|
206
|
+
headers: { "Content-Type": "application/json" },
|
|
207
|
+
body: JSON.stringify({
|
|
208
|
+
cursorUpdates: [
|
|
209
|
+
{ channelId: "C_VALID", ts: "1711333333.000001" },
|
|
210
|
+
{ channelId: "", ts: "1711333333.000002" },
|
|
211
|
+
{ channelId: "C_NO_TS", ts: "" },
|
|
212
|
+
],
|
|
213
|
+
}),
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
expect(resp.status).toBe(200);
|
|
217
|
+
expect(getChannelActivityCursor("C_VALID")!.lastSeenTs).toBe("1711333333.000001");
|
|
218
|
+
expect(getChannelActivityCursor("C_NO_TS")).toBeNull();
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
// ─── fetchChannelActivity (mocked Slack) ─────────────────────────────────────
|
|
223
|
+
|
|
224
|
+
describe("Channel Activity — fetchChannelActivity", () => {
|
|
225
|
+
test("cold-start: channels without cursor get seed cursors, no messages", async () => {
|
|
226
|
+
historyImpl = () =>
|
|
227
|
+
Promise.resolve({
|
|
228
|
+
messages: [{ ts: "1711000000.000100", user: "U123", text: "Latest" }],
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
const result = await fetchChannelActivity(new Map());
|
|
232
|
+
|
|
233
|
+
// Cold start should NOT return messages (prevents flood)
|
|
234
|
+
expect(result.messages).toHaveLength(0);
|
|
235
|
+
// Should have seed cursors for bot-member channels (C001 and C002; C003 is_member=false)
|
|
236
|
+
expect(result.seedCursors.size).toBe(2);
|
|
237
|
+
expect(result.seedCursors.get("C001")).toBe("1711000000.000100");
|
|
238
|
+
expect(result.seedCursors.get("C002")).toBe("1711000000.000100");
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
test("incremental: returns messages newer than cursor, sorted oldest-first", async () => {
|
|
242
|
+
historyImpl = () =>
|
|
243
|
+
Promise.resolve({
|
|
244
|
+
messages: [
|
|
245
|
+
{ ts: "1711000000.000050", user: "U123", text: "At cursor" },
|
|
246
|
+
{ ts: "1711000000.000060", user: "U456", text: "Newer 2" },
|
|
247
|
+
{ ts: "1711000000.000055", user: "U789", text: "Newer 1" },
|
|
248
|
+
],
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
const cursors = new Map([
|
|
252
|
+
["C001", "1711000000.000050"],
|
|
253
|
+
["C002", "1711000000.000050"],
|
|
254
|
+
]);
|
|
255
|
+
const result = await fetchChannelActivity(cursors);
|
|
256
|
+
|
|
257
|
+
// Cursor message skipped, 2 newer per channel × 2 channels = 4
|
|
258
|
+
const c001msgs = result.messages.filter((m) => m.channelId === "C001");
|
|
259
|
+
expect(c001msgs).toHaveLength(2);
|
|
260
|
+
// Sorted oldest-first
|
|
261
|
+
expect(Number.parseFloat(result.messages[0].ts)).toBeLessThanOrEqual(
|
|
262
|
+
Number.parseFloat(result.messages[result.messages.length - 1].ts),
|
|
263
|
+
);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
test("filters bot messages, thread replies, empty text, own bot user", async () => {
|
|
267
|
+
historyImpl = () =>
|
|
268
|
+
Promise.resolve({
|
|
269
|
+
messages: [
|
|
270
|
+
{ ts: "1711000000.000051", user: "U123", text: "Valid message" },
|
|
271
|
+
{ ts: "1711000000.000052", user: "U456", text: "Bot msg", bot_id: "B001" },
|
|
272
|
+
{ ts: "1711000000.000053", user: "U789", text: "Bot subtype", subtype: "bot_message" },
|
|
273
|
+
{ ts: "1711000000.000054", user: "UBOT123", text: "Own bot msg" },
|
|
274
|
+
{ ts: "1711000000.000055", user: "U111", text: "" },
|
|
275
|
+
{
|
|
276
|
+
ts: "1711000000.000056",
|
|
277
|
+
user: "U222",
|
|
278
|
+
text: "Thread reply",
|
|
279
|
+
thread_ts: "1711000000.000001",
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
ts: "1711000000.000057",
|
|
283
|
+
user: "U333",
|
|
284
|
+
text: "Thread parent",
|
|
285
|
+
thread_ts: "1711000000.000057",
|
|
286
|
+
},
|
|
287
|
+
{ ts: "1711000000.000058", user: undefined, text: "No user" },
|
|
288
|
+
],
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
const cursors = new Map([["C001", "1711000000.000050"]]);
|
|
292
|
+
const result = await fetchChannelActivity(cursors, ["C001"]);
|
|
293
|
+
|
|
294
|
+
const texts = result.messages.map((m) => m.text);
|
|
295
|
+
expect(texts).toContain("Valid message");
|
|
296
|
+
expect(texts).toContain("Thread parent");
|
|
297
|
+
expect(texts).not.toContain("Bot msg");
|
|
298
|
+
expect(texts).not.toContain("Bot subtype");
|
|
299
|
+
expect(texts).not.toContain("Own bot msg");
|
|
300
|
+
expect(texts).not.toContain("Thread reply");
|
|
301
|
+
expect(texts).not.toContain("No user");
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
test("allowedChannelIds restricts which channels are processed", async () => {
|
|
305
|
+
historyImpl = () =>
|
|
306
|
+
Promise.resolve({
|
|
307
|
+
messages: [{ ts: "1711000000.000100", user: "U123", text: "Hello" }],
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
const cursors = new Map([
|
|
311
|
+
["C001", "1711000000.000050"],
|
|
312
|
+
["C002", "1711000000.000050"],
|
|
313
|
+
]);
|
|
314
|
+
const result = await fetchChannelActivity(cursors, ["C001"]);
|
|
315
|
+
|
|
316
|
+
const channelIds = new Set(result.messages.map((m) => m.channelId));
|
|
317
|
+
expect(channelIds.has("C001")).toBe(true);
|
|
318
|
+
expect(channelIds.has("C002")).toBe(false);
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
test("gracefully handles errors from conversations.history", async () => {
|
|
322
|
+
historyImpl = () => Promise.reject(new Error("channel_not_found"));
|
|
323
|
+
|
|
324
|
+
const cursors = new Map([["C001", "1711000000.000050"]]);
|
|
325
|
+
const result = await fetchChannelActivity(cursors, ["C001"]);
|
|
326
|
+
|
|
327
|
+
// Should not throw — errors are caught and logged
|
|
328
|
+
expect(result.messages).toHaveLength(0);
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
test("includes channelName in returned messages", async () => {
|
|
332
|
+
historyImpl = () =>
|
|
333
|
+
Promise.resolve({
|
|
334
|
+
messages: [{ ts: "1711000000.000051", user: "U123", text: "Hi" }],
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
const cursors = new Map([["C001", "1711000000.000050"]]);
|
|
338
|
+
const result = await fetchChannelActivity(cursors, ["C001"]);
|
|
339
|
+
|
|
340
|
+
expect(result.messages[0].channelName).toBe("general");
|
|
341
|
+
expect(result.messages[0].channelId).toBe("C001");
|
|
342
|
+
});
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
// ─── Migration ──────────────────────────────────────────────────────────────
|
|
346
|
+
|
|
347
|
+
describe("Channel Activity — migration 015", () => {
|
|
348
|
+
test("channel_activity_cursors table exists and has correct schema", () => {
|
|
349
|
+
upsertChannelActivityCursor("C_SCHEMA_TEST", "1711999999.000001");
|
|
350
|
+
const cursor = getChannelActivityCursor("C_SCHEMA_TEST");
|
|
351
|
+
expect(cursor).not.toBeNull();
|
|
352
|
+
expect(cursor!.channelId).toBe("C_SCHEMA_TEST");
|
|
353
|
+
expect(cursor!.lastSeenTs).toBe("1711999999.000001");
|
|
354
|
+
expect(cursor!.updatedAt).toBeTruthy();
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
test("channelId is PRIMARY KEY — duplicate insert updates instead of failing", () => {
|
|
358
|
+
upsertChannelActivityCursor("C_PK_MIG", "1711000000.000001");
|
|
359
|
+
upsertChannelActivityCursor("C_PK_MIG", "1711000000.000999");
|
|
360
|
+
const cursor = getChannelActivityCursor("C_PK_MIG");
|
|
361
|
+
expect(cursor!.lastSeenTs).toBe("1711000000.000999");
|
|
362
|
+
});
|
|
363
|
+
});
|