@mastra/factory 0.24.0-alpha.0 → 0.24.0-alpha.10
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 +106 -4
- package/dist/auth.d.ts +14 -0
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +45 -9
- package/dist/auth.js.map +1 -1
- package/dist/boards/review.d.ts.map +1 -1
- package/dist/boards/review.js +21 -1
- package/dist/boards/review.js.map +1 -1
- package/dist/boards/work-transition-policy.d.ts.map +1 -1
- package/dist/boards/work-transition-policy.js +12 -1
- package/dist/boards/work-transition-policy.js.map +1 -1
- package/dist/boards/work.d.ts +14 -0
- package/dist/boards/work.d.ts.map +1 -1
- package/dist/boards/work.js +70 -7
- package/dist/boards/work.js.map +1 -1
- package/dist/capabilities/version-control.d.ts +26 -0
- package/dist/capabilities/version-control.d.ts.map +1 -1
- package/dist/factory.d.ts +8 -0
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +354 -27
- package/dist/factory.js.map +1 -1
- package/dist/integrations/base.d.ts +1 -1
- package/dist/integrations/base.d.ts.map +1 -1
- package/dist/integrations/github/default-rules.d.ts.map +1 -1
- package/dist/integrations/github/default-rules.js +1 -16
- package/dist/integrations/github/default-rules.js.map +1 -1
- package/dist/integrations/github/integration.d.ts.map +1 -1
- package/dist/integrations/github/integration.js +37 -1
- package/dist/integrations/github/integration.js.map +1 -1
- package/dist/integrations/github/routes.d.ts +1 -1
- package/dist/integrations/github/routes.d.ts.map +1 -1
- package/dist/integrations/github/routes.js +19 -11
- package/dist/integrations/github/routes.js.map +1 -1
- package/dist/integrations/github/rules.d.ts.map +1 -1
- package/dist/integrations/github/rules.js +26 -22
- package/dist/integrations/github/rules.js.map +1 -1
- package/dist/integrations/github/sandbox-release.d.ts +15 -3
- package/dist/integrations/github/sandbox-release.d.ts.map +1 -1
- package/dist/integrations/github/sandbox-release.js +20 -3
- package/dist/integrations/github/sandbox-release.js.map +1 -1
- package/dist/integrations/github/sandbox.d.ts +2 -3
- package/dist/integrations/github/sandbox.d.ts.map +1 -1
- package/dist/integrations/github/sandbox.js +78 -7
- package/dist/integrations/github/sandbox.js.map +1 -1
- package/dist/integrations/github/session-subscriptions.d.ts.map +1 -1
- package/dist/integrations/github/session-subscriptions.js +1 -2
- package/dist/integrations/github/session-subscriptions.js.map +1 -1
- package/dist/integrations/github/subscriptions.d.ts +7 -4
- package/dist/integrations/github/subscriptions.d.ts.map +1 -1
- package/dist/integrations/github/subscriptions.js +5 -5
- package/dist/integrations/github/subscriptions.js.map +1 -1
- package/dist/integrations/github/webhook.d.ts +4 -2
- package/dist/integrations/github/webhook.d.ts.map +1 -1
- package/dist/integrations/github/webhook.js +59 -36
- package/dist/integrations/github/webhook.js.map +1 -1
- package/dist/integrations/gitlab/api.d.ts +16 -1
- package/dist/integrations/gitlab/api.d.ts.map +1 -1
- package/dist/integrations/gitlab/api.js +9 -0
- package/dist/integrations/gitlab/api.js.map +1 -1
- package/dist/integrations/gitlab/routes.d.ts.map +1 -1
- package/dist/integrations/gitlab/routes.js +10 -6
- package/dist/integrations/gitlab/routes.js.map +1 -1
- package/dist/integrations/gitlab/rules.d.ts.map +1 -1
- package/dist/integrations/gitlab/rules.js +28 -21
- package/dist/integrations/gitlab/rules.js.map +1 -1
- package/dist/integrations/gitlab/session-subscriptions.d.ts.map +1 -1
- package/dist/integrations/gitlab/session-subscriptions.js +1 -2
- package/dist/integrations/gitlab/session-subscriptions.js.map +1 -1
- package/dist/integrations/gitlab/subscriptions.d.ts +7 -4
- package/dist/integrations/gitlab/subscriptions.d.ts.map +1 -1
- package/dist/integrations/gitlab/subscriptions.js +5 -5
- package/dist/integrations/gitlab/subscriptions.js.map +1 -1
- package/dist/integrations/gitlab/version-control.d.ts.map +1 -1
- package/dist/integrations/gitlab/version-control.js +37 -3
- package/dist/integrations/gitlab/version-control.js.map +1 -1
- package/dist/integrations/incidentio/api.d.ts +9 -0
- package/dist/integrations/incidentio/api.d.ts.map +1 -1
- package/dist/integrations/incidentio/api.js +3 -0
- package/dist/integrations/incidentio/api.js.map +1 -1
- package/dist/integrations/incidentio/intake.d.ts +1 -1
- package/dist/integrations/incidentio/intake.d.ts.map +1 -1
- package/dist/integrations/incidentio/intake.js.map +1 -1
- package/dist/integrations/incidentio/integration.d.ts.map +1 -1
- package/dist/integrations/incidentio/integration.js +1 -0
- package/dist/integrations/incidentio/integration.js.map +1 -1
- package/dist/integrations/jira/api.d.ts +7 -0
- package/dist/integrations/jira/api.d.ts.map +1 -1
- package/dist/integrations/jira/api.js +4 -0
- package/dist/integrations/jira/api.js.map +1 -1
- package/dist/integrations/linear/agent-tools.d.ts.map +1 -1
- package/dist/integrations/linear/agent-tools.js.map +1 -1
- package/dist/integrations/platform/api-client.d.ts +6 -0
- package/dist/integrations/platform/api-client.d.ts.map +1 -1
- package/dist/integrations/platform/api-client.js +6 -1
- package/dist/integrations/platform/api-client.js.map +1 -1
- package/dist/integrations/platform/connect/routes.d.ts +1 -1
- package/dist/integrations/platform/connect/routes.d.ts.map +1 -1
- package/dist/integrations/platform/connect/routes.js.map +1 -1
- package/dist/integrations/platform/github/integration.d.ts.map +1 -1
- package/dist/integrations/platform/github/integration.js +27 -1
- package/dist/integrations/platform/github/integration.js.map +1 -1
- package/dist/integrations/platform/incidentio/integration.d.ts +1 -1
- package/dist/integrations/platform/incidentio/integration.d.ts.map +1 -1
- package/dist/integrations/platform/incidentio/integration.js +1 -0
- package/dist/integrations/platform/incidentio/integration.js.map +1 -1
- package/dist/integrations/platform/jira/integration.d.ts +1 -1
- package/dist/integrations/platform/jira/integration.d.ts.map +1 -1
- package/dist/integrations/platform/jira/integration.js +1 -0
- package/dist/integrations/platform/jira/integration.js.map +1 -1
- package/dist/integrations/platform/linear/event-worker.d.ts +1 -1
- package/dist/integrations/platform/linear/event-worker.d.ts.map +1 -1
- package/dist/integrations/platform/linear/event-worker.js.map +1 -1
- package/dist/integrations/slack/integration.d.ts.map +1 -1
- package/dist/integrations/slack/integration.js +33 -6
- package/dist/integrations/slack/integration.js.map +1 -1
- package/dist/integrations/slack/slack.d.ts +16 -5
- package/dist/integrations/slack/slack.d.ts.map +1 -1
- package/dist/integrations/slack/slack.js +137 -64
- package/dist/integrations/slack/slack.js.map +1 -1
- package/dist/integrations/subscription-session.d.ts +14 -0
- package/dist/integrations/subscription-session.d.ts.map +1 -1
- package/dist/integrations/subscription-session.js +30 -16
- package/dist/integrations/subscription-session.js.map +1 -1
- package/dist/mcp/disconnect.d.ts +10 -0
- package/dist/mcp/disconnect.d.ts.map +1 -0
- package/dist/mcp/disconnect.js +44 -0
- package/dist/mcp/disconnect.js.map +1 -0
- package/dist/mcp/routes.d.ts +46 -0
- package/dist/mcp/routes.d.ts.map +1 -0
- package/dist/mcp/routes.js +243 -0
- package/dist/mcp/routes.js.map +1 -0
- package/dist/provider-display-name.d.ts +2 -0
- package/dist/provider-display-name.d.ts.map +1 -0
- package/dist/provider-display-name.js +16 -0
- package/dist/provider-display-name.js.map +1 -0
- package/dist/review-verdict.d.ts +11 -0
- package/dist/review-verdict.d.ts.map +1 -1
- package/dist/review-verdict.js +16 -1
- package/dist/review-verdict.js.map +1 -1
- package/dist/routes/attention-activity.d.ts.map +1 -1
- package/dist/routes/attention-activity.js +5 -2
- package/dist/routes/attention-activity.js.map +1 -1
- package/dist/routes/attention-providers.d.ts +0 -1
- package/dist/routes/attention-providers.d.ts.map +1 -1
- package/dist/routes/attention-providers.js +5 -6
- package/dist/routes/attention-providers.js.map +1 -1
- package/dist/routes/config.d.ts +44 -5
- package/dist/routes/config.d.ts.map +1 -1
- package/dist/routes/config.js +181 -58
- package/dist/routes/config.js.map +1 -1
- package/dist/routes/contracts.d.ts +4 -0
- package/dist/routes/contracts.d.ts.map +1 -1
- package/dist/routes/contracts.js +3 -1
- package/dist/routes/contracts.js.map +1 -1
- package/dist/routes/custom-provider-source.d.ts +8 -0
- package/dist/routes/custom-provider-source.d.ts.map +1 -1
- package/dist/routes/custom-provider-source.js +22 -1
- package/dist/routes/custom-provider-source.js.map +1 -1
- package/dist/routes/oauth.js +1 -1
- package/dist/routes/oauth.js.map +1 -1
- package/dist/routes/projects.d.ts +1 -1
- package/dist/routes/projects.d.ts.map +1 -1
- package/dist/routes/projects.js +1 -1
- package/dist/routes/projects.js.map +1 -1
- package/dist/routes/provider-credentials.d.ts +7 -2
- package/dist/routes/provider-credentials.d.ts.map +1 -1
- package/dist/routes/provider-credentials.js +30 -8
- package/dist/routes/provider-credentials.js.map +1 -1
- package/dist/routes/skills.d.ts +2 -4
- package/dist/routes/skills.d.ts.map +1 -1
- package/dist/routes/skills.js +19 -65
- package/dist/routes/skills.js.map +1 -1
- package/dist/routes/source-control-sessions.d.ts +9 -1
- package/dist/routes/source-control-sessions.d.ts.map +1 -1
- package/dist/routes/source-control-sessions.js +131 -7
- package/dist/routes/source-control-sessions.js.map +1 -1
- package/dist/routes/surface.d.ts +3 -2
- package/dist/routes/surface.d.ts.map +1 -1
- package/dist/routes/surface.js +7 -3
- package/dist/routes/surface.js.map +1 -1
- package/dist/routes/work-items.d.ts.map +1 -1
- package/dist/routes/work-items.js +60 -120
- package/dist/routes/work-items.js.map +1 -1
- package/dist/rules/binding-context.js +7 -7
- package/dist/rules/binding-context.js.map +1 -1
- package/dist/rules/dispatch-errors.d.ts.map +1 -1
- package/dist/rules/dispatch-errors.js +12 -0
- package/dist/rules/dispatch-errors.js.map +1 -1
- package/dist/rules/dispatcher.d.ts +8 -1
- package/dist/rules/dispatcher.d.ts.map +1 -1
- package/dist/rules/dispatcher.js +117 -12
- package/dist/rules/dispatcher.js.map +1 -1
- package/dist/rules/processor.d.ts.map +1 -1
- package/dist/rules/processor.js +2 -1
- package/dist/rules/processor.js.map +1 -1
- package/dist/rules/start-coordinator.d.ts +4 -0
- package/dist/rules/start-coordinator.d.ts.map +1 -1
- package/dist/rules/start-coordinator.js +36 -4
- package/dist/rules/start-coordinator.js.map +1 -1
- package/dist/rules/tools.d.ts.map +1 -1
- package/dist/rules/tools.js +82 -3
- package/dist/rules/tools.js.map +1 -1
- package/dist/rules/transition-service.d.ts +17 -0
- package/dist/rules/transition-service.d.ts.map +1 -1
- package/dist/rules/transition-service.js +25 -0
- package/dist/rules/transition-service.js.map +1 -1
- package/dist/rules/types.d.ts +2 -4
- package/dist/rules/types.d.ts.map +1 -1
- package/dist/rules/types.js +1 -4
- package/dist/rules/types.js.map +1 -1
- package/dist/sandbox/local-factory-sandbox.d.ts.map +1 -1
- package/dist/sandbox/local-factory-sandbox.js +1 -0
- package/dist/sandbox/local-factory-sandbox.js.map +1 -1
- package/dist/sandbox/session-retirement.d.ts +27 -3
- package/dist/sandbox/session-retirement.d.ts.map +1 -1
- package/dist/sandbox/session-retirement.js +158 -20
- package/dist/sandbox/session-retirement.js.map +1 -1
- package/dist/secret-encryption.d.ts.map +1 -1
- package/dist/secret-encryption.js +65 -25
- package/dist/secret-encryption.js.map +1 -1
- package/dist/session/channel-reply-processor.d.ts +36 -0
- package/dist/session/channel-reply-processor.d.ts.map +1 -0
- package/dist/session/channel-reply-processor.js +46 -0
- package/dist/session/channel-reply-processor.js.map +1 -0
- package/dist/session/channel-session-link-processor.d.ts +39 -0
- package/dist/session/channel-session-link-processor.d.ts.map +1 -0
- package/dist/session/channel-session-link-processor.js +100 -0
- package/dist/session/channel-session-link-processor.js.map +1 -0
- package/dist/session/credential-policy.d.ts +1 -1
- package/dist/session/credential-policy.d.ts.map +1 -1
- package/dist/session/credential-policy.js +7 -6
- package/dist/session/credential-policy.js.map +1 -1
- package/dist/session/default-model-hydration.d.ts +1 -0
- package/dist/session/default-model-hydration.d.ts.map +1 -1
- package/dist/session/default-model-hydration.js +3 -2
- package/dist/session/default-model-hydration.js.map +1 -1
- package/dist/session/environment-state-processor.js +1 -1
- package/dist/session/environment-state-processor.js.map +1 -1
- package/dist/session/factory-session.d.ts +19 -1
- package/dist/session/factory-session.d.ts.map +1 -1
- package/dist/session/factory-session.js +5 -2
- package/dist/session/factory-session.js.map +1 -1
- package/dist/session/filesystem-capture.d.ts +3 -3
- package/dist/session/filesystem-capture.d.ts.map +1 -1
- package/dist/session/filesystem-capture.js +16 -17
- package/dist/session/filesystem-capture.js.map +1 -1
- package/dist/session/first-exec-capture.d.ts +1 -1
- package/dist/session/first-exec-capture.d.ts.map +1 -1
- package/dist/session/first-exec-capture.js +1 -1
- package/dist/session/first-exec-capture.js.map +1 -1
- package/dist/session/first-message-capture.d.ts +1 -1
- package/dist/session/first-message-capture.d.ts.map +1 -1
- package/dist/session/first-message-capture.js +1 -1
- package/dist/session/first-message-capture.js.map +1 -1
- package/dist/session/live-sessions.d.ts +1 -1
- package/dist/session/live-sessions.js +1 -1
- package/dist/session/live-sessions.js.map +1 -1
- package/dist/session/memory-settings-hydration.d.ts +1 -1
- package/dist/session/memory-settings-hydration.d.ts.map +1 -1
- package/dist/session/memory-settings-hydration.js +1 -1
- package/dist/session/memory-settings-hydration.js.map +1 -1
- package/dist/session/reply-credential-processor.d.ts +15 -0
- package/dist/session/reply-credential-processor.d.ts.map +1 -0
- package/dist/session/reply-credential-processor.js +38 -0
- package/dist/session/reply-credential-processor.js.map +1 -0
- package/dist/session/resource-migration.d.ts +87 -0
- package/dist/session/resource-migration.d.ts.map +1 -0
- package/dist/session/resource-migration.js +163 -0
- package/dist/session/resource-migration.js.map +1 -0
- package/dist/session/run-audit.d.ts +24 -0
- package/dist/session/run-audit.d.ts.map +1 -1
- package/dist/session/run-audit.js +93 -7
- package/dist/session/run-audit.js.map +1 -1
- package/dist/session/session-threads.d.ts +49 -0
- package/dist/session/session-threads.d.ts.map +1 -0
- package/dist/session/session-threads.js +70 -0
- package/dist/session/session-threads.js.map +1 -0
- package/dist/session/source-control-tools.d.ts +19 -1
- package/dist/session/source-control-tools.d.ts.map +1 -1
- package/dist/session/source-control-tools.js +78 -11
- package/dist/session/source-control-tools.js.map +1 -1
- package/dist/session/thinking-default.d.ts.map +1 -1
- package/dist/session/thinking-default.js +3 -4
- package/dist/session/thinking-default.js.map +1 -1
- package/dist/session/thread-session-stamp.d.ts +21 -0
- package/dist/session/thread-session-stamp.d.ts.map +1 -0
- package/dist/session/thread-session-stamp.js +20 -0
- package/dist/session/thread-session-stamp.js.map +1 -0
- package/dist/session/thread-title-mirror.d.ts +1 -1
- package/dist/session/thread-title-mirror.d.ts.map +1 -1
- package/dist/session/thread-title-mirror.js +1 -1
- package/dist/session/thread-title-mirror.js.map +1 -1
- package/dist/session/work-item-repository.d.ts +1 -1
- package/dist/session/work-item-repository.d.ts.map +1 -1
- package/dist/session/work-item-repository.js.map +1 -1
- package/dist/skills/service.d.ts +6 -8
- package/dist/skills/service.d.ts.map +1 -1
- package/dist/skills/service.js +3 -3
- package/dist/skills/service.js.map +1 -1
- package/dist/storage/domains/audit/agent-audit.d.ts.map +1 -1
- package/dist/storage/domains/audit/agent-audit.js +2 -2
- package/dist/storage/domains/audit/agent-audit.js.map +1 -1
- package/dist/storage/domains/audit/domain.js +1 -1
- package/dist/storage/domains/filesystem/base.d.ts +4 -0
- package/dist/storage/domains/filesystem/base.d.ts.map +1 -1
- package/dist/storage/domains/filesystem/base.js +5 -0
- package/dist/storage/domains/filesystem/base.js.map +1 -1
- package/dist/storage/domains/integrations/base.d.ts +8 -0
- package/dist/storage/domains/integrations/base.d.ts.map +1 -1
- package/dist/storage/domains/integrations/base.js +7 -0
- package/dist/storage/domains/integrations/base.js.map +1 -1
- package/dist/storage/domains/projects/base.d.ts +6 -0
- package/dist/storage/domains/projects/base.d.ts.map +1 -1
- package/dist/storage/domains/projects/base.js +12 -0
- package/dist/storage/domains/projects/base.js.map +1 -1
- package/dist/storage/domains/source-control/base.d.ts +28 -0
- package/dist/storage/domains/source-control/base.d.ts.map +1 -1
- package/dist/storage/domains/source-control/base.js +48 -0
- package/dist/storage/domains/source-control/base.js.map +1 -1
- package/dist/storage/domains/source-control/inmemory.d.ts +8 -0
- package/dist/storage/domains/source-control/inmemory.d.ts.map +1 -1
- package/dist/storage/domains/source-control/inmemory.js +13 -1
- package/dist/storage/domains/source-control/inmemory.js.map +1 -1
- package/dist/storage/domains/work-items/base.d.ts +79 -7
- package/dist/storage/domains/work-items/base.d.ts.map +1 -1
- package/dist/storage/domains/work-items/base.js +172 -30
- package/dist/storage/domains/work-items/base.js.map +1 -1
- package/dist/timing.d.ts +23 -0
- package/dist/timing.d.ts.map +1 -1
- package/dist/timing.js +85 -6
- package/dist/timing.js.map +1 -1
- package/dist/tools/annotations.d.ts +7 -0
- package/dist/tools/annotations.d.ts.map +1 -0
- package/dist/tools/annotations.js +102 -0
- package/dist/tools/annotations.js.map +1 -0
- package/dist/tools/approval-tools.d.ts +15 -0
- package/dist/tools/approval-tools.d.ts.map +1 -0
- package/dist/tools/approval-tools.js +79 -0
- package/dist/tools/approval-tools.js.map +1 -0
- package/dist/tools/deliver.d.ts +71 -0
- package/dist/tools/deliver.d.ts.map +1 -0
- package/dist/tools/deliver.js +129 -0
- package/dist/tools/deliver.js.map +1 -0
- package/dist/tools/index.d.ts +30 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +36 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/messaging-tools.d.ts +28 -0
- package/dist/tools/messaging-tools.d.ts.map +1 -0
- package/dist/tools/messaging-tools.js +396 -0
- package/dist/tools/messaging-tools.js.map +1 -0
- package/dist/tools/permissions.d.ts +31 -0
- package/dist/tools/permissions.d.ts.map +1 -0
- package/dist/tools/permissions.js +48 -0
- package/dist/tools/permissions.js.map +1 -0
- package/dist/tools/read-tools.d.ts +30 -0
- package/dist/tools/read-tools.d.ts.map +1 -0
- package/dist/tools/read-tools.js +293 -0
- package/dist/tools/read-tools.js.map +1 -0
- package/dist/tools/reports.d.ts +174 -0
- package/dist/tools/reports.d.ts.map +1 -0
- package/dist/tools/reports.js +380 -0
- package/dist/tools/reports.js.map +1 -0
- package/dist/tools/run-watch.d.ts +46 -0
- package/dist/tools/run-watch.d.ts.map +1 -0
- package/dist/tools/run-watch.js +102 -0
- package/dist/tools/run-watch.js.map +1 -0
- package/dist/tools/scope.d.ts +54 -0
- package/dist/tools/scope.d.ts.map +1 -0
- package/dist/tools/scope.js +87 -0
- package/dist/tools/scope.js.map +1 -0
- package/dist/tools/session-tools.d.ts +43 -0
- package/dist/tools/session-tools.d.ts.map +1 -0
- package/dist/tools/session-tools.js +410 -0
- package/dist/tools/session-tools.js.map +1 -0
- package/dist/tools/sessions-state-processor.d.ts +41 -0
- package/dist/tools/sessions-state-processor.d.ts.map +1 -0
- package/dist/tools/sessions-state-processor.js +190 -0
- package/dist/tools/sessions-state-processor.js.map +1 -0
- package/dist/tools/side-sessions.d.ts +53 -0
- package/dist/tools/side-sessions.d.ts.map +1 -0
- package/dist/tools/side-sessions.js +83 -0
- package/dist/tools/side-sessions.js.map +1 -0
- package/dist/tools/work-item-tools.d.ts +28 -0
- package/dist/tools/work-item-tools.d.ts.map +1 -0
- package/dist/tools/work-item-tools.js +342 -0
- package/dist/tools/work-item-tools.js.map +1 -0
- package/dist/work-items/create-work-item.d.ts +55 -0
- package/dist/work-items/create-work-item.d.ts.map +1 -0
- package/dist/work-items/create-work-item.js +101 -0
- package/dist/work-items/create-work-item.js.map +1 -0
- package/dist/work-items/decision-actions.d.ts +42 -0
- package/dist/work-items/decision-actions.d.ts.map +1 -0
- package/dist/work-items/decision-actions.js +70 -0
- package/dist/work-items/decision-actions.js.map +1 -0
- package/dist/workspace.d.ts +1 -1
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +15 -7
- package/dist/workspace.js.map +1 -1
- package/factory-skills/factory-gitlab-rereview/SKILL.md +1 -1
- package/factory-skills/factory-gitlab-review/SKILL.md +1 -1
- package/factory-skills/factory-plan/SKILL.md +4 -4
- package/factory-skills/factory-rereview/SKILL.md +3 -1
- package/factory-skills/factory-review/SKILL.md +3 -1
- package/factory-skills/factory-self-review/SKILL.md +46 -0
- package/package.json +15 -15
|
@@ -2,11 +2,27 @@ import { createSlackConnectRoutes } from "./connect-route.js";
|
|
|
2
2
|
import { SlackFeedPublisher } from "./feed-publisher.js";
|
|
3
3
|
import { createSlackChannelsConfig } from "./slack.js";
|
|
4
4
|
//#region src/integrations/slack/integration.ts
|
|
5
|
+
/** Slack renders the status inline next to the app name; keep it to one short line. */
|
|
6
|
+
const MAX_STATUS_LENGTH = 80;
|
|
7
|
+
/**
|
|
8
|
+
* Turns a tool's `description` arg ("Listing the repo root") into a status
|
|
9
|
+
* line that reads after the app name ("<App> is listing the repo root…").
|
|
10
|
+
* Only the first line counts: issue/PR creators carry whole bodies there.
|
|
11
|
+
*/
|
|
12
|
+
function describeToolCall(args) {
|
|
13
|
+
if (!args || typeof args !== "object") return void 0;
|
|
14
|
+
const { description } = args;
|
|
15
|
+
if (typeof description !== "string") return void 0;
|
|
16
|
+
const line = description.split("\n").map((part) => part.replace(/\s+/g, " ").trim()).find(Boolean);
|
|
17
|
+
if (!line) return void 0;
|
|
18
|
+
const text = line.length > MAX_STATUS_LENGTH ? `${line.slice(0, MAX_STATUS_LENGTH - 1)}…` : line;
|
|
19
|
+
return `is ${/^[A-Z][a-z]/.test(text) ? text.charAt(0).toLowerCase() + text.slice(1) : text}`;
|
|
20
|
+
}
|
|
5
21
|
const factoryTypingStatus = (chunk) => {
|
|
6
22
|
switch (chunk.type) {
|
|
7
23
|
case "reasoning-delta": return "is thinking...";
|
|
8
24
|
case "text-delta": return "is typing...";
|
|
9
|
-
case "tool-call": return "is working...";
|
|
25
|
+
case "tool-call": return describeToolCall(chunk.payload.args) ?? "is working...";
|
|
10
26
|
default: return;
|
|
11
27
|
}
|
|
12
28
|
};
|
|
@@ -17,6 +33,21 @@ const factoryTypingStatus = (chunk) => {
|
|
|
17
33
|
function adapterOverrides(options) {
|
|
18
34
|
return Object.fromEntries(Object.entries(options ?? {}).filter(([, value]) => value !== void 0));
|
|
19
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Factory defaults under the caller's overrides. Tool calls stay out of the
|
|
38
|
+
* thread: a Factory session runs hundreds of them, and a row per call (or a
|
|
39
|
+
* card per call) buries the reply. Progress shows through the typing status
|
|
40
|
+
* instead, which names the tool's current step; the session link carries the
|
|
41
|
+
* full trace. `grouped` remains available via `adapterOptions` for callers
|
|
42
|
+
* who want Slack's "Thinking Steps" widget.
|
|
43
|
+
*/
|
|
44
|
+
function factoryAdapterOptions(overrides) {
|
|
45
|
+
return {
|
|
46
|
+
typingStatus: factoryTypingStatus,
|
|
47
|
+
toolDisplay: "hidden",
|
|
48
|
+
...overrides
|
|
49
|
+
};
|
|
50
|
+
}
|
|
20
51
|
var SlackIntegration = class {
|
|
21
52
|
id = "slack";
|
|
22
53
|
/**
|
|
@@ -52,11 +83,7 @@ var SlackIntegration = class {
|
|
|
52
83
|
modelDefaults: ctx.storage.modelDefaults,
|
|
53
84
|
workItems: ctx.runtime?.workItems,
|
|
54
85
|
feed: ctx.feed,
|
|
55
|
-
adapterOptions:
|
|
56
|
-
toolDisplay: "hidden",
|
|
57
|
-
typingStatus: factoryTypingStatus,
|
|
58
|
-
...adapterOverrides(this.#config.adapterOptions)
|
|
59
|
-
}
|
|
86
|
+
adapterOptions: factoryAdapterOptions(adapterOverrides(this.#config.adapterOptions))
|
|
60
87
|
});
|
|
61
88
|
}
|
|
62
89
|
feedPublisher(ctx) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integration.js","names":["#config","#repoBackedSessions"],"sources":["../../../src/integrations/slack/integration.ts"],"sourcesContent":["/**\n * `SlackIntegration` — Slack as a `FactoryIntegration`.\n *\n * Slack contributes two things to a factory: the chat channels that carry\n * inbound messages into agent runs (`channels()`), and the browser-facing\n * account-link routes that bind a Slack sender to a Mastra tenant (`routes()`).\n * Both used to be assembled by hand in the deploy entry, which had to reach\n * into the prepared agent controller by string key, resolve storage domains\n * itself, mint its own state signer, and splice routes onto the factory's\n * assembled server config. Implementing the integration interface instead means\n * the factory does all of that the same way it already does for GitHub and\n * Linear, and the entry's job shrinks to reading Slack's env vars once.\n *\n * Slack ships as a factory built-in alongside GitHub and Linear (it originally\n * lived in `mastracode/web` to keep `@mastra/slack`/`chat` out of this package;\n * the team reversed that so `create-factory` consumers get Slack channels out\n * of the box). The integration interface remains open — third parties still add\n * capabilities by implementing `FactoryIntegration` from outside the package.\n */\n\nimport type { TypingStatusFn } from '@mastra/core/channels';\nimport type { ApiRoute } from '@mastra/core/server';\nimport type { SlackAdapterChannelConfig } from '@mastra/slack';\n\nimport type { WorkItemFeedPublisher } from '../../storage/domains/comments/feed-sync.js';\nimport type { FactoryChannelsConfig, FactoryIntegration, IntegrationContext } from '../base.js';\n\nimport { createSlackConnectRoutes } from './connect-route.js';\nimport { SlackFeedPublisher } from './feed-publisher.js';\nimport { createSlackChannelsConfig } from './slack.js';\n\n/**\n * Slack app credentials, read from env ONCE by the deploy entry. `signingSecret`\n * is required because the Slack adapter validates it at construction — an\n * integration constructed without it would throw during `prepare()` rather than\n * reporting itself unconfigured.\n */\nexport interface SlackIntegrationConfig {\n /** Verifies inbound Slack request signatures. Required. */\n signingSecret: string;\n /** Bot token used to post replies and ephemeral cards. */\n botToken?: string;\n /**\n * OAuth client credentials. Required for account linking: \"Sign in with\n * Slack\" (OIDC) is the only flow that writes a link, so without these the\n * settings surface reports `canConnect: false` and no sender can link.\n */\n clientId?: string;\n clientSecret?: string;\n /**\n * HTTPS origin Slack redirects back to. Slack requires HTTPS, so locally this\n * is the tunnel origin rather than the app's own public URL.\n */\n oidcRedirectBaseUrl?: string;\n /** SPA origin the post-connect redirect returns to. */\n uiOrigin?: string;\n /**\n * Overrides for the Slack channel adapter entry.\n *\n * @example\n * ```ts\n * new SlackIntegration({\n * signingSecret,\n * adapterOptions: {\n * streaming: true,\n * toolDisplay: 'grouped',\n * },\n * });\n * ```\n */\n adapterOptions?: SlackAdapterChannelConfig;\n}\n\nconst factoryTypingStatus: TypingStatusFn = chunk => {\n switch (chunk.type) {\n case 'reasoning-delta':\n return 'is thinking...';\n case 'text-delta':\n return 'is typing...';\n case 'tool-call':\n return 'is working...';\n default:\n return undefined;\n }\n};\n\n/**\n * Drops keys the caller set to `undefined` so spreading the overrides cannot\n * erase the Factory defaults they sit on top of.\n */\nfunction adapterOverrides(options: SlackAdapterChannelConfig | undefined): SlackAdapterChannelConfig {\n return Object.fromEntries(\n Object.entries(options ?? {}).filter(([, value]) => value !== undefined),\n ) as SlackAdapterChannelConfig;\n}\n\nexport class SlackIntegration implements FactoryIntegration {\n readonly id = 'slack';\n /**\n * The OIDC connect flow round-trips a signed `state` through Slack, so the\n * replica handling the callback must be able to verify a state a different\n * replica signed.\n */\n readonly requiresStableStateSigner = true;\n\n readonly #config: SlackIntegrationConfig;\n /**\n * Whether `channels()` found a source-control owner on the context and wired\n * repo-backed sessions. Set at the channels() attach path, which runs once at\n * boot before diagnostics are served.\n */\n #repoBackedSessions = false;\n\n constructor(config: SlackIntegrationConfig) {\n if (!config.signingSecret) {\n throw new Error(\n \"SlackIntegration: 'signingSecret' is required — Slack cannot verify inbound requests without it.\",\n );\n }\n this.#config = config;\n }\n\n channels(ctx: IntegrationContext): FactoryChannelsConfig {\n // Repo-backed sessions select the source-control provider from the linked\n // repository on each Factory project.\n const sourceControls =\n ctx.storage.sourceControls ?? (ctx.storage.sourceControlOwner ? [ctx.storage.sourceControlOwner] : []);\n this.#repoBackedSessions = sourceControls.length > 0;\n return createSlackChannelsConfig({\n slack: {\n clientId: this.#config.clientId,\n clientSecret: this.#config.clientSecret,\n signingSecret: this.#config.signingSecret,\n botToken: this.#config.botToken,\n },\n accountLinks: ctx.storage.channelIdentity,\n projects: ctx.storage.projects,\n sourceControls,\n modelDefaults: ctx.storage.modelDefaults,\n workItems: ctx.runtime?.workItems,\n feed: ctx.feed,\n adapterOptions: {\n toolDisplay: 'hidden',\n typingStatus: factoryTypingStatus,\n ...adapterOverrides(this.#config.adapterOptions),\n },\n });\n }\n\n feedPublisher(ctx: IntegrationContext): WorkItemFeedPublisher {\n return new SlackFeedPublisher({ controller: ctx.controller });\n }\n\n routes(ctx: IntegrationContext): ApiRoute[] {\n const { clientId, clientSecret, oidcRedirectBaseUrl, uiOrigin } = this.#config;\n return createSlackConnectRoutes({\n auth: ctx.auth,\n accountLinks: ctx.storage.channelIdentity,\n tenantStateSigner: ctx.stateSigner,\n oidc:\n clientId && clientSecret && oidcRedirectBaseUrl\n ? { clientId, clientSecret, redirectBaseUrl: oidcRedirectBaseUrl, uiOrigin }\n : undefined,\n projects: ctx.storage.projects,\n });\n }\n\n diagnostics(): Record<string, unknown> {\n const { clientId, clientSecret, botToken, oidcRedirectBaseUrl } = this.#config;\n return {\n configured: true,\n botTokenConfigured: Boolean(botToken),\n oidcConfigured: Boolean(clientId && clientSecret && oidcRedirectBaseUrl),\n repoBackedSessions: this.#repoBackedSessions,\n };\n }\n}\n"],"mappings":";;;;AAyEA,MAAM,uBAAsC,UAAS;CACnD,QAAQ,MAAM,MAAd;EACE,KAAK,mBACH,OAAO;EACT,KAAK,cACH,OAAO;EACT,KAAK,aACH,OAAO;EACT,SACE;CACJ;AACF;;;;;AAMA,SAAS,iBAAiB,SAA2E;CACnG,OAAO,OAAO,YACZ,OAAO,QAAQ,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,GAAG,WAAW,UAAU,KAAA,CAAS,CACzE;AACF;AAEA,IAAa,mBAAb,MAA4D;CAC1D,KAAc;;;;;;CAMd,4BAAqC;CAErC;;;;;;CAMA,sBAAsB;CAEtB,YAAY,QAAgC;EAC1C,IAAI,CAAC,OAAO,eACV,MAAM,IAAI,MACR,kGACF;EAEF,KAAKA,UAAU;CACjB;CAEA,SAAS,KAAgD;EAGvD,MAAM,iBACJ,IAAI,QAAQ,mBAAmB,IAAI,QAAQ,qBAAqB,CAAC,IAAI,QAAQ,kBAAkB,IAAI,CAAC;EACtG,KAAKC,sBAAsB,eAAe,SAAS;EACnD,OAAO,0BAA0B;GAC/B,OAAO;IACL,UAAU,KAAKD,QAAQ;IACvB,cAAc,KAAKA,QAAQ;IAC3B,eAAe,KAAKA,QAAQ;IAC5B,UAAU,KAAKA,QAAQ;GACzB;GACA,cAAc,IAAI,QAAQ;GAC1B,UAAU,IAAI,QAAQ;GACtB;GACA,eAAe,IAAI,QAAQ;GAC3B,WAAW,IAAI,SAAS;GACxB,MAAM,IAAI;GACV,gBAAgB;IACd,aAAa;IACb,cAAc;IACd,GAAG,iBAAiB,KAAKA,QAAQ,cAAc;GACjD;EACF,CAAC;CACH;CAEA,cAAc,KAAgD;EAC5D,OAAO,IAAI,mBAAmB,EAAE,YAAY,IAAI,WAAW,CAAC;CAC9D;CAEA,OAAO,KAAqC;EAC1C,MAAM,EAAE,UAAU,cAAc,qBAAqB,aAAa,KAAKA;EACvE,OAAO,yBAAyB;GAC9B,MAAM,IAAI;GACV,cAAc,IAAI,QAAQ;GAC1B,mBAAmB,IAAI;GACvB,MACE,YAAY,gBAAgB,sBACxB;IAAE;IAAU;IAAc,iBAAiB;IAAqB;GAAS,IACzE,KAAA;GACN,UAAU,IAAI,QAAQ;EACxB,CAAC;CACH;CAEA,cAAuC;EACrC,MAAM,EAAE,UAAU,cAAc,UAAU,wBAAwB,KAAKA;EACvE,OAAO;GACL,YAAY;GACZ,oBAAoB,QAAQ,QAAQ;GACpC,gBAAgB,QAAQ,YAAY,gBAAgB,mBAAmB;GACvE,oBAAoB,KAAKC;EAC3B;CACF;AACF"}
|
|
1
|
+
{"version":3,"file":"integration.js","names":["#config","#repoBackedSessions"],"sources":["../../../src/integrations/slack/integration.ts"],"sourcesContent":["/**\n * `SlackIntegration` — Slack as a `FactoryIntegration`.\n *\n * Slack contributes two things to a factory: the chat channels that carry\n * inbound messages into agent runs (`channels()`), and the browser-facing\n * account-link routes that bind a Slack sender to a Mastra tenant (`routes()`).\n * Both used to be assembled by hand in the deploy entry, which had to reach\n * into the prepared agent controller by string key, resolve storage domains\n * itself, mint its own state signer, and splice routes onto the factory's\n * assembled server config. Implementing the integration interface instead means\n * the factory does all of that the same way it already does for GitHub and\n * Linear, and the entry's job shrinks to reading Slack's env vars once.\n *\n * Slack ships as a factory built-in alongside GitHub and Linear (it originally\n * lived in `mastracode/web` to keep `@mastra/slack`/`chat` out of this package;\n * the team reversed that so `create-factory` consumers get Slack channels out\n * of the box). The integration interface remains open — third parties still add\n * capabilities by implementing `FactoryIntegration` from outside the package.\n */\n\nimport type { TypingStatusFn } from '@mastra/core/channels';\nimport type { ApiRoute } from '@mastra/core/server';\nimport type { SlackAdapterChannelConfig } from '@mastra/slack';\n\nimport type { WorkItemFeedPublisher } from '../../storage/domains/comments/feed-sync.js';\nimport type { FactoryChannelsConfig, FactoryIntegration, IntegrationContext } from '../base.js';\n\nimport { createSlackConnectRoutes } from './connect-route.js';\nimport { SlackFeedPublisher } from './feed-publisher.js';\nimport { createSlackChannelsConfig } from './slack.js';\n\n/**\n * Slack app credentials, read from env ONCE by the deploy entry. `signingSecret`\n * is required because the Slack adapter validates it at construction — an\n * integration constructed without it would throw during `prepare()` rather than\n * reporting itself unconfigured.\n */\nexport interface SlackIntegrationConfig {\n /** Verifies inbound Slack request signatures. Required. */\n signingSecret: string;\n /** Bot token used to post replies and ephemeral cards. */\n botToken?: string;\n /**\n * OAuth client credentials. Required for account linking: \"Sign in with\n * Slack\" (OIDC) is the only flow that writes a link, so without these the\n * settings surface reports `canConnect: false` and no sender can link.\n */\n clientId?: string;\n clientSecret?: string;\n /**\n * HTTPS origin Slack redirects back to. Slack requires HTTPS, so locally this\n * is the tunnel origin rather than the app's own public URL.\n */\n oidcRedirectBaseUrl?: string;\n /** SPA origin the post-connect redirect returns to. */\n uiOrigin?: string;\n /**\n * Overrides for the Slack channel adapter entry.\n *\n * @example\n * ```ts\n * new SlackIntegration({\n * signingSecret,\n * adapterOptions: {\n * streaming: true,\n * toolDisplay: 'grouped',\n * },\n * });\n * ```\n */\n adapterOptions?: SlackAdapterChannelConfig;\n}\n\n/** Slack renders the status inline next to the app name; keep it to one short line. */\nconst MAX_STATUS_LENGTH = 80;\n\n/**\n * Turns a tool's `description` arg (\"Listing the repo root\") into a status\n * line that reads after the app name (\"<App> is listing the repo root…\").\n * Only the first line counts: issue/PR creators carry whole bodies there.\n */\nfunction describeToolCall(args: unknown): string | undefined {\n if (!args || typeof args !== 'object') return undefined;\n const { description } = args as { description?: unknown };\n if (typeof description !== 'string') return undefined;\n const line = description\n .split('\\n')\n .map(part => part.replace(/\\s+/g, ' ').trim())\n .find(Boolean);\n if (!line) return undefined;\n const text = line.length > MAX_STATUS_LENGTH ? `${line.slice(0, MAX_STATUS_LENGTH - 1)}…` : line;\n // Drop the sentence-case capital (\"Listing\" → \"listing\") but leave acronyms\n // and identifiers (\"PR lookup\", \"CI status\") alone.\n return `is ${/^[A-Z][a-z]/.test(text) ? text.charAt(0).toLowerCase() + text.slice(1) : text}`;\n}\n\nconst factoryTypingStatus: TypingStatusFn = chunk => {\n switch (chunk.type) {\n case 'reasoning-delta':\n return 'is thinking...';\n case 'text-delta':\n return 'is typing...';\n case 'tool-call':\n return describeToolCall(chunk.payload.args) ?? 'is working...';\n default:\n return undefined;\n }\n};\n\n/**\n * Drops keys the caller set to `undefined` so spreading the overrides cannot\n * erase the Factory defaults they sit on top of.\n */\nfunction adapterOverrides(options: SlackAdapterChannelConfig | undefined): SlackAdapterChannelConfig {\n return Object.fromEntries(\n Object.entries(options ?? {}).filter(([, value]) => value !== undefined),\n ) as SlackAdapterChannelConfig;\n}\n\n/**\n * Factory defaults under the caller's overrides. Tool calls stay out of the\n * thread: a Factory session runs hundreds of them, and a row per call (or a\n * card per call) buries the reply. Progress shows through the typing status\n * instead, which names the tool's current step; the session link carries the\n * full trace. `grouped` remains available via `adapterOptions` for callers\n * who want Slack's \"Thinking Steps\" widget.\n */\nfunction factoryAdapterOptions(overrides: SlackAdapterChannelConfig): SlackAdapterChannelConfig {\n return { typingStatus: factoryTypingStatus, toolDisplay: 'hidden', ...overrides };\n}\n\nexport class SlackIntegration implements FactoryIntegration {\n readonly id = 'slack';\n /**\n * The OIDC connect flow round-trips a signed `state` through Slack, so the\n * replica handling the callback must be able to verify a state a different\n * replica signed.\n */\n readonly requiresStableStateSigner = true;\n\n readonly #config: SlackIntegrationConfig;\n /**\n * Whether `channels()` found a source-control owner on the context and wired\n * repo-backed sessions. Set at the channels() attach path, which runs once at\n * boot before diagnostics are served.\n */\n #repoBackedSessions = false;\n\n constructor(config: SlackIntegrationConfig) {\n if (!config.signingSecret) {\n throw new Error(\n \"SlackIntegration: 'signingSecret' is required — Slack cannot verify inbound requests without it.\",\n );\n }\n this.#config = config;\n }\n\n channels(ctx: IntegrationContext): FactoryChannelsConfig {\n // Repo-backed sessions select the source-control provider from the linked\n // repository on each Factory project.\n const sourceControls =\n ctx.storage.sourceControls ?? (ctx.storage.sourceControlOwner ? [ctx.storage.sourceControlOwner] : []);\n this.#repoBackedSessions = sourceControls.length > 0;\n return createSlackChannelsConfig({\n slack: {\n clientId: this.#config.clientId,\n clientSecret: this.#config.clientSecret,\n signingSecret: this.#config.signingSecret,\n botToken: this.#config.botToken,\n },\n accountLinks: ctx.storage.channelIdentity,\n projects: ctx.storage.projects,\n sourceControls,\n modelDefaults: ctx.storage.modelDefaults,\n workItems: ctx.runtime?.workItems,\n feed: ctx.feed,\n adapterOptions: factoryAdapterOptions(adapterOverrides(this.#config.adapterOptions)),\n });\n }\n\n feedPublisher(ctx: IntegrationContext): WorkItemFeedPublisher {\n return new SlackFeedPublisher({ controller: ctx.controller });\n }\n\n routes(ctx: IntegrationContext): ApiRoute[] {\n const { clientId, clientSecret, oidcRedirectBaseUrl, uiOrigin } = this.#config;\n return createSlackConnectRoutes({\n auth: ctx.auth,\n accountLinks: ctx.storage.channelIdentity,\n tenantStateSigner: ctx.stateSigner,\n oidc:\n clientId && clientSecret && oidcRedirectBaseUrl\n ? { clientId, clientSecret, redirectBaseUrl: oidcRedirectBaseUrl, uiOrigin }\n : undefined,\n projects: ctx.storage.projects,\n });\n }\n\n diagnostics(): Record<string, unknown> {\n const { clientId, clientSecret, botToken, oidcRedirectBaseUrl } = this.#config;\n return {\n configured: true,\n botTokenConfigured: Boolean(botToken),\n oidcConfigured: Boolean(clientId && clientSecret && oidcRedirectBaseUrl),\n repoBackedSessions: this.#repoBackedSessions,\n };\n }\n}\n"],"mappings":";;;;;AA0EA,MAAM,oBAAoB;;;;;;AAO1B,SAAS,iBAAiB,MAAmC;CAC3D,IAAI,CAAC,QAAQ,OAAO,SAAS,UAAU,OAAO,KAAA;CAC9C,MAAM,EAAE,gBAAgB;CACxB,IAAI,OAAO,gBAAgB,UAAU,OAAO,KAAA;CAC5C,MAAM,OAAO,YACV,MAAM,IAAI,CAAC,CACX,KAAI,SAAQ,KAAK,QAAQ,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAC7C,KAAK,OAAO;CACf,IAAI,CAAC,MAAM,OAAO,KAAA;CAClB,MAAM,OAAO,KAAK,SAAS,oBAAoB,GAAG,KAAK,MAAM,GAAG,oBAAoB,CAAC,EAAE,KAAK;CAG5F,OAAO,MAAM,cAAc,KAAK,IAAI,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,YAAY,IAAI,KAAK,MAAM,CAAC,IAAI;AACzF;AAEA,MAAM,uBAAsC,UAAS;CACnD,QAAQ,MAAM,MAAd;EACE,KAAK,mBACH,OAAO;EACT,KAAK,cACH,OAAO;EACT,KAAK,aACH,OAAO,iBAAiB,MAAM,QAAQ,IAAI,KAAK;EACjD,SACE;CACJ;AACF;;;;;AAMA,SAAS,iBAAiB,SAA2E;CACnG,OAAO,OAAO,YACZ,OAAO,QAAQ,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,GAAG,WAAW,UAAU,KAAA,CAAS,CACzE;AACF;;;;;;;;;AAUA,SAAS,sBAAsB,WAAiE;CAC9F,OAAO;EAAE,cAAc;EAAqB,aAAa;EAAU,GAAG;CAAU;AAClF;AAEA,IAAa,mBAAb,MAA4D;CAC1D,KAAc;;;;;;CAMd,4BAAqC;CAErC;;;;;;CAMA,sBAAsB;CAEtB,YAAY,QAAgC;EAC1C,IAAI,CAAC,OAAO,eACV,MAAM,IAAI,MACR,kGACF;EAEF,KAAKA,UAAU;CACjB;CAEA,SAAS,KAAgD;EAGvD,MAAM,iBACJ,IAAI,QAAQ,mBAAmB,IAAI,QAAQ,qBAAqB,CAAC,IAAI,QAAQ,kBAAkB,IAAI,CAAC;EACtG,KAAKC,sBAAsB,eAAe,SAAS;EACnD,OAAO,0BAA0B;GAC/B,OAAO;IACL,UAAU,KAAKD,QAAQ;IACvB,cAAc,KAAKA,QAAQ;IAC3B,eAAe,KAAKA,QAAQ;IAC5B,UAAU,KAAKA,QAAQ;GACzB;GACA,cAAc,IAAI,QAAQ;GAC1B,UAAU,IAAI,QAAQ;GACtB;GACA,eAAe,IAAI,QAAQ;GAC3B,WAAW,IAAI,SAAS;GACxB,MAAM,IAAI;GACV,gBAAgB,sBAAsB,iBAAiB,KAAKA,QAAQ,cAAc,CAAC;EACrF,CAAC;CACH;CAEA,cAAc,KAAgD;EAC5D,OAAO,IAAI,mBAAmB,EAAE,YAAY,IAAI,WAAW,CAAC;CAC9D;CAEA,OAAO,KAAqC;EAC1C,MAAM,EAAE,UAAU,cAAc,qBAAqB,aAAa,KAAKA;EACvE,OAAO,yBAAyB;GAC9B,MAAM,IAAI;GACV,cAAc,IAAI,QAAQ;GAC1B,mBAAmB,IAAI;GACvB,MACE,YAAY,gBAAgB,sBACxB;IAAE;IAAU;IAAc,iBAAiB;IAAqB;GAAS,IACzE,KAAA;GACN,UAAU,IAAI,QAAQ;EACxB,CAAC;CACH;CAEA,cAAuC;EACrC,MAAM,EAAE,UAAU,cAAc,UAAU,wBAAwB,KAAKA;EACvE,OAAO;GACL,YAAY;GACZ,oBAAoB,QAAQ,QAAQ;GACpC,gBAAgB,QAAQ,YAAY,gBAAgB,mBAAmB;GACvE,oBAAoB,KAAKC;EAC3B;CACF;AACF"}
|
|
@@ -121,9 +121,20 @@ export declare function resolveFactoryForLink({ thread, link, key, accountLinks,
|
|
|
121
121
|
accountLinks: ChannelIdentityStorage;
|
|
122
122
|
projects?: FactoryProjectsStorage;
|
|
123
123
|
}): Promise<FactoryRouteResult>;
|
|
124
|
+
/**
|
|
125
|
+
* Session ids of repo-backed Slack threads, keyed by the message that started
|
|
126
|
+
* them. The resourceId resolver records the session it found or created; the
|
|
127
|
+
* thread-id resolver, which core runs next for the same message, names the new
|
|
128
|
+
* thread after it.
|
|
129
|
+
*/
|
|
130
|
+
export type SlackStartedSessions = WeakMap<object, string>;
|
|
124
131
|
/**
|
|
125
132
|
* Resolve the resourceId for a NEW Slack channel thread. A linked sender whose
|
|
126
|
-
* factory has a repository gets a Factory
|
|
133
|
+
* factory has a repository gets a Factory session. A channel thread belongs to
|
|
134
|
+
* the Factory: its resourceId is the Factory id, so the thread shares the
|
|
135
|
+
* Factory's memory. A DM is private to the sender, so it runs under the
|
|
136
|
+
* sender's user id and shares memory with their own sessions. The session id is recorded in `startedSessions` for the
|
|
137
|
+
* thread-id resolver. The controller
|
|
127
138
|
* session then materializes the repo sandbox via the factory's dynamic
|
|
128
139
|
* workspace (clone + PAT), the session shows up in the web Sessions list, and
|
|
129
140
|
* View Session deep-links land on the normal workspace route.
|
|
@@ -147,17 +158,17 @@ export declare function resolveFactoryForLink({ thread, link, key, accountLinks,
|
|
|
147
158
|
* Pure lookups only — cards for unlinked/unrouted senders are the dispatch
|
|
148
159
|
* gate's job; this hook must never post.
|
|
149
160
|
*/
|
|
150
|
-
export declare function createChannelResourceIdResolver(deps: SlackChannelDeps): ResolveResourceId;
|
|
161
|
+
export declare function createChannelResourceIdResolver(deps: SlackChannelDeps, startedSessions?: SlackStartedSessions): ResolveResourceId;
|
|
151
162
|
/**
|
|
152
163
|
* Thread id for a NEW Slack channel thread. Repo-backed threads take the
|
|
153
|
-
*
|
|
164
|
+
* session id AS their thread id, matching the web convention
|
|
154
165
|
* (FactoryStartCoordinator seeds threads with threadId = sessionId) so
|
|
155
166
|
* `/workspaces/{sessionId}/threads/{sessionId}` resolves Slack-created
|
|
156
|
-
* sessions exactly like web-created ones — no `?
|
|
167
|
+
* sessions exactly like web-created ones — no `?sessionId=` override needed.
|
|
157
168
|
* Chat-only threads keep the default random id: their `channel:...`
|
|
158
169
|
* resourceId is a memory key, not a unique thread id.
|
|
159
170
|
*/
|
|
160
|
-
export declare
|
|
171
|
+
export declare function createChannelThreadIdResolver(startedSessions: SlackStartedSessions): ResolveThreadId;
|
|
161
172
|
/** Create channel sessions with their Factory ownership present in initial controller state. */
|
|
162
173
|
export declare function createChannelSessionResolver(deps: SlackChannelDeps): ChannelSessionResolve;
|
|
163
174
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slack.d.ts","sourceRoot":"","sources":["../../../src/integrations/slack/slack.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,cAAc,EAEd,eAAe,EACf,qBAAqB,EACrB,mBAAmB,EACnB,iBAAiB,EACjB,eAAe,EAChB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"slack.d.ts","sourceRoot":"","sources":["../../../src/integrations/slack/slack.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { actorFromChannelAuthor } from "../../storage/domains/comments/actor.js";
|
|
2
|
+
import { FACTORY_INTERACTIVE_KEY } from "../../auth.js";
|
|
3
|
+
import { isRunBound, memoryResourceOf, sessionStampCounts } from "../../session/session-threads.js";
|
|
2
4
|
import { primeTenantCredentialsForRequestContext, primeTenantCredentialsForTenant } from "../../routes/tenant-credentials.js";
|
|
3
5
|
import { readRequestContextOrgId, seedSessionOrg } from "../../session/org-seed.js";
|
|
4
6
|
import { prepareSessionRunContext } from "../subscription-session.js";
|
|
5
7
|
import { FactorySourceControlConflictError, createSourceControlSessionLookup, hydrateFactorySession, resolveFactorySessionModel, resolveFactorySourceControl, resolvePrimaryEnvironmentRepository } from "../../session/factory-session.js";
|
|
8
|
+
import { FACTORY_SLACK_SENDER_KEY } from "../../tools/scope.js";
|
|
9
|
+
import { markNewChannelSession } from "../../session/channel-session-link-processor.js";
|
|
6
10
|
import { resolveEmojiShortcodes } from "./emoji.js";
|
|
7
11
|
import { slackCommentSource } from "./feed-publisher.js";
|
|
8
12
|
import { ChannelSessionRejectedError } from "@mastra/core/channels";
|
|
@@ -149,7 +153,11 @@ function threadBranch(threadId) {
|
|
|
149
153
|
}
|
|
150
154
|
/**
|
|
151
155
|
* Resolve the resourceId for a NEW Slack channel thread. A linked sender whose
|
|
152
|
-
* factory has a repository gets a Factory
|
|
156
|
+
* factory has a repository gets a Factory session. A channel thread belongs to
|
|
157
|
+
* the Factory: its resourceId is the Factory id, so the thread shares the
|
|
158
|
+
* Factory's memory. A DM is private to the sender, so it runs under the
|
|
159
|
+
* sender's user id and shares memory with their own sessions. The session id is recorded in `startedSessions` for the
|
|
160
|
+
* thread-id resolver. The controller
|
|
153
161
|
* session then materializes the repo sandbox via the factory's dynamic
|
|
154
162
|
* workspace (clone + PAT), the session shows up in the web Sessions list, and
|
|
155
163
|
* View Session deep-links land on the normal workspace route.
|
|
@@ -173,7 +181,7 @@ function threadBranch(threadId) {
|
|
|
173
181
|
* Pure lookups only — cards for unlinked/unrouted senders are the dispatch
|
|
174
182
|
* gate's job; this hook must never post.
|
|
175
183
|
*/
|
|
176
|
-
function createChannelResourceIdResolver(deps) {
|
|
184
|
+
function createChannelResourceIdResolver(deps, startedSessions = /* @__PURE__ */ new WeakMap()) {
|
|
177
185
|
const { accountLinks, projects } = deps;
|
|
178
186
|
const sourceControls = configuredSourceControls(deps);
|
|
179
187
|
return async ({ platform, thread, message }) => {
|
|
@@ -216,13 +224,17 @@ function createChannelResourceIdResolver(deps) {
|
|
|
216
224
|
});
|
|
217
225
|
if (!repo.found) throw new SlackSessionStartError(repo.reason === "connection" ? "Could not start a session: connect source control to this Factory project." : "Could not start a session: link a repository to this Factory project.");
|
|
218
226
|
const branch = threadBranch(thread.id);
|
|
227
|
+
const memoryResourceId = thread.isDM ? link.userId : factoryProjectId;
|
|
219
228
|
const existing = await sourceControl.sessions.getForBranch({
|
|
220
229
|
factoryProjectId,
|
|
221
230
|
userId: link.userId,
|
|
222
231
|
branch
|
|
223
232
|
});
|
|
224
|
-
if (existing)
|
|
225
|
-
|
|
233
|
+
if (existing) {
|
|
234
|
+
startedSessions.set(message, existing.sessionId);
|
|
235
|
+
return memoryResourceId;
|
|
236
|
+
}
|
|
237
|
+
const session = await sourceControl.sessions.create({
|
|
226
238
|
sessionId: globalThis.crypto.randomUUID(),
|
|
227
239
|
projectRepositoryId: repo.projectRepositoryId,
|
|
228
240
|
factoryProjectId,
|
|
@@ -231,7 +243,9 @@ function createChannelResourceIdResolver(deps) {
|
|
|
231
243
|
branch,
|
|
232
244
|
baseBranch: repo.baseBranch,
|
|
233
245
|
visibility: thread.isDM ? "private" : "org"
|
|
234
|
-
})
|
|
246
|
+
});
|
|
247
|
+
startedSessions.set(message, session.sessionId);
|
|
248
|
+
return memoryResourceId;
|
|
235
249
|
} catch (error) {
|
|
236
250
|
if (error instanceof ChannelSessionRejectedError || error instanceof SlackSessionStartError) throw error;
|
|
237
251
|
if (error instanceof FactorySourceControlConflictError) throw new SlackSessionStartError("Could not start a session: this Factory project has repositories from more than one source-control provider.", { cause: error });
|
|
@@ -242,22 +256,55 @@ function createChannelResourceIdResolver(deps) {
|
|
|
242
256
|
}
|
|
243
257
|
/**
|
|
244
258
|
* Thread id for a NEW Slack channel thread. Repo-backed threads take the
|
|
245
|
-
*
|
|
259
|
+
* session id AS their thread id, matching the web convention
|
|
246
260
|
* (FactoryStartCoordinator seeds threads with threadId = sessionId) so
|
|
247
261
|
* `/workspaces/{sessionId}/threads/{sessionId}` resolves Slack-created
|
|
248
|
-
* sessions exactly like web-created ones — no `?
|
|
262
|
+
* sessions exactly like web-created ones — no `?sessionId=` override needed.
|
|
249
263
|
* Chat-only threads keep the default random id: their `channel:...`
|
|
250
264
|
* resourceId is a memory key, not a unique thread id.
|
|
251
265
|
*/
|
|
252
|
-
|
|
266
|
+
function createChannelThreadIdResolver(startedSessions) {
|
|
267
|
+
return ({ message, defaultThreadId }) => startedSessions.get(message) ?? defaultThreadId;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* The Factory session a repo-backed Slack thread belongs to. A thread under
|
|
271
|
+
* the Factory resource is stamped with its session id (the startup migration
|
|
272
|
+
* stamps threads it moved there). An unmigrated thread runs under its session
|
|
273
|
+
* id as its resource, and a newer thread is named after its session. A stamp
|
|
274
|
+
* or thread id only counts under that session's memory resource; a thread
|
|
275
|
+
* named after a session it can't claim is refused. With no session row for
|
|
276
|
+
* either, the thread is addressed by its own id and fails authorization like
|
|
277
|
+
* any session without a row.
|
|
278
|
+
*/
|
|
279
|
+
const UNCLAIMED_SESSION = Symbol("unclaimed-session");
|
|
280
|
+
async function slackThreadSessionId(sessions, thread, loadThread, runs) {
|
|
281
|
+
if (thread.resourceId.startsWith("channel:")) return null;
|
|
282
|
+
const claims = async (sessionId) => {
|
|
283
|
+
const row = await sessions.getBySessionId(sessionId);
|
|
284
|
+
if (!row) return false;
|
|
285
|
+
if (sessionId !== thread.id && !sessionStampCounts(row, thread.resourceId)) return false;
|
|
286
|
+
const own = sessionId === thread.id ? thread : await loadThread(sessionId);
|
|
287
|
+
return thread.resourceId === memoryResourceOf(row, own, await isRunBound(row, runs));
|
|
288
|
+
};
|
|
289
|
+
const stamped = thread.metadata?.factorySessionId;
|
|
290
|
+
if (typeof stamped === "string" && stamped && await claims(stamped)) return stamped;
|
|
291
|
+
if (await sessions.getSourceControlBySessionId(thread.resourceId)) return thread.resourceId;
|
|
292
|
+
if (await claims(thread.id)) return thread.id;
|
|
293
|
+
if (await sessions.getBySessionId(thread.id)) return UNCLAIMED_SESSION;
|
|
294
|
+
return thread.id;
|
|
295
|
+
}
|
|
253
296
|
/** Create channel sessions with their Factory ownership present in initial controller state. */
|
|
254
297
|
function createChannelSessionResolver(deps) {
|
|
255
298
|
const sourceControlSessions = createSourceControlSessionLookup(configuredSourceControls(deps));
|
|
256
299
|
return async ({ controller, thread, requestContext }) => {
|
|
257
|
-
const
|
|
258
|
-
const
|
|
300
|
+
const stored = thread.metadata?.factorySessionId || thread.resourceId.startsWith("channel:") ? thread : await controller.queryThreadById({ threadId: thread.id }) ?? thread;
|
|
301
|
+
const resolved = await slackThreadSessionId(sourceControlSessions, stored, (threadId) => controller.queryThreadById({ threadId }), deps.workItems);
|
|
302
|
+
if (resolved === UNCLAIMED_SESSION) throw new Error(`Slack thread ${thread.id} is named after a Factory session it doesn't belong to.`);
|
|
303
|
+
const sessionId = resolved ?? thread.resourceId;
|
|
304
|
+
const sourceControl = sessionId.startsWith("channel:") ? null : await sourceControlSessions.getSourceControlBySessionId(sessionId);
|
|
305
|
+
const session = sourceControl ? await sourceControl.sessions.getBySessionId(sessionId) : null;
|
|
259
306
|
return controller.createSession({
|
|
260
|
-
id:
|
|
307
|
+
id: sessionId,
|
|
261
308
|
ownerId: controller.id,
|
|
262
309
|
resourceId: thread.resourceId,
|
|
263
310
|
threadId: thread.id,
|
|
@@ -296,15 +343,16 @@ function createChannelSessionStartHook(deps) {
|
|
|
296
343
|
await seedSessionOrg(session, readRequestContextOrgId(requestContext));
|
|
297
344
|
if (!projects) return;
|
|
298
345
|
if (thread.resourceId.startsWith("channel:")) return;
|
|
299
|
-
const
|
|
346
|
+
const sessionId = session.identity.getId();
|
|
347
|
+
const sourceControl = await sourceControlSessions.getSourceControlBySessionId(sessionId);
|
|
300
348
|
if (!sourceControl) return;
|
|
301
|
-
const sourceSession = await sourceControl.sessions.getBySessionId(
|
|
349
|
+
const sourceSession = await sourceControl.sessions.getBySessionId(sessionId);
|
|
302
350
|
if (!sourceSession?.factoryProjectId) return;
|
|
303
351
|
const threadId = session.thread.getId();
|
|
304
352
|
const binding = threadId && deps.workItems ? await deps.workItems.findRunBindingByThread({
|
|
305
353
|
orgId: sourceSession.orgId,
|
|
306
|
-
resourceId:
|
|
307
|
-
sessionId
|
|
354
|
+
resourceId: session.identity.getResourceId(),
|
|
355
|
+
sessionId,
|
|
308
356
|
threadId,
|
|
309
357
|
rejectAmbiguous: true
|
|
310
358
|
}) : null;
|
|
@@ -332,11 +380,15 @@ function createChannelSessionStartHook(deps) {
|
|
|
332
380
|
userId: owner.userId,
|
|
333
381
|
credentialScope: sessionModel.modelScope
|
|
334
382
|
});
|
|
335
|
-
const modeId = session.mode.get();
|
|
336
383
|
const activeThreadId = session.thread.getId();
|
|
384
|
+
if (activeThreadId) await session.thread.setSettingOn({
|
|
385
|
+
threadId: activeThreadId,
|
|
386
|
+
key: "factorySessionId",
|
|
387
|
+
value: sessionId
|
|
388
|
+
});
|
|
337
389
|
if (await migratePersistedModelSelection({
|
|
338
390
|
getMetadata: async () => activeThreadId ? (await session.thread.getById({ threadId: activeThreadId }))?.metadata ?? {} : {},
|
|
339
|
-
modeId,
|
|
391
|
+
modeId: session.mode.get(),
|
|
340
392
|
set: (key, value) => activeThreadId ? session.thread.setSettingOn({
|
|
341
393
|
threadId: activeThreadId,
|
|
342
394
|
key,
|
|
@@ -413,13 +465,17 @@ async function prepareExistingSessionOwnerContext(thread, deps, ctx, options) {
|
|
|
413
465
|
return "ready";
|
|
414
466
|
}
|
|
415
467
|
if (sourceControls.length === 0 || internalThread.resourceId.startsWith("channel:")) return "ready";
|
|
416
|
-
|
|
417
|
-
const
|
|
468
|
+
const sessions = createSourceControlSessionLookup(sourceControls);
|
|
469
|
+
const memory = await ctx.mastra?.getStorage()?.getStore("memory");
|
|
470
|
+
const resolved = await slackThreadSessionId(sessions, internalThread, async (threadId) => memory?.getThreadById({ threadId }), deps.workItems);
|
|
471
|
+
if (resolved === UNCLAIMED_SESSION) return "access-denied";
|
|
472
|
+
const sessionId = resolved ?? internalThread.resourceId;
|
|
473
|
+
const sessionRow = await sessions.getBySessionId(sessionId);
|
|
474
|
+
if (sessionRow && sessionRow.orgId === options.expectedOrgId && sessionRow.visibility === "private" && sessionRow.userId !== options.senderUserId) return "access-denied";
|
|
475
|
+
if (!options.expectedOrgId) throw new Error(`Could not authorize the owner of Slack Factory session ${sessionId}: the sender's account link has no organization.`);
|
|
476
|
+
const preparation = await prepareSessionRunContext(ctx.requestContext, sessionId, { sessions }, { expectedOrgId: options.expectedOrgId });
|
|
418
477
|
if (preparation === "organization-mismatch") return preparation;
|
|
419
|
-
if (preparation !== "prepared") {
|
|
420
|
-
const reason = preparation === "session-not-found" ? "no source-control session row exists for it, or its project repository row is missing" : "its source-control session row has no organization";
|
|
421
|
-
throw new Error(`Could not authorize the owner of Slack Factory session ${internalThread.resourceId}: ${reason}.`);
|
|
422
|
-
}
|
|
478
|
+
if (preparation !== "prepared") throw new Error(`Could not authorize the owner of Slack Factory session ${sessionId}: ${preparation === "session-not-found" ? "no source-control session row exists for it, or its project repository row is missing" : "its source-control session row has no organization"}.`);
|
|
423
479
|
return "ready";
|
|
424
480
|
}
|
|
425
481
|
/**
|
|
@@ -446,10 +502,16 @@ async function gateDispatch(thread, message, deps, ctx, options = { requireInter
|
|
|
446
502
|
id: sender.link.userId,
|
|
447
503
|
organizationId: sender.link.orgId
|
|
448
504
|
});
|
|
449
|
-
|
|
505
|
+
const ownerContext = await prepareExistingSessionOwnerContext(thread, deps, ctx, {
|
|
450
506
|
expectedOrgId: sender.link.orgId,
|
|
507
|
+
senderUserId: sender.link.userId,
|
|
451
508
|
requireInternalThread: options.requireInternalThread
|
|
452
|
-
})
|
|
509
|
+
});
|
|
510
|
+
if (ownerContext === "access-denied") {
|
|
511
|
+
await thread.post("This private Factory session belongs to another user.");
|
|
512
|
+
return null;
|
|
513
|
+
}
|
|
514
|
+
if (ownerContext === "organization-mismatch") {
|
|
453
515
|
await thread.post("This thread belongs to a Factory session in another organization.");
|
|
454
516
|
return null;
|
|
455
517
|
}
|
|
@@ -461,10 +523,19 @@ async function gateDispatch(thread, message, deps, ctx, options = { requireInter
|
|
|
461
523
|
projects
|
|
462
524
|
});
|
|
463
525
|
if (route.status === "blocked") return null;
|
|
464
|
-
if (route.status === "resolved")
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
526
|
+
if (route.status === "resolved") {
|
|
527
|
+
if (sender.link.orgId) {
|
|
528
|
+
ctx.requestContext.set(FACTORY_SLACK_SENDER_KEY, {
|
|
529
|
+
userId: sender.link.userId,
|
|
530
|
+
orgId: sender.link.orgId
|
|
531
|
+
});
|
|
532
|
+
ctx.requestContext.set(FACTORY_INTERACTIVE_KEY, true);
|
|
533
|
+
}
|
|
534
|
+
return { routed: {
|
|
535
|
+
link: sender.link,
|
|
536
|
+
factoryProjectId: route.factoryProjectId
|
|
537
|
+
} };
|
|
538
|
+
}
|
|
468
539
|
}
|
|
469
540
|
return {};
|
|
470
541
|
}
|
|
@@ -528,38 +599,37 @@ async function reportPreDispatchError(thread, message, ctx, error) {
|
|
|
528
599
|
})}`);
|
|
529
600
|
}
|
|
530
601
|
}
|
|
602
|
+
/**
|
|
603
|
+
* What the sender sees while nothing has been posted yet. Core's typing
|
|
604
|
+
* wrapper only sets a status once the agent stream yields its first chunk,
|
|
605
|
+
* and everything before that — credential priming, session rows, the
|
|
606
|
+
* controller start hook, model time-to-first-token — is silent. Pin the same
|
|
607
|
+
* status Factory's `typingStatus` uses for reasoning so the bot visibly
|
|
608
|
+
* "picks up" as soon as the gate passes, and the stream's own status replaces
|
|
609
|
+
* it without a visual change. Slack clears it on the first post; core clears
|
|
610
|
+
* it when a run ends without posting.
|
|
611
|
+
*/
|
|
612
|
+
const EARLY_TYPING_STATUS = "is thinking...";
|
|
613
|
+
function acknowledgeReceipt(thread, deps) {
|
|
614
|
+
if (deps.adapterOptions?.typingStatus === false) return;
|
|
615
|
+
thread.startTyping(EARLY_TYPING_STATUS).catch((error) => {
|
|
616
|
+
console.warn("[slack] early typing status failed", {
|
|
617
|
+
thread: thread.id,
|
|
618
|
+
error
|
|
619
|
+
});
|
|
620
|
+
});
|
|
621
|
+
}
|
|
531
622
|
function createNewSessionChatHandler(deps) {
|
|
532
623
|
return async (thread, message, defaultHandler, ctx) => {
|
|
533
|
-
let gate;
|
|
534
|
-
let isNewSession;
|
|
535
624
|
try {
|
|
536
|
-
|
|
537
|
-
if (!
|
|
538
|
-
|
|
625
|
+
if (!await gateDispatch(thread, message, deps, ctx)) return;
|
|
626
|
+
if (!await thread.isSubscribed()) markNewChannelSession(ctx.requestContext);
|
|
627
|
+
acknowledgeReceipt(thread, deps);
|
|
539
628
|
} catch (error) {
|
|
540
629
|
await reportPreDispatchError(thread, message, ctx, error);
|
|
541
630
|
return;
|
|
542
631
|
}
|
|
543
632
|
await defaultHandler(thread, message);
|
|
544
|
-
if (!isNewSession) return;
|
|
545
|
-
const internalThread = await findInternalThread(ctx.mastra, thread);
|
|
546
|
-
if (!internalThread) {
|
|
547
|
-
console.warn("[onMention] no internal thread found for", thread.id);
|
|
548
|
-
return;
|
|
549
|
-
}
|
|
550
|
-
const isChatOnly = internalThread.resourceId.startsWith("channel:");
|
|
551
|
-
const workspaceSegment = isChatOnly ? "channel" : encodeURIComponent(internalThread.resourceId);
|
|
552
|
-
const threadPath = gate.routed ? `/factories/${encodeURIComponent(gate.routed.factoryProjectId)}/workspaces/${workspaceSegment}/threads/${encodeURIComponent(internalThread.id)}` : `/threads/${internalThread.id}`;
|
|
553
|
-
const needsResourceParam = isChatOnly || !gate.routed;
|
|
554
|
-
const deepLink = process.env.MASTRACODE_PUBLIC_URL ? needsResourceParam ? `${process.env.MASTRACODE_PUBLIC_URL}${threadPath}?resourceId=${encodeURIComponent(internalThread.resourceId)}` : `${process.env.MASTRACODE_PUBLIC_URL}${threadPath}` : void 0;
|
|
555
|
-
if (!deepLink) return;
|
|
556
|
-
await thread.post(Card({
|
|
557
|
-
title: "New session started",
|
|
558
|
-
children: [Actions([LinkButton({
|
|
559
|
-
url: deepLink,
|
|
560
|
-
label: "View session"
|
|
561
|
-
})])]
|
|
562
|
-
}));
|
|
563
633
|
};
|
|
564
634
|
}
|
|
565
635
|
/**
|
|
@@ -622,6 +692,7 @@ const createHandlers = (deps) => {
|
|
|
622
692
|
await reportPreDispatchError(thread, message, ctx, error);
|
|
623
693
|
return;
|
|
624
694
|
}
|
|
695
|
+
acknowledgeReceipt(thread, deps);
|
|
625
696
|
await defaultHandler(thread, message);
|
|
626
697
|
},
|
|
627
698
|
onMention: newSessionChatHandler,
|
|
@@ -633,23 +704,25 @@ function createSlackChannelsConfig(deps) {
|
|
|
633
704
|
...deps.slack,
|
|
634
705
|
webClientOptions: { timeout: SLACK_REQUEST_TIMEOUT_MS }
|
|
635
706
|
});
|
|
707
|
+
const slack = deps.adapterOptions?.streaming === false ? {
|
|
708
|
+
adapter,
|
|
709
|
+
...deps.adapterOptions
|
|
710
|
+
} : {
|
|
711
|
+
adapter,
|
|
712
|
+
...deps.adapterOptions,
|
|
713
|
+
streaming: deps.adapterOptions?.streaming ?? true
|
|
714
|
+
};
|
|
715
|
+
const startedSessions = /* @__PURE__ */ new WeakMap();
|
|
636
716
|
return {
|
|
637
|
-
adapters: { slack
|
|
638
|
-
adapter,
|
|
639
|
-
...deps.adapterOptions
|
|
640
|
-
} : {
|
|
641
|
-
adapter,
|
|
642
|
-
...deps.adapterOptions,
|
|
643
|
-
streaming: deps.adapterOptions?.streaming ?? true
|
|
644
|
-
} },
|
|
717
|
+
adapters: { slack },
|
|
645
718
|
handlers: createHandlers(deps),
|
|
646
|
-
resolveResourceId: createChannelResourceIdResolver(deps),
|
|
647
|
-
resolveThreadId:
|
|
719
|
+
resolveResourceId: createChannelResourceIdResolver(deps, startedSessions),
|
|
720
|
+
resolveThreadId: createChannelThreadIdResolver(startedSessions),
|
|
648
721
|
resolveSession: createChannelSessionResolver(deps),
|
|
649
722
|
onSessionStart: createChannelSessionStartHook(deps)
|
|
650
723
|
};
|
|
651
724
|
}
|
|
652
725
|
//#endregion
|
|
653
|
-
export { createChannelResourceIdResolver, createChannelSessionResolver, createChannelSessionStartHook, createHandlers, createSlackChannelsConfig,
|
|
726
|
+
export { createChannelResourceIdResolver, createChannelSessionResolver, createChannelSessionStartHook, createChannelThreadIdResolver, createHandlers, createSlackChannelsConfig, resolveFactoryForLink, resolveLinkedSender, slackThreadSource };
|
|
654
727
|
|
|
655
728
|
//# sourceMappingURL=slack.js.map
|