@inkeep/agents-work-apps 0.53.2 → 0.53.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/github/mcp/index.js +61 -1
- package/dist/github/mcp/utils.d.ts +18 -1
- package/dist/github/mcp/utils.js +52 -16
- package/dist/github/routes/setup.d.ts +2 -2
- package/dist/github/routes/tokenExchange.d.ts +2 -2
- package/dist/github/routes/webhooks.d.ts +2 -2
- package/dist/slack/dispatcher.js +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/routes/workspaces.js +3 -3
- package/dist/slack/services/blocks/index.d.ts +3 -35
- package/dist/slack/services/blocks/index.js +5 -42
- package/dist/slack/services/client.d.ts +21 -1
- package/dist/slack/services/client.js +43 -1
- 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 +8 -6
- package/dist/slack/services/index.js +9 -7
- 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/github/mcp/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import runDbClient_default from "../../db/runDbClient.js";
|
|
2
2
|
import { githubMcpAuth } from "./auth.js";
|
|
3
3
|
import { ReactionContentSchema } from "./schemas.js";
|
|
4
|
-
import { commitFileChanges, commitNewFile, createIssueCommentReaction, createPullRequestReviewCommentReaction, deleteIssueCommentReaction, deletePullRequestReviewCommentReaction, fetchBranchChangedFiles, fetchComments, fetchPrFileDiffs, fetchPrFiles, fetchPrInfo, formatFileDiff, generatePrMarkdown, getGitHubClientFromRepo, listIssueCommentReactions, listIssueReactions, listPullRequestReviewCommentReactions, visualizeUpdateOperations } from "./utils.js";
|
|
4
|
+
import { commitFileChanges, commitNewFile, createIssueCommentReaction, createPullRequestReviewCommentReaction, deleteIssueCommentReaction, deletePullRequestReviewCommentReaction, fetchBranchChangedFiles, fetchComments, fetchPrFileDiffs, fetchPrFiles, fetchPrInfo, formatFileDiff, generatePrMarkdown, getGitHubClientFromRepo, listIssueCommentReactions, listIssueReactions, listPullRequestReviewCommentReactions, moveFile, visualizeUpdateOperations } from "./utils.js";
|
|
5
5
|
import { z } from "@hono/zod-openapi";
|
|
6
6
|
import { getMcpToolRepositoryAccessWithDetails } from "@inkeep/agents-core";
|
|
7
7
|
import { Hono } from "hono";
|
|
@@ -519,6 +519,66 @@ const getServer = async (toolId) => {
|
|
|
519
519
|
};
|
|
520
520
|
}
|
|
521
521
|
});
|
|
522
|
+
server.tool("move-file", `Move (rename) a file within a repository in a single atomic commit. The file content is preserved and the old path is deleted. ${getAvailableRepositoryString(repositoryAccess)}`, {
|
|
523
|
+
owner: z.string().describe("Repository owner name"),
|
|
524
|
+
repo: z.string().describe("Repository name"),
|
|
525
|
+
branch_name: z.string().describe("Branch to commit the move to"),
|
|
526
|
+
source_path: z.string().describe("Current path of the file (relative to repository root)"),
|
|
527
|
+
destination_path: z.string().describe("New path for the file (relative to repository root)"),
|
|
528
|
+
commit_message: z.string().describe("Commit message for the move")
|
|
529
|
+
}, async ({ owner, repo, branch_name, source_path, destination_path, commit_message }) => {
|
|
530
|
+
try {
|
|
531
|
+
let githubClient;
|
|
532
|
+
try {
|
|
533
|
+
githubClient = getGitHubClientFromRepo(owner, repo, installationIdMap);
|
|
534
|
+
} catch (error) {
|
|
535
|
+
return {
|
|
536
|
+
content: [{
|
|
537
|
+
type: "text",
|
|
538
|
+
text: `Error accessing GitHub: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
539
|
+
}],
|
|
540
|
+
isError: true
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
return { content: [{
|
|
544
|
+
type: "text",
|
|
545
|
+
text: `Successfully moved "${source_path}" to "${destination_path}" in ${owner}/${repo} on branch "${branch_name}"\n\nCommit SHA: ${await moveFile({
|
|
546
|
+
githubClient,
|
|
547
|
+
owner,
|
|
548
|
+
repo,
|
|
549
|
+
sourcePath: source_path,
|
|
550
|
+
destinationPath: destination_path,
|
|
551
|
+
branchName: branch_name,
|
|
552
|
+
commitMessage: commit_message
|
|
553
|
+
})}`
|
|
554
|
+
}] };
|
|
555
|
+
} catch (error) {
|
|
556
|
+
if (error instanceof Error && "status" in error) {
|
|
557
|
+
const apiError = error;
|
|
558
|
+
if (apiError.status === 404) return {
|
|
559
|
+
content: [{
|
|
560
|
+
type: "text",
|
|
561
|
+
text: `Repository ${owner}/${repo}, branch "${branch_name}", or source file "${source_path}" not found.`
|
|
562
|
+
}],
|
|
563
|
+
isError: true
|
|
564
|
+
};
|
|
565
|
+
if (apiError.status === 422) return {
|
|
566
|
+
content: [{
|
|
567
|
+
type: "text",
|
|
568
|
+
text: `Invalid move operation. The destination path "${destination_path}" may already exist or the path is invalid.`
|
|
569
|
+
}],
|
|
570
|
+
isError: true
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
return {
|
|
574
|
+
content: [{
|
|
575
|
+
type: "text",
|
|
576
|
+
text: `Error moving file: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
577
|
+
}],
|
|
578
|
+
isError: true
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
});
|
|
522
582
|
server.tool("create-pull-request", `Create a pull request in a repository. ${getAvailableRepositoryString(repositoryAccess)}`, {
|
|
523
583
|
owner: z.string().describe("Repository owner name"),
|
|
524
584
|
repo: z.string().describe("Repository name"),
|
|
@@ -236,6 +236,23 @@ declare function commitNewFile({
|
|
|
236
236
|
content: string;
|
|
237
237
|
commitMessage: string;
|
|
238
238
|
}): Promise<string>;
|
|
239
|
+
declare function moveFile({
|
|
240
|
+
githubClient,
|
|
241
|
+
owner,
|
|
242
|
+
repo,
|
|
243
|
+
sourcePath,
|
|
244
|
+
destinationPath,
|
|
245
|
+
branchName,
|
|
246
|
+
commitMessage
|
|
247
|
+
}: {
|
|
248
|
+
githubClient: Octokit;
|
|
249
|
+
owner: string;
|
|
250
|
+
repo: string;
|
|
251
|
+
sourcePath: string;
|
|
252
|
+
destinationPath: string;
|
|
253
|
+
branchName: string;
|
|
254
|
+
commitMessage: string;
|
|
255
|
+
}): Promise<string>;
|
|
239
256
|
declare function createIssueCommentReaction(octokit: Octokit, owner: string, repo: string, commentId: number, content: ReactionContent): Promise<{
|
|
240
257
|
id: number;
|
|
241
258
|
content: string;
|
|
@@ -257,4 +274,4 @@ declare function listPullRequestReviewCommentReactions(octokit: Octokit, owner:
|
|
|
257
274
|
declare function listIssueReactions(octokit: Octokit, owner: string, repo: string, issueNumber: number): Promise<ReactionDetail[]>;
|
|
258
275
|
declare function formatFileDiff(pullRequestNumber: number, files: ChangedFile[], includeContents?: boolean): Promise<string>;
|
|
259
276
|
//#endregion
|
|
260
|
-
export { CommitData, LLMUpdateOperation, PullCommit, ReactionDetail, applyOperation, applyOperations, commitFileChanges, commitNewFile, createIssueCommentReaction, createPullRequestReviewCommentReaction, deleteIssueCommentReaction, deletePullRequestReviewCommentReaction, fetchBranchChangedFiles, fetchComments, fetchCommitDetails, fetchPrCommits, fetchPrFileDiffs, fetchPrFiles, fetchPrInfo, formatFileDiff, generatePrMarkdown, getFilePathsInRepo, getGitHubClientFromInstallationId, getGitHubClientFromRepo, listIssueCommentReactions, listIssueReactions, listPullRequestReviewCommentReactions, validateLineNumbers, visualizeUpdateOperations };
|
|
277
|
+
export { CommitData, LLMUpdateOperation, PullCommit, ReactionDetail, applyOperation, applyOperations, commitFileChanges, commitNewFile, createIssueCommentReaction, createPullRequestReviewCommentReaction, deleteIssueCommentReaction, deletePullRequestReviewCommentReaction, fetchBranchChangedFiles, fetchComments, fetchCommitDetails, fetchPrCommits, fetchPrFileDiffs, fetchPrFiles, fetchPrInfo, formatFileDiff, generatePrMarkdown, getFilePathsInRepo, getGitHubClientFromInstallationId, getGitHubClientFromRepo, listIssueCommentReactions, listIssueReactions, listPullRequestReviewCommentReactions, moveFile, validateLineNumbers, visualizeUpdateOperations };
|
package/dist/github/mcp/utils.js
CHANGED
|
@@ -615,33 +615,22 @@ function visualizeUpdateOperations(fileContent, operations) {
|
|
|
615
615
|
return `Error applying operations: ${error instanceof Error ? error.message : "Unknown error"}`;
|
|
616
616
|
}
|
|
617
617
|
}
|
|
618
|
-
async function
|
|
618
|
+
async function commitTreeEntries({ githubClient, owner, repo, branchName, treeEntries, commitMessage }) {
|
|
619
619
|
const currentSha = (await githubClient.rest.git.getRef({
|
|
620
620
|
owner,
|
|
621
621
|
repo,
|
|
622
622
|
ref: `heads/${branchName}`
|
|
623
623
|
})).data.object.sha;
|
|
624
|
-
const
|
|
624
|
+
const currentCommit = await githubClient.rest.git.getCommit({
|
|
625
625
|
owner,
|
|
626
626
|
repo,
|
|
627
627
|
commit_sha: currentSha
|
|
628
|
-
})).data.tree.sha;
|
|
629
|
-
const blob = await githubClient.rest.git.createBlob({
|
|
630
|
-
owner,
|
|
631
|
-
repo,
|
|
632
|
-
content: Buffer.from(content).toString("base64"),
|
|
633
|
-
encoding: "base64"
|
|
634
628
|
});
|
|
635
629
|
const newTree = await githubClient.rest.git.createTree({
|
|
636
630
|
owner,
|
|
637
631
|
repo,
|
|
638
|
-
base_tree:
|
|
639
|
-
tree:
|
|
640
|
-
path: filePath,
|
|
641
|
-
mode: "100644",
|
|
642
|
-
type: "blob",
|
|
643
|
-
sha: blob.data.sha
|
|
644
|
-
}]
|
|
632
|
+
base_tree: currentCommit.data.tree.sha,
|
|
633
|
+
tree: treeEntries
|
|
645
634
|
});
|
|
646
635
|
const newCommit = await githubClient.rest.git.createCommit({
|
|
647
636
|
owner,
|
|
@@ -658,6 +647,26 @@ async function commitContent({ githubClient, owner, repo, filePath, branchName,
|
|
|
658
647
|
});
|
|
659
648
|
return newCommit.data.sha;
|
|
660
649
|
}
|
|
650
|
+
async function commitContent({ githubClient, owner, repo, filePath, branchName, content, commitMessage }) {
|
|
651
|
+
return commitTreeEntries({
|
|
652
|
+
githubClient,
|
|
653
|
+
owner,
|
|
654
|
+
repo,
|
|
655
|
+
branchName,
|
|
656
|
+
treeEntries: [{
|
|
657
|
+
path: filePath,
|
|
658
|
+
mode: "100644",
|
|
659
|
+
type: "blob",
|
|
660
|
+
sha: (await githubClient.rest.git.createBlob({
|
|
661
|
+
owner,
|
|
662
|
+
repo,
|
|
663
|
+
content: Buffer.from(content).toString("base64"),
|
|
664
|
+
encoding: "base64"
|
|
665
|
+
})).data.sha
|
|
666
|
+
}],
|
|
667
|
+
commitMessage
|
|
668
|
+
});
|
|
669
|
+
}
|
|
661
670
|
async function commitFileChanges({ githubClient, owner, repo, fileContent, filePath, branchName, operations, commitMessage }) {
|
|
662
671
|
try {
|
|
663
672
|
return await commitContent({
|
|
@@ -688,6 +697,33 @@ async function commitNewFile({ githubClient, owner, repo, filePath, branchName,
|
|
|
688
697
|
throw new Error(`Failed to commit new file: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
689
698
|
}
|
|
690
699
|
}
|
|
700
|
+
async function moveFile({ githubClient, owner, repo, sourcePath, destinationPath, branchName, commitMessage }) {
|
|
701
|
+
const fileResponse = await githubClient.rest.repos.getContent({
|
|
702
|
+
owner,
|
|
703
|
+
repo,
|
|
704
|
+
path: sourcePath,
|
|
705
|
+
ref: branchName
|
|
706
|
+
});
|
|
707
|
+
if (!("sha" in fileResponse.data) || Array.isArray(fileResponse.data)) throw new Error(`Source path "${sourcePath}" is not a file`);
|
|
708
|
+
return commitTreeEntries({
|
|
709
|
+
githubClient,
|
|
710
|
+
owner,
|
|
711
|
+
repo,
|
|
712
|
+
branchName,
|
|
713
|
+
treeEntries: [{
|
|
714
|
+
path: destinationPath,
|
|
715
|
+
mode: "100644",
|
|
716
|
+
type: "blob",
|
|
717
|
+
sha: fileResponse.data.sha
|
|
718
|
+
}, {
|
|
719
|
+
path: sourcePath,
|
|
720
|
+
mode: "100644",
|
|
721
|
+
type: "blob",
|
|
722
|
+
sha: null
|
|
723
|
+
}],
|
|
724
|
+
commitMessage
|
|
725
|
+
});
|
|
726
|
+
}
|
|
691
727
|
async function createIssueCommentReaction(octokit, owner, repo, commentId, content) {
|
|
692
728
|
const { data } = await octokit.rest.reactions.createForIssueComment({
|
|
693
729
|
owner,
|
|
@@ -795,4 +831,4 @@ async function formatFileDiff(pullRequestNumber, files, includeContents = false)
|
|
|
795
831
|
}
|
|
796
832
|
|
|
797
833
|
//#endregion
|
|
798
|
-
export { applyOperation, applyOperations, commitFileChanges, commitNewFile, createIssueCommentReaction, createPullRequestReviewCommentReaction, deleteIssueCommentReaction, deletePullRequestReviewCommentReaction, fetchBranchChangedFiles, fetchComments, fetchCommitDetails, fetchPrCommits, fetchPrFileDiffs, fetchPrFiles, fetchPrInfo, formatFileDiff, generatePrMarkdown, getFilePathsInRepo, getGitHubClientFromInstallationId, getGitHubClientFromRepo, listIssueCommentReactions, listIssueReactions, listPullRequestReviewCommentReactions, validateLineNumbers, visualizeUpdateOperations };
|
|
834
|
+
export { applyOperation, applyOperations, commitFileChanges, commitNewFile, createIssueCommentReaction, createPullRequestReviewCommentReaction, deleteIssueCommentReaction, deletePullRequestReviewCommentReaction, fetchBranchChangedFiles, fetchComments, fetchCommitDetails, fetchPrCommits, fetchPrFileDiffs, fetchPrFiles, fetchPrInfo, formatFileDiff, generatePrMarkdown, getFilePathsInRepo, getGitHubClientFromInstallationId, getGitHubClientFromRepo, listIssueCommentReactions, listIssueReactions, listPullRequestReviewCommentReactions, moveFile, validateLineNumbers, visualizeUpdateOperations };
|
|
@@ -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,7 +1,7 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
|
-
import * as
|
|
2
|
+
import * as hono_types8 from "hono/types";
|
|
3
3
|
|
|
4
4
|
//#region src/github/routes/tokenExchange.d.ts
|
|
5
|
-
declare const app: Hono<
|
|
5
|
+
declare const app: Hono<hono_types8.BlankEnv, hono_types8.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";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getLogger } from "../../logger.js";
|
|
2
2
|
import runDbClient_default from "../../db/runDbClient.js";
|
|
3
3
|
import { clearWorkspaceConnectionCache, computeWorkspaceConnectionId, deleteWorkspaceInstallation, findWorkspaceConnectionByTeamId, getWorkspaceDefaultAgentFromNango, listWorkspaceInstallations, setWorkspaceDefaultAgent } from "../services/nango.js";
|
|
4
|
-
import { getSlackChannels, getSlackClient, revokeSlackToken } from "../services/client.js";
|
|
4
|
+
import { getBotMemberChannels, getSlackChannels, getSlackClient, revokeSlackToken } from "../services/client.js";
|
|
5
5
|
import "../services/index.js";
|
|
6
6
|
import { requireChannelMemberOrAdmin, requireWorkspaceAdmin } from "../middleware/permissions.js";
|
|
7
7
|
import { OpenAPIHono, z } from "@hono/zod-openapi";
|
|
@@ -380,7 +380,7 @@ app.openapi(createProtectedRoute({
|
|
|
380
380
|
method: "get",
|
|
381
381
|
path: "/{teamId}/channels",
|
|
382
382
|
summary: "List Channels",
|
|
383
|
-
description: "List Slack channels
|
|
383
|
+
description: "List Slack channels where the bot is a member",
|
|
384
384
|
operationId: "slack-list-channels",
|
|
385
385
|
tags: [
|
|
386
386
|
"Work Apps",
|
|
@@ -422,7 +422,7 @@ app.openapi(createProtectedRoute({
|
|
|
422
422
|
const tenantId = workspace.tenantId;
|
|
423
423
|
const slackClient = getSlackClient(workspace.botToken);
|
|
424
424
|
try {
|
|
425
|
-
const channels = await
|
|
425
|
+
const channels = await getBotMemberChannels(slackClient, limit);
|
|
426
426
|
let channelConfigs = [];
|
|
427
427
|
try {
|
|
428
428
|
channelConfigs = await listWorkAppSlackChannelAgentConfigsByTeam(runDbClient_default)(tenantId, teamId);
|
|
@@ -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 };
|