@inkeep/agents-work-apps 0.53.1 → 0.53.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/env.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/webhooks.d.ts +2 -2
- package/dist/slack/dispatcher.js +54 -40
- package/dist/slack/i18n/strings.d.ts +6 -5
- package/dist/slack/i18n/strings.js +7 -10
- package/dist/slack/routes/events.js +1 -1
- package/dist/slack/services/blocks/index.d.ts +3 -35
- package/dist/slack/services/blocks/index.js +5 -42
- package/dist/slack/services/commands/index.js +42 -104
- package/dist/slack/services/events/app-mention.js +8 -31
- package/dist/slack/services/events/block-actions.d.ts +1 -11
- package/dist/slack/services/events/block-actions.js +6 -49
- package/dist/slack/services/events/direct-message.d.ts +11 -0
- package/dist/slack/services/events/direct-message.js +148 -0
- package/dist/slack/services/events/execution.d.ts +20 -0
- package/dist/slack/services/events/execution.js +46 -0
- package/dist/slack/services/events/index.d.ts +5 -3
- package/dist/slack/services/events/index.js +5 -3
- package/dist/slack/services/events/modal-submission.d.ts +1 -21
- package/dist/slack/services/events/modal-submission.js +14 -294
- package/dist/slack/services/events/streaming.d.ts +1 -1
- package/dist/slack/services/events/streaming.js +69 -70
- package/dist/slack/services/events/utils.d.ts +2 -14
- package/dist/slack/services/events/utils.js +2 -13
- package/dist/slack/services/index.d.ts +7 -5
- package/dist/slack/services/index.js +8 -6
- package/dist/slack/services/link-prompt.d.ts +2 -2
- package/dist/slack/services/modals.d.ts +1 -18
- package/dist/slack/services/modals.js +1 -48
- package/dist/slack/services/resume-intent.js +43 -3
- package/dist/slack/socket-mode.js +1 -1
- package/dist/slack/tracer.d.ts +2 -4
- package/dist/slack/tracer.js +1 -3
- package/package.json +2 -2
package/dist/env.d.ts
CHANGED
|
@@ -14,11 +14,11 @@ declare const envSchema: z.ZodObject<{
|
|
|
14
14
|
pentest: "pentest";
|
|
15
15
|
}>>;
|
|
16
16
|
LOG_LEVEL: z.ZodDefault<z.ZodEnum<{
|
|
17
|
-
error: "error";
|
|
18
17
|
trace: "trace";
|
|
19
18
|
debug: "debug";
|
|
20
19
|
info: "info";
|
|
21
20
|
warn: "warn";
|
|
21
|
+
error: "error";
|
|
22
22
|
}>>;
|
|
23
23
|
INKEEP_AGENTS_RUN_DATABASE_URL: z.ZodOptional<z.ZodString>;
|
|
24
24
|
INKEEP_AGENTS_MANAGE_UI_URL: z.ZodOptional<z.ZodString>;
|
|
@@ -44,7 +44,7 @@ declare const envSchema: z.ZodObject<{
|
|
|
44
44
|
declare const env: {
|
|
45
45
|
NODE_ENV: "development" | "production" | "test";
|
|
46
46
|
ENVIRONMENT: "development" | "production" | "test" | "pentest";
|
|
47
|
-
LOG_LEVEL: "
|
|
47
|
+
LOG_LEVEL: "trace" | "debug" | "info" | "warn" | "error";
|
|
48
48
|
INKEEP_AGENTS_RUN_DATABASE_URL?: string | undefined;
|
|
49
49
|
INKEEP_AGENTS_MANAGE_UI_URL?: string | undefined;
|
|
50
50
|
GITHUB_APP_ID?: string | undefined;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
|
-
import * as
|
|
2
|
+
import * as hono_types3 from "hono/types";
|
|
3
3
|
|
|
4
4
|
//#region src/github/mcp/index.d.ts
|
|
5
5
|
declare const app: Hono<{
|
|
6
6
|
Variables: {
|
|
7
7
|
toolId: string;
|
|
8
8
|
};
|
|
9
|
-
},
|
|
9
|
+
}, hono_types3.BlankSchema, "/">;
|
|
10
10
|
//#endregion
|
|
11
11
|
export { app as default };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
|
-
import * as
|
|
2
|
+
import * as hono_types6 from "hono/types";
|
|
3
3
|
|
|
4
4
|
//#region src/github/routes/setup.d.ts
|
|
5
|
-
declare const app: Hono<
|
|
5
|
+
declare const app: Hono<hono_types6.BlankEnv, hono_types6.BlankSchema, "/">;
|
|
6
6
|
//#endregion
|
|
7
7
|
export { app as default };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
|
-
import * as
|
|
2
|
+
import * as hono_types4 from "hono/types";
|
|
3
3
|
|
|
4
4
|
//#region src/github/routes/webhooks.d.ts
|
|
5
5
|
interface WebhookVerificationResult {
|
|
@@ -7,6 +7,6 @@ interface WebhookVerificationResult {
|
|
|
7
7
|
error?: string;
|
|
8
8
|
}
|
|
9
9
|
declare function verifyWebhookSignature(payload: string, signature: string | undefined, secret: string): WebhookVerificationResult;
|
|
10
|
-
declare const app: Hono<
|
|
10
|
+
declare const app: Hono<hono_types4.BlankEnv, hono_types4.BlankSchema, "/">;
|
|
11
11
|
//#endregion
|
|
12
12
|
export { WebhookVerificationResult, app as default, verifyWebhookSignature };
|
package/dist/slack/dispatcher.js
CHANGED
|
@@ -4,18 +4,44 @@ import { sendResponseUrlMessage } from "./services/events/utils.js";
|
|
|
4
4
|
import { getSlackClient } from "./services/client.js";
|
|
5
5
|
import { SLACK_SPAN_KEYS } from "./tracer.js";
|
|
6
6
|
import { handleAppMention } from "./services/events/app-mention.js";
|
|
7
|
-
import { handleMessageShortcut, handleOpenAgentSelectorModal,
|
|
8
|
-
import {
|
|
7
|
+
import { handleMessageShortcut, handleOpenAgentSelectorModal, handleToolApproval } from "./services/events/block-actions.js";
|
|
8
|
+
import { handleDirectMessage } from "./services/events/direct-message.js";
|
|
9
|
+
import { handleModalSubmission } from "./services/events/modal-submission.js";
|
|
9
10
|
import "./services/events/index.js";
|
|
10
11
|
import "./services/index.js";
|
|
11
12
|
import { flushTraces } from "@inkeep/agents-core";
|
|
12
13
|
|
|
13
14
|
//#region src/slack/dispatcher.ts
|
|
14
15
|
const logger = getLogger("slack-dispatcher");
|
|
16
|
+
/**
|
|
17
|
+
* Simple in-memory deduplication for Slack events.
|
|
18
|
+
* Slack may deliver the same event more than once (Socket Mode reconnections,
|
|
19
|
+
* edge cases in the Events API). We track recent event IDs to prevent
|
|
20
|
+
* duplicate processing. Entries expire after 5 minutes.
|
|
21
|
+
*/
|
|
22
|
+
const DEDUP_TTL_MS = 300 * 1e3;
|
|
23
|
+
const recentEventIds = /* @__PURE__ */ new Map();
|
|
24
|
+
function isDuplicateEvent(eventId) {
|
|
25
|
+
if (!eventId) return false;
|
|
26
|
+
const now = Date.now();
|
|
27
|
+
if (recentEventIds.has(eventId)) return true;
|
|
28
|
+
recentEventIds.set(eventId, now);
|
|
29
|
+
if (recentEventIds.size > 500) {
|
|
30
|
+
for (const [id, ts] of recentEventIds) if (now - ts > DEDUP_TTL_MS) recentEventIds.delete(id);
|
|
31
|
+
}
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
15
34
|
async function dispatchSlackEvent(eventType, payload, options, span) {
|
|
16
35
|
const { registerBackgroundWork } = options;
|
|
17
36
|
let outcome = "ignored_unknown_event";
|
|
18
37
|
if (eventType === "event_callback") {
|
|
38
|
+
const eventId = payload.event_id;
|
|
39
|
+
if (isDuplicateEvent(eventId)) {
|
|
40
|
+
outcome = "ignored_duplicate_event";
|
|
41
|
+
span.setAttribute(SLACK_SPAN_KEYS.OUTCOME, outcome);
|
|
42
|
+
logger.info({ eventId }, "Ignoring duplicate event");
|
|
43
|
+
return { outcome };
|
|
44
|
+
}
|
|
19
45
|
const teamId = payload.team_id;
|
|
20
46
|
const event = payload.event;
|
|
21
47
|
const innerEventType = event?.type || "unknown";
|
|
@@ -44,7 +70,7 @@ async function dispatchSlackEvent(eventType, payload, options, span) {
|
|
|
44
70
|
}, "Ignoring edited message");
|
|
45
71
|
return { outcome };
|
|
46
72
|
}
|
|
47
|
-
if (event?.type === "app_mention" && event.channel && event.user && teamId) {
|
|
73
|
+
if (event?.type === "app_mention" && event.channel_type !== "im" && event.channel && event.user && teamId) {
|
|
48
74
|
outcome = "handled";
|
|
49
75
|
span.setAttribute(SLACK_SPAN_KEYS.OUTCOME, outcome);
|
|
50
76
|
const question = (event.text || "").replace(/<@[A-Z0-9]+>/g, "").trim();
|
|
@@ -80,6 +106,31 @@ async function dispatchSlackEvent(eventType, payload, options, span) {
|
|
|
80
106
|
channel: event.channel,
|
|
81
107
|
dispatchedAt
|
|
82
108
|
}, "app_mention work registered");
|
|
109
|
+
} else if (event?.type === "message" && event.channel_type === "im" && event.channel && event.user && teamId) {
|
|
110
|
+
outcome = "handled";
|
|
111
|
+
span.setAttribute(SLACK_SPAN_KEYS.OUTCOME, outcome);
|
|
112
|
+
if (event.thread_ts) span.setAttribute(SLACK_SPAN_KEYS.THREAD_TS, event.thread_ts);
|
|
113
|
+
if (event.ts) span.setAttribute(SLACK_SPAN_KEYS.MESSAGE_TS, event.ts);
|
|
114
|
+
logger.info({
|
|
115
|
+
userId: event.user,
|
|
116
|
+
channel: event.channel,
|
|
117
|
+
teamId
|
|
118
|
+
}, "Handling event: message.im");
|
|
119
|
+
registerBackgroundWork(handleDirectMessage({
|
|
120
|
+
slackUserId: event.user,
|
|
121
|
+
channel: event.channel,
|
|
122
|
+
text: event.text || "",
|
|
123
|
+
threadTs: event.thread_ts,
|
|
124
|
+
messageTs: event.ts || "",
|
|
125
|
+
teamId
|
|
126
|
+
}).catch((err) => {
|
|
127
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
128
|
+
const errorStack = err instanceof Error ? err.stack : void 0;
|
|
129
|
+
logger.error({
|
|
130
|
+
errorMessage,
|
|
131
|
+
errorStack
|
|
132
|
+
}, "Failed to handle direct message (outer catch)");
|
|
133
|
+
}).finally(() => flushTraces()));
|
|
83
134
|
} else {
|
|
84
135
|
outcome = "ignored_unknown_event";
|
|
85
136
|
span.setAttribute(SLACK_SPAN_KEYS.OUTCOME, outcome);
|
|
@@ -209,29 +260,6 @@ async function dispatchSlackEvent(eventType, payload, options, span) {
|
|
|
209
260
|
}, "Failed to handle tool approval");
|
|
210
261
|
}).finally(() => flushTraces()));
|
|
211
262
|
}
|
|
212
|
-
if (action.action_id === "open_follow_up_modal" && action.value && triggerId) {
|
|
213
|
-
anyHandled = true;
|
|
214
|
-
logger.info({
|
|
215
|
-
teamId,
|
|
216
|
-
actionId: action.action_id
|
|
217
|
-
}, "Handling block_action: open_follow_up_modal");
|
|
218
|
-
registerBackgroundWork(handleOpenFollowUpModal({
|
|
219
|
-
triggerId,
|
|
220
|
-
actionValue: action.value,
|
|
221
|
-
teamId,
|
|
222
|
-
responseUrl: responseUrl || void 0
|
|
223
|
-
}).catch(async (err) => {
|
|
224
|
-
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
225
|
-
logger.error({
|
|
226
|
-
errorMessage,
|
|
227
|
-
actionId: action.action_id
|
|
228
|
-
}, "Failed to open follow-up modal");
|
|
229
|
-
if (responseUrl) await sendResponseUrlMessage(responseUrl, {
|
|
230
|
-
text: "Sorry, something went wrong while opening the follow-up form. Please try again.",
|
|
231
|
-
response_type: "ephemeral"
|
|
232
|
-
}).catch((e) => logger.warn({ error: e }, "Failed to send error notification via response URL"));
|
|
233
|
-
}).finally(() => flushTraces()));
|
|
234
|
-
}
|
|
235
263
|
}
|
|
236
264
|
outcome = anyHandled ? "handled" : "ignored_no_action_match";
|
|
237
265
|
span.setAttribute(SLACK_SPAN_KEYS.OUTCOME, outcome);
|
|
@@ -338,20 +366,6 @@ async function dispatchSlackEvent(eventType, payload, options, span) {
|
|
|
338
366
|
}).finally(() => flushTraces()));
|
|
339
367
|
return { outcome };
|
|
340
368
|
}
|
|
341
|
-
if (callbackId === "follow_up_modal") {
|
|
342
|
-
const view = payload.view;
|
|
343
|
-
outcome = "handled";
|
|
344
|
-
span.setAttribute(SLACK_SPAN_KEYS.OUTCOME, outcome);
|
|
345
|
-
logger.info({ callbackId }, "Handling view_submission: follow_up_modal");
|
|
346
|
-
registerBackgroundWork(handleFollowUpSubmission(view).catch((err) => {
|
|
347
|
-
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
348
|
-
logger.error({
|
|
349
|
-
errorMessage,
|
|
350
|
-
callbackId
|
|
351
|
-
}, "Failed to handle follow-up submission");
|
|
352
|
-
}).finally(() => flushTraces()));
|
|
353
|
-
return { outcome };
|
|
354
|
-
}
|
|
355
369
|
outcome = "ignored_unknown_event";
|
|
356
370
|
span.setAttribute(SLACK_SPAN_KEYS.OUTCOME, outcome);
|
|
357
371
|
logger.info({ callbackId }, `Ignoring unhandled view_submission: ${callbackId}`);
|
|
@@ -9,7 +9,6 @@ declare const SlackStrings: {
|
|
|
9
9
|
readonly buttons: {
|
|
10
10
|
readonly triggerAgent: "Trigger Agent";
|
|
11
11
|
readonly send: "Send";
|
|
12
|
-
readonly followUp: "Follow Up";
|
|
13
12
|
readonly cancel: "Cancel";
|
|
14
13
|
readonly openDashboard: "Open Dashboard";
|
|
15
14
|
};
|
|
@@ -17,7 +16,6 @@ declare const SlackStrings: {
|
|
|
17
16
|
readonly triggerAgent: "Trigger Agent";
|
|
18
17
|
readonly triggerAgentThread: "Trigger Agent (Thread)";
|
|
19
18
|
readonly askAboutMessage: "Ask About Message";
|
|
20
|
-
readonly followUp: "Follow Up";
|
|
21
19
|
};
|
|
22
20
|
readonly labels: {
|
|
23
21
|
readonly project: "Project";
|
|
@@ -38,7 +36,6 @@ declare const SlackStrings: {
|
|
|
38
36
|
};
|
|
39
37
|
readonly context: {
|
|
40
38
|
readonly poweredBy: (agentName: string) => string;
|
|
41
|
-
readonly privateResponse: "_Private response_";
|
|
42
39
|
};
|
|
43
40
|
readonly usage: {
|
|
44
41
|
readonly mentionEmpty: string;
|
|
@@ -50,13 +47,17 @@ declare const SlackStrings: {
|
|
|
50
47
|
readonly noProjectsConfigured: "No projects configured. Set up projects in the dashboard.";
|
|
51
48
|
};
|
|
52
49
|
readonly errors: {
|
|
53
|
-
readonly generic: "Something went wrong. Please try again.";
|
|
50
|
+
readonly generic: "Something went wrong processing your request. Please try again.";
|
|
54
51
|
readonly failedToOpenSelector: "Failed to open agent selector. Please try again.";
|
|
52
|
+
readonly noAgentConfigured: "No agent is configured for this workspace. Ask your admin to set up a default agent in the Inkeep dashboard.";
|
|
53
|
+
};
|
|
54
|
+
readonly linkPrompt: {
|
|
55
|
+
readonly intro: "To get started, let's connect your Inkeep account with Slack.";
|
|
55
56
|
};
|
|
56
57
|
readonly help: {
|
|
57
58
|
readonly title: "Inkeep — How to Use";
|
|
58
59
|
readonly publicSection: string;
|
|
59
|
-
readonly
|
|
60
|
+
readonly slashSection: string;
|
|
60
61
|
readonly otherCommands: string;
|
|
61
62
|
readonly docsLink: "<https://docs.inkeep.com/talk-to-your-agents/slack/overview|Learn more>";
|
|
62
63
|
};
|
|
@@ -9,15 +9,13 @@ const SlackStrings = {
|
|
|
9
9
|
buttons: {
|
|
10
10
|
triggerAgent: "Trigger Agent",
|
|
11
11
|
send: "Send",
|
|
12
|
-
followUp: "Follow Up",
|
|
13
12
|
cancel: "Cancel",
|
|
14
13
|
openDashboard: "Open Dashboard"
|
|
15
14
|
},
|
|
16
15
|
modals: {
|
|
17
16
|
triggerAgent: "Trigger Agent",
|
|
18
17
|
triggerAgentThread: "Trigger Agent (Thread)",
|
|
19
|
-
askAboutMessage: "Ask About Message"
|
|
20
|
-
followUp: "Follow Up"
|
|
18
|
+
askAboutMessage: "Ask About Message"
|
|
21
19
|
},
|
|
22
20
|
labels: {
|
|
23
21
|
project: "Project",
|
|
@@ -34,10 +32,7 @@ const SlackStrings = {
|
|
|
34
32
|
additionalInstructionsMessage: "Additional instructions or question about this message..."
|
|
35
33
|
},
|
|
36
34
|
visibility: { includeThreadContext: "Include thread context" },
|
|
37
|
-
context: {
|
|
38
|
-
poweredBy: (agentName) => `Powered by *${agentName}* via Inkeep`,
|
|
39
|
-
privateResponse: "_Private response_"
|
|
40
|
-
},
|
|
35
|
+
context: { poweredBy: (agentName) => `Powered by *${agentName}* via Inkeep` },
|
|
41
36
|
usage: { mentionEmpty: "*Include a message to use your Inkeep agent:*\n\n• `@Inkeep <message>` — Message the default agent (reply appears in a thread)\n• `@Inkeep <message>` in a thread — Includes thread as context\n• `@Inkeep` in a thread — Uses the full thread as context\n\nUse `/inkeep help` for all available commands." },
|
|
42
37
|
status: {
|
|
43
38
|
thinking: (agentName) => `_${agentName} is thinking..._`,
|
|
@@ -46,13 +41,15 @@ const SlackStrings = {
|
|
|
46
41
|
noProjectsConfigured: "No projects configured. Set up projects in the dashboard."
|
|
47
42
|
},
|
|
48
43
|
errors: {
|
|
49
|
-
generic: "Something went wrong. Please try again.",
|
|
50
|
-
failedToOpenSelector: "Failed to open agent selector. Please try again."
|
|
44
|
+
generic: "Something went wrong processing your request. Please try again.",
|
|
45
|
+
failedToOpenSelector: "Failed to open agent selector. Please try again.",
|
|
46
|
+
noAgentConfigured: "No agent is configured for this workspace. Ask your admin to set up a default agent in the Inkeep dashboard."
|
|
51
47
|
},
|
|
48
|
+
linkPrompt: { intro: "To get started, let's connect your Inkeep account with Slack." },
|
|
52
49
|
help: {
|
|
53
50
|
title: "Inkeep — How to Use",
|
|
54
51
|
publicSection: "*Public* — visible to everyone in the channel\n\n• `@Inkeep <message>` — Message the default agent in this channel\n• `@Inkeep <message>` in a thread — Includes thread as context\n• `@Inkeep` in a thread — Uses the full thread as context",
|
|
55
|
-
|
|
52
|
+
slashSection: "*Slash Commands* — visible to everyone in the channel\n\n• `/inkeep <message>` — Message the default agent in this channel\n• `/inkeep` — Open the agent picker to choose an agent and write a prompt",
|
|
56
53
|
otherCommands: "*Other Commands*\n\n• `/inkeep status` — Check your connection and agent config\n• `/inkeep link` / `/inkeep unlink` — Manage account connection\n• `/inkeep help` — Show this message",
|
|
57
54
|
docsLink: "<https://docs.inkeep.com/talk-to-your-agents/slack/overview|Learn more>"
|
|
58
55
|
},
|
|
@@ -3,8 +3,8 @@ import { getLogger } from "../../logger.js";
|
|
|
3
3
|
import runDbClient_default from "../../db/runDbClient.js";
|
|
4
4
|
import { findWorkspaceConnectionByTeamId, getSlackIntegrationId, getSlackNango, updateConnectionMetadata } from "../services/nango.js";
|
|
5
5
|
import { getSlackClient, getSlackUserInfo } from "../services/client.js";
|
|
6
|
-
import { handleCommand } from "../services/commands/index.js";
|
|
7
6
|
import { SLACK_SPAN_KEYS, SLACK_SPAN_NAMES, tracer } from "../tracer.js";
|
|
7
|
+
import { handleCommand } from "../services/commands/index.js";
|
|
8
8
|
import { parseSlackCommandBody, parseSlackEventBody, verifySlackRequest } from "../services/security.js";
|
|
9
9
|
import "../services/index.js";
|
|
10
10
|
import { dispatchSlackEvent } from "../dispatcher.js";
|
|
@@ -5,7 +5,6 @@ import * as slack_block_builder0 from "slack-block-builder";
|
|
|
5
5
|
declare function createErrorMessage(message: string): Readonly<slack_block_builder0.SlackMessageDto>;
|
|
6
6
|
interface ContextBlockParams {
|
|
7
7
|
agentName: string;
|
|
8
|
-
isPrivate?: boolean;
|
|
9
8
|
}
|
|
10
9
|
declare function createContextBlock(params: ContextBlockParams): {
|
|
11
10
|
type: "context";
|
|
@@ -14,37 +13,6 @@ declare function createContextBlock(params: ContextBlockParams): {
|
|
|
14
13
|
text: string;
|
|
15
14
|
}[];
|
|
16
15
|
};
|
|
17
|
-
interface FollowUpButtonParams {
|
|
18
|
-
conversationId: string;
|
|
19
|
-
agentId: string;
|
|
20
|
-
agentName?: string;
|
|
21
|
-
projectId: string;
|
|
22
|
-
tenantId: string;
|
|
23
|
-
teamId: string;
|
|
24
|
-
slackUserId: string;
|
|
25
|
-
channel: string;
|
|
26
|
-
}
|
|
27
|
-
declare function buildFollowUpButton(params: FollowUpButtonParams): {
|
|
28
|
-
type: "button";
|
|
29
|
-
text: {
|
|
30
|
-
type: "plain_text";
|
|
31
|
-
text: "Follow Up";
|
|
32
|
-
emoji: boolean;
|
|
33
|
-
};
|
|
34
|
-
action_id: string;
|
|
35
|
-
value: string;
|
|
36
|
-
}[];
|
|
37
|
-
/**
|
|
38
|
-
* Build Block Kit blocks for a private conversational response.
|
|
39
|
-
* Shows the user's message, a divider, the agent response, context, and a Follow Up button.
|
|
40
|
-
*/
|
|
41
|
-
declare function buildConversationResponseBlocks(params: {
|
|
42
|
-
userMessage: string;
|
|
43
|
-
responseText: string;
|
|
44
|
-
agentName: string;
|
|
45
|
-
isError: boolean;
|
|
46
|
-
followUpParams: FollowUpButtonParams;
|
|
47
|
-
}): any[];
|
|
48
16
|
declare function createUpdatedHelpMessage(): Readonly<slack_block_builder0.SlackMessageDto>;
|
|
49
17
|
declare function createAlreadyLinkedMessage(email: string, linkedAt: string, dashboardUrl: string): Readonly<slack_block_builder0.SlackMessageDto>;
|
|
50
18
|
declare function createUnlinkSuccessMessage(): Readonly<slack_block_builder0.SlackMessageDto>;
|
|
@@ -79,7 +47,7 @@ interface ToolApprovalButtonValue {
|
|
|
79
47
|
agentId: string;
|
|
80
48
|
slackUserId: string;
|
|
81
49
|
channel: string;
|
|
82
|
-
threadTs
|
|
50
|
+
threadTs?: string;
|
|
83
51
|
toolName: string;
|
|
84
52
|
}
|
|
85
53
|
declare const ToolApprovalButtonValueSchema: z.ZodObject<{
|
|
@@ -89,7 +57,7 @@ declare const ToolApprovalButtonValueSchema: z.ZodObject<{
|
|
|
89
57
|
agentId: z.ZodString;
|
|
90
58
|
slackUserId: z.ZodString;
|
|
91
59
|
channel: z.ZodString;
|
|
92
|
-
threadTs: z.ZodString
|
|
60
|
+
threadTs: z.ZodOptional<z.ZodString>;
|
|
93
61
|
toolName: z.ZodString;
|
|
94
62
|
}, z.core.$strip>;
|
|
95
63
|
declare function buildToolApprovalBlocks(params: {
|
|
@@ -152,4 +120,4 @@ declare function buildCitationsBlock(citations: Array<{
|
|
|
152
120
|
}>): any[];
|
|
153
121
|
declare function createCreateInkeepAccountMessage(acceptUrl: string, expiresInMinutes: number): Readonly<slack_block_builder0.SlackMessageDto>;
|
|
154
122
|
//#endregion
|
|
155
|
-
export { AgentConfigSources, ContextBlockParams,
|
|
123
|
+
export { AgentConfigSources, ContextBlockParams, ToolApprovalButtonValue, ToolApprovalButtonValueSchema, buildCitationsBlock, buildDataArtifactBlocks, buildDataComponentBlocks, buildSummaryBreadcrumbBlock, buildToolApprovalBlocks, buildToolApprovalDoneBlocks, buildToolApprovalExpiredBlocks, buildToolOutputErrorBlock, createAlreadyLinkedMessage, createContextBlock, createCreateInkeepAccountMessage, createErrorMessage, createNotLinkedMessage, createSmartLinkMessage, createStatusMessage, createUnlinkSuccessMessage, createUpdatedHelpMessage };
|
|
@@ -7,54 +7,17 @@ function createErrorMessage(message) {
|
|
|
7
7
|
return Message().blocks(Blocks.Section().text(message)).buildToObject();
|
|
8
8
|
}
|
|
9
9
|
function createContextBlock(params) {
|
|
10
|
-
const { agentName
|
|
11
|
-
let text = SlackStrings.context.poweredBy(agentName);
|
|
12
|
-
if (isPrivate) text = `${SlackStrings.context.privateResponse} • ${text}`;
|
|
10
|
+
const { agentName } = params;
|
|
13
11
|
return {
|
|
14
12
|
type: "context",
|
|
15
13
|
elements: [{
|
|
16
14
|
type: "mrkdwn",
|
|
17
|
-
text
|
|
15
|
+
text: SlackStrings.context.poweredBy(agentName)
|
|
18
16
|
}]
|
|
19
17
|
};
|
|
20
18
|
}
|
|
21
|
-
function buildFollowUpButton(params) {
|
|
22
|
-
return [{
|
|
23
|
-
type: "button",
|
|
24
|
-
text: {
|
|
25
|
-
type: "plain_text",
|
|
26
|
-
text: SlackStrings.buttons.followUp,
|
|
27
|
-
emoji: true
|
|
28
|
-
},
|
|
29
|
-
action_id: "open_follow_up_modal",
|
|
30
|
-
value: JSON.stringify(params)
|
|
31
|
-
}];
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Build Block Kit blocks for a private conversational response.
|
|
35
|
-
* Shows the user's message, a divider, the agent response, context, and a Follow Up button.
|
|
36
|
-
*/
|
|
37
|
-
function buildConversationResponseBlocks(params) {
|
|
38
|
-
const { responseText, agentName, isError, followUpParams } = params;
|
|
39
|
-
const blocks = [{
|
|
40
|
-
type: "section",
|
|
41
|
-
text: {
|
|
42
|
-
type: "mrkdwn",
|
|
43
|
-
text: responseText
|
|
44
|
-
}
|
|
45
|
-
}];
|
|
46
|
-
if (!isError) {
|
|
47
|
-
const contextBlock = createContextBlock({ agentName });
|
|
48
|
-
blocks.push(contextBlock);
|
|
49
|
-
blocks.push({
|
|
50
|
-
type: "actions",
|
|
51
|
-
elements: buildFollowUpButton(followUpParams)
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
return blocks;
|
|
55
|
-
}
|
|
56
19
|
function createUpdatedHelpMessage() {
|
|
57
|
-
return Message().blocks(Blocks.Header().text(SlackStrings.help.title), Blocks.Section().text(SlackStrings.help.publicSection), Blocks.Divider(), Blocks.Section().text(SlackStrings.help.
|
|
20
|
+
return Message().blocks(Blocks.Header().text(SlackStrings.help.title), Blocks.Section().text(SlackStrings.help.publicSection), Blocks.Divider(), Blocks.Section().text(SlackStrings.help.slashSection), Blocks.Divider(), Blocks.Section().text(SlackStrings.help.otherCommands), Blocks.Divider(), Blocks.Context().elements(SlackStrings.help.docsLink)).buildToObject();
|
|
58
21
|
}
|
|
59
22
|
function createAlreadyLinkedMessage(email, linkedAt, dashboardUrl) {
|
|
60
23
|
return Message().blocks(Blocks.Section().text(Md.bold("Already linked") + "\n\nYour Slack account is connected to Inkeep.\n\n" + Md.bold("Account:") + ` ${email}\n` + Md.bold("Linked:") + ` ${new Date(linkedAt).toLocaleDateString()}\n\nTo switch accounts, first run \`/inkeep unlink\``), Blocks.Actions().elements(Elements.Button().text(SlackStrings.buttons.openDashboard).url(dashboardUrl).actionId("open_dashboard"))).buildToObject();
|
|
@@ -101,7 +64,7 @@ const ToolApprovalButtonValueSchema = z.object({
|
|
|
101
64
|
agentId: z.string(),
|
|
102
65
|
slackUserId: z.string(),
|
|
103
66
|
channel: z.string(),
|
|
104
|
-
threadTs: z.string(),
|
|
67
|
+
threadTs: z.string().optional(),
|
|
105
68
|
toolName: z.string()
|
|
106
69
|
});
|
|
107
70
|
function buildToolApprovalBlocks(params) {
|
|
@@ -324,4 +287,4 @@ function createCreateInkeepAccountMessage(acceptUrl, expiresInMinutes) {
|
|
|
324
287
|
}
|
|
325
288
|
|
|
326
289
|
//#endregion
|
|
327
|
-
export { ToolApprovalButtonValueSchema, buildCitationsBlock,
|
|
290
|
+
export { ToolApprovalButtonValueSchema, buildCitationsBlock, buildDataArtifactBlocks, buildDataComponentBlocks, buildSummaryBreadcrumbBlock, buildToolApprovalBlocks, buildToolApprovalDoneBlocks, buildToolApprovalExpiredBlocks, buildToolOutputErrorBlock, createAlreadyLinkedMessage, createContextBlock, createCreateInkeepAccountMessage, createErrorMessage, createNotLinkedMessage, createSmartLinkMessage, createStatusMessage, createUnlinkSuccessMessage, createUpdatedHelpMessage };
|
|
@@ -2,14 +2,15 @@ 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 {
|
|
5
|
+
import { fetchAgentsForProject, fetchProjectsForTenant, generateSlackConversationId, getChannelAgentConfig } from "../events/utils.js";
|
|
6
6
|
import { resolveEffectiveAgent } from "../agent-resolution.js";
|
|
7
7
|
import { SlackStrings } from "../../i18n/strings.js";
|
|
8
|
-
import { createAlreadyLinkedMessage,
|
|
8
|
+
import { createAlreadyLinkedMessage, createErrorMessage, createNotLinkedMessage, createStatusMessage, createUnlinkSuccessMessage, createUpdatedHelpMessage } from "../blocks/index.js";
|
|
9
9
|
import { getSlackClient } from "../client.js";
|
|
10
|
+
import { executeAgentPublicly } from "../events/execution.js";
|
|
10
11
|
import { buildLinkPromptMessage, resolveUnlinkedUserAction } from "../link-prompt.js";
|
|
11
12
|
import { buildAgentSelectorModal } from "../modals.js";
|
|
12
|
-
import { deleteWorkAppSlackUserMapping, findWorkAppSlackUserMapping, findWorkAppSlackUserMappingBySlackUser, flushTraces,
|
|
13
|
+
import { deleteWorkAppSlackUserMapping, findWorkAppSlackUserMapping, findWorkAppSlackUserMappingBySlackUser, flushTraces, getWaitUntil, signSlackUserToken } from "@inkeep/agents-core";
|
|
13
14
|
|
|
14
15
|
//#region src/slack/services/commands/index.ts
|
|
15
16
|
const DEFAULT_CLIENT_ID = "work-apps-slack";
|
|
@@ -232,117 +233,54 @@ async function handleQuestionCommand(payload, question, _dashboardUrl, tenantId,
|
|
|
232
233
|
response_type: "ephemeral",
|
|
233
234
|
...createErrorMessage("No default agent configured. Ask your admin to set a workspace default in the dashboard.")
|
|
234
235
|
};
|
|
235
|
-
const
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
236
|
+
const slackClient = getSlackClient(botToken);
|
|
237
|
+
const slackUserToken = await signSlackUserToken({
|
|
238
|
+
inkeepUserId: existingLink.inkeepUserId,
|
|
239
|
+
tenantId: userTenantId,
|
|
240
|
+
slackTeamId: payload.teamId,
|
|
241
|
+
slackUserId: payload.userId,
|
|
242
|
+
slackEnterpriseId: payload.enterpriseId,
|
|
240
243
|
slackAuthorized: resolvedAgent.grantAccessToMembers,
|
|
241
244
|
slackAuthSource: resolvedAgent.source === "none" ? void 0 : resolvedAgent.source,
|
|
242
245
|
slackChannelId: payload.channelId,
|
|
243
246
|
slackAuthorizedProjectId: resolvedAgent.projectId
|
|
244
|
-
})
|
|
247
|
+
});
|
|
248
|
+
const now = Date.now();
|
|
249
|
+
const messageTs = `${Math.floor(now / 1e3)}.${String(now % 1e3).padStart(3, "0")}000`;
|
|
250
|
+
const conversationId = generateSlackConversationId({
|
|
251
|
+
teamId: payload.teamId,
|
|
252
|
+
messageTs,
|
|
253
|
+
agentId: resolvedAgent.agentId
|
|
254
|
+
});
|
|
255
|
+
const questionWork = executeAgentPublicly({
|
|
256
|
+
slackClient,
|
|
257
|
+
channel: payload.channelId,
|
|
258
|
+
slackUserId: payload.userId,
|
|
259
|
+
teamId: payload.teamId,
|
|
260
|
+
jwtToken: slackUserToken,
|
|
261
|
+
projectId: resolvedAgent.projectId,
|
|
262
|
+
agentId: resolvedAgent.agentId,
|
|
263
|
+
agentName: resolvedAgent.agentName || resolvedAgent.agentId,
|
|
264
|
+
question,
|
|
265
|
+
conversationId
|
|
266
|
+
}).catch(async (error) => {
|
|
245
267
|
logger.error({ error }, "Background execution promise rejected");
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
if (waitUntil) waitUntil(questionWork);
|
|
249
|
-
return {};
|
|
250
|
-
}
|
|
251
|
-
async function executeAgentInBackground(payload, existingLink, targetAgent, question, tenantId, channelAuth) {
|
|
252
|
-
try {
|
|
253
|
-
const slackUserToken = await signSlackUserToken({
|
|
254
|
-
inkeepUserId: existingLink.inkeepUserId,
|
|
255
|
-
tenantId,
|
|
256
|
-
slackTeamId: payload.teamId,
|
|
257
|
-
slackUserId: payload.userId,
|
|
258
|
-
slackEnterpriseId: payload.enterpriseId,
|
|
259
|
-
...channelAuth
|
|
260
|
-
});
|
|
261
|
-
const apiBaseUrl = env.INKEEP_AGENTS_API_URL || "http://localhost:3002";
|
|
262
|
-
const controller = new AbortController();
|
|
263
|
-
const timeout = setTimeout(() => controller.abort(), 3e4);
|
|
264
|
-
let response;
|
|
265
|
-
try {
|
|
266
|
-
response = await getInProcessFetch()(`${apiBaseUrl}/run/api/chat`, {
|
|
268
|
+
if (payload.responseUrl) try {
|
|
269
|
+
await fetch(payload.responseUrl, {
|
|
267
270
|
method: "POST",
|
|
268
|
-
headers: {
|
|
269
|
-
"Content-Type": "application/json",
|
|
270
|
-
Authorization: `Bearer ${slackUserToken}`,
|
|
271
|
-
"x-inkeep-project-id": targetAgent.projectId,
|
|
272
|
-
"x-inkeep-agent-id": targetAgent.id
|
|
273
|
-
},
|
|
271
|
+
headers: { "Content-Type": "application/json" },
|
|
274
272
|
body: JSON.stringify({
|
|
275
|
-
messages: [{
|
|
276
|
-
role: "user",
|
|
277
|
-
content: question
|
|
278
|
-
}],
|
|
279
|
-
stream: false
|
|
280
|
-
}),
|
|
281
|
-
signal: controller.signal
|
|
282
|
-
});
|
|
283
|
-
} catch (error) {
|
|
284
|
-
clearTimeout(timeout);
|
|
285
|
-
if (error.name === "AbortError") {
|
|
286
|
-
logger.warn({
|
|
287
|
-
teamId: payload.teamId,
|
|
288
|
-
timeoutMs: 3e4
|
|
289
|
-
}, "Background agent execution timed out");
|
|
290
|
-
await sendResponseUrlMessage(payload.responseUrl, {
|
|
291
273
|
response_type: "ephemeral",
|
|
292
|
-
text:
|
|
293
|
-
})
|
|
294
|
-
return;
|
|
295
|
-
}
|
|
296
|
-
throw error;
|
|
297
|
-
} finally {
|
|
298
|
-
clearTimeout(timeout);
|
|
299
|
-
}
|
|
300
|
-
if (!response.ok) {
|
|
301
|
-
const errorText = await response.text();
|
|
302
|
-
logger.error({
|
|
303
|
-
status: response.status,
|
|
304
|
-
error: errorText,
|
|
305
|
-
agentId: targetAgent.id,
|
|
306
|
-
projectId: targetAgent.projectId
|
|
307
|
-
}, "Run API call failed");
|
|
308
|
-
const apiMessage = extractApiErrorMessage(errorText);
|
|
309
|
-
const errorMessage = apiMessage ? `*Error.* ${apiMessage}` : `Failed to run agent: ${response.status} ${response.statusText}`;
|
|
310
|
-
await sendResponseUrlMessage(payload.responseUrl, {
|
|
311
|
-
response_type: "ephemeral",
|
|
312
|
-
text: errorMessage
|
|
313
|
-
});
|
|
314
|
-
} else {
|
|
315
|
-
const result = await response.json();
|
|
316
|
-
const assistantMessage = result.choices?.[0]?.message?.content || result.message?.content || "No response received";
|
|
317
|
-
logger.info({
|
|
318
|
-
slackUserId: payload.userId,
|
|
319
|
-
agentId: targetAgent.id,
|
|
320
|
-
projectId: targetAgent.projectId,
|
|
321
|
-
tenantId
|
|
322
|
-
}, "Agent execution completed via Slack");
|
|
323
|
-
const contextBlock = createContextBlock({ agentName: targetAgent.name || targetAgent.id });
|
|
324
|
-
await sendResponseUrlMessage(payload.responseUrl, {
|
|
325
|
-
response_type: "in_channel",
|
|
326
|
-
text: assistantMessage,
|
|
327
|
-
blocks: [{
|
|
328
|
-
type: "section",
|
|
329
|
-
text: {
|
|
330
|
-
type: "mrkdwn",
|
|
331
|
-
text: assistantMessage
|
|
332
|
-
}
|
|
333
|
-
}, contextBlock]
|
|
274
|
+
text: SlackStrings.errors.generic
|
|
275
|
+
})
|
|
334
276
|
});
|
|
277
|
+
} catch (e) {
|
|
278
|
+
logger.warn({ e }, "Failed to send error via response_url");
|
|
335
279
|
}
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
}, "Background agent execution failed");
|
|
341
|
-
await sendResponseUrlMessage(payload.responseUrl, {
|
|
342
|
-
response_type: "ephemeral",
|
|
343
|
-
text: "An error occurred while running the agent. Please try again."
|
|
344
|
-
});
|
|
345
|
-
}
|
|
280
|
+
}).finally(() => flushTraces());
|
|
281
|
+
const waitUntil = await getWaitUntil();
|
|
282
|
+
if (waitUntil) waitUntil(questionWork);
|
|
283
|
+
return {};
|
|
346
284
|
}
|
|
347
285
|
async function handleCommand(payload) {
|
|
348
286
|
const text = payload.text.trim();
|