@memberjunction/messaging-adapters 0.0.1 → 5.18.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.
Files changed (58) hide show
  1. package/README.md +231 -43
  2. package/dist/base/BaseMessagingAdapter.d.ts +428 -0
  3. package/dist/base/BaseMessagingAdapter.d.ts.map +1 -0
  4. package/dist/base/BaseMessagingAdapter.js +934 -0
  5. package/dist/base/BaseMessagingAdapter.js.map +1 -0
  6. package/dist/base/message-formatter.d.ts +70 -0
  7. package/dist/base/message-formatter.d.ts.map +1 -0
  8. package/dist/base/message-formatter.js +201 -0
  9. package/dist/base/message-formatter.js.map +1 -0
  10. package/dist/base/types.d.ts +211 -0
  11. package/dist/base/types.d.ts.map +1 -0
  12. package/dist/base/types.js +6 -0
  13. package/dist/base/types.js.map +1 -0
  14. package/dist/index.d.ts +72 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +76 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/slack/SlackAdapter.d.ts +141 -0
  19. package/dist/slack/SlackAdapter.d.ts.map +1 -0
  20. package/dist/slack/SlackAdapter.js +291 -0
  21. package/dist/slack/SlackAdapter.js.map +1 -0
  22. package/dist/slack/SlackMessagingExtension.d.ts +148 -0
  23. package/dist/slack/SlackMessagingExtension.d.ts.map +1 -0
  24. package/dist/slack/SlackMessagingExtension.js +433 -0
  25. package/dist/slack/SlackMessagingExtension.js.map +1 -0
  26. package/dist/slack/slack-block-builder.d.ts +133 -0
  27. package/dist/slack/slack-block-builder.d.ts.map +1 -0
  28. package/dist/slack/slack-block-builder.js +748 -0
  29. package/dist/slack/slack-block-builder.js.map +1 -0
  30. package/dist/slack/slack-formatter.d.ts +37 -0
  31. package/dist/slack/slack-formatter.d.ts.map +1 -0
  32. package/dist/slack/slack-formatter.js +116 -0
  33. package/dist/slack/slack-formatter.js.map +1 -0
  34. package/dist/slack/slack-interactivity.d.ts +38 -0
  35. package/dist/slack/slack-interactivity.d.ts.map +1 -0
  36. package/dist/slack/slack-interactivity.js +414 -0
  37. package/dist/slack/slack-interactivity.js.map +1 -0
  38. package/dist/slack/slack-routes.d.ts +35 -0
  39. package/dist/slack/slack-routes.d.ts.map +1 -0
  40. package/dist/slack/slack-routes.js +98 -0
  41. package/dist/slack/slack-routes.js.map +1 -0
  42. package/dist/teams/TeamsAdapter.d.ts +155 -0
  43. package/dist/teams/TeamsAdapter.d.ts.map +1 -0
  44. package/dist/teams/TeamsAdapter.js +383 -0
  45. package/dist/teams/TeamsAdapter.js.map +1 -0
  46. package/dist/teams/TeamsMessagingExtension.d.ts +75 -0
  47. package/dist/teams/TeamsMessagingExtension.d.ts.map +1 -0
  48. package/dist/teams/TeamsMessagingExtension.js +176 -0
  49. package/dist/teams/TeamsMessagingExtension.js.map +1 -0
  50. package/dist/teams/teams-card-builder.d.ts +94 -0
  51. package/dist/teams/teams-card-builder.d.ts.map +1 -0
  52. package/dist/teams/teams-card-builder.js +648 -0
  53. package/dist/teams/teams-card-builder.js.map +1 -0
  54. package/dist/teams/teams-formatter.d.ts +39 -0
  55. package/dist/teams/teams-formatter.d.ts.map +1 -0
  56. package/dist/teams/teams-formatter.js +107 -0
  57. package/dist/teams/teams-formatter.js.map +1 -0
  58. package/package.json +40 -7
