@inkeep/agents-work-apps 0.50.0 → 0.50.3

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 (57) hide show
  1. package/dist/_virtual/rolldown_runtime.js +32 -0
  2. package/dist/env.d.ts +2 -0
  3. package/dist/env.js +1 -0
  4. package/dist/github/mcp/auth.d.ts +2 -2
  5. package/dist/github/mcp/index.d.ts +2 -2
  6. package/dist/github/mcp/schemas.d.ts +1 -1
  7. package/dist/github/routes/tokenExchange.d.ts +2 -2
  8. package/dist/github/routes/webhooks.d.ts +2 -2
  9. package/dist/node_modules/.pnpm/@slack_logger@4.0.0/node_modules/@slack/logger/dist/index.js +89 -0
  10. package/dist/node_modules/.pnpm/@slack_socket-mode@2.0.5/node_modules/@slack/socket-mode/dist/package.js +85 -0
  11. package/dist/node_modules/.pnpm/@slack_socket-mode@2.0.5/node_modules/@slack/socket-mode/dist/src/SlackWebSocket.js +223 -0
  12. package/dist/node_modules/.pnpm/@slack_socket-mode@2.0.5/node_modules/@slack/socket-mode/dist/src/SocketModeClient.js +367 -0
  13. package/dist/node_modules/.pnpm/@slack_socket-mode@2.0.5/node_modules/@slack/socket-mode/dist/src/UnrecoverableSocketModeStartError.js +20 -0
  14. package/dist/node_modules/.pnpm/@slack_socket-mode@2.0.5/node_modules/@slack/socket-mode/dist/src/errors.js +71 -0
  15. package/dist/node_modules/.pnpm/@slack_socket-mode@2.0.5/node_modules/@slack/socket-mode/dist/src/index.js +44 -0
  16. package/dist/node_modules/.pnpm/@slack_socket-mode@2.0.5/node_modules/@slack/socket-mode/dist/src/logger.js +32 -0
  17. package/dist/node_modules/.pnpm/eventemitter3@5.0.1/node_modules/eventemitter3/index.js +241 -0
  18. package/dist/node_modules/.pnpm/ws@8.19.0/node_modules/ws/index.js +23 -0
  19. package/dist/node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/buffer-util.js +107 -0
  20. package/dist/node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/constants.js +29 -0
  21. package/dist/node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/event-target.js +226 -0
  22. package/dist/node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/extension.js +150 -0
  23. package/dist/node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/limiter.js +57 -0
  24. package/dist/node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/permessage-deflate.js +342 -0
  25. package/dist/node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/receiver.js +457 -0
  26. package/dist/node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/sender.js +505 -0
  27. package/dist/node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/stream.js +123 -0
  28. package/dist/node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/subprotocol.js +46 -0
  29. package/dist/node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/validation.js +203 -0
  30. package/dist/node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/websocket-server.js +385 -0
  31. package/dist/node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/websocket.js +985 -0
  32. package/dist/slack/dispatcher.d.ts +16 -0
  33. package/dist/slack/dispatcher.js +335 -0
  34. package/dist/slack/i18n/strings.d.ts +5 -5
  35. package/dist/slack/i18n/strings.js +9 -9
  36. package/dist/slack/index.d.ts +3 -1
  37. package/dist/slack/index.js +4 -2
  38. package/dist/slack/middleware/permissions.js +120 -107
  39. package/dist/slack/routes/events.js +10 -328
  40. package/dist/slack/routes/oauth.js +6 -3
  41. package/dist/slack/routes/users.js +12 -6
  42. package/dist/slack/routes/workspaces.js +31 -36
  43. package/dist/slack/services/blocks/index.js +7 -11
  44. package/dist/slack/services/commands/index.js +2 -2
  45. package/dist/slack/services/dev-config.d.ts +23 -0
  46. package/dist/slack/services/dev-config.js +91 -0
  47. package/dist/slack/services/events/app-mention.js +6 -17
  48. package/dist/slack/services/events/modal-submission.js +5 -5
  49. package/dist/slack/services/events/streaming.js +4 -3
  50. package/dist/slack/services/events/utils.js +8 -8
  51. package/dist/slack/services/index.js +1 -1
  52. package/dist/slack/services/modals.js +4 -4
  53. package/dist/slack/services/nango.d.ts +2 -0
  54. package/dist/slack/services/nango.js +84 -2
  55. package/dist/slack/socket-mode.d.ts +4 -0
  56. package/dist/slack/socket-mode.js +130 -0
  57. package/package.json +3 -2
