@mastra/factory 0.25.0-alpha.8 → 0.26.0-alpha.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/dist/factory.d.ts +1 -1
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +27 -3
- package/dist/factory.js.map +1 -1
- package/dist/integrations/slack/integration.d.ts +7 -8
- package/dist/integrations/slack/integration.d.ts.map +1 -1
- package/dist/integrations/slack/integration.js +11 -3
- package/dist/integrations/slack/integration.js.map +1 -1
- package/dist/integrations/slack/run-error-card.d.ts.map +1 -1
- package/dist/integrations/slack/run-error-card.js +25 -8
- package/dist/integrations/slack/run-error-card.js.map +1 -1
- package/dist/integrations/slack/slack.d.ts +3 -1
- package/dist/integrations/slack/slack.d.ts.map +1 -1
- package/dist/integrations/slack/slack.js.map +1 -1
- package/dist/routes/config.d.ts +4 -1
- package/dist/routes/config.d.ts.map +1 -1
- package/dist/routes/config.js +153 -56
- package/dist/routes/config.js.map +1 -1
- package/dist/routes/provider-credentials.d.ts +6 -1
- package/dist/routes/provider-credentials.d.ts.map +1 -1
- package/dist/routes/provider-credentials.js +2 -2
- package/dist/routes/provider-credentials.js.map +1 -1
- package/dist/run-error.d.ts +1 -1
- package/dist/run-error.d.ts.map +1 -1
- package/dist/run-error.js +16 -11
- package/dist/run-error.js.map +1 -1
- package/dist/session/reply-credential-processor.d.ts +8 -0
- package/dist/session/reply-credential-processor.d.ts.map +1 -1
- package/dist/session/reply-credential-processor.js +20 -11
- package/dist/session/reply-credential-processor.js.map +1 -1
- package/dist/storage/domains/audit/domain.d.ts.map +1 -1
- package/dist/storage/domains/audit/domain.js +10 -3
- package/dist/storage/domains/audit/domain.js.map +1 -1
- package/package.json +15 -15
|
@@ -24,15 +24,14 @@ import type { ApiRoute } from '@mastra/core/server';
|
|
|
24
24
|
import type { SlackAdapterChannelConfig } from '@mastra/slack';
|
|
25
25
|
import type { WorkItemFeedPublisher } from '../../storage/domains/comments/feed-sync.js';
|
|
26
26
|
import type { FactoryChannelsConfig, FactoryIntegration, IntegrationContext, IntegrationTools } from '../base.js';
|
|
27
|
-
/**
|
|
28
|
-
* Slack app credentials, read from env ONCE by the deploy entry. `signingSecret`
|
|
29
|
-
* is required because the Slack adapter validates it at construction — an
|
|
30
|
-
* integration constructed without it would throw during `prepare()` rather than
|
|
31
|
-
* reporting itself unconfigured.
|
|
32
|
-
*/
|
|
27
|
+
/** Slack app credentials, read from env ONCE by the deploy entry. */
|
|
33
28
|
export interface SlackIntegrationConfig {
|
|
34
|
-
/**
|
|
35
|
-
|
|
29
|
+
/** Slack event transport. Defaults to webhook mode. */
|
|
30
|
+
mode?: 'webhook' | 'socket';
|
|
31
|
+
/** Verifies inbound Slack request signatures. Required in webhook mode. */
|
|
32
|
+
signingSecret?: string;
|
|
33
|
+
/** App-level token used to connect to Slack. Required in socket mode. */
|
|
34
|
+
appToken?: string;
|
|
36
35
|
/** Bot token used to post replies and ephemeral cards. */
|
|
37
36
|
botToken?: string;
|
|
38
37
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integration.d.ts","sourceRoot":"","sources":["../../../src/integrations/slack/integration.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAkB,MAAM,uBAAuB,CAAC;AAC3E,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAE/D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,6CAA6C,CAAC;AAEzF,OAAO,KAAK,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAalH
|
|
1
|
+
{"version":3,"file":"integration.d.ts","sourceRoot":"","sources":["../../../src/integrations/slack/integration.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAkB,MAAM,uBAAuB,CAAC;AAC3E,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAE/D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,6CAA6C,CAAC;AAEzF,OAAO,KAAK,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAalH,qEAAqE;AACrE,MAAM,WAAW,sBAAsB;IACrC,uDAAuD;IACvD,IAAI,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;IAC5B,2EAA2E;IAC3E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,yEAAyE;IACzE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,uDAAuD;IACvD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;;;;;;;;OAaG;IACH,cAAc,CAAC,EAAE,yBAAyB,CAAC;CAC5C;AAyED,qBAAa,gBAAiB,YAAW,kBAAkB;;IACzD,QAAQ,CAAC,EAAE,WAAW;IACtB;;;;OAIG;IACH,QAAQ,CAAC,yBAAyB,QAAQ;IAO1C;;;;;OAKG;IACH,QAAQ,CAAC,UAAU,EAAE;QAAE,KAAK,EAAE,cAAc,EAAE,CAAC;QAAC,MAAM,EAAE,eAAe,EAAE,CAAA;KAAE,CAAC;gBAQhE,MAAM,EAAE,sBAAsB;IAgC1C,UAAU,CAAC,IAAI,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,aAAa,GAAG,IAAI,GAAG,SAAS,CAAA;KAAE,GAAG,IAAI;IAI7E;;;;OAIG;IACG,UAAU,CAAC,EAAE,cAAc,EAAE,EAAE;QAAE,cAAc,EAAE,cAAc,CAAA;KAAE,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAQnG;;;OAGG;IACG,gBAAgB,CAAC,EACrB,WAAW,EACX,cAAc,GACf,EAAE,UAAU,CAAC,WAAW,CAAC,kBAAkB,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAUrF,QAAQ,CAAC,GAAG,EAAE,kBAAkB,GAAG,qBAAqB;IA2BxD,aAAa,CAAC,GAAG,EAAE,kBAAkB,GAAG,qBAAqB;IAI7D,MAAM,CAAC,GAAG,EAAE,kBAAkB,GAAG,QAAQ,EAAE;IAc3C,WAAW,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CASvC"}
|
|
@@ -92,7 +92,10 @@ var SlackIntegration = class {
|
|
|
92
92
|
*/
|
|
93
93
|
#repoBackedSessions = false;
|
|
94
94
|
constructor(config) {
|
|
95
|
-
|
|
95
|
+
const mode = config.mode ?? "webhook";
|
|
96
|
+
if (mode === "webhook" && !config.signingSecret) throw new Error("SlackIntegration: 'signingSecret' is required in webhook mode — Slack cannot verify inbound requests without it.");
|
|
97
|
+
if (mode === "socket" && !config.appToken) throw new Error("SlackIntegration: 'appToken' is required in socket mode — Slack cannot open a Socket Mode connection without it.");
|
|
98
|
+
if (mode === "socket" && !config.botToken) throw new Error("SlackIntegration: 'botToken' is required in socket mode — Factory cannot reply without it.");
|
|
96
99
|
this.#config = config;
|
|
97
100
|
this.#adapterOptions = factoryAdapterOptions(adapterOverrides(config.adapterOptions));
|
|
98
101
|
const textHidden = this.#textHidden;
|
|
@@ -132,11 +135,16 @@ var SlackIntegration = class {
|
|
|
132
135
|
channels(ctx) {
|
|
133
136
|
const sourceControls = ctx.storage.sourceControls ?? (ctx.storage.sourceControlOwner ? [ctx.storage.sourceControlOwner] : []);
|
|
134
137
|
this.#repoBackedSessions = sourceControls.length > 0;
|
|
138
|
+
const mode = this.#config.mode ?? "webhook";
|
|
135
139
|
return createSlackChannelsConfig({
|
|
136
140
|
slack: {
|
|
137
|
-
|
|
138
|
-
|
|
141
|
+
mode: this.#config.mode,
|
|
142
|
+
...mode === "webhook" ? {
|
|
143
|
+
clientId: this.#config.clientId,
|
|
144
|
+
clientSecret: this.#config.clientSecret
|
|
145
|
+
} : {},
|
|
139
146
|
signingSecret: this.#config.signingSecret,
|
|
147
|
+
appToken: this.#config.appToken,
|
|
140
148
|
botToken: this.#config.botToken
|
|
141
149
|
},
|
|
142
150
|
accountLinks: ctx.storage.channelIdentity,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integration.js","names":[],"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 { AgentChannels, TypingStatusFn } from '@mastra/core/channels';\nimport type { InputProcessor, OutputProcessor } from '@mastra/core/processors';\nimport type { RequestContext } from '@mastra/core/request-context';\nimport type { ApiRoute } from '@mastra/core/server';\nimport type { SlackAdapterChannelConfig } from '@mastra/slack';\n\nimport type { WorkItemFeedPublisher } from '../../storage/domains/comments/feed-sync.js';\n\nimport type { FactoryChannelsConfig, FactoryIntegration, IntegrationContext, IntegrationTools } from '../base.js';\nimport { announcePullRequest } from '../channel-thread-announcements.js';\nimport { parseCreatedPullRequest } from '../github/session-subscriptions.js';\nimport { FactoryChannelReplyProcessor } from './channel-reply-processor.js';\nimport { FactoryChannelSessionLinkProcessor } from './channel-session-link-processor.js';\nimport { createSlackConnectRoutes } from './connect-route.js';\nimport { SlackFeedPublisher } from './feed-publisher.js';\nimport { FactoryReplyReminderProcessor } from './reply-reminder-processor.js';\nimport { createReplyTool, FactoryReplyStreamProcessor, REPLY_TOOL_NAME } from './reply-tool.js';\nimport { createShareFileTool, isSlackThread } from './share-file-tool.js';\nimport type { ShareFileToolDeps } from './share-file-tool.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\n/**\n * With reply text hidden, streamed text never reaches the thread, so it gets\n * no status: an \"is typing...\" set after the reply tool's post would sit on\n * the thread with nothing coming, and race the clear at the end of the run.\n */\nconst factoryTypingStatus =\n (textDisplay: SlackAdapterChannelConfig['textDisplay']): TypingStatusFn =>\n chunk => {\n switch (chunk.type) {\n case 'reasoning-delta':\n return 'is thinking...';\n case 'text-delta':\n return textDisplay === 'hidden' ? undefined : 'is typing...';\n // The reply tool's text streams into the thread as the model writes it.\n case 'tool-call-input-streaming-start':\n return chunk.payload.toolName === REPLY_TOOL_NAME ? 'is typing...' : undefined;\n case 'tool-call':\n // Still finishing the streamed reply; don't flip to \"is working...\".\n if (chunk.payload.toolName === REPLY_TOOL_NAME) return 'is typing...';\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 const textDisplay = overrides.textDisplay ?? 'hidden';\n return { typingStatus: factoryTypingStatus(textDisplay), toolDisplay: 'hidden', textDisplay, ...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 readonly #adapterOptions: SlackAdapterChannelConfig;\n /** Bound in `initialize()`; the controller mounts after integrations are built. */\n #channels: ShareFileToolDeps['channels'];\n readonly #deps: ShareFileToolDeps = { channels: () => this.#channels?.() };\n /**\n * With reply text hidden (the Factory default) the agent speaks through\n * `reply_in_thread`, so the prompt, reminder and live streaming for that\n * tool come with it. The setting is fixed for the deployment, so it is\n * decided once here rather than on every run.\n */\n readonly processors: { input: InputProcessor[]; output: OutputProcessor[] };\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 this.#adapterOptions = factoryAdapterOptions(adapterOverrides(config.adapterOptions));\n const textHidden = this.#textHidden;\n this.processors = {\n input: [\n new FactoryChannelReplyProcessor(),\n ...(textHidden ? [new FactoryReplyReminderProcessor()] : []),\n ],\n output: [\n new FactoryChannelSessionLinkProcessor(),\n ...(textHidden ? [new FactoryReplyStreamProcessor(this.#deps)] : []),\n ],\n };\n }\n\n get #textHidden(): boolean {\n return this.#adapterOptions.textDisplay === 'hidden';\n }\n\n initialize(args: { channels?: () => AgentChannels | null | undefined }): void {\n this.#channels = args.channels;\n }\n\n /**\n * `attach_file`, and `reply_in_thread` when reply text is hidden, on runs\n * whose session is linked to a Slack thread. The tools find the thread to\n * post into when they are called.\n */\n async agentTools({ requestContext }: { requestContext: RequestContext }): Promise<IntegrationTools> {\n if (!(await isSlackThread(requestContext, this.#deps))) return {};\n return {\n ...createShareFileTool(requestContext, this.#deps),\n ...(this.#textHidden ? createReplyTool(requestContext, this.#deps) : {}),\n };\n }\n\n /**\n * When a run opens a pull request, post its link into the session's Slack\n * thread.\n */\n async postToolObserver({\n toolContext,\n requestContext,\n }: Parameters<NonNullable<FactoryIntegration['postToolObserver']>>[0]): Promise<void> {\n const url = parseCreatedPullRequest(toolContext);\n const threadId = (requestContext?.get('controller') as { threadId?: string } | undefined)?.threadId;\n if (!url || !requestContext || !threadId || !(await isSlackThread(requestContext, this.#deps))) return;\n // Not awaited: the tool call must not wait on Slack.\n void announcePullRequest(this.#channels?.(), threadId, 'opened', url).catch(error =>\n console.warn('[slack] pull request opened announcement failed', error),\n );\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: this.#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":"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"}
|
|
1
|
+
{"version":3,"file":"integration.js","names":[],"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 { AgentChannels, TypingStatusFn } from '@mastra/core/channels';\nimport type { InputProcessor, OutputProcessor } from '@mastra/core/processors';\nimport type { RequestContext } from '@mastra/core/request-context';\nimport type { ApiRoute } from '@mastra/core/server';\nimport type { SlackAdapterChannelConfig } from '@mastra/slack';\n\nimport type { WorkItemFeedPublisher } from '../../storage/domains/comments/feed-sync.js';\n\nimport type { FactoryChannelsConfig, FactoryIntegration, IntegrationContext, IntegrationTools } from '../base.js';\nimport { announcePullRequest } from '../channel-thread-announcements.js';\nimport { parseCreatedPullRequest } from '../github/session-subscriptions.js';\nimport { FactoryChannelReplyProcessor } from './channel-reply-processor.js';\nimport { FactoryChannelSessionLinkProcessor } from './channel-session-link-processor.js';\nimport { createSlackConnectRoutes } from './connect-route.js';\nimport { SlackFeedPublisher } from './feed-publisher.js';\nimport { FactoryReplyReminderProcessor } from './reply-reminder-processor.js';\nimport { createReplyTool, FactoryReplyStreamProcessor, REPLY_TOOL_NAME } from './reply-tool.js';\nimport { createShareFileTool, isSlackThread } from './share-file-tool.js';\nimport type { ShareFileToolDeps } from './share-file-tool.js';\nimport { createSlackChannelsConfig } from './slack.js';\n\n/** Slack app credentials, read from env ONCE by the deploy entry. */\nexport interface SlackIntegrationConfig {\n /** Slack event transport. Defaults to webhook mode. */\n mode?: 'webhook' | 'socket';\n /** Verifies inbound Slack request signatures. Required in webhook mode. */\n signingSecret?: string;\n /** App-level token used to connect to Slack. Required in socket mode. */\n appToken?: 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\n/**\n * With reply text hidden, streamed text never reaches the thread, so it gets\n * no status: an \"is typing...\" set after the reply tool's post would sit on\n * the thread with nothing coming, and race the clear at the end of the run.\n */\nconst factoryTypingStatus =\n (textDisplay: SlackAdapterChannelConfig['textDisplay']): TypingStatusFn =>\n chunk => {\n switch (chunk.type) {\n case 'reasoning-delta':\n return 'is thinking...';\n case 'text-delta':\n return textDisplay === 'hidden' ? undefined : 'is typing...';\n // The reply tool's text streams into the thread as the model writes it.\n case 'tool-call-input-streaming-start':\n return chunk.payload.toolName === REPLY_TOOL_NAME ? 'is typing...' : undefined;\n case 'tool-call':\n // Still finishing the streamed reply; don't flip to \"is working...\".\n if (chunk.payload.toolName === REPLY_TOOL_NAME) return 'is typing...';\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 const textDisplay = overrides.textDisplay ?? 'hidden';\n return { typingStatus: factoryTypingStatus(textDisplay), toolDisplay: 'hidden', textDisplay, ...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 readonly #adapterOptions: SlackAdapterChannelConfig;\n /** Bound in `initialize()`; the controller mounts after integrations are built. */\n #channels: ShareFileToolDeps['channels'];\n readonly #deps: ShareFileToolDeps = { channels: () => this.#channels?.() };\n /**\n * With reply text hidden (the Factory default) the agent speaks through\n * `reply_in_thread`, so the prompt, reminder and live streaming for that\n * tool come with it. The setting is fixed for the deployment, so it is\n * decided once here rather than on every run.\n */\n readonly processors: { input: InputProcessor[]; output: OutputProcessor[] };\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 const mode = config.mode ?? 'webhook';\n if (mode === 'webhook' && !config.signingSecret) {\n throw new Error(\n \"SlackIntegration: 'signingSecret' is required in webhook mode — Slack cannot verify inbound requests without it.\",\n );\n }\n if (mode === 'socket' && !config.appToken) {\n throw new Error(\"SlackIntegration: 'appToken' is required in socket mode — Slack cannot open a Socket Mode connection without it.\");\n }\n if (mode === 'socket' && !config.botToken) {\n throw new Error(\"SlackIntegration: 'botToken' is required in socket mode — Factory cannot reply without it.\");\n }\n this.#config = config;\n this.#adapterOptions = factoryAdapterOptions(adapterOverrides(config.adapterOptions));\n const textHidden = this.#textHidden;\n this.processors = {\n input: [\n new FactoryChannelReplyProcessor(),\n ...(textHidden ? [new FactoryReplyReminderProcessor()] : []),\n ],\n output: [\n new FactoryChannelSessionLinkProcessor(),\n ...(textHidden ? [new FactoryReplyStreamProcessor(this.#deps)] : []),\n ],\n };\n }\n\n get #textHidden(): boolean {\n return this.#adapterOptions.textDisplay === 'hidden';\n }\n\n initialize(args: { channels?: () => AgentChannels | null | undefined }): void {\n this.#channels = args.channels;\n }\n\n /**\n * `attach_file`, and `reply_in_thread` when reply text is hidden, on runs\n * whose session is linked to a Slack thread. The tools find the thread to\n * post into when they are called.\n */\n async agentTools({ requestContext }: { requestContext: RequestContext }): Promise<IntegrationTools> {\n if (!(await isSlackThread(requestContext, this.#deps))) return {};\n return {\n ...createShareFileTool(requestContext, this.#deps),\n ...(this.#textHidden ? createReplyTool(requestContext, this.#deps) : {}),\n };\n }\n\n /**\n * When a run opens a pull request, post its link into the session's Slack\n * thread.\n */\n async postToolObserver({\n toolContext,\n requestContext,\n }: Parameters<NonNullable<FactoryIntegration['postToolObserver']>>[0]): Promise<void> {\n const url = parseCreatedPullRequest(toolContext);\n const threadId = (requestContext?.get('controller') as { threadId?: string } | undefined)?.threadId;\n if (!url || !requestContext || !threadId || !(await isSlackThread(requestContext, this.#deps))) return;\n // Not awaited: the tool call must not wait on Slack.\n void announcePullRequest(this.#channels?.(), threadId, 'opened', url).catch(error =>\n console.warn('[slack] pull request opened announcement failed', error),\n );\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 const mode = this.#config.mode ?? 'webhook';\n return createSlackChannelsConfig({\n slack: {\n mode: this.#config.mode,\n ...(mode === 'webhook'\n ? { clientId: this.#config.clientId, clientSecret: this.#config.clientSecret }\n : {}),\n signingSecret: this.#config.signingSecret,\n appToken: this.#config.appToken,\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: this.#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":";;;;;;;;;;;;AAmFA,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,EAAqB,EAAE,KAAK;CAG5F,OAAO,MAAM,cAAc,KAAK,IAAI,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,YAAY,IAAI,KAAK,MAAM,CAAC,IAAI;AACzF;;;;;;AAOA,MAAM,uBACH,iBACD,UAAS;CACT,QAAQ,MAAM,MAAd;EACE,KAAK,mBACH,OAAO;EACT,KAAK,cACH,OAAO,gBAAgB,WAAW,KAAA,IAAY;EAEhD,KAAK,mCACH,OAAO,MAAM,QAAQ,aAAA,oBAA+B,iBAAiB,KAAA;EACvE,KAAK;GAEH,IAAI,MAAM,QAAQ,aAAA,mBAA8B,OAAO;GACvD,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,MAAM,cAAc,UAAU,eAAe;CAC7C,OAAO;EAAE,cAAc,oBAAoB,WAAW;EAAG,aAAa;EAAU;EAAa,GAAG;CAAU;AAC5G;AAEA,IAAa,mBAAb,MAA4D;CAC1D,KAAc;;;;;;CAMd,4BAAqC;CAErC;CACA;;CAEA;CACA,QAAoC,EAAE,gBAAgB,KAAK,YAAY,EAAE;;;;;;;CAOzE;;;;;;CAMA,sBAAsB;CAEtB,YAAY,QAAgC;EAC1C,MAAM,OAAO,OAAO,QAAQ;EAC5B,IAAI,SAAS,aAAa,CAAC,OAAO,eAChC,MAAM,IAAI,MACR,kHACF;EAEF,IAAI,SAAS,YAAY,CAAC,OAAO,UAC/B,MAAM,IAAI,MAAM,kHAAkH;EAEpI,IAAI,SAAS,YAAY,CAAC,OAAO,UAC/B,MAAM,IAAI,MAAM,4FAA4F;EAE9G,KAAK,UAAU;EACf,KAAK,kBAAkB,sBAAsB,iBAAiB,OAAO,cAAc,CAAC;EACpF,MAAM,aAAa,KAAK;EACxB,KAAK,aAAa;GAChB,OAAO,CACL,IAAI,6BAA6B,GACjC,GAAI,aAAa,CAAC,IAAI,8BAA8B,CAAC,IAAI,CAAC,CAC5D;GACA,QAAQ,CACN,IAAI,mCAAmC,GACvC,GAAI,aAAa,CAAC,IAAI,4BAA4B,KAAK,KAAK,CAAC,IAAI,CAAC,CACpE;EACF;CACF;CAEA,IAAI,cAAuB;EACzB,OAAO,KAAK,gBAAgB,gBAAgB;CAC9C;CAEA,WAAW,MAAmE;EAC5E,KAAK,YAAY,KAAK;CACxB;;;;;;CAOA,MAAM,WAAW,EAAE,kBAAiF;EAClG,IAAI,CAAE,MAAM,cAAc,gBAAgB,KAAK,KAAK,GAAI,OAAO,CAAC;EAChE,OAAO;GACL,GAAG,oBAAoB,gBAAgB,KAAK,KAAK;GACjD,GAAI,KAAK,cAAc,gBAAgB,gBAAgB,KAAK,KAAK,IAAI,CAAC;EACxE;CACF;;;;;CAMA,MAAM,iBAAiB,EACrB,aACA,kBACoF;EACpF,MAAM,MAAM,wBAAwB,WAAW;EAC/C,MAAM,YAAY,gBAAgB,IAAI,YAAY,EAAA,EAAyC;EAC3F,IAAI,CAAC,OAAO,CAAC,kBAAkB,CAAC,YAAY,CAAE,MAAM,cAAc,gBAAgB,KAAK,KAAK,GAAI;EAEhG,oBAAyB,KAAK,YAAY,GAAG,UAAU,UAAU,GAAG,CAAC,CAAC,OAAM,UAC1E,QAAQ,KAAK,mDAAmD,KAAK,CACvE;CACF;CAEA,SAAS,KAAgD;EAGvD,MAAM,iBACJ,IAAI,QAAQ,mBAAmB,IAAI,QAAQ,qBAAqB,CAAC,IAAI,QAAQ,kBAAkB,IAAI,CAAC;EACtG,KAAK,sBAAsB,eAAe,SAAS;EACnD,MAAM,OAAO,KAAK,QAAQ,QAAQ;EAClC,OAAO,0BAA0B;GAC/B,OAAO;IACL,MAAM,KAAK,QAAQ;IACnB,GAAI,SAAS,YACT;KAAE,UAAU,KAAK,QAAQ;KAAU,cAAc,KAAK,QAAQ;IAAa,IAC3E,CAAC;IACL,eAAe,KAAK,QAAQ;IAC5B,UAAU,KAAK,QAAQ;IACvB,UAAU,KAAK,QAAQ;GACzB;GACA,cAAc,IAAI,QAAQ;GAC1B,UAAU,IAAI,QAAQ;GACtB;GACA,eAAe,IAAI,QAAQ;GAC3B,WAAW,IAAI,SAAS;GACxB,MAAM,IAAI;GACV,gBAAgB,KAAK;EACvB,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,KAAK;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,KAAK;EACvE,OAAO;GACL,YAAY;GACZ,oBAAoB,QAAQ,QAAQ;GACpC,gBAAgB,QAAQ,YAAY,gBAAgB,mBAAmB;GACvE,oBAAoB,KAAK;EAC3B;CACF;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-error-card.d.ts","sourceRoot":"","sources":["../../../src/integrations/slack/run-error-card.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"run-error-card.d.ts","sourceRoot":"","sources":["../../../src/integrations/slack/run-error-card.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAYjE,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,cAAc,EAAE,EAAE,mBAAmB,8BAetF"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { readFactorySessionScope } from "../../rules/binding-context.js";
|
|
2
|
+
import { runCredential } from "../../session/reply-credential-processor.js";
|
|
2
3
|
import { describeRunError } from "../../run-error.js";
|
|
3
4
|
import { modelSettingsLink } from "./channel-reply-processor.js";
|
|
4
5
|
import { sessionLinkFromRequest } from "./channel-session-link-processor.js";
|
|
@@ -9,29 +10,45 @@ function slackRunErrorCard(error, { requestContext }) {
|
|
|
9
10
|
const { title, message, recoveries, model } = describeRunError(error);
|
|
10
11
|
const requestId = providerRequestId(error);
|
|
11
12
|
const facts = [model && `Model: ${model}`, requestId && `Request id: ${requestId}`].filter(Boolean).join(" · ");
|
|
13
|
+
const credential = model && recoveries.includes("credentials") ? credentialLine(requestContext, model) : void 0;
|
|
12
14
|
const links = requestContext ? recoveryLinks(requestContext, recoveries) : [];
|
|
13
15
|
return Card({
|
|
14
16
|
title: `❌ ${title}`,
|
|
15
17
|
children: [
|
|
16
18
|
CardText(message),
|
|
19
|
+
...credential ? [CardText(credential, { style: "muted" })] : [],
|
|
17
20
|
...facts ? [CardText(facts, { style: "muted" })] : [],
|
|
18
21
|
...links.length ? [Actions(links.map((link) => LinkButton(link)))] : []
|
|
19
22
|
]
|
|
20
23
|
});
|
|
21
24
|
}
|
|
25
|
+
/** Only while the session's model is the one that failed: its provider is what names the credential. */
|
|
26
|
+
function credentialLine(requestContext, failedModel) {
|
|
27
|
+
const sessionModelId = (requestContext?.get("controller"))?.session?.modelId;
|
|
28
|
+
if (!sessionModelId) return void 0;
|
|
29
|
+
const slash = sessionModelId.indexOf("/");
|
|
30
|
+
if (slash <= 0 || sessionModelId !== failedModel && sessionModelId.slice(slash + 1) !== failedModel) return void 0;
|
|
31
|
+
const credential = runCredential(requestContext, sessionModelId.slice(0, slash));
|
|
32
|
+
if (!credential) return void 0;
|
|
33
|
+
return `Runs on ${credential.label} · ${credential.ownerId ? "Personal credential" : "Shared by the organization"}`;
|
|
34
|
+
}
|
|
22
35
|
function recoveryLinks(requestContext, recoveries) {
|
|
23
36
|
const factoryProjectId = readFactorySessionScope(requestContext)?.factoryProjectId;
|
|
24
37
|
const settingsUrl = factoryProjectId && modelSettingsLink(factoryProjectId);
|
|
25
38
|
const sessionUrl = sessionLinkFromRequest(requestContext);
|
|
26
39
|
const links = recoveries.flatMap((recovery) => {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
40
|
+
switch (recovery) {
|
|
41
|
+
case "credentials": return settingsUrl ? [{
|
|
42
|
+
url: settingsUrl,
|
|
43
|
+
label: "Review credentials"
|
|
44
|
+
}] : [];
|
|
45
|
+
case "model": return sessionUrl ? [{
|
|
46
|
+
url: sessionUrl,
|
|
47
|
+
label: "Change model"
|
|
48
|
+
}] : [];
|
|
49
|
+
case "continue":
|
|
50
|
+
case "new_session": return [];
|
|
51
|
+
}
|
|
35
52
|
});
|
|
36
53
|
if (links.length || !sessionUrl) return links;
|
|
37
54
|
return [{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-error-card.js","names":[],"sources":["../../../src/integrations/slack/run-error-card.ts"],"sourcesContent":["import type { ChannelErrorContext } from '@mastra/core/channels';\nimport { providerRequestId } from '@mastra/core/error';\nimport type { RequestContext } from '@mastra/core/request-context';\nimport { Actions, Card, CardText, LinkButton } from 'chat';\n\nimport { readFactorySessionScope } from '../../rules/binding-context.js';\nimport type { RunErrorRecovery } from '../../run-error.js';\nimport { describeRunError } from '../../run-error.js';\nimport { modelSettingsLink } from './channel-reply-processor.js';\nimport { sessionLinkFromRequest } from './channel-session-link-processor.js';\n\nexport function slackRunErrorCard(error: Error, { requestContext }: ChannelErrorContext) {\n const { title, message, recoveries, model } = describeRunError(error);\n const requestId = providerRequestId(error);\n const facts = [model && `Model: ${model}`, requestId && `Request id: ${requestId}`].filter(Boolean).join(' · ');\n const links = requestContext ? recoveryLinks(requestContext, recoveries) : [];\n return Card({\n title: `❌ ${title}`,\n children: [\n CardText(message),\n ...(facts ? [CardText(facts, { style: 'muted' })] : []),\n ...(links.length ? [Actions(links.map(link => LinkButton(link)))] : []),\n ],\n });\n}\n\nfunction recoveryLinks(requestContext: RequestContext, recoveries: RunErrorRecovery[]) {\n const factoryProjectId = readFactorySessionScope(requestContext)?.factoryProjectId;\n const settingsUrl = factoryProjectId && modelSettingsLink(factoryProjectId);\n const sessionUrl = sessionLinkFromRequest(requestContext);\n const links = recoveries.flatMap(recovery => {\n
|
|
1
|
+
{"version":3,"file":"run-error-card.js","names":[],"sources":["../../../src/integrations/slack/run-error-card.ts"],"sourcesContent":["import type { AgentControllerRequestContext } from '@mastra/core/agent-controller';\nimport type { ChannelErrorContext } from '@mastra/core/channels';\nimport { providerRequestId } from '@mastra/core/error';\nimport type { RequestContext } from '@mastra/core/request-context';\nimport { Actions, Card, CardText, LinkButton } from 'chat';\n\nimport { readFactorySessionScope } from '../../rules/binding-context.js';\nimport type { RunErrorRecovery } from '../../run-error.js';\nimport { describeRunError } from '../../run-error.js';\nimport { runCredential } from '../../session/reply-credential-processor.js';\nimport { modelSettingsLink } from './channel-reply-processor.js';\nimport { sessionLinkFromRequest } from './channel-session-link-processor.js';\n\nexport function slackRunErrorCard(error: Error, { requestContext }: ChannelErrorContext) {\n const { title, message, recoveries, model } = describeRunError(error);\n const requestId = providerRequestId(error);\n const facts = [model && `Model: ${model}`, requestId && `Request id: ${requestId}`].filter(Boolean).join(' · ');\n const credential = model && recoveries.includes('credentials') ? credentialLine(requestContext, model) : undefined;\n const links = requestContext ? recoveryLinks(requestContext, recoveries) : [];\n return Card({\n title: `❌ ${title}`,\n children: [\n CardText(message),\n ...(credential ? [CardText(credential, { style: 'muted' })] : []),\n ...(facts ? [CardText(facts, { style: 'muted' })] : []),\n ...(links.length ? [Actions(links.map(link => LinkButton(link)))] : []),\n ],\n });\n}\n\n/** Only while the session's model is the one that failed: its provider is what names the credential. */\nfunction credentialLine(requestContext: RequestContext | undefined, failedModel: string): string | undefined {\n const controller = requestContext?.get('controller') as AgentControllerRequestContext | undefined;\n const sessionModelId = controller?.session?.modelId;\n if (!sessionModelId) return undefined;\n const slash = sessionModelId.indexOf('/');\n if (slash <= 0 || (sessionModelId !== failedModel && sessionModelId.slice(slash + 1) !== failedModel))\n return undefined;\n const credential = runCredential(requestContext, sessionModelId.slice(0, slash));\n if (!credential) return undefined;\n return `Runs on ${credential.label} · ${credential.ownerId ? 'Personal credential' : 'Shared by the organization'}`;\n}\n\nfunction recoveryLinks(requestContext: RequestContext, recoveries: RunErrorRecovery[]) {\n const factoryProjectId = readFactorySessionScope(requestContext)?.factoryProjectId;\n const settingsUrl = factoryProjectId && modelSettingsLink(factoryProjectId);\n const sessionUrl = sessionLinkFromRequest(requestContext);\n const links = recoveries.flatMap(recovery => {\n switch (recovery) {\n case 'credentials':\n return settingsUrl ? [{ url: settingsUrl, label: 'Review credentials' }] : [];\n case 'model':\n return sessionUrl ? [{ url: sessionUrl, label: 'Change model' }] : [];\n // A reply in the thread continues the run; a mention in a new thread starts a new session.\n case 'continue':\n case 'new_session':\n return [];\n }\n });\n if (links.length || !sessionUrl) return links;\n return [{ url: sessionUrl, label: 'Open session' }];\n}\n"],"mappings":";;;;;;;;AAaA,SAAgB,kBAAkB,OAAc,EAAE,kBAAuC;CACvF,MAAM,EAAE,OAAO,SAAS,YAAY,UAAU,iBAAiB,KAAK;CACpE,MAAM,YAAY,kBAAkB,KAAK;CACzC,MAAM,QAAQ,CAAC,SAAS,UAAU,SAAS,aAAa,eAAe,WAAW,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,KAAK;CAC9G,MAAM,aAAa,SAAS,WAAW,SAAS,aAAa,IAAI,eAAe,gBAAgB,KAAK,IAAI,KAAA;CACzG,MAAM,QAAQ,iBAAiB,cAAc,gBAAgB,UAAU,IAAI,CAAC;CAC5E,OAAO,KAAK;EACV,OAAO,KAAK;EACZ,UAAU;GACR,SAAS,OAAO;GAChB,GAAI,aAAa,CAAC,SAAS,YAAY,EAAE,OAAO,QAAQ,CAAC,CAAC,IAAI,CAAC;GAC/D,GAAI,QAAQ,CAAC,SAAS,OAAO,EAAE,OAAO,QAAQ,CAAC,CAAC,IAAI,CAAC;GACrD,GAAI,MAAM,SAAS,CAAC,QAAQ,MAAM,KAAI,SAAQ,WAAW,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;EACvE;CACF,CAAC;AACH;;AAGA,SAAS,eAAe,gBAA4C,aAAyC;CAE3G,MAAM,kBADa,gBAAgB,IAAI,YAAY,EAAA,EAChB,SAAS;CAC5C,IAAI,CAAC,gBAAgB,OAAO,KAAA;CAC5B,MAAM,QAAQ,eAAe,QAAQ,GAAG;CACxC,IAAI,SAAS,KAAM,mBAAmB,eAAe,eAAe,MAAM,QAAQ,CAAC,MAAM,aACvF,OAAO,KAAA;CACT,MAAM,aAAa,cAAc,gBAAgB,eAAe,MAAM,GAAG,KAAK,CAAC;CAC/E,IAAI,CAAC,YAAY,OAAO,KAAA;CACxB,OAAO,WAAW,WAAW,MAAM,KAAK,WAAW,UAAU,wBAAwB;AACvF;AAEA,SAAS,cAAc,gBAAgC,YAAgC;CACrF,MAAM,mBAAmB,wBAAwB,cAAc,CAAC,EAAE;CAClE,MAAM,cAAc,oBAAoB,kBAAkB,gBAAgB;CAC1E,MAAM,aAAa,uBAAuB,cAAc;CACxD,MAAM,QAAQ,WAAW,SAAQ,aAAY;EAC3C,QAAQ,UAAR;GACE,KAAK,eACH,OAAO,cAAc,CAAC;IAAE,KAAK;IAAa,OAAO;GAAqB,CAAC,IAAI,CAAC;GAC9E,KAAK,SACH,OAAO,aAAa,CAAC;IAAE,KAAK;IAAY,OAAO;GAAe,CAAC,IAAI,CAAC;GAEtE,KAAK;GACL,KAAK,eACH,OAAO,CAAC;EACZ;CACF,CAAC;CACD,IAAI,MAAM,UAAU,CAAC,YAAY,OAAO;CACxC,OAAO,CAAC;EAAE,KAAK;EAAY,OAAO;CAAe,CAAC;AACpD"}
|
|
@@ -204,9 +204,11 @@ export declare function slackThreadSource(thread: HandlerThread, teamId?: string
|
|
|
204
204
|
export declare const createHandlers: (deps: SlackChannelDeps) => ChannelHandlers;
|
|
205
205
|
/** Slack app credentials, passed in explicitly rather than read from env here. */
|
|
206
206
|
interface SlackCredentials {
|
|
207
|
+
mode?: 'webhook' | 'socket';
|
|
207
208
|
clientId?: string;
|
|
208
209
|
clientSecret?: string;
|
|
209
|
-
signingSecret
|
|
210
|
+
signingSecret?: string;
|
|
211
|
+
appToken?: string;
|
|
210
212
|
botToken?: string;
|
|
211
213
|
}
|
|
212
214
|
export declare function createSlackChannelsConfig(deps: SlackChannelDeps & {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slack.d.ts","sourceRoot":"","sources":["../../../src/integrations/slack/slack.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"slack.d.ts","sourceRoot":"","sources":["../../../src/integrations/slack/slack.ts"],"names":[],"mappings":"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"}
|