@ai-devkit/channel-connector 0.13.2 → 0.13.4

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 (69) hide show
  1. package/dist/ChannelConfigRepository.d.ts +1 -1
  2. package/dist/ChannelConfigRepository.d.ts.map +1 -1
  3. package/dist/ChannelConfigRepository.js +13 -13
  4. package/dist/ChannelConfigRepository.js.map +1 -1
  5. package/dist/ChannelManager.d.ts +1 -1
  6. package/dist/ChannelManager.d.ts.map +1 -1
  7. package/dist/ChannelManager.js +9 -9
  8. package/dist/ChannelManager.js.map +1 -1
  9. package/dist/__tests__/ChannelConfigRepository.test.js +72 -72
  10. package/dist/__tests__/ChannelConfigRepository.test.js.map +1 -1
  11. package/dist/__tests__/ChannelManager.test.js +26 -26
  12. package/dist/__tests__/ChannelManager.test.js.map +1 -1
  13. package/dist/__tests__/adapters/slack/SlackAdapter.test.js +137 -137
  14. package/dist/__tests__/adapters/slack/SlackAdapter.test.js.map +1 -1
  15. package/dist/__tests__/adapters/slack/SlackMessageDelivery.test.js +36 -36
  16. package/dist/__tests__/adapters/slack/SlackMessageDelivery.test.js.map +1 -1
  17. package/dist/__tests__/adapters/slack/slackMarkdown.test.js +25 -25
  18. package/dist/__tests__/adapters/slack/slackMarkdown.test.js.map +1 -1
  19. package/dist/__tests__/adapters/slack/slackQuestion.test.js +41 -41
  20. package/dist/__tests__/adapters/slack/slackQuestion.test.js.map +1 -1
  21. package/dist/__tests__/adapters/telegram/TelegramAdapter.test.js +154 -154
  22. package/dist/__tests__/adapters/telegram/TelegramAdapter.test.js.map +1 -1
  23. package/dist/__tests__/adapters/telegram/telegramHtml.test.js +109 -109
  24. package/dist/__tests__/adapters/telegram/telegramHtml.test.js.map +1 -1
  25. package/dist/adapters/ChannelAdapter.d.ts +1 -1
  26. package/dist/adapters/ChannelAdapter.d.ts.map +1 -1
  27. package/dist/adapters/ChannelAdapter.js +1 -1
  28. package/dist/adapters/ChannelAdapter.js.map +1 -1
  29. package/dist/adapters/slack/SlackAdapter.d.ts +2 -2
  30. package/dist/adapters/slack/SlackAdapter.d.ts.map +1 -1
  31. package/dist/adapters/slack/SlackAdapter.js +17 -17
  32. package/dist/adapters/slack/SlackAdapter.js.map +1 -1
  33. package/dist/adapters/slack/SlackMessageDelivery.d.ts +1 -1
  34. package/dist/adapters/slack/SlackMessageDelivery.d.ts.map +1 -1
  35. package/dist/adapters/slack/SlackMessageDelivery.js +6 -6
  36. package/dist/adapters/slack/SlackMessageDelivery.js.map +1 -1
  37. package/dist/adapters/slack/slackMarkdown.d.ts.map +1 -1
  38. package/dist/adapters/slack/slackMarkdown.js +36 -36
  39. package/dist/adapters/slack/slackMarkdown.js.map +1 -1
  40. package/dist/adapters/slack/slackQuestion.d.ts +7 -7
  41. package/dist/adapters/slack/slackQuestion.d.ts.map +1 -1
  42. package/dist/adapters/slack/slackQuestion.js +13 -13
  43. package/dist/adapters/slack/slackQuestion.js.map +1 -1
  44. package/dist/adapters/telegram/TelegramAdapter.d.ts +2 -2
  45. package/dist/adapters/telegram/TelegramAdapter.d.ts.map +1 -1
  46. package/dist/adapters/telegram/TelegramAdapter.js +30 -27
  47. package/dist/adapters/telegram/TelegramAdapter.js.map +1 -1
  48. package/dist/adapters/telegram/TelegramMessageDelivery.d.ts.map +1 -1
  49. package/dist/adapters/telegram/TelegramMessageDelivery.js +2 -2
  50. package/dist/adapters/telegram/TelegramMessageDelivery.js.map +1 -1
  51. package/dist/adapters/telegram/telegramHtml.d.ts.map +1 -1
  52. package/dist/adapters/telegram/telegramHtml.js +16 -16
  53. package/dist/adapters/telegram/telegramHtml.js.map +1 -1
  54. package/dist/adapters/telegram/telegramKeyboard.d.ts +1 -1
  55. package/dist/adapters/telegram/telegramKeyboard.d.ts.map +1 -1
  56. package/dist/adapters/telegram/telegramKeyboard.js.map +1 -1
  57. package/dist/adapters/telegram/telegramMarkdown.d.ts.map +1 -1
  58. package/dist/adapters/telegram/telegramMarkdown.js +28 -28
  59. package/dist/adapters/telegram/telegramMarkdown.js.map +1 -1
  60. package/dist/index.d.ts +9 -9
  61. package/dist/index.d.ts.map +1 -1
  62. package/dist/index.js +5 -5
  63. package/dist/index.js.map +1 -1
  64. package/dist/types.d.ts +6 -6
  65. package/dist/types.d.ts.map +1 -1
  66. package/dist/types.js +1 -1
  67. package/dist/types.js.map +1 -1
  68. package/package.json +21 -22
  69. package/vitest.config.ts +16 -16
@@ -1,12 +1,12 @@
1
- import { SocketModeClient } from '@slack/socket-mode';
2
- import { WebClient } from '@slack/web-api';
3
- import { SlackMessageDelivery } from './SlackMessageDelivery.js';
4
- import { buildSlackQuestionMessage } from './slackQuestion.js';
5
- export const SLACK_CHANNEL_TYPE = 'slack';
1
+ import { SocketModeClient } from "@slack/socket-mode";
2
+ import { WebClient } from "@slack/web-api";
3
+ import { SlackMessageDelivery } from "./SlackMessageDelivery.js";
4
+ import { buildSlackQuestionMessage } from "./slackQuestion.js";
5
+ export const SLACK_CHANNEL_TYPE = "slack";
6
6
  export async function validateSlackAppToken(appToken, client) {
7
7
  const connections = client ?? new WebClient(appToken).apps.connections;
8
8
  const result = await connections.open();
9
- if (!result.ok || !result.url) throw new Error('Slack app token cannot open Socket Mode');
9
+ if (!result.ok || !result.url) throw new Error("Slack app token cannot open Socket Mode");
10
10
  }