@@ -3,6 +3,7 @@ import { getLogger } from "../../../logger.js";
3
3
  import { createContextBlock } from "../blocks/index.js";
4
4
  import { SlackErrorType, classifyError, getUserFriendlyErrorMessage } from "./utils.js";
5
5
  import { SLACK_SPAN_KEYS, SLACK_SPAN_NAMES, setSpanWithError, tracer } from "../../tracer.js";
6
+ import { getInProcessFetch } from "@inkeep/agents-core";
6
7
 
7
8
  //#region src/slack/services/events/streaming.ts
8
9
  /**
@@ -12,8 +13,8 @@ import { SLACK_SPAN_KEYS, SLACK_SPAN_NAMES, setSpanWithError, tracer } from "../
12
13
  * Streams responses incrementally to Slack using chatStream API.
13
14
  */
14
15
  const logger = getLogger("slack-streaming");
15
- const STREAM_TIMEOUT_MS = 12e4;
16
- const CHATSTREAM_OP_TIMEOUT_MS = 1e4;
16
+ const STREAM_TIMEOUT_MS = 6e5;
17
+ const CHATSTREAM_OP_TIMEOUT_MS = 2e4;
17
18
  /** Shorter timeout for best-effort cleanup in error paths to bound total error handling time. */
18
19
  const CLEANUP_TIMEOUT_MS = 3e3;
