@inkeep/agents-work-apps 0.50.6 → 0.52.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/github/mcp/auth.d.ts +2 -2
  2. package/dist/github/mcp/index.d.ts +2 -2
  3. package/dist/github/mcp/index.js +60 -1
  4. package/dist/github/mcp/schemas.d.ts +1 -1
  5. package/dist/github/mcp/utils.d.ts +10 -1
  6. package/dist/github/mcp/utils.js +87 -1
  7. package/dist/github/routes/setup.d.ts +2 -2
  8. package/dist/github/routes/tokenExchange.d.ts +2 -2
  9. package/dist/github/routes/webhooks.d.ts +2 -2
  10. package/dist/slack/dispatcher.js +24 -1
  11. package/dist/slack/i18n/strings.d.ts +1 -0
  12. package/dist/slack/i18n/strings.js +1 -0
  13. package/dist/slack/routes/events.js +2 -2
  14. package/dist/slack/routes/oauth.js +3 -4
  15. package/dist/slack/routes/users.js +13 -11
  16. package/dist/slack/routes/workspaces.js +85 -1
  17. package/dist/slack/services/blocks/index.d.ts +81 -1
  18. package/dist/slack/services/blocks/index.js +238 -19
  19. package/dist/slack/services/commands/index.d.ts +1 -1
  20. package/dist/slack/services/commands/index.js +98 -4
  21. package/dist/slack/services/events/app-mention.js +2 -2
  22. package/dist/slack/services/events/block-actions.d.ts +12 -1
  23. package/dist/slack/services/events/block-actions.js +126 -2
  24. package/dist/slack/services/events/index.d.ts +2 -2
  25. package/dist/slack/services/events/index.js +2 -2
  26. package/dist/slack/services/events/streaming.d.ts +1 -1
  27. package/dist/slack/services/events/streaming.js +203 -7
  28. package/dist/slack/services/events/utils.d.ts +2 -2
  29. package/dist/slack/services/events/utils.js +5 -2
  30. package/dist/slack/services/index.d.ts +3 -3
  31. package/dist/slack/services/index.js +3 -3
  32. package/dist/slack/services/nango.js +1 -23
  33. package/dist/slack/tracer.d.ts +1 -0
  34. package/dist/slack/tracer.js +2 -1
  35. package/package.json +2 -2
@@ -1,9 +1,9 @@
1
1
  import { env } from "../../../env.js";
2
2
  import { getLogger } from "../../../logger.js";
