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