package/dist/index.js ADDED
@@ -0,0 +1,76 @@
1
+ /**
2
+ * @module @memberjunction/messaging-adapters
3
+ *
4
+ * Messaging platform adapters for MemberJunction AI agents.
5
+ *
6
+ * This package provides Slack and Microsoft Teams integrations that allow
7
+ * MJ AI agents to be invoked directly from messaging platforms. Messages
8
+ * are received via webhooks, routed to the appropriate agent via
9
+ * `AgentRunner.RunAgent()`, and responses are streamed back as rich
10
+ * formatted messages (Slack Block Kit / Teams Adaptive Cards).
11
+ *
12
+ * ## Architecture
13
+ *
14
+ * Built on the MJServer Extension framework (`@memberjunction/server-extensions-core`):
15
+ * - `SlackMessagingExtension` and `TeamsMessagingExtension` extend `BaseServerExtension`
16
+ * - Auto-discovered via `@RegisterClass` and configured in `mj.config.cjs`
17
+ * - Zero MJServer source code changes needed per adapter
18
+ *
19
+ * ## Quick Start
20
+ *
21
+ * ```javascript
22
+ * // mj.config.cjs
23
+ * serverExtensions: [
24
+ * {
25
+ * Enabled: true,
26
+ * DriverClass: 'SlackMessagingExtension',
27
+ * RootPath: '/webhook/slack',
28
+ * Settings: {
29
+ * DefaultAgentName: 'Sage',
30
+ * ContextUserEmail: 'bot@company.com',
31
+ * BotToken: process.env.SLACK_BOT_TOKEN,
32
+ * SigningSecret: process.env.SLACK_SIGNING_SECRET,
33
+ * }
34
+ * }
35
+ * ]
36
+ * ```
37
+ *
38
+ * @see {@link BaseMessagingAdapter} for the shared adapter base class
39
+ * @see {@link SlackMessagingExtension} for Slack integration
40
+ * @see {@link TeamsMessagingExtension} for Teams integration
41
+ */
42
+ // Base adapter and types
43
+ export { BaseMessagingAdapter } from './base/BaseMessagingAdapter.js';
44
+ export { splitMarkdownIntoSections, convertToSlackMrkdwn, convertBoldToSlackFormat, convertLinksToSlackFormat, truncateText, splitTextIntoChunks, } from './base/message-formatter.js';
45
+ // Slack adapter
46
+ export { SlackMessagingExtension } from './slack/SlackMessagingExtension.js';
47
+ export { SlackAdapter } from './slack/SlackAdapter.js';
48
+ export { markdownToBlocks } from './slack/slack-formatter.js';
49
+ export { verifySlackSignature } from './slack/slack-routes.js';
50
+ export { handleSlackInteraction, registerActiveForm } from './slack/slack-interactivity.js';
51
+ export { buildRichResponse, buildAgentContextBlock, buildTextBlocks, buildArtifactCard, buildActionButtons, buildMediaBlocks, buildErrorBlocks, buildMetadataFooter, buildDivider, buildResponseForm, buildFormModal, buildNotificationBlocks, getFullResponseText, } from './slack/slack-block-builder.js';
52
+ // Teams adapter
53
+ export { TeamsMessagingExtension } from './teams/TeamsMessagingExtension.js';
54
+ export { TeamsAdapter } from './teams/TeamsAdapter.js';
55
+ export { markdownToAdaptiveCard } from './teams/teams-formatter.js';
56
+ export { buildRichAdaptiveCard, buildAgentHeader as buildTeamsAgentHeader, buildTextBody as buildTeamsTextBody, buildArtifactCard as buildTeamsArtifactCard, buildActionButtons as buildTeamsActionButtons, buildExplorerLink as buildTeamsExplorerLink, buildMetadataFooter as buildTeamsMetadataFooter, buildErrorCard as buildTeamsErrorCard, buildResponseFormElements as buildTeamsResponseFormElements, } from './teams/teams-card-builder.js';
57
+ /**
58
+ * Tree-shaking prevention function.
59
+ *
60
+ * Import and call this function from your application's entry point
61
+ * (or include it in the class manifest) to ensure the `@RegisterClass`
62
+ * decorators on `SlackMessagingExtension` and `TeamsMessagingExtension`
63
+ * fire at module load time.
64
+ *
65
+ * @example
66
+ * ```typescript
67
+ * import { LoadMessagingAdapters } from '@memberjunction/messaging-adapters';
68
+ * LoadMessagingAdapters();
69
+ * ```
70
+ */
71
+ export function LoadMessagingAdapters() {
72
+ // This function exists solely to create a static reference that
73
+ // prevents bundlers from tree-shaking the @RegisterClass decorators.
74
+ // The decorators fire as a side effect of importing the module.
75
+ }
76
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH,yBAAyB;AACzB,OAAO,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AAUtE,OAAO,EACH,yBAAyB,EACzB,oBAAoB,EACpB,wBAAwB,EACxB,yBAAyB,EACzB,YAAY,EACZ,mBAAmB,GACtB,MAAM,6BAA6B,CAAC;AAErC,gBAAgB;AAChB,OAAO,EAAE,uBAAuB,EAAE,MAAM,oCAAoC,CAAC;AAC7E,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAC5F,OAAO,EACH,iBAAiB,EACjB,sBAAsB,EACtB,eAAe,EACf,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,gBAAgB,EAChB,mBAAmB,EACnB,YAAY,EACZ,iBAAiB,EACjB,cAAc,EACd,uBAAuB,EACvB,mBAAmB,GACtB,MAAM,gCAAgC,CAAC;AAGxC,gBAAgB;AAChB,OAAO,EAAE,uBAAuB,EAAE,MAAM,oCAAoC,CAAC;AAC7E,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AACpE,OAAO,EACH,qBAAqB,EACrB,gBAAgB,IAAI,qBAAqB,EACzC,aAAa,IAAI,kBAAkB,EACnC,iBAAiB,IAAI,sBAAsB,EAC3C,kBAAkB,IAAI,uBAAuB,EAC7C,iBAAiB,IAAI,sBAAsB,EAC3C,mBAAmB,IAAI,wBAAwB,EAC/C,cAAc,IAAI,mBAAmB,EACrC,yBAAyB,IAAI,8BAA8B,GAC9D,MAAM,+BAA+B,CAAC;AAGvC;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,qBAAqB;IACjC,gEAAgE;IAChE,qEAAqE;IACrE,gEAAgE;AACpE,CAAC"}
@@ -0,0 +1,141 @@
1
+ /**
2
+ * @module @memberjunction/messaging-adapters
3
+ * @description Slack-specific messaging adapter implementation.
4
+ *
5
+ * Extends `BaseMessagingAdapter` with Slack API calls for:
6
+ * - Posting and updating messages via the Web API with per-agent identity
7
+ * - Fetching thread history via `conversations.replies`
8
+ * - Rich Block Kit formatting with agent context, artifact cards, and metadata
9
+ * - Multi-word agent name matching via known-name lookup
10
+ * - Looking up user email addresses
11
+ * - Stripping bot @mentions from message text
12
+ */
13
+ import { ExecuteAgentResult, MJAIAgentEntityExtended } from '@memberjunction/ai-core-plus';
14
+ import { BaseMessagingAdapter } from '../base/BaseMessagingAdapter.js';
15
+ import { IncomingMessage, FormattedResponse, MessagingAdapterSettings, AgentResponseMetadata } from '../base/types.js';
16
+ /**
17
+ * Slack-specific adapter that implements all platform operations
18
+ * using the Slack Web API (`@slack/web-api`).
19
+ *
20
+ * ## Features
21
+ * - Per-agent identity: each agent's name and avatar appear on their messages
22
+ * - Thread-based conversation context via `conversations.replies`
23
+ * - Progressive streaming updates via `chat.postMessage` / `chat.update`
24
+ * - Rich Block Kit formatting with agent headers, artifact cards, and metadata
25
+ * - Multi-word agent name matching (e.g., `@Research Agent`)
26
+ * - User email lookup via `users.info` for MJ user mapping
27
+ * - Bot mention stripping for clean agent input
28
+ *
29
+ * ## Authentication
30
+ * Requires a Bot User OAuth Token (`xoxb-...`) with these scopes:
31
+ * - `chat:write` — Post and update messages
32
+ * - `chat:write.customize` — Post with custom username and icon
33
+ * - `channels:history` / `groups:history` / `im:history` — Read thread history
34
+ * - `users:read` / `users:read.email` — Look up user email addresses
35
+ * - `app_mentions:read` — Receive @mention events
36
+ */
37
+ export declare class SlackAdapter extends BaseMessagingAdapter {
38
+ /** Slack Web API client. */
39
+ private client;
40
+ /** The bot's own Slack user ID (e.g., `U0123456`). */
41
+ private botUserID;
42
+ /**
43
+ * Message IDs of "Thinking..." indicators, keyed by `channelId:threadTs`.
44
+ * Per-thread keying prevents concurrent messages from overwriting each other's indicator.
45
+ */
46
+ private thinkingMessageIds;
47
+ /**
48
+ * Slack's maximum message text length. Messages exceeding this cause `msg_too_long`.
49
+ * The `text` field is the plain-text fallback when blocks are present; the rich
50
+ * content is in Block Kit blocks (already limited to 50 blocks with "View Full" button).
51
+ */
52
+ private static readonly MAX_TEXT_LENGTH;
53
+ protected get PlatformName(): string;
54
+ constructor(settings: MessagingAdapterSettings);
55
+ /**
56
+ * Convert a raw Slack event payload into a normalized `IncomingMessage`.
57
+ *
58
+ * Called by `SlackMessagingExtension` when a webhook event is received.
59
+ * Uses multi-word agent name matching against the loaded agent list.
60
+ *
61
+ * @param event - Raw Slack event object from the Events API.
62
+ * @returns Normalized incoming message.
63
+ */
64
+ MapSlackEvent(event: Record<string, unknown>): IncomingMessage;
65
+ /**
66
+ * Fetch the bot's own user ID from the Slack API.
67
+ * This is needed to identify bot messages in thread history.
68
+ */
69
+ protected onInitialize(): Promise<void>;
70
+ protected getBotUserId(): string;
71
+ /**
72
+ * Post a "Thinking..." message as the typing indicator.
73
+ * Shows the agent's identity if available. This message gets replaced
74
+ * in-place by the first streaming update.
75
+ */
76
+ protected showTypingIndicator(message: IncomingMessage, agent?: MJAIAgentEntityExtended): Promise<void>;
77
+ /**
78
+ * Fetch all messages in a Slack thread using `conversations.replies`.
79
+ */
80
+ protected fetchThreadHistory(channelId: string, threadId: string): Promise<IncomingMessage[]>;
81
+ /**
82
+ * Post a new streaming message or update an existing one.
83
+ * Shows the agent's identity on new messages.
84
+ */
85
+ protected sendOrUpdateStreamingMessage(originalMessage: IncomingMessage, currentContent: string, existingMessageId: string | null, agent?: MJAIAgentEntityExtended): Promise<string>;
86
+ /**
87
+ * Send the final formatted response. If a "Thinking..." message exists
88
+ * (no streaming occurred), update it in-place instead of posting a new message.
89
+ */
90
+ protected sendFinalMessage(originalMessage: IncomingMessage, response: FormattedResponse): Promise<void>;
91
+ /**
92
+ * Update the streaming progress message with the final formatted response.
93
+ */
94
+ protected updateFinalMessage(originalMessage: IncomingMessage, messageId: string, response: FormattedResponse): Promise<void>;
95
+ /**
96
+ * Format agent response as rich Slack Block Kit blocks.
97
+ *
98
+ * When a full `ExecuteAgentResult` is available, builds a rich layout with
99
+ * agent header, artifact cards, action buttons, and metadata footer.
100
+ * Falls back to simple markdown→blocks conversion for plain text.
101
+ */
102
+ protected formatResponse(result: ExecuteAgentResult | null, agent: MJAIAgentEntityExtended, responseText: string, metadata?: AgentResponseMetadata): Promise<FormattedResponse>;
103
+ /**
104
+ * Strip the bot's @mention from the message text.
105
+ * Slack @mentions use the format `<@U0123456>`.
106
+ */
107
+ protected stripBotMention(text: string): string;
108
+ /**
109
+ * Look up a Slack user's email address via the Web API.
110
+ *
111
+ * Requires the `users:read.email` scope on the bot token.
112
+ *
113
+ * @param platformUserId - Slack user ID (e.g., `U0123456`).
114
+ * @returns Email address, or `null` if not available.
115
+ */
116
+ protected lookupUserEmail(platformUserId: string): Promise<string | null>;
117
+ /** Build a unique key for per-thread state (thinking indicator). */
118
+ private threadKey;
119
+ /**
120
+ * Truncate text to Slack's message length limit.
121
+ * Appends an ellipsis note if truncated so users know content was cut.
122
+ */
123
+ private truncateForSlack;
124
+ /**
125
+ * Truncate text for use as the `text` fallback when Block Kit blocks are present.
126
+ * The `text` field is only shown in notifications/accessibility — the blocks contain
127
+ * the rich content. Keeping it short avoids `msg_too_long` when blocks are large.
128
+ */
129
+ private truncateForSlackFallback;
130
+ /**
131
+ * Build Slack API params for per-agent identity.
132
+ * Uses `username` and `icon_url` which require the `chat:write.customize` scope.
133
+ * Only includes `icon_url` if it's a valid HTTPS URL.
134
+ */
135
+ private buildIdentityParams;
136
+ /** Build identity params from an agent entity. */
137
+ private buildSlackIdentityParams;
138
+ /** Build identity params from an AgentIdentity object. */
139
+ private buildSlackIdentityParamsFromIdentity;
140
+ }
141
+ //# sourceMappingURL=SlackAdapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SlackAdapter.d.ts","sourceRoot":"","sources":["../../src/slack/SlackAdapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAE3F,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAIvH;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,YAAa,SAAQ,oBAAoB;IAClD,4BAA4B;IAC5B,OAAO,CAAC,MAAM,CAAY;IAE1B,sDAAsD;IACtD,OAAO,CAAC,SAAS,CAAc;IAE/B;;;OAGG;IACH,OAAO,CAAC,kBAAkB,CAA6B;IAEvD;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAU;IAEjD,SAAS,KAAK,YAAY,IAAI,MAAM,CAAoB;gBAE5C,QAAQ,EAAE,wBAAwB;IAK9C;;;;;;;;OAQG;IACI,aAAa,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,eAAe;IAiBrE;;;OAGG;cACa,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAK7C,SAAS,CAAC,YAAY,IAAI,MAAM;IAIhC;;;;OAIG;cACa,mBAAmB,CAAC,OAAO,EAAE,eAAe,EAAE,KAAK,CAAC,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC;IAe7G;;OAEG;cACa,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAqBnG;;;OAGG;cACa,4BAA4B,CACxC,eAAe,EAAE,eAAe,EAChC,cAAc,EAAE,MAAM,EACtB,iBAAiB,EAAE,MAAM,GAAG,IAAI,EAChC,KAAK,CAAC,EAAE,uBAAuB,GAChC,OAAO,CAAC,MAAM,CAAC;IA2BlB;;;OAGG;cACa,gBAAgB,CAAC,eAAe,EAAE,eAAe,EAAE,QAAQ,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAsB9G;;OAEG;cACa,kBAAkB,CAC9B,eAAe,EAAE,eAAe,EAChC,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,iBAAiB,GAC5B,OAAO,CAAC,IAAI,CAAC;IAShB;;;;;;OAMG;cACa,cAAc,CAC1B,MAAM,EAAE,kBAAkB,GAAG,IAAI,EACjC,KAAK,EAAE,uBAAuB,EAC9B,YAAY,EAAE,MAAM,EACpB,QAAQ,CAAC,EAAE,qBAAqB,GACjC,OAAO,CAAC,iBAAiB,CAAC;IAiB7B;;;OAGG;IACH,SAAS,CAAC,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAI/C;;;;;;;OAOG;cACa,eAAe,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAY/E,oEAAoE;IACpE,OAAO,CAAC,SAAS;IAIjB;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IAKxB;;;;OAIG;IACH,OAAO,CAAC,wBAAwB;IAMhC;;;;OAIG;IACH,OAAO,CAAC,mBAAmB;IAS3B,kDAAkD;IAClD,OAAO,CAAC,wBAAwB;IAIhC,0DAA0D;IAC1D,OAAO,CAAC,oCAAoC;CAG/C"}
@@ -0,0 +1,291 @@
1
+ /**
2
+ * @module @memberjunction/messaging-adapters
3
+ * @description Slack-specific messaging adapter implementation.
4
+ *
5
+ * Extends `BaseMessagingAdapter` with Slack API calls for:
6
+ * - Posting and updating messages via the Web API with per-agent identity
7
+ * - Fetching thread history via `conversations.replies`
8
+ * - Rich Block Kit formatting with agent context, artifact cards, and metadata
9
+ * - Multi-word agent name matching via known-name lookup
10
+ * - Looking up user email addresses
11
+ * - Stripping bot @mentions from message text
12
+ */
13
+ import { WebClient } from '@slack/web-api';
14
+ import { LogStatus } from '@memberjunction/core';
15
+ import { BaseMessagingAdapter } from '../base/BaseMessagingAdapter.js';
16
+ import { buildRichResponse } from './slack-block-builder.js';
17
+ /**
18
+ * Slack-specific adapter that implements all platform operations
19
+ * using the Slack Web API (`@slack/web-api`).
20
+ *
21
+ * ## Features
22
+ * - Per-agent identity: each agent's name and avatar appear on their messages
23
+ * - Thread-based conversation context via `conversations.replies`
24
+ * - Progressive streaming updates via `chat.postMessage` / `chat.update`
25
+ * - Rich Block Kit formatting with agent headers, artifact cards, and metadata
26
+ * - Multi-word agent name matching (e.g., `@Research Agent`)
27
+ * - User email lookup via `users.info` for MJ user mapping
28
+ * - Bot mention stripping for clean agent input
29
+ *
30
+ * ## Authentication
31
+ * Requires a Bot User OAuth Token (`xoxb-...`) with these scopes:
32
+ * - `chat:write` — Post and update messages
33
+ * - `chat:write.customize` — Post with custom username and icon
34
+ * - `channels:history` / `groups:history` / `im:history` — Read thread history
35
+ * - `users:read` / `users:read.email` — Look up user email addresses
36
+ * - `app_mentions:read` — Receive @mention events
37
+ */
38
+ export class SlackAdapter extends BaseMessagingAdapter {
39
+ /**
40
+ * Slack's maximum message text length. Messages exceeding this cause `msg_too_long`.
41
+ * The `text` field is the plain-text fallback when blocks are present; the rich
42
+ * content is in Block Kit blocks (already limited to 50 blocks with "View Full" button).
43
+ */
44
+ static { this.MAX_TEXT_LENGTH = 39_000; }
45
+ get PlatformName() { return 'Slack'; }
46
+ constructor(settings) {
47
+ super(settings);
48
+ /** The bot's own Slack user ID (e.g., `U0123456`). */
49
+ this.botUserID = '';
50
+ /**
51
+ * Message IDs of "Thinking..." indicators, keyed by `channelId:threadTs`.
52
+ * Per-thread keying prevents concurrent messages from overwriting each other's indicator.
53
+ */
54
+ this.thinkingMessageIds = new Map();
55
+ this.client = new WebClient(settings.BotToken);
56
+ }
57
+ /**
58
+ * Convert a raw Slack event payload into a normalized `IncomingMessage`.
59
+ *
60
+ * Called by `SlackMessagingExtension` when a webhook event is received.
61
+ * Uses multi-word agent name matching against the loaded agent list.
62
+ *
63
+ * @param event - Raw Slack event object from the Events API.
64
+ * @returns Normalized incoming message.
65
+ */
66
+ MapSlackEvent(event) {
67
+ const text = event.text ?? '';
68
+ return {
69
+ MessageID: event.ts,
70
+ Text: text,
71
+ SenderID: event.user ?? event.bot_id ?? '',
72
+ SenderName: event.username ?? '',
73
+ ChannelID: event.channel,
74
+ ThreadID: event.thread_ts ?? null,
75
+ IsDirectMessage: event.channel_type === 'im',
76
+ IsBotMention: event.type === 'app_mention',
77
+ MentionedAgentNames: this.matchAgentMentions(text),
78
+ Timestamp: new Date(parseFloat(event.ts) * 1000),
79
+ RawEvent: event
80
+ };
81
+ }
82
+ /**
83
+ * Fetch the bot's own user ID from the Slack API.
84
+ * This is needed to identify bot messages in thread history.
85
+ */
86
+ async onInitialize() {
87
+ const authResult = await this.client.auth.test();
88
+ this.botUserID = authResult.user_id;
89
+ }
90
+ getBotUserId() {
91
+ return this.botUserID;
92
+ }
93
+ /**
94
+ * Post a "Thinking..." message as the typing indicator.
95
+ * Shows the agent's identity if available. This message gets replaced
96
+ * in-place by the first streaming update.
97
+ */
98
+ async showTypingIndicator(message, agent) {
99
+ const threadTs = message.ThreadID ?? message.MessageID;
100
+ const identityParams = agent ? this.buildSlackIdentityParams(agent) : {};
101
+ const result = await this.client.chat.postMessage({
102
+ channel: message.ChannelID,
103
+ thread_ts: threadTs,
104
+ text: '_Thinking..._',
105
+ ...identityParams
106
+ });
107
+ if (result.ts) {
108
+ this.thinkingMessageIds.set(this.threadKey(message), result.ts);
109
+ }
110
+ }
111
+ /**
112
+ * Fetch all messages in a Slack thread using `conversations.replies`.
113
+ */
114
+ async fetchThreadHistory(channelId, threadId) {
115
+ const result = await this.client.conversations.replies({
116
+ channel: channelId,
117
+ ts: threadId,
118
+ limit: this.settings.MaxThreadMessages ?? 50
119
+ });
120
+ return (result.messages ?? []).map(msg => ({
121
+ MessageID: msg.ts,
122
+ Text: msg.text ?? '',
123
+ SenderID: msg.user ?? msg.bot_id ?? '',
124
+ SenderName: msg.username ?? '',
125
+ ChannelID: channelId,
126
+ ThreadID: threadId,
127
+ IsDirectMessage: false,
128
+ IsBotMention: false,
129
+ Timestamp: new Date(parseFloat(msg.ts) * 1000),
130
+ RawEvent: msg
131
+ }));
132
+ }
133
+ /**
134
+ * Post a new streaming message or update an existing one.
135
+ * Shows the agent's identity on new messages.
136
+ */
137
+ async sendOrUpdateStreamingMessage(originalMessage, currentContent, existingMessageId, agent) {
138
+ // Reuse the "Thinking..." message for the first streaming update
139
+ const key = this.threadKey(originalMessage);
140
+ const messageToUpdate = existingMessageId ?? this.thinkingMessageIds.get(key) ?? null;
141
+ if (messageToUpdate) {
142
+ this.thinkingMessageIds.delete(key); // Consumed
143
+ await this.client.chat.update({
144
+ channel: originalMessage.ChannelID,
145
+ ts: messageToUpdate,
146
+ text: this.truncateForSlack(currentContent + ' ...')
147
+ });
148
+ return messageToUpdate;
149
+ }
150
+ else {
151
+ const threadTs = originalMessage.ThreadID ?? originalMessage.MessageID;
152
+ const identityParams = agent ? this.buildSlackIdentityParams(agent) : {};
153
+ const result = await this.client.chat.postMessage({
154
+ channel: originalMessage.ChannelID,
155
+ thread_ts: threadTs,
156
+ text: this.truncateForSlack(currentContent + ' ...'),
157
+ ...identityParams
158
+ });
159
+ return result.ts;
160
+ }
161
+ }
162
+ /**
163
+ * Send the final formatted response. If a "Thinking..." message exists
164
+ * (no streaming occurred), update it in-place instead of posting a new message.
165
+ */
166
+ async sendFinalMessage(originalMessage, response) {
167
+ const key = this.threadKey(originalMessage);
168
+ const thinkingId = this.thinkingMessageIds.get(key);
169
+ if (thinkingId) {
170
+ this.thinkingMessageIds.delete(key);
171
+ await this.updateFinalMessage(originalMessage, thinkingId, response);
172
+ return;
173
+ }
174
+ const threadTs = originalMessage.ThreadID ?? originalMessage.MessageID;
175
+ const identityParams = response.AgentIdentity
176
+ ? this.buildSlackIdentityParamsFromIdentity(response.AgentIdentity)
177
+ : {};
178
+ await this.client.chat.postMessage({
179
+ channel: originalMessage.ChannelID,
180
+ thread_ts: threadTs,
181
+ text: this.truncateForSlackFallback(response.PlainText),
182
+ blocks: response.RichPayload.blocks,
183
+ ...identityParams
184
+ });
185
+ }
186
+ /**
187
+ * Update the streaming progress message with the final formatted response.
188
+ */
189
+ async updateFinalMessage(originalMessage, messageId, response) {
190
+ await this.client.chat.update({
191
+ channel: originalMessage.ChannelID,
192
+ ts: messageId,
193
+ text: this.truncateForSlackFallback(response.PlainText),
194
+ blocks: response.RichPayload.blocks
195
+ });
196
+ }
197
+ /**
198
+ * Format agent response as rich Slack Block Kit blocks.
199
+ *
200
+ * When a full `ExecuteAgentResult` is available, builds a rich layout with
201
+ * agent header, artifact cards, action buttons, and metadata footer.
202
+ * Falls back to simple markdown→blocks conversion for plain text.
203
+ */
204
+ async formatResponse(result, agent, responseText, metadata) {
205
+ const identity = this.buildAgentIdentity(agent);
206
+ // Build rich Block Kit layout
207
+ const blocks = buildRichResponse(result, agent, responseText, {
208
+ explorerBaseURL: this.settings.ExplorerBaseURL,
209
+ artifactId: metadata?.ArtifactId,
210
+ conversationId: metadata?.ConversationId,
211
+ });
212
+ return {
213
+ PlainText: responseText,
214
+ RichPayload: { blocks },
215
+ AgentIdentity: identity
216
+ };
217
+ }
218
+ /**
219
+ * Strip the bot's @mention from the message text.
220
+ * Slack @mentions use the format `<@U0123456>`.
221
+ */
222
+ stripBotMention(text) {
223
+ return text.replace(new RegExp(`<@${this.botUserID}>`, 'g'), '').trim();
224
+ }
225
+ /**
226
+ * Look up a Slack user's email address via the Web API.
227
+ *
228
+ * Requires the `users:read.email` scope on the bot token.
229
+ *
230
+ * @param platformUserId - Slack user ID (e.g., `U0123456`).
231
+ * @returns Email address, or `null` if not available.
232
+ */
233
+ async lookupUserEmail(platformUserId) {
234
+ try {
235
+ const result = await this.client.users.info({ user: platformUserId });
236
+ return result.user?.profile?.email ?? null;
237
+ }
238
+ catch (error) {
239
+ LogStatus(`Slack user email lookup failed for '${platformUserId}' (falling back to service account)`);
240
+ return null;
241
+ }
242
+ }
243
+ // ─── Private helpers ─────────────────────────────────────────────
244
+ /** Build a unique key for per-thread state (thinking indicator). */
245
+ threadKey(message) {
246
+ return `${message.ChannelID}:${message.ThreadID ?? message.MessageID}`;
247
+ }
248
+ /**
249
+ * Truncate text to Slack's message length limit.
250
+ * Appends an ellipsis note if truncated so users know content was cut.
251
+ */
252
+ truncateForSlack(text) {
253
+ if (text.length <= SlackAdapter.MAX_TEXT_LENGTH)
254
+ return text;
255
+ return text.slice(0, SlackAdapter.MAX_TEXT_LENGTH - 50) + '\n\n... (message truncated due to length)';
256
+ }
257
+ /**
258
+ * Truncate text for use as the `text` fallback when Block Kit blocks are present.
259
+ * The `text` field is only shown in notifications/accessibility — the blocks contain
260
+ * the rich content. Keeping it short avoids `msg_too_long` when blocks are large.
261
+ */
262
+ truncateForSlackFallback(text) {
263
+ const MAX_FALLBACK = 4000;
264
+ if (text.length <= MAX_FALLBACK)
265
+ return text;
266
+ return text.slice(0, MAX_FALLBACK - 30) + '\n\n(See full response above)';
267
+ }
268
+ /**
269
+ * Build Slack API params for per-agent identity.
270
+ * Uses `username` and `icon_url` which require the `chat:write.customize` scope.
271
+ * Only includes `icon_url` if it's a valid HTTPS URL.
272
+ */
273
+ buildIdentityParams(name, iconUrl) {
274
+ const params = {};
275
+ if (name)
276
+ params.username = name;
277
+ if (iconUrl && typeof iconUrl === 'string' && iconUrl.startsWith('https://')) {
278
+ params.icon_url = iconUrl;
279
+ }
280
+ return params;
281
+ }
282
+ /** Build identity params from an agent entity. */
283
+ buildSlackIdentityParams(agent) {
284
+ return this.buildIdentityParams(agent.Name, agent.LogoURL);
285
+ }
286
+ /** Build identity params from an AgentIdentity object. */
287
+ buildSlackIdentityParamsFromIdentity(identity) {
288
+ return this.buildIdentityParams(identity.Name, identity.IconURL);
289
+ }
290
+ }
291
+ //# sourceMappingURL=SlackAdapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SlackAdapter.js","sourceRoot":"","sources":["../../src/slack/SlackAdapter.ts"],"names":[],"mappings":"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"}