3
- import { createContextBlock } from "../blocks/index.js";
3
+ import { buildCitationsBlock, buildDataArtifactBlocks, buildDataComponentBlocks, buildSummaryBreadcrumbBlock, buildToolApprovalBlocks, buildToolApprovalExpiredBlocks, buildToolOutputErrorBlock, createContextBlock } from "../blocks/index.js";
4
4
  import { SlackErrorType, classifyError, extractApiErrorMessage, 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
+ import { getInProcessFetch, retryWithBackoff } from "@inkeep/agents-core";
7
7
 
8
8
  //#region src/slack/services/events/streaming.ts
9
9
  /**
@@ -50,6 +50,10 @@ async function streamAgentResponse(params) {
50
50
  projectId
51
51
  }, "Starting streaming agent response");
52
52
  const abortController = new AbortController();
53
+ const abortPromise = new Promise((_, reject) => {
54
+ abortController.signal.addEventListener("abort", () => reject(/* @__PURE__ */ new Error("Stream timeout")), { once: true });
55
+ });
56
+ let reader;
53
57
  const timeoutId = setTimeout(() => {
54
58
  logger.warn({
55
59
  channel,
@@ -177,7 +181,7 @@ async function streamAgentResponse(params) {
177
181
  errorMessage
178
182
  };
179
183
  }
180
- const reader = response.body.getReader();
184
+ reader = response.body.getReader();
181
185
  const decoder = new TextDecoder();
182
186
  let buffer = "";
183
187
  let fullText = "";
@@ -187,10 +191,18 @@ async function streamAgentResponse(params) {
187
191
  recipient_user_id: slackUserId,
188
192
  thread_ts: threadTs
189
193
  });
194
+ const pendingApprovalMessages = [];
195
+ const toolCallIdToName = /* @__PURE__ */ new Map();
196
+ const toolErrors = [];
197
+ const citations = [];
198
+ const summaryLabels = [];
199
+ let richMessageCount = 0;
200
+ let richMessageCapWarned = false;
201
+ const MAX_RICH_MESSAGES = 20;
190
202
  try {
191
203
  let agentCompleted = false;
192
204
  while (true) {
193
- const { done, value } = await reader.read();
205
+ const { done, value } = await Promise.race([reader.read(), abortPromise]);
194
206
  if (done) break;
195
207
  buffer += decoder.decode(value, { stream: true });
196
208
  const lines = buffer.split("\n");
@@ -208,6 +220,149 @@ async function streamAgentResponse(params) {
208
220
  }
209
221
  continue;
210
222
  }
223
+ if (data.type === "tool-approval-request" && conversationId) {
224
+ const toolName = data.toolName || "Tool";
225
+ const toolCallId = data.toolCallId;
226
+ const input = data.input;
227
+ const buttonValue = {
228
+ toolCallId,
229
+ conversationId,
230
+ projectId,
231
+ agentId,
232
+ slackUserId,
233
+ channel,
234
+ threadTs,
235
+ toolName
236
+ };
237
+ const approvalPost = await slackClient.chat.postMessage({
238
+ channel,
239
+ thread_ts: threadTs,
240
+ text: `Tool approval required: \`${toolName}\``,
241
+ blocks: buildToolApprovalBlocks({
242
+ toolName,
243
+ input,
244
+ buttonValue: JSON.stringify(buttonValue)
245
+ })
246
+ }).catch((e) => {
247
+ logger.warn({
248
+ error: e,
249
+ toolCallId
250
+ }, "Failed to post tool approval message");
251
+ return null;
252
+ });
253
+ if (approvalPost?.ts) pendingApprovalMessages.push({
254
+ messageTs: approvalPost.ts,
255
+ toolName,
256
+ toolCallId
257
+ });
258
+ clearTimeout(timeoutId);
259
+ continue;
260
+ }
261
+ if (data.type === "tool-input-available" && data.toolCallId && data.toolName) {
262
+ toolCallIdToName.set(String(data.toolCallId), String(data.toolName));
263
+ continue;
264
+ }
265
+ if (data.type === "tool-output-denied" && data.toolCallId) {
266
+ const idx = pendingApprovalMessages.findIndex((m) => m.toolCallId === data.toolCallId);
267
+ if (idx !== -1) pendingApprovalMessages.splice(idx, 1);
268
+ continue;
269
+ }
270
+ if (data.type === "tool-output-error" && data.toolCallId) {
271
+ const toolName = toolCallIdToName.get(String(data.toolCallId)) || "Tool";
272
+ toolErrors.push({
273
+ toolName,
274
+ errorText: String(data.errorText || "Unknown error")
275
+ });
276
+ continue;
277
+ }
278
+ if (data.type === "data-component" && data.data && typeof data.data === "object") {
279
+ if (richMessageCount < MAX_RICH_MESSAGES) {
280
+ const { blocks, overflowJson, componentType } = buildDataComponentBlocks({
281
+ id: String(data.id || ""),
282
+ data: data.data
283
+ });
284
+ if (overflowJson) {
285
+ const label = componentType || "data-component";
286
+ await retryWithBackoff(() => slackClient.files.uploadV2({
287
+ channel_id: channel,
288
+ thread_ts: threadTs,
289
+ filename: `${label}.json`,
290
+ content: overflowJson,
291
+ initial_comment: `📊 ${label}`
292
+ }), { label: "slack-file-upload" }).catch((e) => logger.warn({
293
+ error: e,
294
+ channel,
295
+ threadTs,
296
+ agentId,
297
+ componentType: label
298
+ }, "Failed to upload data component file"));
299
+ } else await slackClient.chat.postMessage({
300
+ channel,
301
+ thread_ts: threadTs,
302
+ text: "📊 Data component",
303
+ blocks
304
+ }).catch((e) => logger.warn({ error: e }, "Failed to post data component"));
305
+ richMessageCount++;
306
+ } else if (!richMessageCapWarned) {
307
+ logger.warn({
308
+ channel,
309
+ threadTs,
310
+ agentId,
311
+ eventType: "data-component",
312
+ MAX_RICH_MESSAGES
313
+ }, "MAX_RICH_MESSAGES cap reached — additional rich content will be dropped");
314
+ richMessageCapWarned = true;
315
+ }
316
+ continue;
317
+ }
318
+ if (data.type === "data-artifact" && data.data && typeof data.data === "object") {
319
+ const artifactData = data.data;
320
+ if (typeof artifactData.type === "string" && artifactData.type.toLowerCase() === "citation") {
321
+ const summary = artifactData.artifactSummary;
322
+ if (summary?.url && !citations.some((c) => c.url === summary.url)) citations.push({
323
+ title: summary.title,
324
+ url: summary.url
325
+ });
326
+ } else if (richMessageCount < MAX_RICH_MESSAGES) {
327
+ const { blocks, overflowContent, artifactName } = buildDataArtifactBlocks({ data: artifactData });
328
+ if (overflowContent) {
329
+ const label = artifactName || "artifact";
330
+ await retryWithBackoff(() => slackClient.files.uploadV2({
331
+ channel_id: channel,
332
+ thread_ts: threadTs,
333
+ filename: `${label}.md`,
334
+ content: overflowContent,
335
+ initial_comment: `📄 ${label}`
336
+ }), { label: "slack-file-upload" }).catch((e) => logger.warn({
337
+ error: e,
338
+ channel,
339
+ threadTs,
340
+ agentId,
341
+ artifactName: label
342
+ }, "Failed to upload artifact file"));
343
+ } else await slackClient.chat.postMessage({
344
+ channel,
345
+ thread_ts: threadTs,
346
+ text: "📄 Data",
347
+ blocks
348
+ }).catch((e) => logger.warn({ error: e }, "Failed to post data artifact"));
349
+ richMessageCount++;
350
+ } else if (!richMessageCapWarned) {
351
+ logger.warn({
352
+ channel,
353
+ threadTs,
354
+ agentId,
355
+ eventType: "data-artifact",
356
+ MAX_RICH_MESSAGES
357
+ }, "MAX_RICH_MESSAGES cap reached — additional rich content will be dropped");
358
+ richMessageCapWarned = true;
359
+ }
360
+ continue;
361
+ }
362
+ if (data.type === "data-summary" && data.data?.label) {
363
+ summaryLabels.push(String(data.data.label));
364
+ continue;
365
+ }
211
366
  if (data.type === "text-start" || data.type === "text-end") continue;
212
367
  if (data.type === "text-delta" && data.delta) {
213
368
  fullText += data.delta;
@@ -225,9 +380,12 @@ async function streamAgentResponse(params) {
225
380
  if (agentCompleted) break;
226
381
  }
227
382
  clearTimeout(timeoutId);
228
- const contextBlock = createContextBlock({ agentName });
383
+ const stopBlocks = [];
384
+ for (const { toolName, errorText } of toolErrors) stopBlocks.push(buildToolOutputErrorBlock(toolName, errorText));
385
+ if (summaryLabels.length > 0) stopBlocks.push(buildSummaryBreadcrumbBlock(summaryLabels));
386
+ stopBlocks.push(createContextBlock({ agentName }));
229
387
  try {
230
- await withTimeout(streamer.stop({ blocks: [contextBlock] }), CHATSTREAM_OP_TIMEOUT_MS, "streamer.stop");
388
+ await withTimeout(streamer.stop({ blocks: stopBlocks.slice(0, 50) }), CHATSTREAM_OP_TIMEOUT_MS, "streamer.stop");
231
389
  } catch (stopError) {
232
390
  span.setAttribute(SLACK_SPAN_KEYS.STREAM_FINALIZATION_FAILED, true);
233
391
  logger.warn({
@@ -237,6 +395,15 @@ async function streamAgentResponse(params) {
237
395
  responseLength: fullText.length
238
396
  }, "Failed to finalize chatStream — content was already delivered");
239
397
  }
398
+ if (citations.length > 0) {
399
+ const citationBlocks = buildCitationsBlock(citations);
400
+ if (citationBlocks.length > 0) await slackClient.chat.postMessage({
401
+ channel,
402
+ thread_ts: threadTs,
403
+ text: "📚 Sources",
404
+ blocks: citationBlocks
405
+ }).catch((e) => logger.warn({ error: e }, "Failed to post citations"));
406
+ }
240
407
  if (thinkingMessageTs) try {
241
408
  await slackClient.chat.delete({
242
409
  channel,
@@ -250,13 +417,26 @@ async function streamAgentResponse(params) {
250
417
  threadTs,
251
418
  responseLength: fullText.length,
252
419
  agentId,
253
- conversationId
420
+ conversationId,
421
+ toolErrorCount: toolErrors.length,
422
+ citationCount: citations.length,
423
+ richMessageCount
254
424
  }, "Streaming completed");
255
425
  span.end();
256
426
  return { success: true };
257
427
  } catch (streamError) {
258
428
  clearTimeout(timeoutId);
429
+ reader?.cancel().catch(() => {});
259
430
  if (streamError instanceof Error) setSpanWithError(span, streamError);
431
+ for (const { messageTs, toolName } of pendingApprovalMessages) await slackClient.chat.update({
432
+ channel,
433
+ ts: messageTs,
434
+ text: `⏱️ Expired · \`${toolName}\``,
435
+ blocks: buildToolApprovalExpiredBlocks({ toolName })
436
+ }).catch((e) => logger.warn({
437
+ error: e,
438
+ messageTs
439
+ }, "Failed to expire approval message"));
260
440
  if (fullText.length > 0) {
261
441
  span.setAttribute(SLACK_SPAN_KEYS.CONTENT_ALREADY_DELIVERED, true);
262
442
  logger.warn({
@@ -275,6 +455,22 @@ async function streamAgentResponse(params) {
275
455
  span.end();
276
456
  return { success: true };
277
457
  }
458
+ if (pendingApprovalMessages.length > 0) {
459
+ for (const { toolName } of pendingApprovalMessages) await slackClient.chat.postMessage({
460
+ channel,
461
+ thread_ts: threadTs,
462
+ text: `Approval for \`${toolName}\` has expired.`
463
+ }).catch((e) => logger.warn({ error: e }, "Failed to send approval expired notification"));
464
+ await withTimeout(streamer.stop(), CLEANUP_TIMEOUT_MS, "streamer.stop-cleanup").catch((e) => logger.warn({ error: e }, "Failed to stop streamer during error cleanup"));
465
+ if (thinkingMessageTs) try {
466
+ await slackClient.chat.delete({
467
+ channel,
468
+ ts: thinkingMessageTs
469
+ });
470
+ } catch {}
471
+ span.end();
472
+ return { success: true };
473
+ }
278
474
  logger.error({ streamError }, "Error during Slack streaming");
279
475
  await withTimeout(streamer.stop(), CLEANUP_TIMEOUT_MS, "streamer.stop-cleanup").catch((e) => logger.warn({ error: e }, "Failed to stop streamer during error cleanup"));
280
476
  if (thinkingMessageTs) try {
@@ -8,12 +8,12 @@ import { AgentOption } from "../modals.js";
8
8
  * Called on every @mention and /inkeep command — caching avoids redundant DB queries.
9
9
  */
10
10
  declare function findCachedUserMapping(tenantId: string, slackUserId: string, teamId: string, clientId?: string): Promise<{
11
- id: string;
11
+ slackUserId: string;
12
12
  createdAt: string;
13
13
  updatedAt: string;
14
+ id: string;
14
15
  tenantId: string;
15
16
  clientId: string;
16
- slackUserId: string;
17
17
  slackTeamId: string;
18
18
  slackEnterpriseId: string | null;
19
19
  inkeepUserId: string;
@@ -239,9 +239,12 @@ async function resolveChannelAgentConfig(teamId, channelId, workspace) {
239
239
  async function sendResponseUrlMessage(responseUrl, message) {
240
240
  try {
241
241
  const payload = { text: message.text };
242
- if (message.replace_original) payload.replace_original = true;
242
+ if (message.replace_original === true) payload.replace_original = true;
243
243
  else if (message.delete_original) payload.delete_original = true;
244
- else if (message.response_type) payload.response_type = message.response_type;
244
+ else {
245
+ payload.replace_original = false;
246
+ if (message.response_type) payload.response_type = message.response_type;
247
+ }
245
248
  if (message.blocks) payload.blocks = message.blocks;
246
249
  logger.info({
247
250
  hasBlocks: !!message.blocks,
@@ -1,11 +1,11 @@
1
1
  import { AgentResolutionParams, ResolvedAgentConfig, getAgentConfigSources, resolveEffectiveAgent } from "./agent-resolution.js";
2
- import { AgentConfigSources, ContextBlockParams, FollowUpButtonParams, buildConversationResponseBlocks, buildFollowUpButton, createAlreadyLinkedMessage, createContextBlock, createErrorMessage, createJwtLinkMessage, createNotLinkedMessage, createStatusMessage, createUnlinkSuccessMessage, createUpdatedHelpMessage } from "./blocks/index.js";
2
+ import { AgentConfigSources, ContextBlockParams, FollowUpButtonParams, ToolApprovalButtonValue, ToolApprovalButtonValueSchema, buildCitationsBlock, buildConversationResponseBlocks, buildDataArtifactBlocks, buildDataComponentBlocks, buildFollowUpButton, buildSummaryBreadcrumbBlock, buildToolApprovalBlocks, buildToolApprovalDoneBlocks, buildToolApprovalExpiredBlocks, buildToolOutputErrorBlock, createAlreadyLinkedMessage, createContextBlock, createCreateInkeepAccountMessage, createErrorMessage, createJwtLinkMessage, createNotLinkedMessage, createStatusMessage, createUnlinkSuccessMessage, createUpdatedHelpMessage } from "./blocks/index.js";
3
3
  import { checkUserIsChannelMember, getSlackChannelInfo, getSlackChannels, getSlackClient, getSlackTeamInfo, getSlackUserInfo, postMessage, postMessageInThread, revokeSlackToken } from "./client.js";
4
4
  import { DefaultAgentConfig, SlackWorkspaceConnection, WorkspaceInstallData, clearWorkspaceConnectionCache, computeWorkspaceConnectionId, createConnectSession, deleteWorkspaceInstallation, findWorkspaceConnectionByTeamId, getConnectionAccessToken, getSlackIntegrationId, getSlackNango, getWorkspaceDefaultAgentFromNango, listWorkspaceInstallations, setWorkspaceDefaultAgent, storeWorkspaceInstallation, updateConnectionMetadata } from "./nango.js";
5
5
  import { SlackCommandPayload, SlackCommandResponse } from "./types.js";
6
6
  import { handleAgentPickerCommand, handleCommand, handleHelpCommand, handleLinkCommand, handleQuestionCommand, handleStatusCommand, handleUnlinkCommand } from "./commands/index.js";
7
7
  import { InlineSelectorMetadata, handleAppMention } from "./events/app-mention.js";
8
- import { handleMessageShortcut, handleOpenAgentSelectorModal, handleOpenFollowUpModal } from "./events/block-actions.js";
8
+ import { handleMessageShortcut, handleOpenAgentSelectorModal, handleOpenFollowUpModal, handleToolApproval } from "./events/block-actions.js";
9
9
  import { handleFollowUpSubmission, handleModalSubmission } from "./events/modal-submission.js";
10
10
  import { AgentOption, BuildAgentSelectorModalParams, BuildMessageShortcutModalParams, FollowUpModalMetadata, ModalMetadata, buildAgentSelectorModal, buildFollowUpModal, buildMessageShortcutModal } from "./modals.js";
11
11
  import { SlackErrorType, checkIfBotThread, classifyError, extractApiErrorMessage, fetchAgentsForProject, fetchProjectsForTenant, findCachedUserMapping, generateSlackConversationId, getChannelAgentConfig, getThreadContext, getUserFriendlyErrorMessage, getWorkspaceDefaultAgent, markdownToMrkdwn, sendResponseUrlMessage } from "./events/utils.js";
@@ -13,4 +13,4 @@ import { StreamResult, streamAgentResponse } from "./events/streaming.js";
13
13
  import "./events/index.js";
14
14
  import { parseSlackCommandBody, parseSlackEventBody, verifySlackRequest } from "./security.js";
15
15
  import { getBotTokenForTeam, setBotTokenForTeam } from "./workspace-tokens.js";
16
- export { AgentConfigSources, AgentOption, AgentResolutionParams, BuildAgentSelectorModalParams, BuildMessageShortcutModalParams, ContextBlockParams, DefaultAgentConfig, FollowUpButtonParams, FollowUpModalMetadata, InlineSelectorMetadata, ModalMetadata, ResolvedAgentConfig, SlackCommandPayload, SlackCommandResponse, SlackErrorType, SlackWorkspaceConnection, StreamResult, WorkspaceInstallData, buildAgentSelectorModal, buildConversationResponseBlocks, buildFollowUpButton, buildFollowUpModal, buildMessageShortcutModal, checkIfBotThread, checkUserIsChannelMember, classifyError, clearWorkspaceConnectionCache, computeWorkspaceConnectionId, createAlreadyLinkedMessage, createConnectSession, createContextBlock, createErrorMessage, createJwtLinkMessage, createNotLinkedMessage, createStatusMessage, createUnlinkSuccessMessage, createUpdatedHelpMessage, deleteWorkspaceInstallation, extractApiErrorMessage, 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 };
16
+ export { AgentConfigSources, AgentOption, AgentResolutionParams, BuildAgentSelectorModalParams, BuildMessageShortcutModalParams, ContextBlockParams, DefaultAgentConfig, FollowUpButtonParams, FollowUpModalMetadata, InlineSelectorMetadata, ModalMetadata, ResolvedAgentConfig, SlackCommandPayload, SlackCommandResponse, SlackErrorType, SlackWorkspaceConnection, StreamResult, ToolApprovalButtonValue, ToolApprovalButtonValueSchema, WorkspaceInstallData, buildAgentSelectorModal, buildCitationsBlock, buildConversationResponseBlocks, buildDataArtifactBlocks, buildDataComponentBlocks, buildFollowUpButton, buildFollowUpModal, buildMessageShortcutModal, buildSummaryBreadcrumbBlock, buildToolApprovalBlocks, buildToolApprovalDoneBlocks, buildToolApprovalExpiredBlocks, buildToolOutputErrorBlock, checkIfBotThread, checkUserIsChannelMember, classifyError, clearWorkspaceConnectionCache, computeWorkspaceConnectionId, createAlreadyLinkedMessage, createConnectSession, createContextBlock, createCreateInkeepAccountMessage, createErrorMessage, createJwtLinkMessage, createNotLinkedMessage, createStatusMessage, createUnlinkSuccessMessage, createUpdatedHelpMessage, deleteWorkspaceInstallation, extractApiErrorMessage, 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, handleToolApproval, handleUnlinkCommand, listWorkspaceInstallations, markdownToMrkdwn, parseSlackCommandBody, parseSlackEventBody, postMessage, postMessageInThread, resolveEffectiveAgent, revokeSlackToken, sendResponseUrlMessage, setBotTokenForTeam, setWorkspaceDefaultAgent, storeWorkspaceInstallation, streamAgentResponse, updateConnectionMetadata, verifySlackRequest };
@@ -1,16 +1,16 @@
1
1
  import { clearWorkspaceConnectionCache, computeWorkspaceConnectionId, createConnectSession, deleteWorkspaceInstallation, findWorkspaceConnectionByTeamId, getConnectionAccessToken, getSlackIntegrationId, getSlackNango, getWorkspaceDefaultAgentFromNango, listWorkspaceInstallations, setWorkspaceDefaultAgent, storeWorkspaceInstallation, updateConnectionMetadata } from "./nango.js";
2
2
  import { getAgentConfigSources, resolveEffectiveAgent } from "./agent-resolution.js";
3
- import { buildConversationResponseBlocks, buildFollowUpButton, createAlreadyLinkedMessage, createContextBlock, createErrorMessage, createJwtLinkMessage, createNotLinkedMessage, createStatusMessage, createUnlinkSuccessMessage, createUpdatedHelpMessage } from "./blocks/index.js";
3
+ import { ToolApprovalButtonValueSchema, buildCitationsBlock, buildConversationResponseBlocks, buildDataArtifactBlocks, buildDataComponentBlocks, buildFollowUpButton, buildSummaryBreadcrumbBlock, buildToolApprovalBlocks, buildToolApprovalDoneBlocks, buildToolApprovalExpiredBlocks, buildToolOutputErrorBlock, createAlreadyLinkedMessage, createContextBlock, createCreateInkeepAccountMessage, createErrorMessage, createJwtLinkMessage, createNotLinkedMessage, createStatusMessage, createUnlinkSuccessMessage, createUpdatedHelpMessage } from "./blocks/index.js";
4
4
  import { checkUserIsChannelMember, getSlackChannelInfo, getSlackChannels, getSlackClient, getSlackTeamInfo, getSlackUserInfo, postMessage, postMessageInThread, revokeSlackToken } from "./client.js";
5
5
  import { SlackErrorType, checkIfBotThread, classifyError, extractApiErrorMessage, 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
8
  import { streamAgentResponse } from "./events/streaming.js";
9
9
  import { handleAppMention } from "./events/app-mention.js";
10
- import { handleMessageShortcut, handleOpenAgentSelectorModal, handleOpenFollowUpModal } from "./events/block-actions.js";
10
+ import { handleMessageShortcut, handleOpenAgentSelectorModal, handleOpenFollowUpModal, handleToolApproval } from "./events/block-actions.js";
11
11
  import { handleFollowUpSubmission, handleModalSubmission } from "./events/modal-submission.js";
12
12
  import "./events/index.js";
13
13
  import { parseSlackCommandBody, parseSlackEventBody, verifySlackRequest } from "./security.js";
14
14
  import { getBotTokenForTeam, setBotTokenForTeam } from "./workspace-tokens.js";
15
15
 
16
- export { SlackErrorType, buildAgentSelectorModal, buildConversationResponseBlocks, buildFollowUpButton, buildFollowUpModal, buildMessageShortcutModal, checkIfBotThread, checkUserIsChannelMember, classifyError, clearWorkspaceConnectionCache, computeWorkspaceConnectionId, createAlreadyLinkedMessage, createConnectSession, createContextBlock, createErrorMessage, createJwtLinkMessage, createNotLinkedMessage, createStatusMessage, createUnlinkSuccessMessage, createUpdatedHelpMessage, deleteWorkspaceInstallation, extractApiErrorMessage, 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 };
16
+ export { SlackErrorType, ToolApprovalButtonValueSchema, buildAgentSelectorModal, buildCitationsBlock, buildConversationResponseBlocks, buildDataArtifactBlocks, buildDataComponentBlocks, buildFollowUpButton, buildFollowUpModal, buildMessageShortcutModal, buildSummaryBreadcrumbBlock, buildToolApprovalBlocks, buildToolApprovalDoneBlocks, buildToolApprovalExpiredBlocks, buildToolOutputErrorBlock, checkIfBotThread, checkUserIsChannelMember, classifyError, clearWorkspaceConnectionCache, computeWorkspaceConnectionId, createAlreadyLinkedMessage, createConnectSession, createContextBlock, createCreateInkeepAccountMessage, createErrorMessage, createJwtLinkMessage, createNotLinkedMessage, createStatusMessage, createUnlinkSuccessMessage, createUpdatedHelpMessage, deleteWorkspaceInstallation, extractApiErrorMessage, 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, handleToolApproval, handleUnlinkCommand, listWorkspaceInstallations, markdownToMrkdwn, parseSlackCommandBody, parseSlackEventBody, postMessage, postMessageInThread, resolveEffectiveAgent, revokeSlackToken, sendResponseUrlMessage, setBotTokenForTeam, setWorkspaceDefaultAgent, storeWorkspaceInstallation, streamAgentResponse, updateConnectionMetadata, verifySlackRequest };
@@ -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 { getDevDefaultAgent, isSlackDevMode, loadSlackDevConfig, saveSlackDevConfig } from "./dev-config.js";
5
- import { findWorkAppSlackWorkspaceBySlackTeamId } from "@inkeep/agents-core";
5
+ import { findWorkAppSlackWorkspaceBySlackTeamId, retryWithBackoff } from "@inkeep/agents-core";
6
6
  import { Nango } from "@nangohq/node";
7
7
 
8
8
  //#region src/slack/services/nango.ts
@@ -30,28 +30,6 @@ const workspaceConnectionCache = /* @__PURE__ */ new Map();
30
30
  const CACHE_TTL_MS = 6e4;
31
31
  const logger = getLogger("slack-nango");
32
32
  /**
33
- * Retry a function with exponential backoff for transient failures.
34
- * Retries on AbortError (timeout) and 5xx HTTP errors.
35
- */
36
- async function retryWithBackoff(fn, maxAttempts = 3) {
37
- for (let attempt = 1; attempt <= maxAttempts; attempt++) try {
38
- return await fn();
39
- } catch (error) {
40
- const isTimeout = error.name === "AbortError";
41
- const isServerError = typeof error.status === "number" && error.status >= 500;
42
- if (!(isTimeout || isServerError) || attempt === maxAttempts) throw error;
43
- const delay = Math.min(500 * 2 ** (attempt - 1), 2e3) + Math.random() * 100;
44
- logger.warn({
45
- attempt,
46
- maxAttempts,
47
- isTimeout,
48
- delay: Math.round(delay)
49
- }, "Retrying Nango API call after transient failure");
50
- await new Promise((resolve) => setTimeout(resolve, delay));
51
- }
52
- throw new Error("Unreachable");
53
- }
54
- /**
55
33
  * Evict expired entries from workspace cache to bound memory.
56
34
  */
57
35
  function evictWorkspaceCache() {
@@ -15,6 +15,7 @@ declare const SLACK_SPAN_NAMES: {
15
15
  readonly OPEN_FOLLOW_UP_MODAL: "slack.open_follow_up_modal";
16
16
  readonly PROJECT_SELECT_UPDATE: "slack.project_select_update";
17
17
  readonly CALL_AGENT_API: "slack.call_agent_api";
18
+ readonly TOOL_APPROVAL: "slack.tool_approval";
18
19
  };
19
20
  declare const SLACK_SPAN_KEYS: {
20
21
  readonly TEAM_ID: "slack.team_id";
@@ -13,7 +13,8 @@ const SLACK_SPAN_NAMES = {
13
13
  OPEN_AGENT_SELECTOR_MODAL: "slack.open_agent_selector_modal",
14
14
  OPEN_FOLLOW_UP_MODAL: "slack.open_follow_up_modal",
15
15
  PROJECT_SELECT_UPDATE: "slack.project_select_update",
16
- CALL_AGENT_API: "slack.call_agent_api"
16
+ CALL_AGENT_API: "slack.call_agent_api",
17
+ TOOL_APPROVAL: "slack.tool_approval"
17
18
  };
18
19
  const SLACK_SPAN_KEYS = {
19
20
  TEAM_ID: "slack.team_id",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inkeep/agents-work-apps",
3
- "version": "0.50.6",
3
+ "version": "0.52.0",
4
4
  "description": "First party integrations for Inkeep Agents",
5
5
  "type": "module",
6
6
  "license": "SEE LICENSE IN LICENSE.md",
@@ -33,7 +33,7 @@
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.6"
36
+ "@inkeep/agents-core": "0.52.0"
37
37
  },
38
38
  "peerDependencies": {
39
39
  "@hono/zod-openapi": "^1.1.5",