19
20
  /**
@@ -59,7 +60,7 @@ async function streamAgentResponse(params) {
59
60
  }, STREAM_TIMEOUT_MS);
60
61
  let response;
61
62
  try {
62
- response = await fetch(`${apiUrl.replace(/\/$/, "")}/run/api/chat`, {
63
+ response = await getInProcessFetch()(`${apiUrl.replace(/\/$/, "")}/run/api/chat`, {
63
64
  method: "POST",
64
65
  headers: {
65
66
  "Content-Type": "application/json",
@@ -2,7 +2,7 @@ import { env } from "../../../env.js";
2
2
  import { getLogger } from "../../../logger.js";
3
3
  import runDbClient_default from "../../../db/runDbClient.js";
4
4
  import { findWorkspaceConnectionByTeamId } from "../nango.js";
5
- import { InternalServices, findWorkAppSlackChannelAgentConfig, findWorkAppSlackUserMapping, generateInternalServiceToken } from "@inkeep/agents-core";
5
+ import { InternalServices, findWorkAppSlackChannelAgentConfig, findWorkAppSlackUserMapping, generateInternalServiceToken, getInProcessFetch } from "@inkeep/agents-core";
6
6
 
7
7
  //#region src/slack/services/events/utils.ts
8
8
  /**
@@ -97,11 +97,11 @@ function classifyError(error, httpStatus) {
97
97
  function getUserFriendlyErrorMessage(errorType, agentName) {
98
98
  const agent = agentName || "The agent";
99
99
  switch (errorType) {
100
- case SlackErrorType.TIMEOUT: return `⏱️ *Request timed out*\n\n${agent} took too long to respond. This can happen with complex queries.\n\n*Try:*\n• Simplifying your question\n• Breaking it into smaller parts\n• Trying again in a moment`;
101
- case SlackErrorType.RATE_LIMIT: return `⚠️ *Too many requests*\n\nYou've hit the rate limit. Please wait a moment before trying again.\n\n*Tip:* Space out your requests to avoid this.`;
102
- case SlackErrorType.AUTH_ERROR: return `🔐 *Authentication issue*\n\nThere was a problem with your account connection.\n\n*Try:*\n• Running \`/inkeep link\` to re-link your account\n• Contacting your workspace admin if the issue persists`;
103
- case SlackErrorType.API_ERROR: return `❌ *Something went wrong*\n\n${agent} encountered an error processing your request.\n\n*Try:*\n• Rephrasing your question\n• Trying again in a moment\n• Using \`/inkeep help\` for more options`;
104
- default: return `❌ *Unexpected error*\n\nSomething went wrong while processing your request.\n\n*Try:*\n• Trying again in a moment\n• Using \`/inkeep help\` for more options`;
100
+ case SlackErrorType.TIMEOUT: return `*Request timed out.* ${agent} took too long to respond. Try again with a simpler question.`;
101
+ case SlackErrorType.RATE_LIMIT: return "*Rate limited.* Wait a moment and try again.";
102
+ case SlackErrorType.AUTH_ERROR: return "*Authentication error.* Run `/inkeep link` to reconnect your account.";
103
+ case SlackErrorType.API_ERROR: return `*Something went wrong.* ${agent} encountered an error. Try again in a moment.`;
104
+ default: return "*Unexpected error.* Something went wrong. Try again in a moment.";
105
105
  }
106
106
  }
107
107
  const INTERNAL_FETCH_TIMEOUT_MS = 1e4;
@@ -114,7 +114,7 @@ async function fetchProjectsForTenant(tenantId) {
114
114
  const controller = new AbortController();
115
115
  const timeout = setTimeout(() => controller.abort(), INTERNAL_FETCH_TIMEOUT_MS);
116
116
  try {
117
- const response = await fetch(`${apiUrl}/manage/tenants/${tenantId}/projects?limit=50`, {
117
+ const response = await getInProcessFetch()(`${apiUrl}/manage/tenants/${tenantId}/projects?limit=50`, {
118
118
  method: "GET",
119
119
  headers: {
120
120
  Authorization: `Bearer ${token}`,
@@ -160,7 +160,7 @@ async function fetchAgentsForProject(tenantId, projectId) {
160
160
  const controller = new AbortController();
161
161
  const timeout = setTimeout(() => controller.abort(), INTERNAL_FETCH_TIMEOUT_MS);
162
162
  try {
163
- const response = await fetch(`${apiUrl}/manage/tenants/${tenantId}/projects/${projectId}/agents?limit=50`, {
163
+ const response = await getInProcessFetch()(`${apiUrl}/manage/tenants/${tenantId}/projects/${projectId}/agents?limit=50`, {
164
164
  method: "GET",
165
165
  headers: {
166
166
  Authorization: `Bearer ${token}`,
@@ -5,12 +5,12 @@ import { checkUserIsChannelMember, getSlackChannelInfo, getSlackChannels, getSla
5
5
  import { SlackErrorType, checkIfBotThread, classifyError, fetchAgentsForProject, fetchProjectsForTenant, findCachedUserMapping, generateSlackConversationId, getChannelAgentConfig, getThreadContext, getUserFriendlyErrorMessage, getWorkspaceDefaultAgent, markdownToMrkdwn, sendResponseUrlMessage } from "./events/utils.js";
6
6
  import { buildAgentSelectorModal, buildFollowUpModal, buildMessageShortcutModal } from "./modals.js";
7
7
  import { handleAgentPickerCommand, handleCommand, handleHelpCommand, handleLinkCommand, handleQuestionCommand, handleStatusCommand, handleUnlinkCommand } from "./commands/index.js";
8
- import { getBotTokenForTeam, setBotTokenForTeam } from "./workspace-tokens.js";
9
8
  import { streamAgentResponse } from "./events/streaming.js";
10
9
  import { handleAppMention } from "./events/app-mention.js";
11
10
  import { handleMessageShortcut, handleOpenAgentSelectorModal, handleOpenFollowUpModal } from "./events/block-actions.js";
12
11
  import { handleFollowUpSubmission, handleModalSubmission } from "./events/modal-submission.js";
13
12
  import "./events/index.js";
14
13
  import { parseSlackCommandBody, parseSlackEventBody, verifySlackRequest } from "./security.js";
14
+ import { getBotTokenForTeam, setBotTokenForTeam } from "./workspace-tokens.js";
15
15
 
16
16
  export { SlackErrorType, buildAgentSelectorModal, buildConversationResponseBlocks, buildFollowUpButton, buildFollowUpModal, buildMessageShortcutModal, checkIfBotThread, checkUserIsChannelMember, classifyError, clearWorkspaceConnectionCache, computeWorkspaceConnectionId, createAlreadyLinkedMessage, createConnectSession, createContextBlock, createErrorMessage, createJwtLinkMessage, createNotLinkedMessage, createStatusMessage, createUnlinkSuccessMessage, createUpdatedHelpMessage, deleteWorkspaceInstallation, fetchAgentsForProject, fetchProjectsForTenant, findCachedUserMapping, findWorkspaceConnectionByTeamId, generateSlackConversationId, getAgentConfigSources, getBotTokenForTeam, getChannelAgentConfig, getConnectionAccessToken, getSlackChannelInfo, getSlackChannels, getSlackClient, getSlackIntegrationId, getSlackNango, getSlackTeamInfo, getSlackUserInfo, getThreadContext, getUserFriendlyErrorMessage, getWorkspaceDefaultAgent, getWorkspaceDefaultAgentFromNango, handleAgentPickerCommand, handleAppMention, handleCommand, handleFollowUpSubmission, handleHelpCommand, handleLinkCommand, handleMessageShortcut, handleModalSubmission, handleOpenAgentSelectorModal, handleOpenFollowUpModal, handleQuestionCommand, handleStatusCommand, handleUnlinkCommand, listWorkspaceInstallations, markdownToMrkdwn, parseSlackCommandBody, parseSlackEventBody, postMessage, postMessageInThread, resolveEffectiveAgent, revokeSlackToken, sendResponseUrlMessage, setBotTokenForTeam, setWorkspaceDefaultAgent, storeWorkspaceInstallation, streamAgentResponse, updateConnectionMetadata, verifySlackRequest };
@@ -66,7 +66,7 @@ function buildAgentSelectorModal(params) {
66
66
  }
67
67
  }, {
68
68
  type: "input",
69
- block_id: "agent_select_block",
69
+ block_id: selectedProjectId ? `agent_select_block_${selectedProjectId}` : "agent_select_block",
70
70
  element: {
71
71
  type: "static_select",
72
72
  action_id: "agent_select",
@@ -137,7 +137,7 @@ function buildAgentSelectorModal(params) {
137
137
  type: "context",
138
138
  elements: [{
139
139
  type: "mrkdwn",
140
- text: `⚙️ <${manageUiBaseUrl}${dashboardUrl}|Open Dashboard>`
140
+ text: `<${manageUiBaseUrl}${dashboardUrl}|Open Dashboard>`
141
141
  }]
142
142
  });
143
143
  return {
@@ -283,7 +283,7 @@ function buildMessageShortcutModal(params) {
283
283
  },
284
284
  {
285
285
  type: "input",
286
- block_id: "agent_select_block",
286
+ block_id: selectedProjectId ? `agent_select_block_${selectedProjectId}` : "agent_select_block",
287
287
  element: {
288
288
  type: "static_select",
289
289
  action_id: "agent_select",
@@ -325,7 +325,7 @@ function buildMessageShortcutModal(params) {
325
325
  type: "context",
326
326
  elements: [{
327
327
  type: "mrkdwn",
328
- text: `⚙️ <${manageUiBaseUrl}${dashboardUrl}|Open Dashboard>`
328
+ text: `<${manageUiBaseUrl}${dashboardUrl}|Open Dashboard>`
329
329
  }]
330
330
  });
331
331
  return {
@@ -30,6 +30,8 @@ interface SlackWorkspaceConnection {
30
30
  /**
31
31
  * Find a workspace connection by Slack team ID.
32
32
  * Uses PostgreSQL first (O(1)) with in-memory caching, then falls back to Nango.
33
+ * In development mode with .slack-dev.json present, returns a local connection
34
+ * built from the dev config file instead of hitting Nango.
33
35
  *
34
36
  * Performance: This function is called on every @mention and command.
35
37
  * The PostgreSQL-first approach with caching provides O(1) lookups.
@@ -1,6 +1,7 @@
1
1
  import { env } from "../../env.js";
2
2
  import { getLogger } from "../../logger.js";
3
3
  import runDbClient_default from "../../db/runDbClient.js";
4
+ import { getDevDefaultAgent, isSlackDevMode, loadSlackDevConfig, saveSlackDevConfig } from "./dev-config.js";
4
5
  import { findWorkAppSlackWorkspaceBySlackTeamId } from "@inkeep/agents-core";
5
6
  import { Nango } from "@nangohq/node";
6
7
 
@@ -78,6 +79,10 @@ function getSlackIntegrationId() {
78
79
  return env.NANGO_SLACK_INTEGRATION_ID || "slack-agent";
79
80
  }
80
81
  async function createConnectSession(params) {
82
+ if (isSlackDevMode()) {
83
+ logger.debug({}, "Skipping Nango connect session in dev mode");
84
+ return null;
85
+ }
81
86
  try {
82
87
  const nango = getSlackNango();
83
88
  const integrationId = getSlackIntegrationId();
@@ -105,6 +110,7 @@ async function createConnectSession(params) {
105
110
  }
106
111
  }
107
112
  async function getConnectionAccessToken(connectionId) {
113
+ if (isSlackDevMode()) return loadSlackDevConfig()?.botToken ?? null;
108
114
  try {
109
115
  const nango = getSlackNango();
110
116
  const integrationId = getSlackIntegrationId();
@@ -117,9 +123,27 @@ async function getConnectionAccessToken(connectionId) {
117
123
  return null;
118
124
  }
119
125
  }
126
+ function buildDevWorkspaceConnection(devConfig, teamId) {
127
+ const connection = {
128
+ connectionId: `dev:${teamId}`,
129
+ teamId,
130
+ teamName: devConfig.teamName || "dev",
131
+ botToken: devConfig.botToken,
132
+ tenantId: "default",
133
+ defaultAgent: getDevDefaultAgent(devConfig) ?? void 0
134
+ };
135
+ evictWorkspaceCache();
136
+ workspaceConnectionCache.set(teamId, {
137
+ connection,
138
+ expiresAt: Date.now() + CACHE_TTL_MS
139
+ });
140
+ return connection;
141
+ }
120
142
  /**
121
143
  * Find a workspace connection by Slack team ID.
122
144
  * Uses PostgreSQL first (O(1)) with in-memory caching, then falls back to Nango.
145
+ * In development mode with .slack-dev.json present, returns a local connection
146
+ * built from the dev config file instead of hitting Nango.
123
147
  *
124
148
  * Performance: This function is called on every @mention and command.
125
149
  * The PostgreSQL-first approach with caching provides O(1) lookups.
@@ -130,6 +154,15 @@ async function findWorkspaceConnectionByTeamId(teamId) {
130
154
  logger.debug({ teamId }, "Workspace connection cache hit");
131
155
  return cached.connection;
132
156
  }
157
+ if (isSlackDevMode()) {
158
+ const devConfig = loadSlackDevConfig();
159
+ if (devConfig) {
160
+ logger.debug({ teamId }, "Using .slack-dev.json for workspace connection");
161
+ return buildDevWorkspaceConnection(devConfig, teamId);
162
+ }
163
+ logger.debug({ teamId }, "No .slack-dev.json found returning null");
164
+ return null;
165
+ }
133
166
  try {
134
167
  const dbWorkspace = await findWorkAppSlackWorkspaceBySlackTeamId(runDbClient_default)(teamId);
135
168
  if (dbWorkspace?.nangoConnectionId) {
@@ -155,7 +188,7 @@ async function findWorkspaceConnectionByTeamId(teamId) {
155
188
  }
156
189
  }
157
190
  logger.debug({ teamId }, "PostgreSQL lookup failed, falling back to Nango iteration");
158
- return findWorkspaceConnectionByTeamIdFromNango(teamId);
191
+ return await findWorkspaceConnectionByTeamIdFromNango(teamId);
159
192
  } catch (error) {
160
193
  logger.error({
161
194
  error,
@@ -165,6 +198,7 @@ async function findWorkspaceConnectionByTeamId(teamId) {
165
198
  }
166
199
  }
167
200
  async function getWorkspaceDefaultAgentFromNangoByConnectionId(connectionId) {
201
+ if (isSlackDevMode()) return getDevDefaultAgent(loadSlackDevConfig());
168
202
  try {
169
203
  const nango = getSlackNango();
170
204
  const integrationId = getSlackIntegrationId();
@@ -234,10 +268,23 @@ function clearWorkspaceConnectionCache(teamId) {
234
268
  else workspaceConnectionCache.clear();
235
269
  }
236
270
  async function updateConnectionMetadata(connectionId, metadata) {
271
+ if (isSlackDevMode()) {
272
+ const devConfig = loadSlackDevConfig();
273
+ if (!devConfig) return false;
274
+ devConfig.metadata = {
275
+ ...devConfig.metadata,
276
+ ...metadata
277
+ };
278
+ return saveSlackDevConfig(devConfig);
279
+ }
237
280
  try {
238
281
  const nango = getSlackNango();
239
282
  const integrationId = getSlackIntegrationId();
240
- await nango.updateMetadata(integrationId, connectionId, metadata);
283
+ const lastUpdatedAt = (/* @__PURE__ */ new Date()).toISOString();
284
+ await nango.updateMetadata(integrationId, connectionId, {
285
+ ...metadata,
286
+ last_updated_at: lastUpdatedAt
287
+ });
241
288
  return true;
