@inkeep/agents-work-apps 0.50.1 → 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/routes/setup.d.ts +2 -2
  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
@@ -2,8 +2,9 @@ import { getLogger } from "../../logger.js";
2
2
  import runDbClient_default from "../../db/runDbClient.js";
3
3
  import { createConnectSession } from "../services/nango.js";
4
4
  import "../services/index.js";
5
- import { OpenAPIHono, createRoute, z } from "@hono/zod-openapi";
5
+ import { OpenAPIHono, z } from "@hono/zod-openapi";
6
6
  import { createWorkAppSlackUserMapping, deleteWorkAppSlackUserMapping, findWorkAppSlackUserMapping, findWorkAppSlackUserMappingByInkeepUserId, verifySlackLinkToken } from "@inkeep/agents-core";
7
+ import { createProtectedRoute, inheritedWorkAppsAuth } from "@inkeep/agents-core/middleware";
7
8
 
8
9
  //#region src/slack/routes/users.ts
9
10
  /**
@@ -29,7 +30,7 @@ function isAuthorizedForUser(c, requestedUserId) {
29
30
  return false;
30
31
  }
31
32
  const app = new OpenAPIHono();
32
- app.openapi(createRoute({
33
+ app.openapi(createProtectedRoute({
33
34
  method: "get",
34
35
  path: "/link-status",
35
36
  summary: "Check Link Status",
@@ -40,6 +41,7 @@ app.openapi(createRoute({
40
41
  "Slack",
41
42
  "Users"
42
43
  ],
44
+ permission: inheritedWorkAppsAuth(),
43
45
  request: { query: z.object({
44
46
  slackUserId: z.string(),
45
47
  slackTeamId: z.string(),
@@ -67,7 +69,7 @@ app.openapi(createRoute({
67
69
  });
68
70
  return c.json({ linked: false });
69
71
  });
70
- app.openapi(createRoute({
72
+ app.openapi(createProtectedRoute({
71
73
  method: "post",
72
74
  path: "/link/verify-token",
73
75
  summary: "Verify Link Token",
@@ -78,6 +80,7 @@ app.openapi(createRoute({
78
80
  "Slack",
79
81
  "Users"
80
82
  ],
83
+ permission: inheritedWorkAppsAuth(),
81
84
  request: { body: { content: { "application/json": { schema: z.object({
82
85
  token: z.string().min(1),
83
86
  userId: z.string().min(1),
@@ -171,7 +174,7 @@ app.openapi(createRoute({
171
174
  return c.json({ error: "Failed to verify link. Please try again." }, 500);
172
175
  }
173
176
  });
174
- app.openapi(createRoute({
177
+ app.openapi(createProtectedRoute({
175
178
  method: "post",
176
179
  path: "/connect",
177
180
  summary: "Create Nango Connect Session",
@@ -182,6 +185,7 @@ app.openapi(createRoute({
182
185
  "Slack",
183
186
  "Users"
184
187
  ],
188
+ permission: inheritedWorkAppsAuth(),
185
189
  request: { body: { content: { "application/json": { schema: z.object({
186
190
  userId: z.string().describe("Inkeep user ID"),
187
191
  userEmail: z.string().optional().describe("User email"),
@@ -217,7 +221,7 @@ app.openapi(createRoute({
217
221
  if (!session) return c.json({ error: "Failed to create session" }, 500);
218
222
  return c.json(session);
219
223
  });
220
- app.openapi(createRoute({
224
+ app.openapi(createProtectedRoute({
221
225
  method: "post",
222
226
  path: "/disconnect",
223
227
  summary: "Disconnect User",
@@ -228,6 +232,7 @@ app.openapi(createRoute({
228
232
  "Slack",
229
233
  "Users"
230
234
  ],
235
+ permission: inheritedWorkAppsAuth(),
231
236
  request: { body: { content: { "application/json": { schema: z.object({
232
237
  userId: z.string().optional().describe("Inkeep user ID"),
233
238
  slackUserId: z.string().optional().describe("Slack user ID"),
@@ -285,7 +290,7 @@ app.openapi(createRoute({
285
290
  return c.json({ error: "Failed to disconnect" }, 500);
286
291
  }
287
292
  });
288
- app.openapi(createRoute({
293
+ app.openapi(createProtectedRoute({
289
294
  method: "get",
290
295
  path: "/status",
291
296
  summary: "Get Connection Status",
@@ -296,6 +301,7 @@ app.openapi(createRoute({
296
301
  "Slack",
297
302
  "Users"
298
303
  ],
304
+ permission: inheritedWorkAppsAuth(),
299
305
  request: { query: z.object({ userId: z.string().describe("Inkeep user ID") }) },
300
306
  responses: {
301
307
  200: {
@@ -4,8 +4,9 @@ import { clearWorkspaceConnectionCache, computeWorkspaceConnectionId, deleteWork
4
4
  import { getSlackChannels, getSlackClient, revokeSlackToken } from "../services/client.js";
5
5
  import "../services/index.js";
6
6
  import { requireChannelMemberOrAdmin, requireWorkspaceAdmin } from "../middleware/permissions.js";
7
- import { OpenAPIHono, createRoute, z } from "@hono/zod-openapi";
7
+ import { OpenAPIHono, z } from "@hono/zod-openapi";
8
8
  import { deleteAllWorkAppSlackChannelAgentConfigsByTeam, deleteAllWorkAppSlackUserMappingsByTeam, deleteWorkAppSlackChannelAgentConfig, deleteWorkAppSlackWorkspaceByNangoConnectionId, findWorkAppSlackChannelAgentConfig, listWorkAppSlackChannelAgentConfigsByTeam, listWorkAppSlackUserMappingsByTeam, upsertWorkAppSlackChannelAgentConfig } from "@inkeep/agents-core";
9
+ import { createProtectedRoute, inheritedWorkAppsAuth } from "@inkeep/agents-core/middleware";
9
10
 
10
11
  //#region src/slack/routes/workspaces.ts
11
12
  /**
@@ -39,22 +40,6 @@ function verifyTenantOwnership(c, workspaceTenantId) {
39
40
  return sessionTenantId === workspaceTenantId;
40
41
  }
41
42
  const app = new OpenAPIHono();
42
- app.use("/:teamId/settings", async (c, next) => {
43
- if (c.req.method === "PUT") return requireWorkspaceAdmin()(c, next);
44
- return next();
45
- });
46
- app.use("/:teamId", async (c, next) => {
47
- if (c.req.method === "DELETE") return requireWorkspaceAdmin()(c, next);
48
- return next();
49
- });
50
- app.use("/:teamId/channels/:channelId/settings", async (c, next) => {
51
- if (c.req.method === "PUT" || c.req.method === "DELETE") return requireChannelMemberOrAdmin()(c, next);
52
- return next();
53
- });
54
- app.use("/:teamId/test-message", async (c, next) => {
55
- if (c.req.method === "POST") return requireWorkspaceAdmin()(c, next);
56
- return next();
57
- });
58
43
  const ChannelAgentConfigSchema = z.object({
59
44
  projectId: z.string(),
60
45
  agentId: z.string(),
@@ -62,7 +47,7 @@ const ChannelAgentConfigSchema = z.object({
62
47
  projectName: z.string().optional()
63
48
  });
64
49
  const WorkspaceSettingsSchema = z.object({ defaultAgent: ChannelAgentConfigSchema.optional() });
65
- app.openapi(createRoute({
50
+ app.openapi(createProtectedRoute({
66
51
  method: "get",
67
52
  path: "/",
68
53
  summary: "List Workspaces",
@@ -73,6 +58,7 @@ app.openapi(createRoute({
73
58
  "Slack",
74
59
  "Workspaces"
75
60
  ],
61
+ permission: inheritedWorkAppsAuth(),
76
62
  responses: { 200: {
77
63
  description: "List of workspaces",
78
64
  content: { "application/json": { schema: z.object({ workspaces: z.array(z.object({
@@ -111,7 +97,7 @@ app.openapi(createRoute({
111
97
  return c.json({ workspaces: [] });
112
98
  }
113
99
  });
114
- app.openapi(createRoute({
100
+ app.openapi(createProtectedRoute({
115
101
  method: "get",
116
102
  path: "/{teamId}",
117
103
  summary: "Get Workspace",
@@ -122,6 +108,7 @@ app.openapi(createRoute({
122
108
  "Slack",
123
109
  "Workspaces"
124
110
  ],
111
+ permission: inheritedWorkAppsAuth(),
125
112
  request: { params: z.object({ teamId: z.string() }) },
126
113
  responses: {
127
114
  200: {
@@ -155,7 +142,7 @@ app.openapi(createRoute({
155
142
  defaultAgent
156
143
  });
157
144
  });
158
- app.openapi(createRoute({
145
+ app.openapi(createProtectedRoute({
159
146
  method: "get",
160
147
  path: "/{teamId}/settings",
161
148
  summary: "Get Workspace Settings",
@@ -166,6 +153,7 @@ app.openapi(createRoute({
166
153
  "Slack",
167
154
  "Workspaces"
168
155
  ],
156
+ permission: inheritedWorkAppsAuth(),
169
157
  request: { params: z.object({ teamId: z.string() }) },
170
158
  responses: { 200: {
171
159
  description: "Workspace settings",
@@ -188,7 +176,7 @@ app.openapi(createRoute({
188
176
  };
189
177
  return c.json({ defaultAgent });
190
178
  });
191
- app.openapi(createRoute({
179
+ app.openapi(createProtectedRoute({
192
180
  method: "put",
193
181
  path: "/{teamId}/settings",
194
182
  summary: "Update Workspace Settings",
@@ -199,6 +187,7 @@ app.openapi(createRoute({
199
187
  "Slack",
200
188
  "Workspaces"
201
189
  ],
190
+ permission: requireWorkspaceAdmin(),
202
191
  request: {
203
192
  params: z.object({ teamId: z.string() }),
204
193
  body: { content: { "application/json": { schema: WorkspaceSettingsSchema } } }
@@ -232,7 +221,7 @@ app.openapi(createRoute({
232
221
  }
233
222
  return c.json({ success: true });
234
223
  });
235
- app.openapi(createRoute({
224
+ app.openapi(createProtectedRoute({
236
225
  method: "delete",
237
226
  path: "/{teamId}",
238
227
  summary: "Uninstall Workspace",
@@ -243,6 +232,7 @@ app.openapi(createRoute({
243
232
  "Slack",
244
233
  "Workspaces"
245
234
  ],
235
+ permission: requireWorkspaceAdmin(),
246
236
  request: { params: z.object({ teamId: z.string() }) },
247
237
  responses: {
248
238
  200: {
@@ -301,7 +291,7 @@ app.openapi(createRoute({
301
291
  return c.json({ error: "Failed to uninstall workspace" }, 500);
302
292
  }
303
293
  });
304
- app.openapi(createRoute({
294
+ app.openapi(createProtectedRoute({
305
295
  method: "get",
306
296
  path: "/{teamId}/channels",
307
297
  summary: "List Channels",
@@ -312,6 +302,7 @@ app.openapi(createRoute({
312
302
  "Slack",
313
303
  "Channels"
314
304
  ],
305
+ permission: inheritedWorkAppsAuth(),
315
306
  request: {
316
307
  params: z.object({ teamId: z.string() }),
317
308
  query: z.object({
@@ -385,7 +376,7 @@ app.openapi(createRoute({
385
376
  return c.json({ error: "Failed to list channels" }, 500);
386
377
  }
387
378
  });
388
- app.openapi(createRoute({
379
+ app.openapi(createProtectedRoute({
389
380
  method: "get",
390
381
  path: "/{teamId}/channels/{channelId}/settings",
391
382
  summary: "Get Channel Settings",
@@ -396,6 +387,7 @@ app.openapi(createRoute({
396
387
  "Slack",
397
388
  "Channels"
398
389
  ],
390
+ permission: inheritedWorkAppsAuth(),
399
391
  request: { params: z.object({
400
392
  teamId: z.string(),
401
393
  channelId: z.string()
@@ -428,7 +420,7 @@ app.openapi(createRoute({
428
420
  } : void 0
429
421
  });
430
422
  });
431
- app.openapi(createRoute({
423
+ app.openapi(createProtectedRoute({
432
424
  method: "put",
433
425
  path: "/{teamId}/channels/{channelId}/settings",
434
426
  summary: "Set Channel Default Agent",
@@ -439,6 +431,7 @@ app.openapi(createRoute({
439
431
  "Slack",
440
432
  "Channels"
441
433
  ],
434
+ permission: requireChannelMemberOrAdmin(),
442
435
  request: {
443
436
  params: z.object({
444
437
  teamId: z.string(),
@@ -490,11 +483,7 @@ app.openapi(createRoute({
490
483
  configId: config.id
491
484
  });
492
485
  });
493
- app.use("/:teamId/channels/bulk", async (c, next) => {
494
- if (c.req.method === "PUT" || c.req.method === "DELETE") return requireWorkspaceAdmin()(c, next);
495
- return next();
496
- });
497
- app.openapi(createRoute({
486
+ app.openapi(createProtectedRoute({
498
487
  method: "put",
499
488
  path: "/{teamId}/channels/bulk",
500
489
  summary: "Bulk Set Channel Agents",
@@ -505,6 +494,7 @@ app.openapi(createRoute({
505
494
  "Slack",
506
495
  "Channels"
507
496
  ],
497
+ permission: requireWorkspaceAdmin(),
508
498
  request: {
509
499
  params: z.object({ teamId: z.string() }),
510
500
  body: { content: { "application/json": { schema: z.object({
@@ -591,7 +581,7 @@ app.openapi(createRoute({
591
581
  errors: errors.length > 0 ? errors : void 0
592
582
  });
593
583
  });
594
- app.openapi(createRoute({
584
+ app.openapi(createProtectedRoute({
595
585
  method: "delete",
596
586
  path: "/{teamId}/channels/bulk",
597
587
  summary: "Bulk Remove Channel Configs",
@@ -602,6 +592,7 @@ app.openapi(createRoute({
602
592
  "Slack",
603
593
  "Channels"
604
594
  ],
595
+ permission: requireWorkspaceAdmin(),
605
596
  request: {
606
597
  params: z.object({ teamId: z.string() }),
607
598
  body: { content: { "application/json": { schema: z.object({ channelIds: z.array(z.string()).min(1) }) } } }
@@ -638,7 +629,7 @@ app.openapi(createRoute({
638
629
  removed
639
630
  });
640
631
  });
641
- app.openapi(createRoute({
632
+ app.openapi(createProtectedRoute({
642
633
  method: "delete",
643
634
  path: "/{teamId}/channels/{channelId}/settings",
644
635
  summary: "Remove Channel Config",
@@ -649,6 +640,7 @@ app.openapi(createRoute({
649
640
  "Slack",
650
641
  "Channels"
651
642
  ],
643
+ permission: requireChannelMemberOrAdmin(),
652
644
  request: { params: z.object({
653
645
  teamId: z.string(),
654
646
  channelId: z.string()
@@ -673,7 +665,7 @@ app.openapi(createRoute({
673
665
  }, "Removed channel agent config");
674
666
  return c.json({ success: deleted });
675
667
  });
676
- app.openapi(createRoute({
668
+ app.openapi(createProtectedRoute({
677
669
  method: "get",
678
670
  path: "/{teamId}/users",
679
671
  summary: "List Linked Users",
@@ -684,6 +676,7 @@ app.openapi(createRoute({
684
676
  "Slack",
685
677
  "Users"
686
678
  ],
679
+ permission: inheritedWorkAppsAuth(),
687
680
  request: { params: z.object({ teamId: z.string() }) },
688
681
  responses: { 200: {
689
682
  description: "List of linked users",
@@ -723,7 +716,7 @@ app.openapi(createRoute({
723
716
  lastUsedAt: link.lastUsedAt || void 0
724
717
  })) });
725
718
  });
726
- app.openapi(createRoute({
719
+ app.openapi(createProtectedRoute({
727
720
  method: "get",
728
721
  path: "/{teamId}/health",
729
722
  summary: "Check Workspace Health",
@@ -734,6 +727,7 @@ app.openapi(createRoute({
734
727
  "Slack",
735
728
  "Workspaces"
736
729
  ],
730
+ permission: inheritedWorkAppsAuth(),
737
731
  request: { params: z.object({ teamId: z.string() }) },
738
732
  responses: {
739
733
  200: {
@@ -819,7 +813,7 @@ app.openapi(createRoute({
819
813
  });
820
814
  }
821
815
  });
822
- app.openapi(createRoute({
816
+ app.openapi(createProtectedRoute({
823
817
  method: "post",
824
818
  path: "/{teamId}/test-message",
825
819
  summary: "Send Test Message",
@@ -830,6 +824,7 @@ app.openapi(createRoute({
830
824
  "Slack",
831
825
  "Workspaces"
832
826
  ],
827
+ permission: requireWorkspaceAdmin(),
833
828
  request: {
834
829
  params: z.object({ teamId: z.string() }),
835
830
  body: { content: { "application/json": { schema: z.object({
@@ -859,7 +854,7 @@ app.openapi(createRoute({
859
854
  }, 404);
860
855
  try {
861
856
  const slackClient = getSlackClient(workspace.botToken);
862
- const testMessage = message || "*Test message from Inkeep*\n\nYour Slack integration is working correctly!";
857
+ const testMessage = message || "*Test message from Inkeep*\n\nYour Slack integration is working correctly.";
863
858
  const result = await slackClient.chat.postMessage({
864
859
  channel: channelId,
865
860
  text: testMessage,
@@ -3,7 +3,7 @@ import { Blocks, Elements, Md, Message } from "slack-block-builder";
3
3
 
4
4
  //#region src/slack/services/blocks/index.ts
5
5
  function createErrorMessage(message) {
6
- return Message().blocks(Blocks.Section().text(`❌ ${message}`)).buildToObject();
6
+ return Message().blocks(Blocks.Section().text(message)).buildToObject();
7
7
  }
8
8
  function createContextBlock(params) {
9
9
  const { agentName, isPrivate = false } = params;
@@ -40,7 +40,7 @@ function buildConversationResponseBlocks(params) {
40
40
  type: "context",
41
41
  elements: [{
42
42
  type: "mrkdwn",
43
- text: `💬 *You:* ${userMessage.length > 200 ? `${userMessage.slice(0, 200)}...` : userMessage}`
43
+ text: `*You:* ${userMessage.length > 200 ? `${userMessage.slice(0, 200)}...` : userMessage}`
44
44
  }]
45
45
  },
46
46
  { type: "divider" },
@@ -69,27 +69,23 @@ function createUpdatedHelpMessage() {
69
69
  return Message().blocks(Blocks.Section().text(`${Md.bold(SlackStrings.help.title)}`), Blocks.Section().text(SlackStrings.help.publicSection), Blocks.Divider(), Blocks.Section().text(SlackStrings.help.privateSection), Blocks.Divider(), Blocks.Section().text(SlackStrings.help.otherCommands), Blocks.Divider(), Blocks.Section().text(SlackStrings.help.docsLink)).buildToObject();
70
70
  }
71
71
  function createAlreadyLinkedMessage(email, linkedAt, dashboardUrl) {
72
- return Message().blocks(Blocks.Section().text(Md.bold("Already Linked!") + "\n\nYour Slack account is already connected to Inkeep.\n\n" + Md.bold("Inkeep Account:") + ` ${email}\n` + Md.bold("Linked:") + ` ${new Date(linkedAt).toLocaleDateString()}\n\nTo switch accounts, first run \`/inkeep unlink\``), Blocks.Actions().elements(Elements.Button().text(SlackStrings.buttons.openDashboard).url(dashboardUrl).actionId("open_dashboard"))).buildToObject();
72
+ return Message().blocks(Blocks.Section().text(Md.bold("Already linked") + "\n\nYour Slack account is connected to Inkeep.\n\n" + Md.bold("Account:") + ` ${email}\n` + Md.bold("Linked:") + ` ${new Date(linkedAt).toLocaleDateString()}\n\nTo switch accounts, first run \`/inkeep unlink\``), Blocks.Actions().elements(Elements.Button().text(SlackStrings.buttons.openDashboard).url(dashboardUrl).actionId("open_dashboard"))).buildToObject();
73
73
  }
74
74
  function createUnlinkSuccessMessage() {
75
- return Message().blocks(Blocks.Section().text(Md.bold("Account Unlinked") + "\n\nYour Slack account has been disconnected from Inkeep.\n\nTo use Inkeep agents again, run `/inkeep link` to connect a new account.")).buildToObject();
75
+ return Message().blocks(Blocks.Section().text(Md.bold("Account unlinked") + "\n\nYour Slack account has been disconnected from Inkeep.\n\nRun `/inkeep link` to connect a new account.")).buildToObject();
76
76
  }
77
77
  function createNotLinkedMessage() {
78
- return Message().blocks(Blocks.Section().text(Md.bold("Not Linked") + "\n\nYour Slack account is not connected to Inkeep.\n\nRun `/inkeep link` to connect your account.")).buildToObject();
78
+ return Message().blocks(Blocks.Section().text(Md.bold("Not linked") + "\n\nYour Slack account is not connected to Inkeep. Run `/inkeep link` to connect.")).buildToObject();
79
79
  }
80
80
  function createStatusMessage(email, linkedAt, dashboardUrl, agentConfigs) {
81
81
  const { effective } = agentConfigs;
82
82
  let agentLine;
83
83
  if (effective) agentLine = `${Md.bold("Agent:")} ${effective.agentName || effective.agentId}`;
84
84
  else agentLine = `${Md.bold("Agent:")} None configured\n${Md.italic("Ask your admin to set up an agent in the dashboard.")}`;
85
- return Message().blocks(Blocks.Section().text(Md.bold("Connected to Inkeep") + `\n\n${Md.bold("Account:")} ${email}\n${Md.bold("Linked:")} ${new Date(linkedAt).toLocaleDateString()}\n` + agentLine), Blocks.Actions().elements(Elements.Button().text(SlackStrings.buttons.openDashboard).url(dashboardUrl).actionId("open_dashboard"))).buildToObject();
85
+ return Message().blocks(Blocks.Section().text(Md.bold("Connected to Inkeep") + `\n\n${Md.bold("Account:")} ${email}\n${Md.bold("Linked:")} ${new Date(linkedAt).toLocaleDateString()}\n` + agentLine), Blocks.Actions().elements(Elements.Button().text(SlackStrings.buttons.openDashboard).url(dashboardUrl).actionId("open_dashboard"))).buildToObject();
86
86
  }
87
87
  function createJwtLinkMessage(linkUrl, expiresInMinutes) {
88
- return Message().blocks(Blocks.Section().text(`${Md.bold("🔗 Link your Inkeep account")}\n\nConnect your Slack and Inkeep accounts to unlock AI-powered assistance:`), Blocks.Section().text(`${Md.bold("What you can do after linking:")}\n• Ask questions with \`/inkeep [question]\` or \`@Inkeep\`
89
- • Get personalized responses from AI agents
90
- • Set your own default agent preferences`), Blocks.Section().text(`${Md.bold("How to link:")}\n1. Click the button below
91
- 2. Sign in to Inkeep (or create an account)
92
- 3. Done! Come back here and start asking questions`), Blocks.Actions().elements(Elements.Button().text("🔗 Link Account").url(linkUrl).actionId("link_account").primary()), Blocks.Context().elements(`This link expires in ${expiresInMinutes} minutes`)).buildToObject();
88
+ return Message().blocks(Blocks.Section().text(`${Md.bold("Link your Inkeep account")}\n\nConnect your Slack and Inkeep accounts to use Inkeep agents.`), Blocks.Actions().elements(Elements.Button().text("Link Account").url(linkUrl).actionId("link_account").primary()), Blocks.Context().elements(`This link expires in ${expiresInMinutes} minutes.`)).buildToObject();
93
89
  }
94
90
 
95
91
  //#endregion
@@ -8,7 +8,7 @@ import { createAlreadyLinkedMessage, createContextBlock, createErrorMessage, cre
8
8
  import { getSlackClient } from "../client.js";
9
9
  import { fetchAgentsForProject, fetchProjectsForTenant, getChannelAgentConfig, sendResponseUrlMessage } from "../events/utils.js";
10
10
  import { buildAgentSelectorModal } from "../modals.js";
11
- import { deleteWorkAppSlackUserMapping, findWorkAppSlackUserMapping, findWorkAppSlackUserMappingBySlackUser, flushTraces, getWaitUntil, signSlackLinkToken, signSlackUserToken } from "@inkeep/agents-core";
11
+ import { deleteWorkAppSlackUserMapping, findWorkAppSlackUserMapping, findWorkAppSlackUserMappingBySlackUser, flushTraces, getInProcessFetch, getWaitUntil, signSlackLinkToken, signSlackUserToken } from "@inkeep/agents-core";
12
12
 
13
13
  //#region src/slack/services/commands/index.ts
14
14
  const DEFAULT_CLIENT_ID = "work-apps-slack";
@@ -253,7 +253,7 @@ async function executeAgentInBackground(payload, existingLink, targetAgent, ques
253
253
  const timeout = setTimeout(() => controller.abort(), 3e4);
254
254
  let response;
255
255
  try {
256
- response = await fetch(`${apiBaseUrl}/run/api/chat`, {
256
+ response = await getInProcessFetch()(`${apiBaseUrl}/run/api/chat`, {
257
257
  method: "POST",
258
258
  headers: {
259
259
  "Content-Type": "application/json",
@@ -0,0 +1,23 @@
1
+ import { DefaultAgentConfig } from "./nango.js";
2
+
3
+ //#region src/slack/services/dev-config.d.ts
4
+ interface SlackDevConfig {
5
+ devId: string;
6
+ appId: string;
7
+ clientId: string;
8
+ clientSecret: string;
9
+ signingSecret: string;
10
+ appToken: string;
11
+ botToken: string;
12
+ teamId: string;
13
+ teamName: string;
14
+ configRefreshToken?: string;
15
+ metadata?: Record<string, string>;
16
+ }
17
+ declare function isSlackDevMode(): boolean;
18
+ declare function loadSlackDevConfig(): SlackDevConfig | null;
19
+ declare function getDevDefaultAgent(config: SlackDevConfig | null): DefaultAgentConfig | null;
20
+ declare function resetDevConfigCache(): void;
21
+ declare function saveSlackDevConfig(config: SlackDevConfig): boolean;
22
+ //#endregion
23
+ export { SlackDevConfig, getDevDefaultAgent, isSlackDevMode, loadSlackDevConfig, resetDevConfigCache, saveSlackDevConfig };
@@ -0,0 +1,91 @@
1
+ import { env } from "../../env.js";
2
+ import { getLogger } from "../../logger.js";
3
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
4
+ import { dirname, join, parse } from "node:path";
5
+
6
+ //#region src/slack/services/dev-config.ts
7
+ const logger = getLogger("slack-dev-config");
8
+ const DEV_CONFIG_FILENAME = ".slack-dev.json";
9
+ const CACHE_TTL_MS = 5e3;
10
+ let cachedConfig = null;
11
+ let cacheExpiresAt = 0;
12
+ let resolvedConfigPath;
13
+ function findDevConfigPath() {
14
+ let dir = process.cwd();
15
+ while (true) {
16
+ const candidate = join(dir, DEV_CONFIG_FILENAME);
17
+ if (existsSync(candidate)) return candidate;
18
+ const parent = dirname(dir);
19
+ if (parent === dir || parse(dir).root === dir) break;
20
+ dir = parent;
21
+ }
22
+ return null;
23
+ }
24
+ function getDevConfigPath() {
25
+ if (resolvedConfigPath !== void 0) return resolvedConfigPath;
26
+ resolvedConfigPath = findDevConfigPath();
27
+ return resolvedConfigPath;
28
+ }
29
+ let devModeChecked = false;
30
+ let devModeResult = false;
31
+ function isSlackDevMode() {
32
+ if (devModeChecked) return devModeResult;
33
+ devModeResult = env.ENVIRONMENT === "development" && getDevConfigPath() !== null;
34
+ devModeChecked = true;
35
+ return devModeResult;
36
+ }
37
+ function loadSlackDevConfig() {
38
+ if (cachedConfig && Date.now() < cacheExpiresAt) return cachedConfig;
39
+ const configPath = getDevConfigPath();
40
+ if (!configPath) return null;
41
+ try {
42
+ const raw = readFileSync(configPath, "utf-8");
43
+ cachedConfig = JSON.parse(raw);
44
+ cacheExpiresAt = Date.now() + CACHE_TTL_MS;
45
+ return cachedConfig;
46
+ } catch (error) {
47
+ logger.error({
48
+ error,
49
+ configPath
50
+ }, "Failed to read .slack-dev.json");
51
+ return null;
52
+ }
53
+ }
54
+ function getDevDefaultAgent(config) {
55
+ if (!config?.metadata?.default_agent) return null;
56
+ try {
57
+ return JSON.parse(config.metadata.default_agent);
58
+ } catch (error) {
59
+ logger.warn({
60
+ error,
61
+ rawValue: config.metadata.default_agent
62
+ }, "Failed to parse default_agent metadata as JSON - check .slack-dev.json format");
63
+ return null;
64
+ }
65
+ }
66
+ function resetDevConfigCache() {
67
+ cachedConfig = null;
68
+ cacheExpiresAt = 0;
69
+ resolvedConfigPath = void 0;
70
+ devModeChecked = false;
71
+ devModeResult = false;
72
+ }
73
+ function saveSlackDevConfig(config) {
74
+ const configPath = getDevConfigPath();
75
+ if (!configPath) return false;
76
+ try {
77
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
78
+ cachedConfig = config;
79
+ cacheExpiresAt = Date.now() + CACHE_TTL_MS;
80
+ return true;
81
+ } catch (error) {
82
+ logger.error({
83
+ error,
84
+ configPath
85
+ }, "Failed to write .slack-dev.json");
86
+ return false;
87
+ }
88
+ }
89
+
90
+ //#endregion
91
+ export { getDevDefaultAgent, isSlackDevMode, loadSlackDevConfig, resetDevConfigCache, saveSlackDevConfig };
@@ -5,7 +5,6 @@ import { SlackStrings } from "../../i18n/strings.js";
5
5
  import { getSlackChannelInfo, getSlackClient, getSlackUserInfo, postMessageInThread } from "../client.js";
6
6
  import { checkIfBotThread, classifyError, findCachedUserMapping, formatChannelContext, generateSlackConversationId, getThreadContext, getUserFriendlyErrorMessage, resolveChannelAgentConfig, timedOp } from "./utils.js";
7
7
  import { SLACK_SPAN_KEYS, SLACK_SPAN_NAMES, setSpanWithError, tracer } from "../../tracer.js";
8
- import { getBotTokenForTeam } from "../workspace-tokens.js";
9
8
  import { streamAgentResponse } from "./streaming.js";
10
9
  import { signSlackUserToken } from "@inkeep/agents-core";
11
10
 
@@ -60,19 +59,18 @@ async function handleAppMention(params) {
60
59
  label: "workspace connection lookup",
61
60
  context: { teamId }
62
61
  });
63
- const botToken = workspaceConnection?.botToken || getBotTokenForTeam(teamId) || env.SLACK_BOT_TOKEN;
64
- if (!botToken) {
62
+ if (!workspaceConnection?.botToken) {
65
63
  logger.error({ teamId }, "No bot token available — cannot respond to @mention");
66
64
  span.end();
67
65
  return;
68
66
  }
69
- const tenantId = workspaceConnection?.tenantId;
67
+ const { botToken, tenantId } = workspaceConnection;
70
68
  if (!tenantId) {
71
69
  logger.error({ teamId }, "Workspace connection has no tenantId — workspace may need reinstall");
72
70
  await getSlackClient(botToken).chat.postEphemeral({
73
71
  channel,
74
72
  user: slackUserId,
75
- text: "⚠️ This workspace is not properly configured. Please reinstall the Slack app from the Inkeep dashboard."
73
+ text: "This workspace is not properly configured. Please reinstall the Slack app from the Inkeep dashboard."
76
74
  }).catch((e) => logger.warn({
77
75
  error: e,
78
76
  channel
@@ -104,7 +102,7 @@ async function handleAppMention(params) {
104
102
  channel,
105
103
  user: slackUserId,
106
104
  thread_ts: isInThread ? threadTs : void 0,
107
- text: `⚙️ No agents configured for this workspace.\n\n👉 *<${dashboardUrl}|Set up agents in the dashboard>*`
105
+ text: `No agents configured for this workspace. *<${dashboardUrl}|Set up agents in the dashboard>*`
108
106
  });
109
107
  span.end();
110
108
  return;
@@ -122,11 +120,7 @@ async function handleAppMention(params) {
122
120
  channel,
123
121
  user: slackUserId,
124
122
  thread_ts: isInThread ? threadTs : void 0,
125
- text: `🔗 *Link your account to use @Inkeep*
126
-
127
- Run \`/inkeep link\` to connect your Slack and Inkeep accounts.
128
-
129
- This workspace uses: *${agentDisplayName}*`
123
+ text: "*Link your account to use @Inkeep*\n\nRun `/inkeep link` to connect your Slack and Inkeep accounts."
130
124
  });
131
125
  span.end();
132
126
  return;
@@ -162,12 +156,7 @@ This workspace uses: *${agentDisplayName}*`
162
156
  channel,
163
157
  user: slackUserId,
164
158
  thread_ts: threadTs,
165
- text: `💬 *Continue the conversation*
166
-
167
- Just type your follow-up — no need to mention me in this thread.
168
- Or use \`@Inkeep <prompt>\` to run a new prompt.
169
-
170
- _Using: ${agentDisplayName}_`
159
+ text: "*Continue the conversation*\n\nType your follow-up directly in this thread — no need to mention me.\nOr use `@Inkeep <prompt>` to start a new prompt."
171
160
  });
172
161
  span.end();
173
162
  return;
@@ -6,7 +6,7 @@ import { buildConversationResponseBlocks } from "../blocks/index.js";
6
6
  import { getSlackClient } from "../client.js";
7
7
  import { classifyError, findCachedUserMapping, generateSlackConversationId, getThreadContext, getUserFriendlyErrorMessage, markdownToMrkdwn, sendResponseUrlMessage } from "./utils.js";
8
8
  import { SLACK_SPAN_KEYS, SLACK_SPAN_NAMES, setSpanWithError, tracer } from "../../tracer.js";
9
- import { signSlackUserToken } from "@inkeep/agents-core";
9
+ import { getInProcessFetch, signSlackUserToken } from "@inkeep/agents-core";
10
10
 
11
11
  //#region src/slack/services/events/modal-submission.ts
12
12
  /**
@@ -29,7 +29,7 @@ async function handleModalSubmission(view) {
29
29
  span.setAttribute(SLACK_SPAN_KEYS.USER_ID, metadata.slackUserId || "");
30
30
  span.setAttribute(SLACK_SPAN_KEYS.TENANT_ID, metadata.tenantId || "");
31
31
  const values = view.state?.values || {};
32
- const agentSelectValue = values.agent_select_block?.agent_select;
32
+ const agentSelectValue = Object.values(values).map((block) => block.agent_select).find(Boolean);
33
33
  const questionValue = values.question_block?.question_input;
34
34
  const includeContextValue = values.context_block?.include_context_checkbox;
35
35
  const question = questionValue?.value || "";
@@ -90,7 +90,7 @@ async function handleModalSubmission(view) {
90
90
  await slackClient.chat.postEphemeral({
91
91
  channel: metadata.channel,
92
92
  user: metadata.slackUserId,
93
- text: "🔗 You need to link your account first. Use `/inkeep link` to get started."
93
+ text: "Link your account first. Run `/inkeep link` to connect."
94
94
  });
95
95
  span.end();
96
96
  return;
@@ -215,7 +215,7 @@ async function handleFollowUpSubmission(view) {
215
215
  await slackClient.chat.postEphemeral({
216
216
  channel,
217
217
  user: slackUserId,
218
- text: "🔗 You need to link your account first. Use `/inkeep link` to get started."
218
+ text: "Link your account first. Run `/inkeep link` to connect."
219
219
  });
220
220
  span.end();
221
221
  return;
@@ -305,7 +305,7 @@ async function callAgentApi(params) {
305
305
  const timeout = setTimeout(() => controller.abort(), 3e4);
306
306
  let response;
307
307
  try {
308
- response = await fetch(`${apiBaseUrl}/run/api/chat`, {
308
+ response = await getInProcessFetch()(`${apiBaseUrl}/run/api/chat`, {
309
309
  method: "POST",
310
310
  headers: {
311
311
  "Content-Type": "application/json",