@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.
- package/dist/github/mcp/auth.d.ts +2 -2
- package/dist/github/mcp/index.d.ts +2 -2
- package/dist/github/mcp/index.js +60 -1
- package/dist/github/mcp/schemas.d.ts +1 -1
- package/dist/github/mcp/utils.d.ts +10 -1
- package/dist/github/mcp/utils.js +87 -1
- package/dist/github/routes/setup.d.ts +2 -2
- package/dist/github/routes/tokenExchange.d.ts +2 -2
- package/dist/github/routes/webhooks.d.ts +2 -2
- package/dist/slack/dispatcher.js +24 -1
- package/dist/slack/i18n/strings.d.ts +1 -0
- package/dist/slack/i18n/strings.js +1 -0
- package/dist/slack/routes/events.js +2 -2
- package/dist/slack/routes/oauth.js +3 -4
- package/dist/slack/routes/users.js +13 -11
- package/dist/slack/routes/workspaces.js +85 -1
- package/dist/slack/services/blocks/index.d.ts +81 -1
- package/dist/slack/services/blocks/index.js +238 -19
- package/dist/slack/services/commands/index.d.ts +1 -1
- package/dist/slack/services/commands/index.js +98 -4
- package/dist/slack/services/events/app-mention.js +2 -2
- package/dist/slack/services/events/block-actions.d.ts +12 -1
- package/dist/slack/services/events/block-actions.js +126 -2
- package/dist/slack/services/events/index.d.ts +2 -2
- package/dist/slack/services/events/index.js +2 -2
- package/dist/slack/services/events/streaming.d.ts +1 -1
- package/dist/slack/services/events/streaming.js +203 -7
- package/dist/slack/services/events/utils.d.ts +2 -2
- package/dist/slack/services/events/utils.js +5 -2
- package/dist/slack/services/index.d.ts +3 -3
- package/dist/slack/services/index.js +3 -3
- package/dist/slack/services/nango.js +1 -23
- package/dist/slack/tracer.d.ts +1 -0
- package/dist/slack/tracer.js +2 -1
- 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
|
-
|
|
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
|
|
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:
|
|
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
|
-
|
|
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
|
|
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() {
|
package/dist/slack/tracer.d.ts
CHANGED
|
@@ -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";
|
package/dist/slack/tracer.js
CHANGED
|
@@ -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.
|
|
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.
|
|
36
|
+
"@inkeep/agents-core": "0.52.0"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
39
|
"@hono/zod-openapi": "^1.1.5",
|