@inkeep/agents-work-apps 0.0.0-dev-20260204182014 → 0.0.0-dev-20260204210021
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/db/index.d.ts +1 -2
- package/dist/db/index.js +1 -2
- package/dist/db/runDbClient.d.ts +2 -2
- package/dist/env.d.ts +2 -24
- package/dist/env.js +1 -12
- package/dist/github/routes/setup.d.ts +2 -2
- package/dist/github/routes/tokenExchange.d.ts +2 -2
- package/package.json +2 -10
- package/dist/db/manageDbClient.d.ts +0 -7
- package/dist/db/manageDbClient.js +0 -16
- package/dist/slack/index.d.ts +0 -19
- package/dist/slack/index.js +0 -29
- package/dist/slack/middleware/permissions.d.ts +0 -16
- package/dist/slack/middleware/permissions.js +0 -49
- package/dist/slack/routes/events.d.ts +0 -10
- package/dist/slack/routes/events.js +0 -319
- package/dist/slack/routes/index.d.ts +0 -11
- package/dist/slack/routes/index.js +0 -64
- package/dist/slack/routes/internal.d.ts +0 -10
- package/dist/slack/routes/internal.js +0 -107
- package/dist/slack/routes/oauth.d.ts +0 -12
- package/dist/slack/routes/oauth.js +0 -218
- package/dist/slack/routes/resources.d.ts +0 -10
- package/dist/slack/routes/resources.js +0 -163
- package/dist/slack/routes/users.d.ts +0 -15
- package/dist/slack/routes/users.js +0 -430
- package/dist/slack/routes/workspaces.d.ts +0 -10
- package/dist/slack/routes/workspaces.js +0 -828
- package/dist/slack/routes.d.ts +0 -7
- package/dist/slack/routes.js +0 -12
- package/dist/slack/services/agent-resolution.d.ts +0 -49
- package/dist/slack/services/agent-resolution.js +0 -135
- package/dist/slack/services/api-client.d.ts +0 -161
- package/dist/slack/services/api-client.js +0 -248
- package/dist/slack/services/auth/index.d.ts +0 -61
- package/dist/slack/services/auth/index.js +0 -164
- package/dist/slack/services/blocks/index.d.ts +0 -60
- package/dist/slack/services/blocks/index.js +0 -143
- package/dist/slack/services/client.d.ts +0 -78
- package/dist/slack/services/client.js +0 -152
- package/dist/slack/services/commands/index.d.ts +0 -15
- package/dist/slack/services/commands/index.js +0 -556
- package/dist/slack/services/events/app-mention.d.ts +0 -41
- package/dist/slack/services/events/app-mention.js +0 -212
- package/dist/slack/services/events/block-actions.d.ts +0 -47
- package/dist/slack/services/events/block-actions.js +0 -287
- package/dist/slack/services/events/index.d.ts +0 -6
- package/dist/slack/services/events/index.js +0 -7
- package/dist/slack/services/events/modal-submission.d.ts +0 -12
- package/dist/slack/services/events/modal-submission.js +0 -279
- package/dist/slack/services/events/streaming.d.ts +0 -27
- package/dist/slack/services/events/streaming.js +0 -285
- package/dist/slack/services/events/utils.d.ts +0 -129
- package/dist/slack/services/events/utils.js +0 -315
- package/dist/slack/services/index.d.ts +0 -18
- package/dist/slack/services/index.js +0 -18
- package/dist/slack/services/modals.d.ts +0 -67
- package/dist/slack/services/modals.js +0 -203
- package/dist/slack/services/nango.d.ts +0 -82
- package/dist/slack/services/nango.js +0 -326
- package/dist/slack/services/security.d.ts +0 -35
- package/dist/slack/services/security.js +0 -65
- package/dist/slack/services/types.d.ts +0 -26
- package/dist/slack/services/types.js +0 -1
- package/dist/slack/services/workspace-tokens.d.ts +0 -37
- package/dist/slack/services/workspace-tokens.js +0 -39
- package/dist/slack/types.d.ts +0 -10
- package/dist/slack/types.js +0 -1
|
@@ -1,279 +0,0 @@
|
|
|
1
|
-
import { env } from "../../../env.js";
|
|
2
|
-
import { getLogger } from "../../../logger.js";
|
|
3
|
-
import runDbClient_default from "../../../db/runDbClient.js";
|
|
4
|
-
import { findWorkspaceConnectionByTeamId } from "../nango.js";
|
|
5
|
-
import { getSlackClient } from "../client.js";
|
|
6
|
-
import { classifyError, getThreadContext, getUserFriendlyErrorMessage, markdownToMrkdwn, sendResponseUrlMessage } from "./utils.js";
|
|
7
|
-
import { findWorkAppSlackUserMapping, signSlackUserToken } from "@inkeep/agents-core";
|
|
8
|
-
|
|
9
|
-
//#region src/slack/services/events/modal-submission.ts
|
|
10
|
-
/**
|
|
11
|
-
* Handler for Slack modal submission events
|
|
12
|
-
*/
|
|
13
|
-
const logger = getLogger("slack-modal-submission");
|
|
14
|
-
async function handleModalSubmission(view) {
|
|
15
|
-
try {
|
|
16
|
-
const metadata = JSON.parse(view.private_metadata || "{}");
|
|
17
|
-
const values = view.state?.values || {};
|
|
18
|
-
const agentSelectValue = values.agent_select_block?.agent_select;
|
|
19
|
-
const questionValue = values.question_block?.question_input;
|
|
20
|
-
const visibilityValue = values.visibility_block?.visibility_checkbox;
|
|
21
|
-
const includeContextValue = values.context_block?.include_context_checkbox;
|
|
22
|
-
const question = questionValue?.value || "";
|
|
23
|
-
const isEphemeral = visibilityValue?.selected_options?.some((o) => o.value === "ephemeral") || false;
|
|
24
|
-
const includeContext = includeContextValue?.selected_options?.some((o) => o.value === "include_context") ?? true;
|
|
25
|
-
let agentId = metadata.selectedAgentId;
|
|
26
|
-
let projectId = metadata.selectedProjectId;
|
|
27
|
-
if (agentSelectValue?.selected_option?.value) try {
|
|
28
|
-
const parsed = JSON.parse(agentSelectValue.selected_option.value);
|
|
29
|
-
agentId = parsed.agentId;
|
|
30
|
-
projectId = parsed.projectId;
|
|
31
|
-
} catch {
|
|
32
|
-
logger.warn({ value: agentSelectValue.selected_option.value }, "Failed to parse agent select value");
|
|
33
|
-
}
|
|
34
|
-
if (!agentId || !projectId) {
|
|
35
|
-
logger.error({ metadata }, "Missing agent or project ID in modal submission");
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
const workspaceConnection = await findWorkspaceConnectionByTeamId(metadata.teamId);
|
|
39
|
-
if (!workspaceConnection?.botToken) {
|
|
40
|
-
logger.error({ teamId: metadata.teamId }, "No bot token for modal submission");
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
const slackClient = getSlackClient(workspaceConnection.botToken);
|
|
44
|
-
const tenantId = metadata.tenantId;
|
|
45
|
-
let fullQuestion = question;
|
|
46
|
-
if (metadata.isInThread && metadata.threadTs && includeContext) {
|
|
47
|
-
const contextMessages = await getThreadContext(slackClient, metadata.channel, metadata.threadTs);
|
|
48
|
-
if (contextMessages) fullQuestion = question ? `Based on the following conversation:\n\n${contextMessages}\n\nUser request: ${question}` : `Based on the following conversation, please provide a helpful response or summary:\n\n${contextMessages}`;
|
|
49
|
-
}
|
|
50
|
-
if (!fullQuestion) {
|
|
51
|
-
logger.warn({ metadata }, "No question provided in modal submission");
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
const existingLink = await findWorkAppSlackUserMapping(runDbClient_default)(tenantId, metadata.slackUserId, metadata.teamId, "work-apps-slack");
|
|
55
|
-
if (!existingLink) {
|
|
56
|
-
if (isEphemeral) await slackClient.chat.postEphemeral({
|
|
57
|
-
channel: metadata.channel,
|
|
58
|
-
user: metadata.slackUserId,
|
|
59
|
-
text: "🔗 You need to link your account first. Use `/inkeep link` to get started."
|
|
60
|
-
});
|
|
61
|
-
else await slackClient.chat.postMessage({
|
|
62
|
-
channel: metadata.channel,
|
|
63
|
-
thread_ts: metadata.threadTs || metadata.messageTs,
|
|
64
|
-
text: "🔗 You need to link your account first. Use `/inkeep link` to get started."
|
|
65
|
-
});
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
const slackUserToken = await signSlackUserToken({
|
|
69
|
-
inkeepUserId: existingLink.inkeepUserId,
|
|
70
|
-
tenantId,
|
|
71
|
-
slackTeamId: metadata.teamId,
|
|
72
|
-
slackUserId: metadata.slackUserId
|
|
73
|
-
});
|
|
74
|
-
const apiBaseUrl = env.INKEEP_AGENTS_API_URL || "http://localhost:3002";
|
|
75
|
-
const replyThreadTs = metadata.threadTs || metadata.messageTs;
|
|
76
|
-
let thinkingMessageTs;
|
|
77
|
-
if (isEphemeral) {
|
|
78
|
-
const thinkingPayload = {
|
|
79
|
-
channel: metadata.channel,
|
|
80
|
-
user: metadata.slackUserId,
|
|
81
|
-
text: `_${agentId} is thinking..._`
|
|
82
|
-
};
|
|
83
|
-
if (metadata.isInThread && metadata.threadTs) thinkingPayload.thread_ts = metadata.threadTs;
|
|
84
|
-
await slackClient.chat.postEphemeral(thinkingPayload);
|
|
85
|
-
} else thinkingMessageTs = (await slackClient.chat.postMessage({
|
|
86
|
-
channel: metadata.channel,
|
|
87
|
-
thread_ts: replyThreadTs,
|
|
88
|
-
text: `_${agentId} is thinking..._`
|
|
89
|
-
})).ts;
|
|
90
|
-
const response = await fetch(`${apiBaseUrl}/run/api/chat`, {
|
|
91
|
-
method: "POST",
|
|
92
|
-
headers: {
|
|
93
|
-
"Content-Type": "application/json",
|
|
94
|
-
Authorization: `Bearer ${slackUserToken}`,
|
|
95
|
-
"x-inkeep-project-id": projectId,
|
|
96
|
-
"x-inkeep-agent-id": agentId
|
|
97
|
-
},
|
|
98
|
-
body: JSON.stringify({
|
|
99
|
-
messages: [{
|
|
100
|
-
role: "user",
|
|
101
|
-
content: fullQuestion
|
|
102
|
-
}],
|
|
103
|
-
stream: false
|
|
104
|
-
})
|
|
105
|
-
});
|
|
106
|
-
let responseText = "No response received";
|
|
107
|
-
let isError = false;
|
|
108
|
-
if (response.ok) {
|
|
109
|
-
const result = await response.json();
|
|
110
|
-
responseText = markdownToMrkdwn(result.choices?.[0]?.message?.content || result.message?.content || responseText);
|
|
111
|
-
} else {
|
|
112
|
-
isError = true;
|
|
113
|
-
responseText = getUserFriendlyErrorMessage(classifyError(null, response.status), agentId);
|
|
114
|
-
logger.warn({
|
|
115
|
-
status: response.status,
|
|
116
|
-
statusText: response.statusText,
|
|
117
|
-
agentId
|
|
118
|
-
}, "Agent API returned error");
|
|
119
|
-
}
|
|
120
|
-
if (isEphemeral) try {
|
|
121
|
-
if (isError) {
|
|
122
|
-
const ephemeralPayload = {
|
|
123
|
-
channel: metadata.channel,
|
|
124
|
-
user: metadata.slackUserId,
|
|
125
|
-
text: responseText,
|
|
126
|
-
blocks: [{
|
|
127
|
-
type: "section",
|
|
128
|
-
text: {
|
|
129
|
-
type: "mrkdwn",
|
|
130
|
-
text: responseText
|
|
131
|
-
}
|
|
132
|
-
}]
|
|
133
|
-
};
|
|
134
|
-
if (metadata.isInThread && metadata.threadTs) ephemeralPayload.thread_ts = metadata.threadTs;
|
|
135
|
-
await slackClient.chat.postEphemeral(ephemeralPayload);
|
|
136
|
-
} else {
|
|
137
|
-
const shareButtons = [];
|
|
138
|
-
if (metadata.isInThread && metadata.threadTs) shareButtons.push({
|
|
139
|
-
type: "button",
|
|
140
|
-
text: {
|
|
141
|
-
type: "plain_text",
|
|
142
|
-
text: "Share to Thread",
|
|
143
|
-
emoji: true
|
|
144
|
-
},
|
|
145
|
-
action_id: "share_to_thread",
|
|
146
|
-
style: "primary",
|
|
147
|
-
value: JSON.stringify({
|
|
148
|
-
channelId: metadata.channel,
|
|
149
|
-
threadTs: metadata.threadTs,
|
|
150
|
-
text: responseText,
|
|
151
|
-
agentName: agentId
|
|
152
|
-
})
|
|
153
|
-
});
|
|
154
|
-
shareButtons.push({
|
|
155
|
-
type: "button",
|
|
156
|
-
text: {
|
|
157
|
-
type: "plain_text",
|
|
158
|
-
text: "Share to Channel",
|
|
159
|
-
emoji: true
|
|
160
|
-
},
|
|
161
|
-
action_id: "share_to_channel",
|
|
162
|
-
value: JSON.stringify({
|
|
163
|
-
channelId: metadata.channel,
|
|
164
|
-
text: responseText,
|
|
165
|
-
agentName: agentId
|
|
166
|
-
})
|
|
167
|
-
});
|
|
168
|
-
const ephemeralPayload = {
|
|
169
|
-
channel: metadata.channel,
|
|
170
|
-
user: metadata.slackUserId,
|
|
171
|
-
text: responseText,
|
|
172
|
-
blocks: [
|
|
173
|
-
{
|
|
174
|
-
type: "section",
|
|
175
|
-
text: {
|
|
176
|
-
type: "mrkdwn",
|
|
177
|
-
text: responseText
|
|
178
|
-
}
|
|
179
|
-
},
|
|
180
|
-
{
|
|
181
|
-
type: "context",
|
|
182
|
-
elements: [{
|
|
183
|
-
type: "mrkdwn",
|
|
184
|
-
text: `Powered by *${agentId}* via Inkeep • Only visible to you`
|
|
185
|
-
}]
|
|
186
|
-
},
|
|
187
|
-
{
|
|
188
|
-
type: "actions",
|
|
189
|
-
elements: shareButtons
|
|
190
|
-
}
|
|
191
|
-
]
|
|
192
|
-
};
|
|
193
|
-
if (metadata.isInThread && metadata.threadTs) ephemeralPayload.thread_ts = metadata.threadTs;
|
|
194
|
-
await slackClient.chat.postEphemeral(ephemeralPayload);
|
|
195
|
-
}
|
|
196
|
-
} catch (ephemeralError) {
|
|
197
|
-
logger.error({ ephemeralError }, "Failed to post ephemeral message");
|
|
198
|
-
}
|
|
199
|
-
else {
|
|
200
|
-
if (isError) await slackClient.chat.postMessage({
|
|
201
|
-
channel: metadata.channel,
|
|
202
|
-
thread_ts: replyThreadTs,
|
|
203
|
-
text: responseText,
|
|
204
|
-
blocks: [{
|
|
205
|
-
type: "section",
|
|
206
|
-
text: {
|
|
207
|
-
type: "mrkdwn",
|
|
208
|
-
text: responseText
|
|
209
|
-
}
|
|
210
|
-
}]
|
|
211
|
-
});
|
|
212
|
-
else await slackClient.chat.postMessage({
|
|
213
|
-
channel: metadata.channel,
|
|
214
|
-
thread_ts: replyThreadTs,
|
|
215
|
-
text: responseText,
|
|
216
|
-
blocks: [{
|
|
217
|
-
type: "section",
|
|
218
|
-
text: {
|
|
219
|
-
type: "mrkdwn",
|
|
220
|
-
text: responseText
|
|
221
|
-
}
|
|
222
|
-
}, {
|
|
223
|
-
type: "context",
|
|
224
|
-
elements: [{
|
|
225
|
-
type: "mrkdwn",
|
|
226
|
-
text: `Powered by *${agentId}* via Inkeep`
|
|
227
|
-
}]
|
|
228
|
-
}]
|
|
229
|
-
});
|
|
230
|
-
if (thinkingMessageTs) try {
|
|
231
|
-
await slackClient.chat.delete({
|
|
232
|
-
channel: metadata.channel,
|
|
233
|
-
ts: thinkingMessageTs
|
|
234
|
-
});
|
|
235
|
-
} catch (deleteError) {
|
|
236
|
-
logger.warn({ deleteError }, "Failed to delete thinking message");
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
if (metadata.buttonResponseUrl) try {
|
|
240
|
-
await sendResponseUrlMessage(metadata.buttonResponseUrl, {
|
|
241
|
-
text: "",
|
|
242
|
-
delete_original: true
|
|
243
|
-
});
|
|
244
|
-
} catch (deleteError) {
|
|
245
|
-
logger.warn({ deleteError }, "Failed to delete button message");
|
|
246
|
-
}
|
|
247
|
-
logger.info({
|
|
248
|
-
agentId,
|
|
249
|
-
projectId,
|
|
250
|
-
tenantId,
|
|
251
|
-
slackUserId: metadata.slackUserId,
|
|
252
|
-
isEphemeral
|
|
253
|
-
}, "Modal submission agent execution completed");
|
|
254
|
-
} catch (error) {
|
|
255
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
256
|
-
logger.error({
|
|
257
|
-
errorMessage: errorMsg,
|
|
258
|
-
view
|
|
259
|
-
}, "Failed to handle modal submission");
|
|
260
|
-
try {
|
|
261
|
-
const metadata = JSON.parse(view.private_metadata || "{}");
|
|
262
|
-
const workspaceConnection = await findWorkspaceConnectionByTeamId(metadata.teamId);
|
|
263
|
-
if (workspaceConnection?.botToken) {
|
|
264
|
-
const slackClient = getSlackClient(workspaceConnection.botToken);
|
|
265
|
-
const userMessage = getUserFriendlyErrorMessage(classifyError(error));
|
|
266
|
-
await slackClient.chat.postEphemeral({
|
|
267
|
-
channel: metadata.channel,
|
|
268
|
-
user: metadata.slackUserId,
|
|
269
|
-
text: userMessage
|
|
270
|
-
});
|
|
271
|
-
}
|
|
272
|
-
} catch (notifyError) {
|
|
273
|
-
logger.error({ notifyError }, "Failed to notify user of modal submission error");
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
//#endregion
|
|
279
|
-
export { handleModalSubmission };
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { getSlackClient } from "../client.js";
|
|
2
|
-
import { SlackErrorType } from "./utils.js";
|
|
3
|
-
|
|
4
|
-
//#region src/slack/services/events/streaming.d.ts
|
|
5
|
-
|
|
6
|
-
interface StreamResult {
|
|
7
|
-
success: boolean;
|
|
8
|
-
errorType?: SlackErrorType;
|
|
9
|
-
errorMessage?: string;
|
|
10
|
-
}
|
|
11
|
-
declare function streamAgentResponse(params: {
|
|
12
|
-
slackClient: ReturnType<typeof getSlackClient>;
|
|
13
|
-
channel: string;
|
|
14
|
-
threadTs: string;
|
|
15
|
-
thinkingMessageTs: string;
|
|
16
|
-
slackUserId: string;
|
|
17
|
-
teamId: string;
|
|
18
|
-
jwtToken: string;
|
|
19
|
-
projectId: string;
|
|
20
|
-
agentId: string;
|
|
21
|
-
question: string;
|
|
22
|
-
agentName: string;
|
|
23
|
-
conversationId?: string;
|
|
24
|
-
isEphemeral?: boolean;
|
|
25
|
-
}): Promise<StreamResult>;
|
|
26
|
-
//#endregion
|
|
27
|
-
export { StreamResult, streamAgentResponse };
|
|
@@ -1,285 +0,0 @@
|
|
|
1
|
-
import { env } from "../../../env.js";
|
|
2
|
-
import { getLogger } from "../../../logger.js";
|
|
3
|
-
import { SlackErrorType, classifyError, getUserFriendlyErrorMessage, markdownToMrkdwn } from "./utils.js";
|
|
4
|
-
|
|
5
|
-
//#region src/slack/services/events/streaming.ts
|
|
6
|
-
/**
|
|
7
|
-
* Slack streaming utilities for agent responses
|
|
8
|
-
*
|
|
9
|
-
* Uses SlackUserToken JWT for authentication to Run API.
|
|
10
|
-
*/
|
|
11
|
-
const logger = getLogger("slack-streaming");
|
|
12
|
-
async function streamAgentResponse(params) {
|
|
13
|
-
const { slackClient, channel, threadTs, thinkingMessageTs, slackUserId, teamId, jwtToken, projectId, agentId, question, agentName, conversationId, isEphemeral = false } = params;
|
|
14
|
-
const apiUrl = env.INKEEP_AGENTS_API_URL || "http://localhost:3002";
|
|
15
|
-
logger.debug({
|
|
16
|
-
conversationId,
|
|
17
|
-
channel,
|
|
18
|
-
threadTs
|
|
19
|
-
}, "Streaming agent response with conversation context");
|
|
20
|
-
const response = await fetch(`${apiUrl.replace(/\/$/, "")}/run/api/chat`, {
|
|
21
|
-
method: "POST",
|
|
22
|
-
headers: {
|
|
23
|
-
"Content-Type": "application/json",
|
|
24
|
-
Authorization: `Bearer ${jwtToken}`,
|
|
25
|
-
"x-inkeep-project-id": projectId,
|
|
26
|
-
"x-inkeep-agent-id": agentId
|
|
27
|
-
},
|
|
28
|
-
body: JSON.stringify({
|
|
29
|
-
messages: [{
|
|
30
|
-
role: "user",
|
|
31
|
-
content: question
|
|
32
|
-
}],
|
|
33
|
-
stream: true,
|
|
34
|
-
...conversationId && { conversationId }
|
|
35
|
-
})
|
|
36
|
-
});
|
|
37
|
-
if (!response.ok) {
|
|
38
|
-
const errorBody = await response.text().catch(() => "Unknown error");
|
|
39
|
-
logger.error({
|
|
40
|
-
status: response.status,
|
|
41
|
-
errorBody
|
|
42
|
-
}, "Agent streaming request failed");
|
|
43
|
-
const errorType = classifyError(null, response.status);
|
|
44
|
-
const errorMessage = getUserFriendlyErrorMessage(errorType, agentName);
|
|
45
|
-
if (isEphemeral) await slackClient.chat.postEphemeral({
|
|
46
|
-
channel,
|
|
47
|
-
user: slackUserId,
|
|
48
|
-
thread_ts: threadTs,
|
|
49
|
-
text: errorMessage
|
|
50
|
-
});
|
|
51
|
-
else await slackClient.chat.postMessage({
|
|
52
|
-
channel,
|
|
53
|
-
thread_ts: threadTs,
|
|
54
|
-
text: errorMessage
|
|
55
|
-
});
|
|
56
|
-
if (thinkingMessageTs) try {
|
|
57
|
-
await slackClient.chat.delete({
|
|
58
|
-
channel,
|
|
59
|
-
ts: thinkingMessageTs
|
|
60
|
-
});
|
|
61
|
-
} catch {}
|
|
62
|
-
return {
|
|
63
|
-
success: false,
|
|
64
|
-
errorType,
|
|
65
|
-
errorMessage
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
if (!response.body) {
|
|
69
|
-
const errorType = SlackErrorType.API_ERROR;
|
|
70
|
-
const errorMessage = getUserFriendlyErrorMessage(errorType, agentName);
|
|
71
|
-
await slackClient.chat.postMessage({
|
|
72
|
-
channel,
|
|
73
|
-
thread_ts: threadTs,
|
|
74
|
-
text: errorMessage
|
|
75
|
-
});
|
|
76
|
-
if (thinkingMessageTs) try {
|
|
77
|
-
await slackClient.chat.delete({
|
|
78
|
-
channel,
|
|
79
|
-
ts: thinkingMessageTs
|
|
80
|
-
});
|
|
81
|
-
} catch {}
|
|
82
|
-
return {
|
|
83
|
-
success: false,
|
|
84
|
-
errorType,
|
|
85
|
-
errorMessage
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
const reader = response.body.getReader();
|
|
89
|
-
const decoder = new TextDecoder();
|
|
90
|
-
let buffer = "";
|
|
91
|
-
let fullText = "";
|
|
92
|
-
if (isEphemeral) try {
|
|
93
|
-
while (true) {
|
|
94
|
-
const { done, value } = await reader.read();
|
|
95
|
-
if (done) break;
|
|
96
|
-
buffer += decoder.decode(value, { stream: true });
|
|
97
|
-
const lines = buffer.split("\n");
|
|
98
|
-
buffer = lines.pop() || "";
|
|
99
|
-
for (const line of lines) {
|
|
100
|
-
if (!line.startsWith("data: ")) continue;
|
|
101
|
-
const jsonStr = line.slice(6).trim();
|
|
102
|
-
if (!jsonStr || jsonStr === "[DONE]") continue;
|
|
103
|
-
try {
|
|
104
|
-
const data = JSON.parse(jsonStr);
|
|
105
|
-
if (data.type === "data-operation" || data.type === "text-start" || data.type === "text-end") continue;
|
|
106
|
-
if (data.type === "text-delta" && data.delta) fullText += data.delta;
|
|
107
|
-
else if (data.object === "chat.completion.chunk" && data.choices?.[0]?.delta?.content) {
|
|
108
|
-
const content = data.choices[0].delta.content;
|
|
109
|
-
try {
|
|
110
|
-
if (JSON.parse(content).type === "data-operation") continue;
|
|
111
|
-
} catch {}
|
|
112
|
-
fullText += content;
|
|
113
|
-
}
|
|
114
|
-
} catch {}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
const formattedText = markdownToMrkdwn(fullText);
|
|
118
|
-
const contextBlock = {
|
|
119
|
-
type: "context",
|
|
120
|
-
elements: [{
|
|
121
|
-
type: "mrkdwn",
|
|
122
|
-
text: `_Private response_ • Powered by *${agentName}* via Inkeep`
|
|
123
|
-
}]
|
|
124
|
-
};
|
|
125
|
-
const shareButtons = [];
|
|
126
|
-
if (threadTs) shareButtons.push({
|
|
127
|
-
type: "button",
|
|
128
|
-
text: {
|
|
129
|
-
type: "plain_text",
|
|
130
|
-
text: "Share to Thread",
|
|
131
|
-
emoji: true
|
|
132
|
-
},
|
|
133
|
-
action_id: "share_to_thread",
|
|
134
|
-
style: "primary",
|
|
135
|
-
value: JSON.stringify({
|
|
136
|
-
channelId: channel,
|
|
137
|
-
threadTs,
|
|
138
|
-
text: formattedText,
|
|
139
|
-
agentName
|
|
140
|
-
})
|
|
141
|
-
});
|
|
142
|
-
shareButtons.push({
|
|
143
|
-
type: "button",
|
|
144
|
-
text: {
|
|
145
|
-
type: "plain_text",
|
|
146
|
-
text: "Share to Channel",
|
|
147
|
-
emoji: true
|
|
148
|
-
},
|
|
149
|
-
action_id: "share_to_channel",
|
|
150
|
-
value: JSON.stringify({
|
|
151
|
-
channelId: channel,
|
|
152
|
-
text: formattedText,
|
|
153
|
-
agentName
|
|
154
|
-
})
|
|
155
|
-
});
|
|
156
|
-
await slackClient.chat.postEphemeral({
|
|
157
|
-
channel,
|
|
158
|
-
user: slackUserId,
|
|
159
|
-
thread_ts: threadTs,
|
|
160
|
-
text: formattedText,
|
|
161
|
-
blocks: [
|
|
162
|
-
{
|
|
163
|
-
type: "section",
|
|
164
|
-
text: {
|
|
165
|
-
type: "mrkdwn",
|
|
166
|
-
text: formattedText
|
|
167
|
-
}
|
|
168
|
-
},
|
|
169
|
-
contextBlock,
|
|
170
|
-
{
|
|
171
|
-
type: "actions",
|
|
172
|
-
elements: shareButtons
|
|
173
|
-
}
|
|
174
|
-
]
|
|
175
|
-
});
|
|
176
|
-
logger.debug({
|
|
177
|
-
channel,
|
|
178
|
-
threadTs,
|
|
179
|
-
responseLength: fullText.length,
|
|
180
|
-
isEphemeral
|
|
181
|
-
}, "Ephemeral response posted");
|
|
182
|
-
return { success: true };
|
|
183
|
-
} catch (error) {
|
|
184
|
-
logger.error({ error }, "Error posting ephemeral response");
|
|
185
|
-
const errorType = classifyError(error);
|
|
186
|
-
const errorMessage = getUserFriendlyErrorMessage(errorType, agentName);
|
|
187
|
-
try {
|
|
188
|
-
await slackClient.chat.postEphemeral({
|
|
189
|
-
channel,
|
|
190
|
-
user: slackUserId,
|
|
191
|
-
thread_ts: threadTs,
|
|
192
|
-
text: errorMessage
|
|
193
|
-
});
|
|
194
|
-
} catch {}
|
|
195
|
-
return {
|
|
196
|
-
success: false,
|
|
197
|
-
errorType,
|
|
198
|
-
errorMessage
|
|
199
|
-
};
|
|
200
|
-
}
|
|
201
|
-
const streamer = slackClient.chatStream({
|
|
202
|
-
channel,
|
|
203
|
-
recipient_team_id: teamId,
|
|
204
|
-
recipient_user_id: slackUserId,
|
|
205
|
-
thread_ts: threadTs
|
|
206
|
-
});
|
|
207
|
-
try {
|
|
208
|
-
while (true) {
|
|
209
|
-
const { done, value } = await reader.read();
|
|
210
|
-
if (done) break;
|
|
211
|
-
buffer += decoder.decode(value, { stream: true });
|
|
212
|
-
const lines = buffer.split("\n");
|
|
213
|
-
buffer = lines.pop() || "";
|
|
214
|
-
for (const line of lines) {
|
|
215
|
-
if (!line.startsWith("data: ")) continue;
|
|
216
|
-
const jsonStr = line.slice(6).trim();
|
|
217
|
-
if (!jsonStr || jsonStr === "[DONE]") continue;
|
|
218
|
-
try {
|
|
219
|
-
const data = JSON.parse(jsonStr);
|
|
220
|
-
if (data.type === "data-operation") continue;
|
|
221
|
-
if (data.type === "text-start" || data.type === "text-end") continue;
|
|
222
|
-
if (data.type === "text-delta" && data.delta) {
|
|
223
|
-
fullText += data.delta;
|
|
224
|
-
await streamer.append({ markdown_text: data.delta });
|
|
225
|
-
} else if (data.object === "chat.completion.chunk" && data.choices?.[0]?.delta?.content) {
|
|
226
|
-
const content = data.choices[0].delta.content;
|
|
227
|
-
try {
|
|
228
|
-
if (JSON.parse(content).type === "data-operation") continue;
|
|
229
|
-
} catch {}
|
|
230
|
-
fullText += content;
|
|
231
|
-
await streamer.append({ markdown_text: content });
|
|
232
|
-
}
|
|
233
|
-
} catch {}
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
const contextBlock = {
|
|
237
|
-
type: "context",
|
|
238
|
-
elements: [{
|
|
239
|
-
type: "mrkdwn",
|
|
240
|
-
text: `Powered by *${agentName}* via Inkeep`
|
|
241
|
-
}]
|
|
242
|
-
};
|
|
243
|
-
await streamer.stop({ blocks: [contextBlock] });
|
|
244
|
-
if (thinkingMessageTs) try {
|
|
245
|
-
await slackClient.chat.delete({
|
|
246
|
-
channel,
|
|
247
|
-
ts: thinkingMessageTs
|
|
248
|
-
});
|
|
249
|
-
} catch (deleteError) {
|
|
250
|
-
logger.warn({ deleteError }, "Failed to delete acknowledgement message");
|
|
251
|
-
}
|
|
252
|
-
logger.debug({
|
|
253
|
-
channel,
|
|
254
|
-
threadTs,
|
|
255
|
-
responseLength: fullText.length
|
|
256
|
-
}, "Streaming completed");
|
|
257
|
-
return { success: true };
|
|
258
|
-
} catch (streamError) {
|
|
259
|
-
logger.error({ streamError }, "Error during Slack streaming");
|
|
260
|
-
await streamer.stop();
|
|
261
|
-
if (thinkingMessageTs) try {
|
|
262
|
-
await slackClient.chat.delete({
|
|
263
|
-
channel,
|
|
264
|
-
ts: thinkingMessageTs
|
|
265
|
-
});
|
|
266
|
-
} catch {}
|
|
267
|
-
const errorType = classifyError(streamError);
|
|
268
|
-
const errorMessage = getUserFriendlyErrorMessage(errorType, agentName);
|
|
269
|
-
try {
|
|
270
|
-
await slackClient.chat.postMessage({
|
|
271
|
-
channel,
|
|
272
|
-
thread_ts: threadTs,
|
|
273
|
-
text: errorMessage
|
|
274
|
-
});
|
|
275
|
-
} catch {}
|
|
276
|
-
return {
|
|
277
|
-
success: false,
|
|
278
|
-
errorType,
|
|
279
|
-
errorMessage
|
|
280
|
-
};
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
//#endregion
|
|
285
|
-
export { streamAgentResponse };
|