11
11
  export async function validateSlackCredentials(botToken, client) {
12
12
  const authClient = client ?? {
@@ -14,7 +14,7 @@ export async function validateSlackCredentials(botToken, client) {
14
14
  };
15
15
  const identity = await authClient.authTest();
16
16
  if (!identity.ok || !identity.user_id || !identity.team_id) {
17
- throw new Error('Slack bot token returned incomplete identity');
17
+ throw new Error("Slack bot token returned incomplete identity");
18
18
  }
19
19
  return {
20
20
  ...identity.app_id ? {
@@ -48,16 +48,16 @@ export class SlackAdapter {
48
48
  }
49
49
  async start() {
50
50
  if (!this.listenersRegistered) {
51
- this.socket.on('slack_event', (payload)=>{
51
+ this.socket.on("slack_event", (payload)=>{
52
52
  void this.handleSlackEvent(payload).catch(()=>undefined);
53
53
  });
54
- this.socket.on('interactive', (payload)=>{
54
+ this.socket.on("interactive", (payload)=>{
55
55
  void this.handleInteraction(payload).catch(()=>undefined);
56
56
  });
57
- this.socket.on('connected', ()=>{
57
+ this.socket.on("connected", ()=>{
58
58
  this.running = true;
59
59
  });
60
- this.socket.on('disconnected', ()=>{
60
+ this.socket.on("disconnected", ()=>{
61
61
  this.running = false;
62
62
  });
63
63
  this.listenersRegistered = true;
@@ -83,13 +83,13 @@ export class SlackAdapter {
83
83
  }
84
84
  async sendQuestion(chatId, question) {
85
85
  const response = await this.web.chat.postMessage(buildSlackQuestionMessage(chatId, question));
86
- if (!response.ts) throw new Error('Slack did not return a question message timestamp');
86
+ if (!response.ts) throw new Error("Slack did not return a question message timestamp");
87
87
  return {
88
88
  messageId: response.ts
89
89
  };
90
90
  }
91
91
  async finalizeInteraction(chatId, messageId) {
92
- if (!this.web.chat.update) throw new Error('Slack Web API chat.update is unavailable');
92
+ if (!this.web.chat.update) throw new Error("Slack Web API chat.update is unavailable");
93
93
  await this.web.chat.update({
94
94
  channel: chatId,
95
95
  ts: messageId,
@@ -97,7 +97,7 @@ export class SlackAdapter {
97
97
  });
98
98
  }
99
99
  async handleSlackEvent(envelope) {
100
- if (envelope.type !== 'events_api') return;
100
+ if (envelope.type !== "events_api") return;
101
101
  await envelope.ack?.();
102
102
  const body = envelope.body;
103
103
  const event = body?.event;
@@ -114,7 +114,7 @@ export class SlackAdapter {
114
114
  text: String(event.text),
115
115
  timestamp: new Date(Number(event.event_ts ?? event.ts) * 1000),
116
116
  messageId: eventId,
117
- threadId: typeof event.thread_ts === 'string' ? event.thread_ts : undefined,
117
+ threadId: typeof event.thread_ts === "string" ? event.thread_ts : undefined,
118
118
  workspaceId: body.team_id,
119
119
  metadata: {
120
120
  slackTs: event.ts
@@ -125,7 +125,7 @@ export class SlackAdapter {
125
125
  }
126
126
  }
127
127
  isValidDirectMessage(body, event) {
128
- return body.team_id === this.config.workspaceId && body.is_ext_shared_channel !== true && event.type === 'message' && event.channel_type === 'im' && event.user !== this.config.botUserId && typeof event.text === 'string' && typeof event.ts === 'string' && event.subtype === undefined && event.bot_id === undefined;
128
+ return body.team_id === this.config.workspaceId && body.is_ext_shared_channel !== true && event.type === "message" && event.channel_type === "im" && event.user !== this.config.botUserId && typeof event.text === "string" && typeof event.ts === "string" && event.subtype === undefined && event.bot_id === undefined;
129
129
  }
130
130
  rememberEvent(eventId) {
131
131
  this.recentEventIds.set(eventId, Date.now());
@@ -145,7 +145,7 @@ export class SlackAdapter {
145
145
  const chatId = body?.channel?.id;
146
146
  const messageId = body?.container?.message_ts;
147
147
  if (!body || !action || !interactionId || !this.interactionHandler) return;
148
- if (body.type !== 'block_actions' || typeof workspaceId !== 'string' || typeof userId !== 'string' || typeof chatId !== 'string' || workspaceId !== this.config.workspaceId || typeof messageId !== 'string' || action.action_id !== 'ai_devkit_question' || typeof action.value !== 'string' || this.recentEventIds.has(`interaction:${interactionId}`)) return;
148
+ if (body.type !== "block_actions" || typeof workspaceId !== "string" || typeof userId !== "string" || typeof chatId !== "string" || workspaceId !== this.config.workspaceId || typeof messageId !== "string" || action.action_id !== "ai_devkit_question" || typeof action.value !== "string" || this.recentEventIds.has(`interaction:${interactionId}`)) return;
149
149
  this.rememberEvent(`interaction:${interactionId}`);
150
150
  try {
151
151
  await this.interactionHandler({
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/adapters/slack/SlackAdapter.ts"],"sourcesContent":["import { SocketModeClient } from '@slack/socket-mode';\nimport { WebClient } from '@slack/web-api';\nimport type { InteractiveChannelAdapter } from '../ChannelAdapter.js';\nimport type {\n ChannelQuestion,\n IncomingInteraction,\n IncomingMessage,\n SentMessage,\n SlackConfig,\n} from '../../types.js';\nimport { SlackMessageDelivery } from './SlackMessageDelivery.js';\nimport { buildSlackQuestionMessage } from './slackQuestion.js';\n\nexport const SLACK_CHANNEL_TYPE = 'slack';\n\ninterface SlackAuthClient {\n authTest(): Promise<{ ok?: boolean; app_id?: string; user_id?: string; team_id?: string; team?: string }>;\n}\n\nexport interface SlackIdentity {\n appId?: string;\n botUserId: string;\n workspaceId: string;\n workspaceName?: string;\n}\n\ninterface SlackConnectionsClient {\n open(): Promise<{ ok?: boolean; url?: string }>;\n}\n\nexport async function validateSlackAppToken(appToken: string, client?: SlackConnectionsClient): Promise<void> {\n const connections = client ?? new WebClient(appToken).apps.connections;\n const result = await connections.open();\n if (!result.ok || !result.url) throw new Error('Slack app token cannot open Socket Mode');\n}\n\nexport async function validateSlackCredentials(botToken: string, client?: SlackAuthClient): Promise<SlackIdentity> {\n const authClient = client ?? {\n authTest: () => new WebClient(botToken).auth.test(),\n };\n const identity = await authClient.authTest();\n if (!identity.ok || !identity.user_id || !identity.team_id) {\n throw new Error('Slack bot token returned incomplete identity');\n }\n return {\n ...(identity.app_id ? { appId: identity.app_id } : {}),\n botUserId: identity.user_id,\n workspaceId: identity.team_id,\n workspaceName: identity.team,\n };\n}\n\ninterface SocketClientLike {\n on(event: string, listener: (payload?: unknown) => void): unknown;\n start(): Promise<unknown>;\n disconnect(): Promise<unknown>;\n}\n\ninterface WebClientLike {\n chat: {\n postMessage(input: Record<string, unknown>): Promise<{ ok?: boolean; ts?: string }>;\n update?(input: Record<string, unknown>): Promise<unknown>;\n };\n}\n\ninterface SlackAdapterDependencies {\n socketClient?: SocketClientLike;\n webClient?: WebClientLike;\n}\n\ninterface SlackEventEnvelope {\n type?: string;\n ack?: () => Promise<void>;\n body?: {\n team_id?: string;\n event_id?: string;\n is_ext_shared_channel?: boolean;\n event?: Record<string, unknown>;\n };\n}\n\nexport class SlackAdapter implements InteractiveChannelAdapter {\n readonly type = SLACK_CHANNEL_TYPE;\n private readonly socket: SocketClientLike;\n private readonly web: WebClientLike;\n private readonly delivery: SlackMessageDelivery;\n private readonly recentEventIds = new Map<string, number>();\n private messageHandler: ((message: IncomingMessage) => Promise<void>) | null = null;\n private interactionHandler: ((interaction: IncomingInteraction) => Promise<void>) | null = null;\n private running = false;\n private listenersRegistered = false;\n\n constructor(private readonly config: SlackConfig, dependencies: SlackAdapterDependencies = {}) {\n this.socket = dependencies.socketClient ?? new SocketModeClient({ appToken: config.appToken });\n this.web = dependencies.webClient ?? new WebClient(config.botToken) as unknown as WebClientLike;\n this.delivery = new SlackMessageDelivery({\n postMessage: (input) => this.web.chat.postMessage(input),\n });\n }\n\n async start(): Promise<void> {\n if (!this.listenersRegistered) {\n this.socket.on('slack_event', (payload) => {\n void this.handleSlackEvent(payload as SlackEventEnvelope).catch(() => undefined);\n });\n this.socket.on('interactive', (payload) => {\n void this.handleInteraction(payload as SlackInteractionEnvelope).catch(() => undefined);\n });\n this.socket.on('connected', () => { this.running = true; });\n this.socket.on('disconnected', () => { this.running = false; });\n this.listenersRegistered = true;\n }\n await this.socket.start();\n this.running = true;\n }\n\n async stop(): Promise<void> {\n this.running = false;\n await this.socket.disconnect();\n }\n\n onMessage(handler: (message: IncomingMessage) => Promise<void>): void {\n this.messageHandler = handler;\n }\n\n onInteraction(handler: (interaction: IncomingInteraction) => Promise<void>): void {\n this.interactionHandler = handler;\n }\n\n async sendMessage(chatId: string, text: string): Promise<SentMessage> {\n return this.delivery.send(chatId, text);\n }\n\n async isHealthy(): Promise<boolean> {\n return this.running;\n }\n\n async sendQuestion(chatId: string, question: ChannelQuestion): Promise<SentMessage> {\n const response = await this.web.chat.postMessage(buildSlackQuestionMessage(chatId, question));\n if (!response.ts) throw new Error('Slack did not return a question message timestamp');\n return { messageId: response.ts };\n }\n\n async finalizeInteraction(chatId: string, messageId: string): Promise<void> {\n if (!this.web.chat.update) throw new Error('Slack Web API chat.update is unavailable');\n await this.web.chat.update({ channel: chatId, ts: messageId, blocks: [] });\n }\n\n private async handleSlackEvent(envelope: SlackEventEnvelope): Promise<void> {\n if (envelope.type !== 'events_api') return;\n await envelope.ack?.();\n const body = envelope.body;\n const event = body?.event;\n const eventId = body?.event_id;\n if (!body || !event || !eventId || !this.messageHandler) return;\n if (!this.isValidDirectMessage(body, event)) return;\n if (this.recentEventIds.has(eventId)) return;\n this.rememberEvent(eventId);\n\n try {\n await this.messageHandler({\n channelType: SLACK_CHANNEL_TYPE,\n chatId: String(event.channel),\n userId: String(event.user),\n text: String(event.text),\n timestamp: new Date(Number(event.event_ts ?? event.ts) * 1000),\n messageId: eventId,\n threadId: typeof event.thread_ts === 'string' ? event.thread_ts : undefined,\n workspaceId: body.team_id,\n metadata: { slackTs: event.ts },\n });\n } catch {\n // The channel consumer reports terminal errors; never reject a Socket Mode listener.\n }\n }\n\n private isValidDirectMessage(body: NonNullable<SlackEventEnvelope['body']>, event: Record<string, unknown>): boolean {\n return body.team_id === this.config.workspaceId\n && body.is_ext_shared_channel !== true\n && event.type === 'message'\n && event.channel_type === 'im'\n && event.user !== this.config.botUserId\n && typeof event.text === 'string'\n && typeof event.ts === 'string'\n && event.subtype === undefined\n && event.bot_id === undefined;\n }\n\n private rememberEvent(eventId: string): void {\n this.recentEventIds.set(eventId, Date.now());\n while (this.recentEventIds.size > 1000) {\n const oldest = this.recentEventIds.keys().next().value as string | undefined;\n if (!oldest) break;\n this.recentEventIds.delete(oldest);\n }\n }\n\n private async handleInteraction(envelope: SlackInteractionEnvelope): Promise<void> {\n await envelope.ack?.();\n const body = envelope.body;\n const action = body?.actions?.[0];\n const interactionId = action?.action_ts;\n const workspaceId = body?.team?.id;\n const userId = body?.user?.id;\n const chatId = body?.channel?.id;\n const messageId = body?.container?.message_ts;\n if (!body || !action || !interactionId || !this.interactionHandler) return;\n if (body.type !== 'block_actions'\n || typeof workspaceId !== 'string'\n || typeof userId !== 'string'\n || typeof chatId !== 'string'\n || workspaceId !== this.config.workspaceId\n || typeof messageId !== 'string'\n || action.action_id !== 'ai_devkit_question'\n || typeof action.value !== 'string'\n || this.recentEventIds.has(`interaction:${interactionId}`)) return;\n this.rememberEvent(`interaction:${interactionId}`);\n try {\n await this.interactionHandler({\n channelType: SLACK_CHANNEL_TYPE,\n chatId,\n userId,\n workspaceId,\n interactionId,\n messageId,\n actionId: action.action_id,\n value: action.value,\n timestamp: new Date(Number(interactionId) * 1000),\n });\n } catch {\n // Keep Socket Mode listener failures isolated from the SDK event loop.\n }\n }\n}\n\ninterface SlackInteractionEnvelope {\n ack?: () => Promise<void>;\n body?: {\n type?: string;\n team?: { id?: string };\n user?: { id?: string };\n channel?: { id?: string };\n container?: { message_ts?: string };\n actions?: Array<{ action_id?: string; action_ts?: string; value?: string }>;\n };\n}\n"],"names":["SocketModeClient","WebClient","SlackMessageDelivery","buildSlackQuestionMessage","SLACK_CHANNEL_TYPE","validateSlackAppToken","appToken","client","connections","apps","result","open","ok","url","Error","validateSlackCredentials","botToken","authClient","authTest","auth","test","identity","user_id","team_id","app_id","appId","botUserId","workspaceId","workspaceName","team","SlackAdapter","type","socket","web","delivery","recentEventIds","Map","messageHandler","interactionHandler","running","listenersRegistered","config","dependencies","socketClient","webClient","postMessage","input","chat","start","on","payload","handleSlackEvent","catch","undefined","handleInteraction","stop","disconnect","onMessage","handler","onInteraction","sendMessage","chatId","text","send","isHealthy","sendQuestion","question","response","ts","messageId","finalizeInteraction","update","channel","blocks","envelope","ack","body","event","eventId","event_id","isValidDirectMessage","has","rememberEvent","channelType","String","userId","user","timestamp","Date","Number","event_ts","threadId","thread_ts","metadata","slackTs","is_ext_shared_channel","channel_type","subtype","bot_id","set","now","size","oldest","keys","next","value","delete","action","actions","interactionId","action_ts","id","container","message_ts","action_id","actionId"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/adapters/slack/SlackAdapter.ts"],"sourcesContent":["import { SocketModeClient } from \"@slack/socket-mode\";\nimport { WebClient } from \"@slack/web-api\";\nimport type { InteractiveChannelAdapter } from \"../ChannelAdapter.js\";\nimport type {\n ChannelQuestion,\n IncomingInteraction,\n IncomingMessage,\n SentMessage,\n SlackConfig,\n} from \"../../types.js\";\nimport { SlackMessageDelivery } from \"./SlackMessageDelivery.js\";\nimport { buildSlackQuestionMessage } from \"./slackQuestion.js\";\n\nexport const SLACK_CHANNEL_TYPE = \"slack\";\n\ninterface SlackAuthClient {\n authTest(): Promise<{\n ok?: boolean;\n app_id?: string;\n user_id?: string;\n team_id?: string;\n team?: string;\n }>;\n}\n\nexport interface SlackIdentity {\n appId?: string;\n botUserId: string;\n workspaceId: string;\n workspaceName?: string;\n}\n\ninterface SlackConnectionsClient {\n open(): Promise<{ ok?: boolean; url?: string }>;\n}\n\nexport async function validateSlackAppToken(\n appToken: string,\n client?: SlackConnectionsClient,\n): Promise<void> {\n const connections = client ?? new WebClient(appToken).apps.connections;\n const result = await connections.open();\n if (!result.ok || !result.url) throw new Error(\"Slack app token cannot open Socket Mode\");\n}\n\nexport async function validateSlackCredentials(\n botToken: string,\n client?: SlackAuthClient,\n): Promise<SlackIdentity> {\n const authClient = client ?? {\n authTest: () => new WebClient(botToken).auth.test(),\n };\n const identity = await authClient.authTest();\n if (!identity.ok || !identity.user_id || !identity.team_id) {\n throw new Error(\"Slack bot token returned incomplete identity\");\n }\n return {\n ...(identity.app_id ? { appId: identity.app_id } : {}),\n botUserId: identity.user_id,\n workspaceId: identity.team_id,\n workspaceName: identity.team,\n };\n}\n\ninterface SocketClientLike {\n on(event: string, listener: (payload?: unknown) => void): unknown;\n start(): Promise<unknown>;\n disconnect(): Promise<unknown>;\n}\n\ninterface WebClientLike {\n chat: {\n postMessage(input: Record<string, unknown>): Promise<{ ok?: boolean; ts?: string }>;\n update?(input: Record<string, unknown>): Promise<unknown>;\n };\n}\n\ninterface SlackAdapterDependencies {\n socketClient?: SocketClientLike;\n webClient?: WebClientLike;\n}\n\ninterface SlackEventEnvelope {\n type?: string;\n ack?: () => Promise<void>;\n body?: {\n team_id?: string;\n event_id?: string;\n is_ext_shared_channel?: boolean;\n event?: Record<string, unknown>;\n };\n}\n\nexport class SlackAdapter implements InteractiveChannelAdapter {\n readonly type = SLACK_CHANNEL_TYPE;\n private readonly socket: SocketClientLike;\n private readonly web: WebClientLike;\n private readonly delivery: SlackMessageDelivery;\n private readonly recentEventIds = new Map<string, number>();\n private messageHandler: ((message: IncomingMessage) => Promise<void>) | null = null;\n private interactionHandler: ((interaction: IncomingInteraction) => Promise<void>) | null = null;\n private running = false;\n private listenersRegistered = false;\n\n constructor(\n private readonly config: SlackConfig,\n dependencies: SlackAdapterDependencies = {},\n ) {\n this.socket = dependencies.socketClient ?? new SocketModeClient({ appToken: config.appToken });\n this.web =\n dependencies.webClient ?? (new WebClient(config.botToken) as unknown as WebClientLike);\n this.delivery = new SlackMessageDelivery({\n postMessage: (input) => this.web.chat.postMessage(input),\n });\n }\n\n async start(): Promise<void> {\n if (!this.listenersRegistered) {\n this.socket.on(\"slack_event\", (payload) => {\n void this.handleSlackEvent(payload as SlackEventEnvelope).catch(() => undefined);\n });\n this.socket.on(\"interactive\", (payload) => {\n void this.handleInteraction(payload as SlackInteractionEnvelope).catch(() => undefined);\n });\n this.socket.on(\"connected\", () => {\n this.running = true;\n });\n this.socket.on(\"disconnected\", () => {\n this.running = false;\n });\n this.listenersRegistered = true;\n }\n await this.socket.start();\n this.running = true;\n }\n\n async stop(): Promise<void> {\n this.running = false;\n await this.socket.disconnect();\n }\n\n onMessage(handler: (message: IncomingMessage) => Promise<void>): void {\n this.messageHandler = handler;\n }\n\n onInteraction(handler: (interaction: IncomingInteraction) => Promise<void>): void {\n this.interactionHandler = handler;\n }\n\n async sendMessage(chatId: string, text: string): Promise<SentMessage> {\n return this.delivery.send(chatId, text);\n }\n\n async isHealthy(): Promise<boolean> {\n return this.running;\n }\n\n async sendQuestion(chatId: string, question: ChannelQuestion): Promise<SentMessage> {\n const response = await this.web.chat.postMessage(buildSlackQuestionMessage(chatId, question));\n if (!response.ts) throw new Error(\"Slack did not return a question message timestamp\");\n return { messageId: response.ts };\n }\n\n async finalizeInteraction(chatId: string, messageId: string): Promise<void> {\n if (!this.web.chat.update) throw new Error(\"Slack Web API chat.update is unavailable\");\n await this.web.chat.update({ channel: chatId, ts: messageId, blocks: [] });\n }\n\n private async handleSlackEvent(envelope: SlackEventEnvelope): Promise<void> {\n if (envelope.type !== \"events_api\") return;\n await envelope.ack?.();\n const body = envelope.body;\n const event = body?.event;\n const eventId = body?.event_id;\n if (!body || !event || !eventId || !this.messageHandler) return;\n if (!this.isValidDirectMessage(body, event)) return;\n if (this.recentEventIds.has(eventId)) return;\n this.rememberEvent(eventId);\n\n try {\n await this.messageHandler({\n channelType: SLACK_CHANNEL_TYPE,\n chatId: String(event.channel),\n userId: String(event.user),\n text: String(event.text),\n timestamp: new Date(Number(event.event_ts ?? event.ts) * 1000),\n messageId: eventId,\n threadId: typeof event.thread_ts === \"string\" ? event.thread_ts : undefined,\n workspaceId: body.team_id,\n metadata: { slackTs: event.ts },\n });\n } catch {\n // The channel consumer reports terminal errors; never reject a Socket Mode listener.\n }\n }\n\n private isValidDirectMessage(\n body: NonNullable<SlackEventEnvelope[\"body\"]>,\n event: Record<string, unknown>,\n ): boolean {\n return (\n body.team_id === this.config.workspaceId &&\n body.is_ext_shared_channel !== true &&\n event.type === \"message\" &&\n event.channel_type === \"im\" &&\n event.user !== this.config.botUserId &&\n typeof event.text === \"string\" &&\n typeof event.ts === \"string\" &&\n event.subtype === undefined &&\n event.bot_id === undefined\n );\n }\n\n private rememberEvent(eventId: string): void {\n this.recentEventIds.set(eventId, Date.now());\n while (this.recentEventIds.size > 1000) {\n const oldest = this.recentEventIds.keys().next().value as string | undefined;\n if (!oldest) break;\n this.recentEventIds.delete(oldest);\n }\n }\n\n private async handleInteraction(envelope: SlackInteractionEnvelope): Promise<void> {\n await envelope.ack?.();\n const body = envelope.body;\n const action = body?.actions?.[0];\n const interactionId = action?.action_ts;\n const workspaceId = body?.team?.id;\n const userId = body?.user?.id;\n const chatId = body?.channel?.id;\n const messageId = body?.container?.message_ts;\n if (!body || !action || !interactionId || !this.interactionHandler) return;\n if (\n body.type !== \"block_actions\" ||\n typeof workspaceId !== \"string\" ||\n typeof userId !== \"string\" ||\n typeof chatId !== \"string\" ||\n workspaceId !== this.config.workspaceId ||\n typeof messageId !== \"string\" ||\n action.action_id !== \"ai_devkit_question\" ||\n typeof action.value !== \"string\" ||\n this.recentEventIds.has(`interaction:${interactionId}`)\n )\n return;\n this.rememberEvent(`interaction:${interactionId}`);\n try {\n await this.interactionHandler({\n channelType: SLACK_CHANNEL_TYPE,\n chatId,\n userId,\n workspaceId,\n interactionId,\n messageId,\n actionId: action.action_id,\n value: action.value,\n timestamp: new Date(Number(interactionId) * 1000),\n });\n } catch {\n // Keep Socket Mode listener failures isolated from the SDK event loop.\n }\n }\n}\n\ninterface SlackInteractionEnvelope {\n ack?: () => Promise<void>;\n body?: {\n type?: string;\n team?: { id?: string };\n user?: { id?: string };\n channel?: { id?: string };\n container?: { message_ts?: string };\n actions?: Array<{ action_id?: string; action_ts?: string; value?: string }>;\n };\n}\n"],"names":["SocketModeClient","WebClient","SlackMessageDelivery","buildSlackQuestionMessage","SLACK_CHANNEL_TYPE","validateSlackAppToken","appToken","client","connections","apps","result","open","ok","url","Error","validateSlackCredentials","botToken","authClient","authTest","auth","test","identity","user_id","team_id","app_id","appId","botUserId","workspaceId","workspaceName","team","SlackAdapter","type","socket","web","delivery","recentEventIds","Map","messageHandler","interactionHandler","running","listenersRegistered","config","dependencies","socketClient","webClient","postMessage","input","chat","start","on","payload","handleSlackEvent","catch","undefined","handleInteraction","stop","disconnect","onMessage","handler","onInteraction","sendMessage","chatId","text","send","isHealthy","sendQuestion","question","response","ts","messageId","finalizeInteraction","update","channel","blocks","envelope","ack","body","event","eventId","event_id","isValidDirectMessage","has","rememberEvent","channelType","String","userId","user","timestamp","Date","Number","event_ts","threadId","thread_ts","metadata","slackTs","is_ext_shared_channel","channel_type","subtype","bot_id","set","now","size","oldest","keys","next","value","delete","action","actions","interactionId","action_ts","id","container","message_ts","action_id","actionId"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import type { SentMessage } from '../../types.js';
1
+ import type { SentMessage } from "../../types.js";
2
2
  interface SlackPoster {
3
3
  postMessage(input: {
4
4
  channel: string;
@@ -1 +1 @@
1
- {"version":3,"file":"SlackMessageDelivery.d.ts","sourceRoot":"","sources":["../../../src/adapters/slack/SlackMessageDelivery.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAGlD,UAAU,WAAW;IACjB,WAAW,CAAC,KAAK,EAAE;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,IAAI,CAAC;QACb,YAAY,EAAE,KAAK,CAAC;QACpB,YAAY,EAAE,KAAK,CAAC;QACpB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC;KACtB,GAAG,OAAO,CAAC;QAAE,EAAE,CAAC,EAAE,OAAO,CAAC;QAAC,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC9C;AAED,UAAU,eAAe;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACnD;AAUD,qBAAa,oBAAoB;IAMjB,OAAO,CAAC,QAAQ,CAAC,MAAM;IALnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwC;IAC/D,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA0C;gBAEnC,MAAM,EAAE,WAAW,EAAE,OAAO,GAAE,eAAoB;IAMzE,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;YA4BrD,OAAO;YAqBP,iBAAiB;CAUlC"}
1
+ {"version":3,"file":"SlackMessageDelivery.d.ts","sourceRoot":"","sources":["../../../src/adapters/slack/SlackMessageDelivery.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAGlD,UAAU,WAAW;IACnB,WAAW,CAAC,KAAK,EAAE;QACjB,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,IAAI,CAAC;QACb,YAAY,EAAE,KAAK,CAAC;QACpB,YAAY,EAAE,KAAK,CAAC;QACpB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC;KACpB,GAAG,OAAO,CAAC;QAAE,EAAE,CAAC,EAAE,OAAO,CAAC;QAAC,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC5C;AAED,UAAU,eAAe;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACjD;AAUD,qBAAa,oBAAoB;IAO7B,OAAO,CAAC,QAAQ,CAAC,MAAM;IANzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwC;IAC/D,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA0C;gBAG7C,MAAM,EAAE,WAAW,EACpC,OAAO,GAAE,eAAoB;IAOzB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;YA4BrD,OAAO;YAqBP,iBAAiB;CAYhC"}
@@ -1,4 +1,4 @@
1
- import { chunkMarkdownForSlack, SLACK_MAX_MESSAGE_LENGTH } from './slackMarkdown.js';
1
+ import { chunkMarkdownForSlack, SLACK_MAX_MESSAGE_LENGTH } from "./slackMarkdown.js";
2
2
  const defaultSleep = (milliseconds)=>new Promise((resolve)=>setTimeout(resolve, milliseconds));
3
3
  export class SlackMessageDelivery {
4
4
  client;
@@ -18,7 +18,7 @@ export class SlackMessageDelivery {
18
18
  pending: 0
19
19
  };
20
20
  if (state.pending >= this.maxQueueSize) {
21
- throw new Error('Slack message delivery queue is full');
21
+ throw new Error("Slack message delivery queue is full");
22
22
  }
23
23
  state.pending += 1;
24
24
  this.states.set(channel, state);
@@ -42,7 +42,7 @@ export class SlackMessageDelivery {
42
42
  }
43
43
  async deliver(channel, markdown) {
44
44
  const chunks = chunkMarkdownForSlack(markdown, this.maxMessageLength);
45
- if (chunks.length === 0) throw new Error('Cannot send an empty Slack message');
45
+ if (chunks.length === 0) throw new Error("Cannot send an empty Slack message");
46
46
  let parentTs;
47
47
  for (const text of chunks){
48
48
  const response = await this.postWithRateLimit({
@@ -55,7 +55,7 @@ export class SlackMessageDelivery {
55
55
  thread_ts: parentTs
56
56
  } : {}
57
57
  });
58
- if (!response.ts) throw new Error('Slack did not return a message timestamp');
58
+ if (!response.ts) throw new Error("Slack did not return a message timestamp");
59
59
  parentTs ??= response.ts;
60
60
  }
61
61
  return {
@@ -75,9 +75,9 @@ export class SlackMessageDelivery {
75
75
  }
76
76
  }
77
77
  function readRetryAfter(error) {
78
- if (!error || typeof error !== 'object') return undefined;
78
+ if (!error || typeof error !== "object") return undefined;
79
79
  const value = error.retryAfter;
80
- return typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : undefined;
80
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : undefined;
81
81
  }
82
82
 
83
83
  //# sourceMappingURL=SlackMessageDelivery.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/adapters/slack/SlackMessageDelivery.ts"],"sourcesContent":["import type { SentMessage } from '../../types.js';\nimport { chunkMarkdownForSlack, SLACK_MAX_MESSAGE_LENGTH } from './slackMarkdown.js';\n\ninterface SlackPoster {\n postMessage(input: {\n channel: string;\n text: string;\n mrkdwn: true;\n unfurl_links: false;\n unfurl_media: false;\n thread_ts?: string;\n blocks?: unknown[];\n }): Promise<{ ok?: boolean; ts?: string }>;\n}\n\ninterface DeliveryOptions {\n maxMessageLength?: number;\n maxQueueSize?: number;\n sleep?: (milliseconds: number) => Promise<void>;\n}\n\ninterface ConversationState {\n tail: Promise<void>;\n pending: number;\n}\n\nconst defaultSleep = (milliseconds: number): Promise<void> =>\n new Promise((resolve) => setTimeout(resolve, milliseconds));\n\nexport class SlackMessageDelivery {\n private readonly states = new Map<string, ConversationState>();\n private readonly maxMessageLength: number;\n private readonly maxQueueSize: number;\n private readonly sleep: (milliseconds: number) => Promise<void>;\n\n constructor(private readonly client: SlackPoster, options: DeliveryOptions = {}) {\n this.maxMessageLength = options.maxMessageLength ?? SLACK_MAX_MESSAGE_LENGTH;\n this.maxQueueSize = options.maxQueueSize ?? 100;\n this.sleep = options.sleep ?? defaultSleep;\n }\n\n async send(channel: string, markdown: string): Promise<SentMessage> {\n const state = this.states.get(channel) ?? { tail: Promise.resolve(), pending: 0 };\n if (state.pending >= this.maxQueueSize) {\n throw new Error('Slack message delivery queue is full');\n }\n state.pending += 1;\n this.states.set(channel, state);\n\n let resolveResult!: (result: SentMessage) => void;\n let rejectResult!: (error: unknown) => void;\n const result = new Promise<SentMessage>((resolve, reject) => {\n resolveResult = resolve;\n rejectResult = reject;\n });\n\n state.tail = state.tail.then(async () => {\n try {\n resolveResult(await this.deliver(channel, markdown));\n } catch (error) {\n rejectResult(error);\n } finally {\n state.pending -= 1;\n if (state.pending === 0) this.states.delete(channel);\n }\n });\n return result;\n }\n\n private async deliver(channel: string, markdown: string): Promise<SentMessage> {\n const chunks = chunkMarkdownForSlack(markdown, this.maxMessageLength);\n if (chunks.length === 0) throw new Error('Cannot send an empty Slack message');\n let parentTs: string | undefined;\n\n for (const text of chunks) {\n const response = await this.postWithRateLimit({\n channel,\n text,\n mrkdwn: true,\n unfurl_links: false,\n unfurl_media: false,\n ...(parentTs ? { thread_ts: parentTs } : {}),\n });\n if (!response.ts) throw new Error('Slack did not return a message timestamp');\n parentTs ??= response.ts;\n }\n\n return { messageId: parentTs!, threadId: parentTs };\n }\n\n private async postWithRateLimit(input: Parameters<SlackPoster['postMessage']>[0]): Promise<{ ok?: boolean; ts?: string }> {\n try {\n return await this.client.postMessage(input);\n } catch (error) {\n const retryAfter = readRetryAfter(error);\n if (retryAfter === undefined) throw error;\n await this.sleep(Math.min(retryAfter * 1000, 60_000));\n return this.client.postMessage(input);\n }\n }\n}\n\nfunction readRetryAfter(error: unknown): number | undefined {\n if (!error || typeof error !== 'object') return undefined;\n const value = (error as { retryAfter?: unknown }).retryAfter;\n return typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : undefined;\n}\n"],"names":["chunkMarkdownForSlack","SLACK_MAX_MESSAGE_LENGTH","defaultSleep","milliseconds","Promise","resolve","setTimeout","SlackMessageDelivery","states","Map","maxMessageLength","maxQueueSize","sleep","client","options","send","channel","markdown","state","get","tail","pending","Error","set","resolveResult","rejectResult","result","reject","then","deliver","error","delete","chunks","length","parentTs","text","response","postWithRateLimit","mrkdwn","unfurl_links","unfurl_media","thread_ts","ts","messageId","threadId","input","postMessage","retryAfter","readRetryAfter","undefined","Math","min","value","Number","isFinite"],"mappings":"AACA,SAASA,qBAAqB,EAAEC,wBAAwB,QAAQ,qBAAqB;AAyBrF,MAAMC,eAAe,CAACC,eAClB,IAAIC,QAAQ,CAACC,UAAYC,WAAWD,SAASF;AAEjD,OAAO,MAAMI;;IACQC,SAAS,IAAIC,MAAiC;IAC9CC,iBAAyB;IACzBC,aAAqB;IACrBC,MAA+C;IAEhE,YAAY,AAAiBC,MAAmB,EAAEC,UAA2B,CAAC,CAAC,CAAE;aAApDD,SAAAA;QACzB,IAAI,CAACH,gBAAgB,GAAGI,QAAQJ,gBAAgB,IAAIT;QACpD,IAAI,CAACU,YAAY,GAAGG,QAAQH,YAAY,IAAI;QAC5C,IAAI,CAACC,KAAK,GAAGE,QAAQF,KAAK,IAAIV;IAClC;IAEA,MAAMa,KAAKC,OAAe,EAAEC,QAAgB,EAAwB;QAChE,MAAMC,QAAQ,IAAI,CAACV,MAAM,CAACW,GAAG,CAACH,YAAY;YAAEI,MAAMhB,QAAQC,OAAO;YAAIgB,SAAS;QAAE;QAChF,IAAIH,MAAMG,OAAO,IAAI,IAAI,CAACV,YAAY,EAAE;YACpC,MAAM,IAAIW,MAAM;QACpB;QACAJ,MAAMG,OAAO,IAAI;QACjB,IAAI,CAACb,MAAM,CAACe,GAAG,CAACP,SAASE;QAEzB,IAAIM;QACJ,IAAIC;QACJ,MAAMC,SAAS,IAAItB,QAAqB,CAACC,SAASsB;YAC9CH,gBAAgBnB;YAChBoB,eAAeE;QACnB;QAEAT,MAAME,IAAI,GAAGF,MAAME,IAAI,CAACQ,IAAI,CAAC;YACzB,IAAI;gBACAJ,cAAc,MAAM,IAAI,CAACK,OAAO,CAACb,SAASC;YAC9C,EAAE,OAAOa,OAAO;gBACZL,aAAaK;YACjB,SAAU;gBACNZ,MAAMG,OAAO,IAAI;gBACjB,IAAIH,MAAMG,OAAO,KAAK,GAAG,IAAI,CAACb,MAAM,CAACuB,MAAM,CAACf;YAChD;QACJ;QACA,OAAOU;IACX;IAEA,MAAcG,QAAQb,OAAe,EAAEC,QAAgB,EAAwB;QAC3E,MAAMe,SAAShC,sBAAsBiB,UAAU,IAAI,CAACP,gBAAgB;QACpE,IAAIsB,OAAOC,MAAM,KAAK,GAAG,MAAM,IAAIX,MAAM;QACzC,IAAIY;QAEJ,KAAK,MAAMC,QAAQH,OAAQ;YACvB,MAAMI,WAAW,MAAM,IAAI,CAACC,iBAAiB,CAAC;gBAC1CrB;gBACAmB;gBACAG,QAAQ;gBACRC,cAAc;gBACdC,cAAc;gBACd,GAAIN,WAAW;oBAAEO,WAAWP;gBAAS,IAAI,CAAC,CAAC;YAC/C;YACA,IAAI,CAACE,SAASM,EAAE,EAAE,MAAM,IAAIpB,MAAM;YAClCY,aAAaE,SAASM,EAAE;QAC5B;QAEA,OAAO;YAAEC,WAAWT;YAAWU,UAAUV;QAAS;IACtD;IAEA,MAAcG,kBAAkBQ,KAAgD,EAA0C;QACtH,IAAI;YACA,OAAO,MAAM,IAAI,CAAChC,MAAM,CAACiC,WAAW,CAACD;QACzC,EAAE,OAAOf,OAAO;YACZ,MAAMiB,aAAaC,eAAelB;YAClC,IAAIiB,eAAeE,WAAW,MAAMnB;YACpC,MAAM,IAAI,CAAClB,KAAK,CAACsC,KAAKC,GAAG,CAACJ,aAAa,MAAM;YAC7C,OAAO,IAAI,CAAClC,MAAM,CAACiC,WAAW,CAACD;QACnC;IACJ;AACJ;AAEA,SAASG,eAAelB,KAAc;IAClC,IAAI,CAACA,SAAS,OAAOA,UAAU,UAAU,OAAOmB;IAChD,MAAMG,QAAQ,AAACtB,MAAmCiB,UAAU;IAC5D,OAAO,OAAOK,UAAU,YAAYC,OAAOC,QAAQ,CAACF,UAAUA,SAAS,IAAIA,QAAQH;AACvF"}
1
+ {"version":3,"sources":["../../../src/adapters/slack/SlackMessageDelivery.ts"],"sourcesContent":["import type { SentMessage } from \"../../types.js\";\nimport { chunkMarkdownForSlack, SLACK_MAX_MESSAGE_LENGTH } from \"./slackMarkdown.js\";\n\ninterface SlackPoster {\n postMessage(input: {\n channel: string;\n text: string;\n mrkdwn: true;\n unfurl_links: false;\n unfurl_media: false;\n thread_ts?: string;\n blocks?: unknown[];\n }): Promise<{ ok?: boolean; ts?: string }>;\n}\n\ninterface DeliveryOptions {\n maxMessageLength?: number;\n maxQueueSize?: number;\n sleep?: (milliseconds: number) => Promise<void>;\n}\n\ninterface ConversationState {\n tail: Promise<void>;\n pending: number;\n}\n\nconst defaultSleep = (milliseconds: number): Promise<void> =>\n new Promise((resolve) => setTimeout(resolve, milliseconds));\n\nexport class SlackMessageDelivery {\n private readonly states = new Map<string, ConversationState>();\n private readonly maxMessageLength: number;\n private readonly maxQueueSize: number;\n private readonly sleep: (milliseconds: number) => Promise<void>;\n\n constructor(\n private readonly client: SlackPoster,\n options: DeliveryOptions = {},\n ) {\n this.maxMessageLength = options.maxMessageLength ?? SLACK_MAX_MESSAGE_LENGTH;\n this.maxQueueSize = options.maxQueueSize ?? 100;\n this.sleep = options.sleep ?? defaultSleep;\n }\n\n async send(channel: string, markdown: string): Promise<SentMessage> {\n const state = this.states.get(channel) ?? { tail: Promise.resolve(), pending: 0 };\n if (state.pending >= this.maxQueueSize) {\n throw new Error(\"Slack message delivery queue is full\");\n }\n state.pending += 1;\n this.states.set(channel, state);\n\n let resolveResult!: (result: SentMessage) => void;\n let rejectResult!: (error: unknown) => void;\n const result = new Promise<SentMessage>((resolve, reject) => {\n resolveResult = resolve;\n rejectResult = reject;\n });\n\n state.tail = state.tail.then(async () => {\n try {\n resolveResult(await this.deliver(channel, markdown));\n } catch (error) {\n rejectResult(error);\n } finally {\n state.pending -= 1;\n if (state.pending === 0) this.states.delete(channel);\n }\n });\n return result;\n }\n\n private async deliver(channel: string, markdown: string): Promise<SentMessage> {\n const chunks = chunkMarkdownForSlack(markdown, this.maxMessageLength);\n if (chunks.length === 0) throw new Error(\"Cannot send an empty Slack message\");\n let parentTs: string | undefined;\n\n for (const text of chunks) {\n const response = await this.postWithRateLimit({\n channel,\n text,\n mrkdwn: true,\n unfurl_links: false,\n unfurl_media: false,\n ...(parentTs ? { thread_ts: parentTs } : {}),\n });\n if (!response.ts) throw new Error(\"Slack did not return a message timestamp\");\n parentTs ??= response.ts;\n }\n\n return { messageId: parentTs!, threadId: parentTs };\n }\n\n private async postWithRateLimit(\n input: Parameters<SlackPoster[\"postMessage\"]>[0],\n ): Promise<{ ok?: boolean; ts?: string }> {\n try {\n return await this.client.postMessage(input);\n } catch (error) {\n const retryAfter = readRetryAfter(error);\n if (retryAfter === undefined) throw error;\n await this.sleep(Math.min(retryAfter * 1000, 60_000));\n return this.client.postMessage(input);\n }\n }\n}\n\nfunction readRetryAfter(error: unknown): number | undefined {\n if (!error || typeof error !== \"object\") return undefined;\n const value = (error as { retryAfter?: unknown }).retryAfter;\n return typeof value === \"number\" && Number.isFinite(value) && value >= 0 ? value : undefined;\n}\n"],"names":["chunkMarkdownForSlack","SLACK_MAX_MESSAGE_LENGTH","defaultSleep","milliseconds","Promise","resolve","setTimeout","SlackMessageDelivery","states","Map","maxMessageLength","maxQueueSize","sleep","client","options","send","channel","markdown","state","get","tail","pending","Error","set","resolveResult","rejectResult","result","reject","then","deliver","error","delete","chunks","length","parentTs","text","response","postWithRateLimit","mrkdwn","unfurl_links","unfurl_media","thread_ts","ts","messageId","threadId","input","postMessage","retryAfter","readRetryAfter","undefined","Math","min","value","Number","isFinite"],"mappings":"AACA,SAASA,qBAAqB,EAAEC,wBAAwB,QAAQ,qBAAqB;AAyBrF,MAAMC,eAAe,CAACC,eACpB,IAAIC,QAAQ,CAACC,UAAYC,WAAWD,SAASF;AAE/C,OAAO,MAAMI;;IACMC,SAAS,IAAIC,MAAiC;IAC9CC,iBAAyB;IACzBC,aAAqB;IACrBC,MAA+C;IAEhE,YACE,AAAiBC,MAAmB,EACpCC,UAA2B,CAAC,CAAC,CAC7B;aAFiBD,SAAAA;QAGjB,IAAI,CAACH,gBAAgB,GAAGI,QAAQJ,gBAAgB,IAAIT;QACpD,IAAI,CAACU,YAAY,GAAGG,QAAQH,YAAY,IAAI;QAC5C,IAAI,CAACC,KAAK,GAAGE,QAAQF,KAAK,IAAIV;IAChC;IAEA,MAAMa,KAAKC,OAAe,EAAEC,QAAgB,EAAwB;QAClE,MAAMC,QAAQ,IAAI,CAACV,MAAM,CAACW,GAAG,CAACH,YAAY;YAAEI,MAAMhB,QAAQC,OAAO;YAAIgB,SAAS;QAAE;QAChF,IAAIH,MAAMG,OAAO,IAAI,IAAI,CAACV,YAAY,EAAE;YACtC,MAAM,IAAIW,MAAM;QAClB;QACAJ,MAAMG,OAAO,IAAI;QACjB,IAAI,CAACb,MAAM,CAACe,GAAG,CAACP,SAASE;QAEzB,IAAIM;QACJ,IAAIC;QACJ,MAAMC,SAAS,IAAItB,QAAqB,CAACC,SAASsB;YAChDH,gBAAgBnB;YAChBoB,eAAeE;QACjB;QAEAT,MAAME,IAAI,GAAGF,MAAME,IAAI,CAACQ,IAAI,CAAC;YAC3B,IAAI;gBACFJ,cAAc,MAAM,IAAI,CAACK,OAAO,CAACb,SAASC;YAC5C,EAAE,OAAOa,OAAO;gBACdL,aAAaK;YACf,SAAU;gBACRZ,MAAMG,OAAO,IAAI;gBACjB,IAAIH,MAAMG,OAAO,KAAK,GAAG,IAAI,CAACb,MAAM,CAACuB,MAAM,CAACf;YAC9C;QACF;QACA,OAAOU;IACT;IAEA,MAAcG,QAAQb,OAAe,EAAEC,QAAgB,EAAwB;QAC7E,MAAMe,SAAShC,sBAAsBiB,UAAU,IAAI,CAACP,gBAAgB;QACpE,IAAIsB,OAAOC,MAAM,KAAK,GAAG,MAAM,IAAIX,MAAM;QACzC,IAAIY;QAEJ,KAAK,MAAMC,QAAQH,OAAQ;YACzB,MAAMI,WAAW,MAAM,IAAI,CAACC,iBAAiB,CAAC;gBAC5CrB;gBACAmB;gBACAG,QAAQ;gBACRC,cAAc;gBACdC,cAAc;gBACd,GAAIN,WAAW;oBAAEO,WAAWP;gBAAS,IAAI,CAAC,CAAC;YAC7C;YACA,IAAI,CAACE,SAASM,EAAE,EAAE,MAAM,IAAIpB,MAAM;YAClCY,aAAaE,SAASM,EAAE;QAC1B;QAEA,OAAO;YAAEC,WAAWT;YAAWU,UAAUV;QAAS;IACpD;IAEA,MAAcG,kBACZQ,KAAgD,EACR;QACxC,IAAI;YACF,OAAO,MAAM,IAAI,CAAChC,MAAM,CAACiC,WAAW,CAACD;QACvC,EAAE,OAAOf,OAAO;YACd,MAAMiB,aAAaC,eAAelB;YAClC,IAAIiB,eAAeE,WAAW,MAAMnB;YACpC,MAAM,IAAI,CAAClB,KAAK,CAACsC,KAAKC,GAAG,CAACJ,aAAa,MAAM;YAC7C,OAAO,IAAI,CAAClC,MAAM,CAACiC,WAAW,CAACD;QACjC;IACF;AACF;AAEA,SAASG,eAAelB,KAAc;IACpC,IAAI,CAACA,SAAS,OAAOA,UAAU,UAAU,OAAOmB;IAChD,MAAMG,QAAQ,AAACtB,MAAmCiB,UAAU;IAC5D,OAAO,OAAOK,UAAU,YAAYC,OAAOC,QAAQ,CAACF,UAAUA,SAAS,IAAIA,QAAQH;AACrF"}
@@ -1 +1 @@
1
- {"version":3,"file":"slackMarkdown.d.ts","sourceRoot":"","sources":["../../../src/adapters/slack/slackMarkdown.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAG7C,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED,wBAAgB,qBAAqB,CACjC,QAAQ,EAAE,MAAM,EAChB,SAAS,SAA2B,GACrC,MAAM,EAAE,CAwCV"}
1
+ {"version":3,"file":"slackMarkdown.d.ts","sourceRoot":"","sources":["../../../src/adapters/slack/slackMarkdown.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAG7C,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,SAAS,SAA2B,GACnC,MAAM,EAAE,CAwCV"}
@@ -1,8 +1,8 @@
1
- import { Marked } from 'marked';
1
+ import { Marked } from "marked";
2
2
  export const SLACK_MAX_MESSAGE_LENGTH = 4000;
3
3
  const lexer = new Marked();
4
4
  export function escapeSlackText(text) {
5
- return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
5
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
6
6
  }
7
7
  export function markdownToSlackMrkdwn(markdown) {
8
8
  return renderBlocks(lexer.lexer(markdown)).trimEnd();
@@ -10,13 +10,13 @@ export function markdownToSlackMrkdwn(markdown) {
10
10
  export function chunkMarkdownForSlack(markdown, maxLength = SLACK_MAX_MESSAGE_LENGTH) {
11
11
  const tokens = lexer.lexer(markdown);
12
12
  const chunks = [];
13
- let current = '';
13
+ let current = "";
14
14
  const append = (rendered)=>{
15
15
  if (!rendered) return;
16
16
  if (rendered.length > maxLength) {
17
17
  if (current) {
18
18
  chunks.push(current.trimEnd());
19
- current = '';
19
+ current = "";
20
20
  }
21
21
  chunks.push(...splitRendered(rendered.trimEnd(), maxLength));
22
22
  return;
@@ -30,12 +30,12 @@ export function chunkMarkdownForSlack(markdown, maxLength = SLACK_MAX_MESSAGE_LE
30
30
  }
31
31
  };
32
32
  for (const token of tokens){
33
- if (token.type === 'code') {
33
+ if (token.type === "code") {
34
34
  const rendered = renderCode(token);
35
35
  if (rendered.length > maxLength) {
36
36
  if (current) {
37
37
  chunks.push(current.trimEnd());
38
- current = '';
38
+ current = "";
39
39
  }
40
40
  chunks.push(...splitCode(token.text, maxLength));
41
41
  continue;
@@ -47,80 +47,80 @@ export function chunkMarkdownForSlack(markdown, maxLength = SLACK_MAX_MESSAGE_LE
47
47
  return chunks;
48
48
  }
49
49
  function renderBlocks(tokens) {
50
- return tokens.map(renderBlock).join('');
50
+ return tokens.map(renderBlock).join("");
51
51
  }
52
52
  function renderBlock(token) {
53
53
  switch(token.type){
54
- case 'heading':
54
+ case "heading":
55
55
  return `*${renderInline(token.tokens)}*\n\n`;
56
- case 'paragraph':
56
+ case "paragraph":
57
57
  return `${renderInline(token.tokens)}\n\n`;
58
- case 'text':
58
+ case "text":
59
59
  {
60
60
  const text = token;
61
61
  return text.tokens ? renderInline(text.tokens) : escapeSlackText(text.text);
62
62
  }
63
- case 'code':
63
+ case "code":
64
64
  return `${renderCode(token)}\n\n`;
65
- case 'blockquote':
66
- return renderBlocks(token.tokens).trimEnd().split('\n').map((line)=>`> ${line}`).join('\n') + '\n\n';
67
- case 'list':
65
+ case "blockquote":
66
+ return renderBlocks(token.tokens).trimEnd().split("\n").map((line)=>`> ${line}`).join("\n") + "\n\n";
67
+ case "list":
68
68
  return renderList(token);
69
- case 'space':
70
- return '';
71
- case 'hr':
72
- return '—\n\n';
69
+ case "space":
70
+ return "";
71
+ case "hr":
72
+ return "—\n\n";
73
73
  default:
74
- return escapeSlackText(token.raw ?? '');
74
+ return escapeSlackText(token.raw ?? "");
75
75
  }
76
76
  }
77
77
  function renderInline(tokens) {
78
78
  return tokens.map((token)=>{
79
79
  switch(token.type){
80
- case 'text':
80
+ case "text":
81
81
  return escapeSlackText(token.text);
82
- case 'strong':
82
+ case "strong":
83
83
  return `*${renderInline(token.tokens)}*`;
84
- case 'em':
84
+ case "em":
85
85
  return `_${renderInline(token.tokens)}_`;
86
- case 'del':
86
+ case "del":
87
87
  return `~${renderInline(token.tokens)}~`;
88
- case 'codespan':
88
+ case "codespan":
89
89
  return `\`${escapeSlackText(token.text)}\``;
90
- case 'link':
90
+ case "link":
91
91
  {
92
92
  const link = token;
93
93
  return `<${escapeLinkTarget(link.href)}|${renderInline(link.tokens)}>`;
94
94
  }
95
- case 'br':
96
- return '\n';
95
+ case "br":
96
+ return "\n";
97
97
  default:
98
- return escapeSlackText(token.raw ?? '');
98
+ return escapeSlackText(token.raw ?? "");
99
99
  }
100
- }).join('');
100
+ }).join("");
101
101
  }
102
102
  function renderCode(token) {
103
103
  return `\`\`\`\n${escapeSlackText(token.text)}\n\`\`\``;
104
104
  }
105
105
  function renderList(token) {
106
106
  return token.items.map((item, index)=>{
107
- const marker = token.ordered ? `${(token.start || 1) + index}.` : '•';
107
+ const marker = token.ordered ? `${(token.start || 1) + index}.` : "•";
108
108
  return `${marker} ${renderBlocks(item.tokens).trim()}\n`;
109
- }).join('') + '\n';
109
+ }).join("") + "\n";
110
110
  }
111
111
  function escapeLinkTarget(href) {
112
- return href.replace(/&/g, '&amp;').replace(/>/g, '%3E').replace(/\|/g, '%7C');
112
+ return href.replace(/&/g, "&amp;").replace(/>/g, "%3E").replace(/\|/g, "%7C");
113
113
  }
114
114
  function splitCode(code, maxLength) {
115
115
  const wrapperLength = 8;
116
116
  const contentLimit = Math.max(1, maxLength - wrapperLength);
117
117
  const parts = [];
118
- let current = '';
118
+ let current = "";
119
119
  for (const char of Array.from(code)){
120
120
  const escaped = escapeSlackText(char);
121
121
  if (current.length + escaped.length > contentLimit && current) {
122
122
  parts.push(current);
123
- current = '';
123
+ current = "";
124
124
  }
125
125
  current += escaped;
126
126
  }
@@ -132,13 +132,13 @@ function splitRendered(text, maxLength) {
132
132
  }
133
133
  function splitText(text, maxLength) {
134
134
  const chunks = [];
135
- let current = '';
135
+ let current = "";
136
136
  for (const unit of text.match(/.*(?:\n|$)|./gu) ?? []){
137
137
  if (!unit) continue;
138
138
  for (const char of Array.from(unit)){
139
139
  if (current.length + char.length > maxLength && current) {
140
140
  chunks.push(current);
141
- current = '';
141
+ current = "";
142
142
  }
143
143
  current += char;
144
144
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/adapters/slack/slackMarkdown.ts"],"sourcesContent":["import { Marked, type Token, type Tokens } from 'marked';\n\nexport const SLACK_MAX_MESSAGE_LENGTH = 4000;\nconst lexer = new Marked();\n\nexport function escapeSlackText(text: string): string {\n return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n}\n\nexport function markdownToSlackMrkdwn(markdown: string): string {\n return renderBlocks(lexer.lexer(markdown)).trimEnd();\n}\n\nexport function chunkMarkdownForSlack(\n markdown: string,\n maxLength = SLACK_MAX_MESSAGE_LENGTH,\n): string[] {\n const tokens = lexer.lexer(markdown);\n const chunks: string[] = [];\n let current = '';\n\n const append = (rendered: string): void => {\n if (!rendered) return;\n if (rendered.length > maxLength) {\n if (current) {\n chunks.push(current.trimEnd());\n current = '';\n }\n chunks.push(...splitRendered(rendered.trimEnd(), maxLength));\n return;\n }\n const candidate = current ? `${current}${rendered}` : rendered;\n if (candidate.trimEnd().length <= maxLength) {\n current = candidate;\n } else {\n chunks.push(current.trimEnd());\n current = rendered;\n }\n };\n\n for (const token of tokens) {\n if (token.type === 'code') {\n const rendered = renderCode(token as Tokens.Code);\n if (rendered.length > maxLength) {\n if (current) {\n chunks.push(current.trimEnd());\n current = '';\n }\n chunks.push(...splitCode((token as Tokens.Code).text, maxLength));\n continue;\n }\n }\n append(renderBlock(token));\n }\n if (current.trimEnd()) chunks.push(current.trimEnd());\n return chunks;\n}\n\nfunction renderBlocks(tokens: Token[]): string {\n return tokens.map(renderBlock).join('');\n}\n\nfunction renderBlock(token: Token): string {\n switch (token.type) {\n case 'heading':\n return `*${renderInline((token as Tokens.Heading).tokens)}*\\n\\n`;\n case 'paragraph':\n return `${renderInline((token as Tokens.Paragraph).tokens)}\\n\\n`;\n case 'text': {\n const text = token as Tokens.Text;\n return text.tokens ? renderInline(text.tokens) : escapeSlackText(text.text);\n }\n case 'code':\n return `${renderCode(token as Tokens.Code)}\\n\\n`;\n case 'blockquote':\n return renderBlocks((token as Tokens.Blockquote).tokens)\n .trimEnd().split('\\n').map((line) => `> ${line}`).join('\\n') + '\\n\\n';\n case 'list':\n return renderList(token as Tokens.List);\n case 'space':\n return '';\n case 'hr':\n return '—\\n\\n';\n default:\n return escapeSlackText(token.raw ?? '');\n }\n}\n\nfunction renderInline(tokens: Token[]): string {\n return tokens.map((token) => {\n switch (token.type) {\n case 'text':\n return escapeSlackText((token as Tokens.Text).text);\n case 'strong':\n return `*${renderInline((token as Tokens.Strong).tokens)}*`;\n case 'em':\n return `_${renderInline((token as Tokens.Em).tokens)}_`;\n case 'del':\n return `~${renderInline((token as Tokens.Del).tokens)}~`;\n case 'codespan':\n return `\\`${escapeSlackText((token as Tokens.Codespan).text)}\\``;\n case 'link': {\n const link = token as Tokens.Link;\n return `<${escapeLinkTarget(link.href)}|${renderInline(link.tokens)}>`;\n }\n case 'br':\n return '\\n';\n default:\n return escapeSlackText(token.raw ?? '');\n }\n }).join('');\n}\n\nfunction renderCode(token: Tokens.Code): string {\n return `\\`\\`\\`\\n${escapeSlackText(token.text)}\\n\\`\\`\\``;\n}\n\nfunction renderList(token: Tokens.List): string {\n return token.items.map((item, index) => {\n const marker = token.ordered ? `${(token.start || 1) + index}.` : '•';\n return `${marker} ${renderBlocks(item.tokens).trim()}\\n`;\n }).join('') + '\\n';\n}\n\nfunction escapeLinkTarget(href: string): string {\n return href.replace(/&/g, '&amp;').replace(/>/g, '%3E').replace(/\\|/g, '%7C');\n}\n\nfunction splitCode(code: string, maxLength: number): string[] {\n const wrapperLength = 8;\n const contentLimit = Math.max(1, maxLength - wrapperLength);\n const parts: string[] = [];\n let current = '';\n for (const char of Array.from(code)) {\n const escaped = escapeSlackText(char);\n if (current.length + escaped.length > contentLimit && current) {\n parts.push(current);\n current = '';\n }\n current += escaped;\n }\n if (current) parts.push(current);\n return parts.map((part) => `\\`\\`\\`\\n${part}\\n\\`\\`\\``);\n}\n\nfunction splitRendered(text: string, maxLength: number): string[] {\n return splitText(text, maxLength);\n}\n\nfunction splitText(text: string, maxLength: number): string[] {\n const chunks: string[] = [];\n let current = '';\n for (const unit of text.match(/.*(?:\\n|$)|./gu) ?? []) {\n if (!unit) continue;\n for (const char of Array.from(unit)) {\n if (current.length + char.length > maxLength && current) {\n chunks.push(current);\n current = '';\n }\n current += char;\n }\n }\n if (current) chunks.push(current);\n return chunks;\n}\n"],"names":["Marked","SLACK_MAX_MESSAGE_LENGTH","lexer","escapeSlackText","text","replace","markdownToSlackMrkdwn","markdown","renderBlocks","trimEnd","chunkMarkdownForSlack","maxLength","tokens","chunks","current","append","rendered","length","push","splitRendered","candidate","token","type","renderCode","splitCode","renderBlock","map","join","renderInline","split","line","renderList","raw","link","escapeLinkTarget","href","items","item","index","marker","ordered","start","trim","code","wrapperLength","contentLimit","Math","max","parts","char","Array","from","escaped","part","splitText","unit","match"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/adapters/slack/slackMarkdown.ts"],"sourcesContent":["import { Marked, type Token, type Tokens } from \"marked\";\n\nexport const SLACK_MAX_MESSAGE_LENGTH = 4000;\nconst lexer = new Marked();\n\nexport function escapeSlackText(text: string): string {\n return text.replace(/&/g, \"&amp;\").replace(/</g, \"&lt;\").replace(/>/g, \"&gt;\");\n}\n\nexport function markdownToSlackMrkdwn(markdown: string): string {\n return renderBlocks(lexer.lexer(markdown)).trimEnd();\n}\n\nexport function chunkMarkdownForSlack(\n markdown: string,\n maxLength = SLACK_MAX_MESSAGE_LENGTH,\n): string[] {\n const tokens = lexer.lexer(markdown);\n const chunks: string[] = [];\n let current = \"\";\n\n const append = (rendered: string): void => {\n if (!rendered) return;\n if (rendered.length > maxLength) {\n if (current) {\n chunks.push(current.trimEnd());\n current = \"\";\n }\n chunks.push(...splitRendered(rendered.trimEnd(), maxLength));\n return;\n }\n const candidate = current ? `${current}${rendered}` : rendered;\n if (candidate.trimEnd().length <= maxLength) {\n current = candidate;\n } else {\n chunks.push(current.trimEnd());\n current = rendered;\n }\n };\n\n for (const token of tokens) {\n if (token.type === \"code\") {\n const rendered = renderCode(token as Tokens.Code);\n if (rendered.length > maxLength) {\n if (current) {\n chunks.push(current.trimEnd());\n current = \"\";\n }\n chunks.push(...splitCode((token as Tokens.Code).text, maxLength));\n continue;\n }\n }\n append(renderBlock(token));\n }\n if (current.trimEnd()) chunks.push(current.trimEnd());\n return chunks;\n}\n\nfunction renderBlocks(tokens: Token[]): string {\n return tokens.map(renderBlock).join(\"\");\n}\n\nfunction renderBlock(token: Token): string {\n switch (token.type) {\n case \"heading\":\n return `*${renderInline((token as Tokens.Heading).tokens)}*\\n\\n`;\n case \"paragraph\":\n return `${renderInline((token as Tokens.Paragraph).tokens)}\\n\\n`;\n case \"text\": {\n const text = token as Tokens.Text;\n return text.tokens ? renderInline(text.tokens) : escapeSlackText(text.text);\n }\n case \"code\":\n return `${renderCode(token as Tokens.Code)}\\n\\n`;\n case \"blockquote\":\n return (\n renderBlocks((token as Tokens.Blockquote).tokens)\n .trimEnd()\n .split(\"\\n\")\n .map((line) => `> ${line}`)\n .join(\"\\n\") + \"\\n\\n\"\n );\n case \"list\":\n return renderList(token as Tokens.List);\n case \"space\":\n return \"\";\n case \"hr\":\n return \"—\\n\\n\";\n default:\n return escapeSlackText(token.raw ?? \"\");\n }\n}\n\nfunction renderInline(tokens: Token[]): string {\n return tokens\n .map((token) => {\n switch (token.type) {\n case \"text\":\n return escapeSlackText((token as Tokens.Text).text);\n case \"strong\":\n return `*${renderInline((token as Tokens.Strong).tokens)}*`;\n case \"em\":\n return `_${renderInline((token as Tokens.Em).tokens)}_`;\n case \"del\":\n return `~${renderInline((token as Tokens.Del).tokens)}~`;\n case \"codespan\":\n return `\\`${escapeSlackText((token as Tokens.Codespan).text)}\\``;\n case \"link\": {\n const link = token as Tokens.Link;\n return `<${escapeLinkTarget(link.href)}|${renderInline(link.tokens)}>`;\n }\n case \"br\":\n return \"\\n\";\n default:\n return escapeSlackText(token.raw ?? \"\");\n }\n })\n .join(\"\");\n}\n\nfunction renderCode(token: Tokens.Code): string {\n return `\\`\\`\\`\\n${escapeSlackText(token.text)}\\n\\`\\`\\``;\n}\n\nfunction renderList(token: Tokens.List): string {\n return (\n token.items\n .map((item, index) => {\n const marker = token.ordered ? `${(token.start || 1) + index}.` : \"•\";\n return `${marker} ${renderBlocks(item.tokens).trim()}\\n`;\n })\n .join(\"\") + \"\\n\"\n );\n}\n\nfunction escapeLinkTarget(href: string): string {\n return href.replace(/&/g, \"&amp;\").replace(/>/g, \"%3E\").replace(/\\|/g, \"%7C\");\n}\n\nfunction splitCode(code: string, maxLength: number): string[] {\n const wrapperLength = 8;\n const contentLimit = Math.max(1, maxLength - wrapperLength);\n const parts: string[] = [];\n let current = \"\";\n for (const char of Array.from(code)) {\n const escaped = escapeSlackText(char);\n if (current.length + escaped.length > contentLimit && current) {\n parts.push(current);\n current = \"\";\n }\n current += escaped;\n }\n if (current) parts.push(current);\n return parts.map((part) => `\\`\\`\\`\\n${part}\\n\\`\\`\\``);\n}\n\nfunction splitRendered(text: string, maxLength: number): string[] {\n return splitText(text, maxLength);\n}\n\nfunction splitText(text: string, maxLength: number): string[] {\n const chunks: string[] = [];\n let current = \"\";\n for (const unit of text.match(/.*(?:\\n|$)|./gu) ?? []) {\n if (!unit) continue;\n for (const char of Array.from(unit)) {\n if (current.length + char.length > maxLength && current) {\n chunks.push(current);\n current = \"\";\n }\n current += char;\n }\n }\n if (current) chunks.push(current);\n return chunks;\n}\n"],"names":["Marked","SLACK_MAX_MESSAGE_LENGTH","lexer","escapeSlackText","text","replace","markdownToSlackMrkdwn","markdown","renderBlocks","trimEnd","chunkMarkdownForSlack","maxLength","tokens","chunks","current","append","rendered","length","push","splitRendered","candidate","token","type","renderCode","splitCode","renderBlock","map","join","renderInline","split","line","renderList","raw","link","escapeLinkTarget","href","items","item","index","marker","ordered","start","trim","code","wrapperLength","contentLimit","Math","max","parts","char","Array","from","escaped","part","splitText","unit","match"],"mappings":"AAAA,SAASA,MAAM,QAAiC,SAAS;AAEzD,OAAO,MAAMC,2BAA2B,KAAK;AAC7C,MAAMC,QAAQ,IAAIF;AAElB,OAAO,SAASG,gBAAgBC,IAAY;IAC1C,OAAOA,KAAKC,OAAO,CAAC,MAAM,SAASA,OAAO,CAAC,MAAM,QAAQA,OAAO,CAAC,MAAM;AACzE;AAEA,OAAO,SAASC,sBAAsBC,QAAgB;IACpD,OAAOC,aAAaN,MAAMA,KAAK,CAACK,WAAWE,OAAO;AACpD;AAEA,OAAO,SAASC,sBACdH,QAAgB,EAChBI,YAAYV,wBAAwB;IAEpC,MAAMW,SAASV,MAAMA,KAAK,CAACK;IAC3B,MAAMM,SAAmB,EAAE;IAC3B,IAAIC,UAAU;IAEd,MAAMC,SAAS,CAACC;QACd,IAAI,CAACA,UAAU;QACf,IAAIA,SAASC,MAAM,GAAGN,WAAW;YAC/B,IAAIG,SAAS;gBACXD,OAAOK,IAAI,CAACJ,QAAQL,OAAO;gBAC3BK,UAAU;YACZ;YACAD,OAAOK,IAAI,IAAIC,cAAcH,SAASP,OAAO,IAAIE;YACjD;QACF;QACA,MAAMS,YAAYN,UAAU,GAAGA,UAAUE,UAAU,GAAGA;QACtD,IAAII,UAAUX,OAAO,GAAGQ,MAAM,IAAIN,WAAW;YAC3CG,UAAUM;QACZ,OAAO;YACLP,OAAOK,IAAI,CAACJ,QAAQL,OAAO;YAC3BK,UAAUE;QACZ;IACF;IAEA,KAAK,MAAMK,SAAST,OAAQ;QAC1B,IAAIS,MAAMC,IAAI,KAAK,QAAQ;YACzB,MAAMN,WAAWO,WAAWF;YAC5B,IAAIL,SAASC,MAAM,GAAGN,WAAW;gBAC/B,IAAIG,SAAS;oBACXD,OAAOK,IAAI,CAACJ,QAAQL,OAAO;oBAC3BK,UAAU;gBACZ;gBACAD,OAAOK,IAAI,IAAIM,UAAU,AAACH,MAAsBjB,IAAI,EAAEO;gBACtD;YACF;QACF;QACAI,OAAOU,YAAYJ;IACrB;IACA,IAAIP,QAAQL,OAAO,IAAII,OAAOK,IAAI,CAACJ,QAAQL,OAAO;IAClD,OAAOI;AACT;AAEA,SAASL,aAAaI,MAAe;IACnC,OAAOA,OAAOc,GAAG,CAACD,aAAaE,IAAI,CAAC;AACtC;AAEA,SAASF,YAAYJ,KAAY;IAC/B,OAAQA,MAAMC,IAAI;QAChB,KAAK;YACH,OAAO,CAAC,CAAC,EAAEM,aAAa,AAACP,MAAyBT,MAAM,EAAE,KAAK,CAAC;QAClE,KAAK;YACH,OAAO,GAAGgB,aAAa,AAACP,MAA2BT,MAAM,EAAE,IAAI,CAAC;QAClE,KAAK;YAAQ;gBACX,MAAMR,OAAOiB;gBACb,OAAOjB,KAAKQ,MAAM,GAAGgB,aAAaxB,KAAKQ,MAAM,IAAIT,gBAAgBC,KAAKA,IAAI;YAC5E;QACA,KAAK;YACH,OAAO,GAAGmB,WAAWF,OAAsB,IAAI,CAAC;QAClD,KAAK;YACH,OACEb,aAAa,AAACa,MAA4BT,MAAM,EAC7CH,OAAO,GACPoB,KAAK,CAAC,MACNH,GAAG,CAAC,CAACI,OAAS,CAAC,EAAE,EAAEA,MAAM,EACzBH,IAAI,CAAC,QAAQ;QAEpB,KAAK;YACH,OAAOI,WAAWV;QACpB,KAAK;YACH,OAAO;QACT,KAAK;YACH,OAAO;QACT;YACE,OAAOlB,gBAAgBkB,MAAMW,GAAG,IAAI;IACxC;AACF;AAEA,SAASJ,aAAahB,MAAe;IACnC,OAAOA,OACJc,GAAG,CAAC,CAACL;QACJ,OAAQA,MAAMC,IAAI;YAChB,KAAK;gBACH,OAAOnB,gBAAgB,AAACkB,MAAsBjB,IAAI;YACpD,KAAK;gBACH,OAAO,CAAC,CAAC,EAAEwB,aAAa,AAACP,MAAwBT,MAAM,EAAE,CAAC,CAAC;YAC7D,KAAK;gBACH,OAAO,CAAC,CAAC,EAAEgB,aAAa,AAACP,MAAoBT,MAAM,EAAE,CAAC,CAAC;YACzD,KAAK;gBACH,OAAO,CAAC,CAAC,EAAEgB,aAAa,AAACP,MAAqBT,MAAM,EAAE,CAAC,CAAC;YAC1D,KAAK;gBACH,OAAO,CAAC,EAAE,EAAET,gBAAgB,AAACkB,MAA0BjB,IAAI,EAAE,EAAE,CAAC;YAClE,KAAK;gBAAQ;oBACX,MAAM6B,OAAOZ;oBACb,OAAO,CAAC,CAAC,EAAEa,iBAAiBD,KAAKE,IAAI,EAAE,CAAC,EAAEP,aAAaK,KAAKrB,MAAM,EAAE,CAAC,CAAC;gBACxE;YACA,KAAK;gBACH,OAAO;YACT;gBACE,OAAOT,gBAAgBkB,MAAMW,GAAG,IAAI;QACxC;IACF,GACCL,IAAI,CAAC;AACV;AAEA,SAASJ,WAAWF,KAAkB;IACpC,OAAO,CAAC,QAAQ,EAAElB,gBAAgBkB,MAAMjB,IAAI,EAAE,QAAQ,CAAC;AACzD;AAEA,SAAS2B,WAAWV,KAAkB;IACpC,OACEA,MAAMe,KAAK,CACRV,GAAG,CAAC,CAACW,MAAMC;QACV,MAAMC,SAASlB,MAAMmB,OAAO,GAAG,GAAG,AAACnB,CAAAA,MAAMoB,KAAK,IAAI,CAAA,IAAKH,MAAM,CAAC,CAAC,GAAG;QAClE,OAAO,GAAGC,OAAO,CAAC,EAAE/B,aAAa6B,KAAKzB,MAAM,EAAE8B,IAAI,GAAG,EAAE,CAAC;IAC1D,GACCf,IAAI,CAAC,MAAM;AAElB;AAEA,SAASO,iBAAiBC,IAAY;IACpC,OAAOA,KAAK9B,OAAO,CAAC,MAAM,SAASA,OAAO,CAAC,MAAM,OAAOA,OAAO,CAAC,OAAO;AACzE;AAEA,SAASmB,UAAUmB,IAAY,EAAEhC,SAAiB;IAChD,MAAMiC,gBAAgB;IACtB,MAAMC,eAAeC,KAAKC,GAAG,CAAC,GAAGpC,YAAYiC;IAC7C,MAAMI,QAAkB,EAAE;IAC1B,IAAIlC,UAAU;IACd,KAAK,MAAMmC,QAAQC,MAAMC,IAAI,CAACR,MAAO;QACnC,MAAMS,UAAUjD,gBAAgB8C;QAChC,IAAInC,QAAQG,MAAM,GAAGmC,QAAQnC,MAAM,GAAG4B,gBAAgB/B,SAAS;YAC7DkC,MAAM9B,IAAI,CAACJ;YACXA,UAAU;QACZ;QACAA,WAAWsC;IACb;IACA,IAAItC,SAASkC,MAAM9B,IAAI,CAACJ;IACxB,OAAOkC,MAAMtB,GAAG,CAAC,CAAC2B,OAAS,CAAC,QAAQ,EAAEA,KAAK,QAAQ,CAAC;AACtD;AAEA,SAASlC,cAAcf,IAAY,EAAEO,SAAiB;IACpD,OAAO2C,UAAUlD,MAAMO;AACzB;AAEA,SAAS2C,UAAUlD,IAAY,EAAEO,SAAiB;IAChD,MAAME,SAAmB,EAAE;IAC3B,IAAIC,UAAU;IACd,KAAK,MAAMyC,QAAQnD,KAAKoD,KAAK,CAAC,qBAAqB,EAAE,CAAE;QACrD,IAAI,CAACD,MAAM;QACX,KAAK,MAAMN,QAAQC,MAAMC,IAAI,CAACI,MAAO;YACnC,IAAIzC,QAAQG,MAAM,GAAGgC,KAAKhC,MAAM,GAAGN,aAAaG,SAAS;gBACvDD,OAAOK,IAAI,CAACJ;gBACZA,UAAU;YACZ;YACAA,WAAWmC;QACb;IACF;IACA,IAAInC,SAASD,OAAOK,IAAI,CAACJ;IACzB,OAAOD;AACT"}
@@ -1,9 +1,9 @@
1
- import type { ChannelQuestion } from '../../types.js';
1
+ import type { ChannelQuestion } from "../../types.js";
2
2
  interface SlackQuestionButton {
3
- type: 'button';
4
- action_id: 'ai_devkit_question';
3
+ type: "button";
4
+ action_id: "ai_devkit_question";
5
5
  text: {
6
- type: 'plain_text';
6
+ type: "plain_text";
7
7
  text: string;
8
8
  };
9
9
  value: string;
@@ -13,14 +13,14 @@ type SlackQuestionMessage = Record<string, unknown> & {
13
13
  text: string;
14
14
  blocks: [
15
15
  {
16
- type: 'section';
16
+ type: "section";
17
17
  text: {
18
- type: 'mrkdwn';
18
+ type: "mrkdwn";
19
19
  text: string;
20
20
  };
21
21
  },
22
22
  {
23
- type: 'actions';
23
+ type: "actions";
24
24
  elements: SlackQuestionButton[];
25
25
  }
26
26
  ];
@@ -1 +1 @@
1
- {"version":3,"file":"slackQuestion.d.ts","sourceRoot":"","sources":["../../../src/adapters/slack/slackQuestion.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAGtD,UAAU,mBAAmB;IACzB,IAAI,EAAE,QAAQ,CAAC;IACf,SAAS,EAAE,oBAAoB,CAAC;IAChC,IAAI,EAAE;QAAE,IAAI,EAAE,YAAY,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3C,KAAK,EAAE,MAAM,CAAC;CACjB;AAED,KAAK,oBAAoB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IAClD,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE;QACJ;YACI,IAAI,EAAE,SAAS,CAAC;YAChB,IAAI,EAAE;gBAAE,IAAI,EAAE,QAAQ,CAAC;gBAAC,IAAI,EAAE,MAAM,CAAA;aAAE,CAAC;SAC1C;QACD;YACI,IAAI,EAAE,SAAS,CAAC;YAChB,QAAQ,EAAE,mBAAmB,EAAE,CAAC;SACnC;KACJ,CAAC;CACL,CAAC;AAEF,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,GAAG,oBAAoB,CAoCzG"}
1
+ {"version":3,"file":"slackQuestion.d.ts","sourceRoot":"","sources":["../../../src/adapters/slack/slackQuestion.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAGtD,UAAU,mBAAmB;IAC3B,IAAI,EAAE,QAAQ,CAAC;IACf,SAAS,EAAE,oBAAoB,CAAC;IAChC,IAAI,EAAE;QAAE,IAAI,EAAE,YAAY,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3C,KAAK,EAAE,MAAM,CAAC;CACf;AAED,KAAK,oBAAoB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IACpD,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE;QACN;YACE,IAAI,EAAE,SAAS,CAAC;YAChB,IAAI,EAAE;gBAAE,IAAI,EAAE,QAAQ,CAAC;gBAAC,IAAI,EAAE,MAAM,CAAA;aAAE,CAAC;SACxC;QACD;YACE,IAAI,EAAE,SAAS,CAAC;YAChB,QAAQ,EAAE,mBAAmB,EAAE,CAAC;SACjC;KACF,CAAC;CACH,CAAC;AAEF,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,eAAe,GACxB,oBAAoB,CAoCtB"}
@@ -1,23 +1,23 @@
1
- import { escapeSlackText } from './slackMarkdown.js';
1
+ import { escapeSlackText } from "./slackMarkdown.js";
2
2
  export function buildSlackQuestionMessage(chatId, question) {
3
- const fallbackPrefix = question.header ? `${question.header}: ` : '';
3
+ const fallbackPrefix = question.header ? `${question.header}: ` : "";
4
4
  const fallback = escapeSlackText(`${fallbackPrefix}${question.question}`);
5
5
  const buttons = question.options.map((option)=>({
6
- type: 'button',
7
- action_id: 'ai_devkit_question',
6
+ type: "button",
7
+ action_id: "ai_devkit_question",
8
8
  text: {
9
- type: 'plain_text',
9
+ type: "plain_text",
10
10
  text: option.label.slice(0, 75)
11
11
  },
12
12
  value: `${question.id}:${option.value}`.slice(0, 2000)
13
13
  }));
14
14
  if (question.allowSkip) {
15
15
  buttons.push({
16
- type: 'button',
17
- action_id: 'ai_devkit_question',
16
+ type: "button",
17
+ action_id: "ai_devkit_question",
18
18
  text: {
19
- type: 'plain_text',
20
- text: 'Skip'
19
+ type: "plain_text",
20
+ text: "Skip"
21
21
  },
22
22
  value: `${question.id}:skip`
23
23
  });
@@ -27,14 +27,14 @@ export function buildSlackQuestionMessage(chatId, question) {
27
27
  text: fallback,
28
28
  blocks: [
29
29
  {
30
- type: 'section',
30
+ type: "section",
31
31
  text: {
32
- type: 'mrkdwn',
33
- text: `*${escapeSlackText(question.header ?? 'Question')}*\n${escapeSlackText(question.question)}`
32
+ type: "mrkdwn",
33
+ text: `*${escapeSlackText(question.header ?? "Question")}*\n${escapeSlackText(question.question)}`
34
34
  }
35
35
  },
36
36
  {
37
- type: 'actions',
37
+ type: "actions",
38
38
  elements: buttons
39
39
  }
40
40
  ]