242
289
  } catch (error) {
243
290
  logger.error({
@@ -248,6 +295,17 @@ async function updateConnectionMetadata(connectionId, metadata) {
248
295
  }
249
296
  }
250
297
  async function setWorkspaceDefaultAgent(teamId, defaultAgent) {
298
+ if (isSlackDevMode()) {
299
+ const devConfig = loadSlackDevConfig();
300
+ if (!devConfig) return false;
301
+ devConfig.metadata = {
302
+ ...devConfig.metadata,
303
+ default_agent: defaultAgent ? JSON.stringify(defaultAgent) : ""
304
+ };
305
+ const saved = saveSlackDevConfig(devConfig);
306
+ if (saved) clearWorkspaceConnectionCache(teamId);
307
+ return saved;
308
+ }
251
309
  try {
252
310
  const workspace = await findWorkspaceConnectionByTeamId(teamId);
253
311
  if (!workspace) {
@@ -266,6 +324,7 @@ async function setWorkspaceDefaultAgent(teamId, defaultAgent) {
266
324
  }
267
325
  }
268
326
  async function getWorkspaceDefaultAgentFromNango(teamId) {
327
+ if (isSlackDevMode()) return getDevDefaultAgent(loadSlackDevConfig());
269
328
  try {
270
329
  return (await findWorkspaceConnectionByTeamId(teamId))?.defaultAgent || null;
271
330
  } catch (error) {
@@ -294,6 +353,13 @@ async function storeWorkspaceInstallation(data) {
294
353
  teamId: data.teamId,
295
354
  enterpriseId: data.enterpriseId
296
355
  });
356
+ if (isSlackDevMode()) {
357
+ logger.debug({ connectionId }, "Skipping Nango store in dev mode");
358
+ return {
359
+ connectionId: `dev:${data.teamId}`,
360
+ success: true
361
+ };
362
+ }
297
363
  try {
298
364
  const integrationId = getSlackIntegrationId();
299
365
  const secretKey = env.NANGO_SLACK_SECRET_KEY || env.NANGO_SECRET_KEY;
@@ -406,6 +472,18 @@ async function storeWorkspaceInstallation(data) {
406
472
  * List all workspace installations from Nango.
407
473
  */
408
474
  async function listWorkspaceInstallations() {
475
+ if (isSlackDevMode()) {
476
+ const devConfig = loadSlackDevConfig();
477
+ if (!devConfig) return [];
478
+ return [{
479
+ connectionId: `dev:${devConfig.teamId}`,
480
+ teamId: devConfig.teamId,
481
+ teamName: devConfig.teamName,
482
+ botToken: devConfig.botToken,
483
+ tenantId: "default",
484
+ defaultAgent: getDevDefaultAgent(devConfig) ?? void 0
485
+ }];
486
+ }
409
487
  try {
410
488
  const nango = getSlackNango();
411
489
  const integrationId = getSlackIntegrationId();
@@ -445,6 +523,10 @@ async function listWorkspaceInstallations() {
445
523
  * Delete a workspace installation from Nango.
446
524
  */
447
525
  async function deleteWorkspaceInstallation(connectionId) {
526
+ if (isSlackDevMode()) {
527
+ logger.debug({ connectionId }, "Skipping Nango delete in dev mode");
528
+ return true;
529
+ }
448
530
  try {
449
531
  const nango = getSlackNango();
450
532
  const integrationId = getSlackIntegrationId();
@@ -0,0 +1,4 @@
1
+ //#region src/slack/socket-mode.d.ts
2
+ declare function startSocketMode(appToken: string): Promise<void>;
3
+ //#endregion
4
+ export { startSocketMode };
@@ -0,0 +1,130 @@
1
+ import { __toESM } from "../_virtual/rolldown_runtime.js";
2
+ import { getLogger } from "../logger.js";
3
+ import { handleCommand } from "./services/commands/index.js";
4
+ import { SLACK_SPAN_KEYS, SLACK_SPAN_NAMES, tracer } from "./tracer.js";
5
+ import "./services/index.js";
6
+ import { dispatchSlackEvent } from "./dispatcher.js";
7
+ import { flushTraces } from "@inkeep/agents-core";
8
+ import { SpanStatusCode } from "@opentelemetry/api";
9
+
10
+ //#region src/slack/socket-mode.ts
11
+ const logger = getLogger("slack-socket-mode");
12
+ const GLOBAL_KEY = "__inkeep_slack_socket_mode_client__";
13
+ async function startSocketMode(appToken) {
14
+ if (globalThis[GLOBAL_KEY]) {
15
+ logger.info({}, "Socket Mode client already running (HMR reload detected), skipping");
16
+ return;
17
+ }
18
+ const { SocketModeClient } = await import("../node_modules/.pnpm/@slack_socket-mode@2.0.5/node_modules/@slack/socket-mode/dist/src/index.js").then((m) => /* @__PURE__ */ __toESM(m.default, 1));
19
+ const client = new SocketModeClient({ appToken });
20
+ setupSocketModeListeners(client);
21
+ await client.start();
22
+ globalThis[GLOBAL_KEY] = client;
23
+ logger.info({}, "Slack Socket Mode client started");
24
+ }
25
+ function registerBackgroundWork(work) {
26
+ work.catch((err) => {
27
+ logger.error({ error: err }, "Background work failed in Socket Mode");
28
+ });
29
+ }
30
+ function setupSocketModeListeners(client) {
31
+ client.on("error", (...args) => {
32
+ logger.error({ error: args[0] }, "Socket Mode client error");
33
+ });
34
+ client.on("disconnected", () => {
35
+ logger.warn({}, "Socket Mode client disconnected");
36
+ });
37
+ client.on("reconnecting", () => {
38
+ logger.info({}, "Socket Mode client reconnecting...");
39
+ });
40
+ client.on("slack_event", async (...args) => {
41
+ const { ack, body, type } = args[0];
42
+ if (type !== "events_api") return;
43
+ await ack();
44
+ await tracer.startActiveSpan(SLACK_SPAN_NAMES.WEBHOOK, async (span) => {
45
+ try {
46
+ const eventType = body.event?.type ? "event_callback" : body.type || "";
47
+ span.setAttribute(SLACK_SPAN_KEYS.EVENT_TYPE, eventType);
48
+ span.updateName(`${SLACK_SPAN_NAMES.WEBHOOK} ${eventType}`);
49
+ await dispatchSlackEvent(eventType, body, { registerBackgroundWork }, span);
50
+ } catch (error) {
51
+ span.setAttribute(SLACK_SPAN_KEYS.OUTCOME, "error");
52
+ span.setStatus({
53
+ code: SpanStatusCode.ERROR,
54
+ message: String(error)
55
+ });
56
+ if (error instanceof Error) span.recordException(error);
57
+ logger.error({ error }, "Error handling Socket Mode event");
58
+ } finally {
59
+ span.end();
60
+ await flushTraces();
61
+ }
62
+ });
63
+ });
64
+ client.on("interactive", async (...args) => {
65
+ const { ack, body } = args[0];
66
+ const eventType = body.type || "";
67
+ await ack((await tracer.startActiveSpan(`${SLACK_SPAN_NAMES.WEBHOOK} ${eventType}`, async (span) => {
68
+ try {
69
+ span.setAttribute(SLACK_SPAN_KEYS.EVENT_TYPE, eventType);
70
+ return await dispatchSlackEvent(eventType, body, { registerBackgroundWork }, span);
71
+ } catch (error) {
72
+ span.setAttribute(SLACK_SPAN_KEYS.OUTCOME, "error");
73
+ span.setStatus({
74
+ code: SpanStatusCode.ERROR,
75
+ message: String(error)
76
+ });
77
+ if (error instanceof Error) span.recordException(error);
78
+ logger.error({ error }, "Error handling Socket Mode interactive event");
79
+ return { outcome: "error" };
80
+ } finally {
81
+ span.end();
82
+ }
83
+ })).response);
84
+ await flushTraces();
85
+ });
86
+ client.on("slash_commands", async (...args) => {
87
+ const { ack, body } = args[0];
88
+ await ack();
89
+ await tracer.startActiveSpan(`${SLACK_SPAN_NAMES.WEBHOOK} slash_command`, async (span) => {
90
+ try {
91
+ span.setAttribute(SLACK_SPAN_KEYS.EVENT_TYPE, "slash_command");
92
+ if (body.command) span.setAttribute("slack.command", body.command);
93
+ if (body.team_id) span.setAttribute(SLACK_SPAN_KEYS.TEAM_ID, body.team_id);
94
+ const commandPayload = {
95
+ command: body.command || "",
96
+ text: body.text || "",
97
+ userId: body.user_id || "",
98
+ userName: body.user_name || "",
99
+ teamId: body.team_id || "",
100
+ teamDomain: body.team_domain || "",
101
+ enterpriseId: body.enterprise_id,
102
+ channelId: body.channel_id || "",
103
+ channelName: body.channel_name || "",
104
+ responseUrl: body.response_url || "",
105
+ triggerId: body.trigger_id || ""
106
+ };
107
+ const response = await handleCommand(commandPayload);
108
+ if (Object.keys(response).length > 0 && commandPayload.responseUrl) await fetch(commandPayload.responseUrl, {
109
+ method: "POST",
110
+ headers: { "Content-Type": "application/json" },
111
+ body: JSON.stringify(response)
112
+ });
113
+ } catch (error) {
114
+ span.setAttribute(SLACK_SPAN_KEYS.OUTCOME, "error");
115
+ span.setStatus({
116
+ code: SpanStatusCode.ERROR,
117
+ message: String(error)
118
+ });
119
+ if (error instanceof Error) span.recordException(error);
120
+ logger.error({ error }, "Error handling Socket Mode slash command");
121
+ } finally {
122
+ span.end();
123
+ await flushTraces();
124
+ }
125
+ });
126
+ });
127
+ }
128
+
129
+ //#endregion
130
+ export { startSocketMode };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inkeep/agents-work-apps",
3
- "version": "0.50.0",
3
+ "version": "0.50.3",
4
4
  "description": "First party integrations for Inkeep Agents",
5
5
  "type": "module",
6
6
  "license": "SEE LICENSE IN LICENSE.md",
@@ -33,13 +33,14 @@
33
33
  "jose": "^6.1.0",
34
34
  "minimatch": "^10.1.1",
35
35
  "slack-block-builder": "^2.8.0",
36
- "@inkeep/agents-core": "0.50.0"
36
+ "@inkeep/agents-core": "0.50.3"
37
37
  },
38
38
  "peerDependencies": {
39
39
  "@hono/zod-openapi": "^1.1.5",
40
40
  "zod": "^4.3.6"
41
41
  },
42
42
  "devDependencies": {
43
+ "@slack/socket-mode": "^2.0.5",
43
44
  "@types/node": "^20.11.24",
44
45
  "typescript": "^5.9.2",
45
46
  "vitest": "^3.2.4"