@inkeep/agents-work-apps 0.0.0-dev-20260212214459 → 0.0.0-dev-20260213074206
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/index.d.ts +3 -3
- package/dist/github/mcp/auth.d.ts +2 -2
- package/dist/github/mcp/index.d.ts +2 -2
- 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/routes/events.js +352 -203
- package/dist/slack/services/events/app-mention.js +211 -160
- package/dist/slack/services/events/block-actions.js +225 -181
- package/dist/slack/services/events/modal-submission.js +309 -258
- package/dist/slack/services/events/streaming.js +193 -171
- package/dist/slack/tracer.d.ts +40 -0
- package/dist/slack/tracer.js +39 -0
- package/package.json +4 -3
|
@@ -4,6 +4,7 @@ import { SlackStrings } from "../../i18n/strings.js";
|
|
|
4
4
|
import { getSlackClient } from "../client.js";
|
|
5
5
|
import { fetchAgentsForProject, fetchProjectsForTenant, getChannelAgentConfig, sendResponseUrlMessage } from "./utils.js";
|
|
6
6
|
import { buildAgentSelectorModal, buildFollowUpModal, buildMessageShortcutModal } from "../modals.js";
|
|
7
|
+
import { SLACK_SPAN_KEYS, SLACK_SPAN_NAMES, setSpanWithError, tracer } from "../../tracer.js";
|
|
7
8
|
|
|
8
9
|
//#region src/slack/services/events/block-actions.ts
|
|
9
10
|
/**
|
|
@@ -15,206 +16,249 @@ const logger = getLogger("slack-block-actions");
|
|
|
15
16
|
* Handle opening the agent selector modal when user clicks "Select Agent" button
|
|
16
17
|
*/
|
|
17
18
|
async function handleOpenAgentSelectorModal(params) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
19
|
+
return tracer.startActiveSpan(SLACK_SPAN_NAMES.OPEN_AGENT_SELECTOR_MODAL, async (span) => {
|
|
20
|
+
const { triggerId, actionValue, teamId, responseUrl } = params;
|
|
21
|
+
span.setAttribute(SLACK_SPAN_KEYS.TEAM_ID, teamId);
|
|
22
|
+
try {
|
|
23
|
+
const { channel, threadTs, messageTs, slackUserId, tenantId } = JSON.parse(actionValue);
|
|
24
|
+
span.setAttribute(SLACK_SPAN_KEYS.CHANNEL_ID, channel);
|
|
25
|
+
span.setAttribute(SLACK_SPAN_KEYS.USER_ID, slackUserId);
|
|
26
|
+
span.setAttribute(SLACK_SPAN_KEYS.TENANT_ID, tenantId);
|
|
27
|
+
const isInThread = Boolean(threadTs && threadTs !== messageTs);
|
|
28
|
+
const workspaceConnection = await findWorkspaceConnectionByTeamId(teamId);
|
|
29
|
+
if (!workspaceConnection?.botToken) {
|
|
30
|
+
logger.error({ teamId }, "No bot token for modal");
|
|
31
|
+
span.end();
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const slackClient = getSlackClient(workspaceConnection.botToken);
|
|
35
|
+
let projectList = await fetchProjectsForTenant(tenantId);
|
|
36
|
+
if (projectList.length === 0) {
|
|
37
|
+
const defaultAgent = await getChannelAgentConfig(teamId, channel);
|
|
38
|
+
if (defaultAgent) projectList = [{
|
|
39
|
+
id: defaultAgent.projectId,
|
|
40
|
+
name: defaultAgent.projectName || defaultAgent.projectId
|
|
41
|
+
}];
|
|
42
|
+
}
|
|
43
|
+
if (projectList.length === 0) {
|
|
44
|
+
logger.info({
|
|
45
|
+
teamId,
|
|
46
|
+
channel,
|
|
47
|
+
tenantId
|
|
48
|
+
}, "No projects configured — cannot open selector");
|
|
49
|
+
await slackClient.chat.postEphemeral({
|
|
50
|
+
channel,
|
|
51
|
+
user: slackUserId,
|
|
52
|
+
thread_ts: isInThread ? threadTs : void 0,
|
|
53
|
+
text: SlackStrings.status.noProjectsConfigured
|
|
54
|
+
});
|
|
55
|
+
span.end();
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const firstProject = projectList[0];
|
|
59
|
+
let agentList = await fetchAgentsForProject(tenantId, firstProject.id);
|
|
60
|
+
if (agentList.length === 0) {
|
|
61
|
+
const defaultAgent = await getChannelAgentConfig(teamId, channel);
|
|
62
|
+
if (defaultAgent && defaultAgent.projectId === firstProject.id) agentList = [{
|
|
63
|
+
id: defaultAgent.agentId,
|
|
64
|
+
name: defaultAgent.agentName || defaultAgent.agentId,
|
|
65
|
+
projectId: defaultAgent.projectId,
|
|
66
|
+
projectName: defaultAgent.projectName || defaultAgent.projectId
|
|
67
|
+
}];
|
|
68
|
+
}
|
|
69
|
+
const modalMetadata = {
|
|
38
70
|
channel,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
71
|
+
threadTs: isInThread ? threadTs : void 0,
|
|
72
|
+
messageTs,
|
|
73
|
+
teamId,
|
|
74
|
+
slackUserId,
|
|
75
|
+
tenantId,
|
|
76
|
+
isInThread,
|
|
77
|
+
buttonResponseUrl: responseUrl
|
|
78
|
+
};
|
|
79
|
+
const modal = buildAgentSelectorModal({
|
|
80
|
+
projects: projectList,
|
|
81
|
+
agents: agentList.map((a) => ({
|
|
82
|
+
id: a.id,
|
|
83
|
+
name: a.name,
|
|
84
|
+
projectId: a.projectId,
|
|
85
|
+
projectName: a.projectName || a.projectId
|
|
86
|
+
})),
|
|
87
|
+
metadata: modalMetadata,
|
|
88
|
+
selectedProjectId: firstProject.id
|
|
42
89
|
});
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
90
|
+
await slackClient.views.open({
|
|
91
|
+
trigger_id: triggerId,
|
|
92
|
+
view: modal
|
|
93
|
+
});
|
|
94
|
+
logger.info({
|
|
95
|
+
teamId,
|
|
96
|
+
channel,
|
|
97
|
+
threadTs,
|
|
98
|
+
projectCount: projectList.length,
|
|
99
|
+
agentCount: agentList.length
|
|
100
|
+
}, "Opened agent selector modal");
|
|
101
|
+
span.end();
|
|
102
|
+
} catch (error) {
|
|
103
|
+
if (error instanceof Error) setSpanWithError(span, error);
|
|
104
|
+
logger.error({
|
|
105
|
+
error,
|
|
106
|
+
teamId
|
|
107
|
+
}, "Failed to open agent selector modal");
|
|
108
|
+
if (responseUrl) await sendResponseUrlMessage(responseUrl, {
|
|
109
|
+
text: SlackStrings.errors.failedToOpenSelector,
|
|
110
|
+
response_type: "ephemeral"
|
|
111
|
+
}).catch((e) => logger.warn({ error: e }, "Failed to send selector error notification"));
|
|
112
|
+
span.end();
|
|
55
113
|
}
|
|
56
|
-
|
|
57
|
-
channel,
|
|
58
|
-
threadTs: isInThread ? threadTs : void 0,
|
|
59
|
-
messageTs,
|
|
60
|
-
teamId,
|
|
61
|
-
slackUserId,
|
|
62
|
-
tenantId,
|
|
63
|
-
isInThread,
|
|
64
|
-
buttonResponseUrl: responseUrl
|
|
65
|
-
};
|
|
66
|
-
const modal = buildAgentSelectorModal({
|
|
67
|
-
projects: projectList,
|
|
68
|
-
agents: agentList.map((a) => ({
|
|
69
|
-
id: a.id,
|
|
70
|
-
name: a.name,
|
|
71
|
-
projectId: a.projectId,
|
|
72
|
-
projectName: a.projectName || a.projectId
|
|
73
|
-
})),
|
|
74
|
-
metadata: modalMetadata,
|
|
75
|
-
selectedProjectId: firstProject.id
|
|
76
|
-
});
|
|
77
|
-
await slackClient.views.open({
|
|
78
|
-
trigger_id: triggerId,
|
|
79
|
-
view: modal
|
|
80
|
-
});
|
|
81
|
-
logger.info({
|
|
82
|
-
teamId,
|
|
83
|
-
channel,
|
|
84
|
-
threadTs,
|
|
85
|
-
projectCount: projectList.length,
|
|
86
|
-
agentCount: agentList.length
|
|
87
|
-
}, "Opened agent selector modal");
|
|
88
|
-
} catch (error) {
|
|
89
|
-
logger.error({
|
|
90
|
-
error,
|
|
91
|
-
teamId
|
|
92
|
-
}, "Failed to open agent selector modal");
|
|
93
|
-
if (responseUrl) await sendResponseUrlMessage(responseUrl, {
|
|
94
|
-
text: SlackStrings.errors.failedToOpenSelector,
|
|
95
|
-
response_type: "ephemeral"
|
|
96
|
-
}).catch((e) => logger.warn({ error: e }, "Failed to send selector error notification"));
|
|
97
|
-
}
|
|
114
|
+
});
|
|
98
115
|
}
|
|
99
116
|
/**
|
|
100
117
|
* Handle "Follow Up" button click.
|
|
101
118
|
* Opens a prompt-only modal that carries the conversationId for multi-turn context.
|
|
102
119
|
*/
|
|
103
120
|
async function handleOpenFollowUpModal(params) {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
121
|
+
return tracer.startActiveSpan(SLACK_SPAN_NAMES.OPEN_FOLLOW_UP_MODAL, async (span) => {
|
|
122
|
+
const { triggerId, actionValue, teamId, responseUrl } = params;
|
|
123
|
+
span.setAttribute(SLACK_SPAN_KEYS.TEAM_ID, teamId);
|
|
124
|
+
try {
|
|
125
|
+
const metadata = JSON.parse(actionValue);
|
|
126
|
+
span.setAttribute(SLACK_SPAN_KEYS.CONVERSATION_ID, metadata.conversationId || "");
|
|
127
|
+
span.setAttribute(SLACK_SPAN_KEYS.AGENT_ID, metadata.agentId || "");
|
|
128
|
+
const workspaceConnection = await findWorkspaceConnectionByTeamId(teamId);
|
|
129
|
+
if (!workspaceConnection?.botToken) {
|
|
130
|
+
logger.error({ teamId }, "No bot token for follow-up modal");
|
|
131
|
+
span.end();
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const slackClient = getSlackClient(workspaceConnection.botToken);
|
|
135
|
+
const modal = buildFollowUpModal(metadata);
|
|
136
|
+
await slackClient.views.open({
|
|
137
|
+
trigger_id: triggerId,
|
|
138
|
+
view: modal
|
|
139
|
+
});
|
|
140
|
+
logger.info({
|
|
141
|
+
teamId,
|
|
142
|
+
conversationId: metadata.conversationId,
|
|
143
|
+
agentId: metadata.agentId
|
|
144
|
+
}, "Opened follow-up modal");
|
|
145
|
+
span.end();
|
|
146
|
+
} catch (error) {
|
|
147
|
+
if (error instanceof Error) setSpanWithError(span, error);
|
|
148
|
+
logger.error({
|
|
149
|
+
error,
|
|
150
|
+
teamId
|
|
151
|
+
}, "Failed to open follow-up modal");
|
|
152
|
+
if (responseUrl) await sendResponseUrlMessage(responseUrl, {
|
|
153
|
+
text: "Failed to open follow-up dialog. Please try again.",
|
|
154
|
+
response_type: "ephemeral"
|
|
155
|
+
}).catch((e) => logger.warn({ error: e }, "Failed to send follow-up error notification"));
|
|
156
|
+
span.end();
|
|
111
157
|
}
|
|
112
|
-
|
|
113
|
-
const modal = buildFollowUpModal(metadata);
|
|
114
|
-
await slackClient.views.open({
|
|
115
|
-
trigger_id: triggerId,
|
|
116
|
-
view: modal
|
|
117
|
-
});
|
|
118
|
-
logger.info({
|
|
119
|
-
teamId,
|
|
120
|
-
conversationId: metadata.conversationId,
|
|
121
|
-
agentId: metadata.agentId
|
|
122
|
-
}, "Opened follow-up modal");
|
|
123
|
-
} catch (error) {
|
|
124
|
-
logger.error({
|
|
125
|
-
error,
|
|
126
|
-
teamId
|
|
127
|
-
}, "Failed to open follow-up modal");
|
|
128
|
-
if (responseUrl) await sendResponseUrlMessage(responseUrl, {
|
|
129
|
-
text: "Failed to open follow-up dialog. Please try again.",
|
|
130
|
-
response_type: "ephemeral"
|
|
131
|
-
}).catch((e) => logger.warn({ error: e }, "Failed to send follow-up error notification"));
|
|
132
|
-
}
|
|
158
|
+
});
|
|
133
159
|
}
|
|
134
160
|
/**
|
|
135
161
|
* Handle message shortcut (context menu action on a message)
|
|
136
162
|
* Opens a modal with the message content pre-filled as context
|
|
137
163
|
*/
|
|
138
164
|
async function handleMessageShortcut(params) {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
165
|
+
return tracer.startActiveSpan(SLACK_SPAN_NAMES.MESSAGE_SHORTCUT, async (span) => {
|
|
166
|
+
const { triggerId, teamId, channelId, userId, messageTs, messageText, threadTs, responseUrl } = params;
|
|
167
|
+
span.setAttribute(SLACK_SPAN_KEYS.TEAM_ID, teamId);
|
|
168
|
+
span.setAttribute(SLACK_SPAN_KEYS.CHANNEL_ID, channelId);
|
|
169
|
+
span.setAttribute(SLACK_SPAN_KEYS.USER_ID, userId);
|
|
170
|
+
span.setAttribute(SLACK_SPAN_KEYS.MESSAGE_TS, messageTs);
|
|
171
|
+
if (threadTs) span.setAttribute(SLACK_SPAN_KEYS.THREAD_TS, threadTs);
|
|
172
|
+
try {
|
|
173
|
+
const workspaceConnection = await findWorkspaceConnectionByTeamId(teamId);
|
|
174
|
+
if (!workspaceConnection?.botToken) {
|
|
175
|
+
logger.error({ teamId }, "No bot token for message shortcut modal");
|
|
176
|
+
span.end();
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
const tenantId = workspaceConnection.tenantId;
|
|
180
|
+
span.setAttribute(SLACK_SPAN_KEYS.TENANT_ID, tenantId);
|
|
181
|
+
const slackClient = getSlackClient(workspaceConnection.botToken);
|
|
182
|
+
let projectList = await fetchProjectsForTenant(tenantId);
|
|
183
|
+
if (projectList.length === 0) {
|
|
184
|
+
const defaultAgent = await getChannelAgentConfig(teamId, channelId);
|
|
185
|
+
if (defaultAgent) projectList = [{
|
|
186
|
+
id: defaultAgent.projectId,
|
|
187
|
+
name: defaultAgent.projectName || defaultAgent.projectId
|
|
188
|
+
}];
|
|
189
|
+
}
|
|
190
|
+
if (projectList.length === 0) {
|
|
191
|
+
logger.info({
|
|
192
|
+
teamId,
|
|
193
|
+
channelId,
|
|
194
|
+
tenantId
|
|
195
|
+
}, "No projects configured — cannot open message shortcut modal");
|
|
196
|
+
await slackClient.chat.postEphemeral({
|
|
197
|
+
channel: channelId,
|
|
198
|
+
user: userId,
|
|
199
|
+
text: SlackStrings.status.noProjectsConfigured
|
|
200
|
+
});
|
|
201
|
+
span.end();
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
const firstProject = projectList[0];
|
|
205
|
+
let agentList = await fetchAgentsForProject(tenantId, firstProject.id);
|
|
206
|
+
if (agentList.length === 0) {
|
|
207
|
+
const defaultAgent = await getChannelAgentConfig(teamId, channelId);
|
|
208
|
+
if (defaultAgent && defaultAgent.projectId === firstProject.id) agentList = [{
|
|
209
|
+
id: defaultAgent.agentId,
|
|
210
|
+
name: defaultAgent.agentName || defaultAgent.agentId,
|
|
211
|
+
projectId: defaultAgent.projectId,
|
|
212
|
+
projectName: defaultAgent.projectName || defaultAgent.projectId
|
|
213
|
+
}];
|
|
214
|
+
}
|
|
215
|
+
const modalMetadata = {
|
|
158
216
|
channel: channelId,
|
|
159
|
-
|
|
160
|
-
|
|
217
|
+
threadTs,
|
|
218
|
+
messageTs,
|
|
219
|
+
teamId,
|
|
220
|
+
slackUserId: userId,
|
|
221
|
+
tenantId,
|
|
222
|
+
isInThread: Boolean(threadTs),
|
|
223
|
+
messageContext: messageText
|
|
224
|
+
};
|
|
225
|
+
const modal = buildMessageShortcutModal({
|
|
226
|
+
projects: projectList,
|
|
227
|
+
agents: agentList.map((a) => ({
|
|
228
|
+
id: a.id,
|
|
229
|
+
name: a.name,
|
|
230
|
+
projectId: a.projectId,
|
|
231
|
+
projectName: a.projectName || a.projectId
|
|
232
|
+
})),
|
|
233
|
+
metadata: modalMetadata,
|
|
234
|
+
selectedProjectId: firstProject.id,
|
|
235
|
+
messageContext: messageText
|
|
161
236
|
});
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
237
|
+
await slackClient.views.open({
|
|
238
|
+
trigger_id: triggerId,
|
|
239
|
+
view: modal
|
|
240
|
+
});
|
|
241
|
+
logger.info({
|
|
242
|
+
teamId,
|
|
243
|
+
channelId,
|
|
244
|
+
messageTs,
|
|
245
|
+
projectCount: projectList.length,
|
|
246
|
+
agentCount: agentList.length
|
|
247
|
+
}, "Opened message shortcut modal");
|
|
248
|
+
span.end();
|
|
249
|
+
} catch (error) {
|
|
250
|
+
if (error instanceof Error) setSpanWithError(span, error);
|
|
251
|
+
logger.error({
|
|
252
|
+
error,
|
|
253
|
+
teamId
|
|
254
|
+
}, "Failed to open message shortcut modal");
|
|
255
|
+
if (responseUrl) await sendResponseUrlMessage(responseUrl, {
|
|
256
|
+
text: SlackStrings.errors.failedToOpenSelector,
|
|
257
|
+
response_type: "ephemeral"
|
|
258
|
+
}).catch((e) => logger.warn({ error: e }, "Failed to send shortcut error notification"));
|
|
259
|
+
span.end();
|
|
174
260
|
}
|
|
175
|
-
|
|
176
|
-
channel: channelId,
|
|
177
|
-
threadTs,
|
|
178
|
-
messageTs,
|
|
179
|
-
teamId,
|
|
180
|
-
slackUserId: userId,
|
|
181
|
-
tenantId,
|
|
182
|
-
isInThread: Boolean(threadTs),
|
|
183
|
-
messageContext: messageText
|
|
184
|
-
};
|
|
185
|
-
const modal = buildMessageShortcutModal({
|
|
186
|
-
projects: projectList,
|
|
187
|
-
agents: agentList.map((a) => ({
|
|
188
|
-
id: a.id,
|
|
189
|
-
name: a.name,
|
|
190
|
-
projectId: a.projectId,
|
|
191
|
-
projectName: a.projectName || a.projectId
|
|
192
|
-
})),
|
|
193
|
-
metadata: modalMetadata,
|
|
194
|
-
selectedProjectId: firstProject.id,
|
|
195
|
-
messageContext: messageText
|
|
196
|
-
});
|
|
197
|
-
await slackClient.views.open({
|
|
198
|
-
trigger_id: triggerId,
|
|
199
|
-
view: modal
|
|
200
|
-
});
|
|
201
|
-
logger.info({
|
|
202
|
-
teamId,
|
|
203
|
-
channelId,
|
|
204
|
-
messageTs,
|
|
205
|
-
projectCount: projectList.length,
|
|
206
|
-
agentCount: agentList.length
|
|
207
|
-
}, "Opened message shortcut modal");
|
|
208
|
-
} catch (error) {
|
|
209
|
-
logger.error({
|
|
210
|
-
error,
|
|
211
|
-
teamId
|
|
212
|
-
}, "Failed to open message shortcut modal");
|
|
213
|
-
if (responseUrl) await sendResponseUrlMessage(responseUrl, {
|
|
214
|
-
text: SlackStrings.errors.failedToOpenSelector,
|
|
215
|
-
response_type: "ephemeral"
|
|
216
|
-
}).catch((e) => logger.warn({ error: e }, "Failed to send shortcut error notification"));
|
|
217
|
-
}
|
|
261
|
+
});
|
|
218
262
|
}
|
|
219
263
|
|
|
220
264
|
//#endregion
|