@inkeep/agents-work-apps 0.52.0 → 0.53.0
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/auth.d.ts +2 -2
- package/dist/github/mcp/index.d.ts +2 -2
- package/dist/github/mcp/index.js +61 -25
- package/dist/github/mcp/schemas.d.ts +1 -1
- package/dist/github/mcp/utils.d.ts +2 -1
- package/dist/github/mcp/utils.js +16 -1
- 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 +10 -1
- package/dist/slack/routes/oauth.js +21 -1
- package/dist/slack/services/agent-resolution.js +66 -18
- package/dist/slack/services/blocks/index.d.ts +1 -0
- package/dist/slack/services/blocks/index.js +1 -4
- package/dist/slack/services/commands/index.js +2 -2
- package/dist/slack/services/events/app-mention.js +1 -1
- package/dist/slack/services/events/block-actions.js +1 -1
- package/dist/slack/services/events/modal-submission.js +14 -7
- package/dist/slack/services/events/streaming.js +9 -12
- package/dist/slack/services/events/utils.d.ts +6 -4
- package/dist/slack/services/events/utils.js +5 -5
- package/dist/slack/services/index.js +1 -1
- package/dist/slack/services/modals.d.ts +1 -0
- package/dist/slack/services/modals.js +6 -4
- package/dist/slack/tracer.d.ts +1 -1
- package/package.json +2 -2
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as hono0 from "hono";
|
|
2
2
|
|
|
3
3
|
//#region src/github/mcp/auth.d.ts
|
|
4
|
-
declare const githubMcpAuth: () =>
|
|
4
|
+
declare const githubMcpAuth: () => hono0.MiddlewareHandler<{
|
|
5
5
|
Variables: {
|
|
6
6
|
toolId: string;
|
|
7
7
|
};
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
|
-
import * as
|
|
2
|
+
import * as hono_types0 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_types0.BlankSchema, "/">;
|
|
10
10
|
//#endregion
|
|
11
11
|
export { app as default };
|
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, listPullRequestReviewCommentReactions, visualizeUpdateOperations } from "./utils.js";
|
|
4
|
+
import { commitFileChanges, commitNewFile, createIssueCommentReaction, createPullRequestReviewCommentReaction, deleteIssueCommentReaction, deletePullRequestReviewCommentReaction, fetchBranchChangedFiles, fetchComments, fetchPrFileDiffs, fetchPrFiles, fetchPrInfo, formatFileDiff, generatePrMarkdown, getGitHubClientFromRepo, listIssueCommentReactions, listIssueReactions, listPullRequestReviewCommentReactions, 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";
|
|
@@ -666,13 +666,17 @@ const getServer = async (toolId) => {
|
|
|
666
666
|
};
|
|
667
667
|
}
|
|
668
668
|
});
|
|
669
|
-
server.tool("add-
|
|
669
|
+
server.tool("add-reaction", `Add a reaction to a pull request body, a general PR comment, or an inline PR review comment. ${getAvailableRepositoryString(repositoryAccess)}`, {
|
|
670
670
|
owner: z.string().describe("Repository owner name"),
|
|
671
671
|
repo: z.string().describe("Repository name"),
|
|
672
|
-
|
|
673
|
-
|
|
672
|
+
target_id: z.number().describe("The target to react to: a comment ID for issue_comment/review_comment, or the pull request number for pull_request"),
|
|
673
|
+
target_type: z.enum([
|
|
674
|
+
"pull_request",
|
|
675
|
+
"issue_comment",
|
|
676
|
+
"review_comment"
|
|
677
|
+
]).describe("The type of target: \"pull_request\" for the PR body itself, \"issue_comment\" for general pull request comments, \"review_comment\" for inline PR review comments"),
|
|
674
678
|
reaction: ReactionContentSchema.describe("The reaction emoji to add: +1, -1, laugh, hooray, confused, heart, rocket, or eyes")
|
|
675
|
-
}, async ({ owner, repo,
|
|
679
|
+
}, async ({ owner, repo, target_id, target_type, reaction }) => {
|
|
676
680
|
try {
|
|
677
681
|
let githubClient;
|
|
678
682
|
try {
|
|
@@ -686,9 +690,23 @@ const getServer = async (toolId) => {
|
|
|
686
690
|
isError: true
|
|
687
691
|
};
|
|
688
692
|
}
|
|
693
|
+
let result;
|
|
694
|
+
if (target_type === "pull_request") {
|
|
695
|
+
const { data } = await githubClient.rest.reactions.createForIssue({
|
|
696
|
+
owner,
|
|
697
|
+
repo,
|
|
698
|
+
issue_number: target_id,
|
|
699
|
+
content: reaction
|
|
700
|
+
});
|
|
701
|
+
result = {
|
|
702
|
+
id: data.id,
|
|
703
|
+
content: data.content
|
|
704
|
+
};
|
|
705
|
+
} else if (target_type === "issue_comment") result = await createIssueCommentReaction(githubClient, owner, repo, target_id, reaction);
|
|
706
|
+
else result = await createPullRequestReviewCommentReaction(githubClient, owner, repo, target_id, reaction);
|
|
689
707
|
return { content: [{
|
|
690
708
|
type: "text",
|
|
691
|
-
text: `Successfully added ${reaction} reaction to ${
|
|
709
|
+
text: `Successfully added ${reaction} reaction to ${target_type === "pull_request" ? `PR #${target_id}` : `${target_type} ${target_id}`} in ${owner}/${repo}\n\nReaction ID: ${result.id}`
|
|
692
710
|
}] };
|
|
693
711
|
} catch (error) {
|
|
694
712
|
if (error instanceof Error && "status" in error) {
|
|
@@ -696,21 +714,21 @@ const getServer = async (toolId) => {
|
|
|
696
714
|
if (apiError.status === 404) return {
|
|
697
715
|
content: [{
|
|
698
716
|
type: "text",
|
|
699
|
-
text: `
|
|
717
|
+
text: `Target ${target_id} (${target_type}) not found in ${owner}/${repo}.`
|
|
700
718
|
}],
|
|
701
719
|
isError: true
|
|
702
720
|
};
|
|
703
721
|
if (apiError.status === 422) return {
|
|
704
722
|
content: [{
|
|
705
723
|
type: "text",
|
|
706
|
-
text: `Invalid reaction. Ensure the reaction type is valid and the
|
|
724
|
+
text: `Invalid reaction. Ensure the reaction type is valid and the target exists.`
|
|
707
725
|
}],
|
|
708
726
|
isError: true
|
|
709
727
|
};
|
|
710
728
|
if (apiError.status === 403) return {
|
|
711
729
|
content: [{
|
|
712
730
|
type: "text",
|
|
713
|
-
text: `Access denied when adding reaction to
|
|
731
|
+
text: `Access denied when adding reaction to ${target_type} ${target_id} in ${owner}/${repo}. Your GitHub App may not have sufficient permissions to create reactions.`
|
|
714
732
|
}],
|
|
715
733
|
isError: true
|
|
716
734
|
};
|
|
@@ -724,13 +742,17 @@ const getServer = async (toolId) => {
|
|
|
724
742
|
};
|
|
725
743
|
}
|
|
726
744
|
});
|
|
727
|
-
server.tool("remove-
|
|
745
|
+
server.tool("remove-reaction", `Remove a reaction from a pull request body, a general PR comment, or an inline PR review comment. Requires the reaction ID (returned when adding a reaction or from list-reactions). ${getAvailableRepositoryString(repositoryAccess)}`, {
|
|
728
746
|
owner: z.string().describe("Repository owner name"),
|
|
729
747
|
repo: z.string().describe("Repository name"),
|
|
730
|
-
|
|
731
|
-
|
|
748
|
+
target_id: z.number().describe("The target the reaction belongs to: a comment ID for issue_comment/review_comment, or the pull request number for pull_request"),
|
|
749
|
+
target_type: z.enum([
|
|
750
|
+
"pull_request",
|
|
751
|
+
"issue_comment",
|
|
752
|
+
"review_comment"
|
|
753
|
+
]).describe("The type of target: \"pull_request\" for the PR body itself, \"issue_comment\" for general pull request comments, \"review_comment\" for inline PR review comments"),
|
|
732
754
|
reaction_id: z.number().describe("The ID of the reaction to remove")
|
|
733
|
-
}, async ({ owner, repo,
|
|
755
|
+
}, async ({ owner, repo, target_id, target_type, reaction_id }) => {
|
|
734
756
|
try {
|
|
735
757
|
let githubClient;
|
|
736
758
|
try {
|
|
@@ -744,18 +766,24 @@ const getServer = async (toolId) => {
|
|
|
744
766
|
isError: true
|
|
745
767
|
};
|
|
746
768
|
}
|
|
747
|
-
if (
|
|
748
|
-
|
|
769
|
+
if (target_type === "pull_request") await githubClient.rest.reactions.deleteForIssue({
|
|
770
|
+
owner,
|
|
771
|
+
repo,
|
|
772
|
+
issue_number: target_id,
|
|
773
|
+
reaction_id
|
|
774
|
+
});
|
|
775
|
+
else if (target_type === "issue_comment") await deleteIssueCommentReaction(githubClient, owner, repo, target_id, reaction_id);
|
|
776
|
+
else await deletePullRequestReviewCommentReaction(githubClient, owner, repo, target_id, reaction_id);
|
|
749
777
|
return { content: [{
|
|
750
778
|
type: "text",
|
|
751
|
-
text: `Successfully removed reaction ${reaction_id} from ${
|
|
779
|
+
text: `Successfully removed reaction ${reaction_id} from ${target_type === "pull_request" ? `PR #${target_id}` : `${target_type} ${target_id}`} in ${owner}/${repo}`
|
|
752
780
|
}] };
|
|
753
781
|
} catch (error) {
|
|
754
782
|
if (error instanceof Error && "status" in error) {
|
|
755
783
|
if (error.status === 404) return {
|
|
756
784
|
content: [{
|
|
757
785
|
type: "text",
|
|
758
|
-
text: `
|
|
786
|
+
text: `Target ${target_id} (${target_type}) or reaction ${reaction_id} not found in ${owner}/${repo}.`
|
|
759
787
|
}],
|
|
760
788
|
isError: true
|
|
761
789
|
};
|
|
@@ -769,12 +797,16 @@ const getServer = async (toolId) => {
|
|
|
769
797
|
};
|
|
770
798
|
}
|
|
771
799
|
});
|
|
772
|
-
server.tool("list-
|
|
800
|
+
server.tool("list-reactions", `List all reactions on a pull request body, a general PR comment, or an inline PR review comment. Returns each reaction's ID (needed for removal). ${getAvailableRepositoryString(repositoryAccess)}`, {
|
|
773
801
|
owner: z.string().describe("Repository owner name"),
|
|
774
802
|
repo: z.string().describe("Repository name"),
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
803
|
+
target_id: z.number().describe("The target to list reactions for: a comment ID for issue_comment/review_comment, or the pull request number for pull_request"),
|
|
804
|
+
target_type: z.enum([
|
|
805
|
+
"pull_request",
|
|
806
|
+
"issue_comment",
|
|
807
|
+
"review_comment"
|
|
808
|
+
]).describe("The type of target: \"pull_request\" for the PR body itself, \"issue_comment\" for general pull request comments, \"review_comment\" for inline PR review comments")
|
|
809
|
+
}, async ({ owner, repo, target_id, target_type }) => {
|
|
778
810
|
try {
|
|
779
811
|
let githubClient;
|
|
780
812
|
try {
|
|
@@ -788,22 +820,26 @@ const getServer = async (toolId) => {
|
|
|
788
820
|
isError: true
|
|
789
821
|
};
|
|
790
822
|
}
|
|
791
|
-
|
|
823
|
+
let reactions = [];
|
|
824
|
+
if (target_type === "pull_request") reactions = await listIssueReactions(githubClient, owner, repo, target_id);
|
|
825
|
+
else if (target_type === "issue_comment") reactions = await listIssueCommentReactions(githubClient, owner, repo, target_id);
|
|
826
|
+
else reactions = await listPullRequestReviewCommentReactions(githubClient, owner, repo, target_id);
|
|
827
|
+
const label = target_type === "pull_request" ? `PR #${target_id}` : `${target_type} ${target_id}`;
|
|
792
828
|
if (reactions.length === 0) return { content: [{
|
|
793
829
|
type: "text",
|
|
794
|
-
text: `No reactions found on ${
|
|
830
|
+
text: `No reactions found on ${label} in ${owner}/${repo}.`
|
|
795
831
|
}] };
|
|
796
832
|
const formatted = reactions.map((r) => `• ${r.content} by @${r.user} (reaction_id: ${r.id})`).join("\n");
|
|
797
833
|
return { content: [{
|
|
798
834
|
type: "text",
|
|
799
|
-
text: `Found ${reactions.length} reaction(s) on ${
|
|
835
|
+
text: `Found ${reactions.length} reaction(s) on ${label} in ${owner}/${repo}:\n\n${formatted}`
|
|
800
836
|
}] };
|
|
801
837
|
} catch (error) {
|
|
802
838
|
if (error instanceof Error && "status" in error) {
|
|
803
839
|
if (error.status === 404) return {
|
|
804
840
|
content: [{
|
|
805
841
|
type: "text",
|
|
806
|
-
text: `
|
|
842
|
+
text: `Target ${target_id} (${target_type}) not found in ${owner}/${repo}.`
|
|
807
843
|
}],
|
|
808
844
|
isError: true
|
|
809
845
|
};
|
|
@@ -254,6 +254,7 @@ interface ReactionDetail {
|
|
|
254
254
|
}
|
|
255
255
|
declare function listIssueCommentReactions(octokit: Octokit, owner: string, repo: string, commentId: number): Promise<ReactionDetail[]>;
|
|
256
256
|
declare function listPullRequestReviewCommentReactions(octokit: Octokit, owner: string, repo: string, commentId: number): Promise<ReactionDetail[]>;
|
|
257
|
+
declare function listIssueReactions(octokit: Octokit, owner: string, repo: string, issueNumber: number): Promise<ReactionDetail[]>;
|
|
257
258
|
declare function formatFileDiff(pullRequestNumber: number, files: ChangedFile[], includeContents?: boolean): Promise<string>;
|
|
258
259
|
//#endregion
|
|
259
|
-
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, listPullRequestReviewCommentReactions, validateLineNumbers, visualizeUpdateOperations };
|
|
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 };
|
package/dist/github/mcp/utils.js
CHANGED
|
@@ -758,6 +758,21 @@ async function listPullRequestReviewCommentReactions(octokit, owner, repo, comme
|
|
|
758
758
|
});
|
|
759
759
|
return reactions;
|
|
760
760
|
}
|
|
761
|
+
async function listIssueReactions(octokit, owner, repo, issueNumber) {
|
|
762
|
+
const reactions = [];
|
|
763
|
+
for await (const response of octokit.paginate.iterator(octokit.rest.reactions.listForIssue, {
|
|
764
|
+
owner,
|
|
765
|
+
repo,
|
|
766
|
+
issue_number: issueNumber,
|
|
767
|
+
per_page: 100
|
|
768
|
+
})) for (const r of response.data) reactions.push({
|
|
769
|
+
id: r.id,
|
|
770
|
+
content: r.content,
|
|
771
|
+
user: r.user?.login ?? "unknown",
|
|
772
|
+
createdAt: r.created_at
|
|
773
|
+
});
|
|
774
|
+
return reactions;
|
|
775
|
+
}
|
|
761
776
|
async function formatFileDiff(pullRequestNumber, files, includeContents = false) {
|
|
762
777
|
let output = `## File Patches for PR #${pullRequestNumber}\n\n`;
|
|
763
778
|
output += `Found ${files.length} file(s) matching the requested paths.\n\n`;
|
|
@@ -780,4 +795,4 @@ async function formatFileDiff(pullRequestNumber, files, includeContents = false)
|
|
|
780
795
|
}
|
|
781
796
|
|
|
782
797
|
//#endregion
|
|
783
|
-
export { applyOperation, applyOperations, commitFileChanges, commitNewFile, createIssueCommentReaction, createPullRequestReviewCommentReaction, deleteIssueCommentReaction, deletePullRequestReviewCommentReaction, fetchBranchChangedFiles, fetchComments, fetchCommitDetails, fetchPrCommits, fetchPrFileDiffs, fetchPrFiles, fetchPrInfo, formatFileDiff, generatePrMarkdown, getFilePathsInRepo, getGitHubClientFromInstallationId, getGitHubClientFromRepo, listIssueCommentReactions, listPullRequestReviewCommentReactions, validateLineNumbers, visualizeUpdateOperations };
|
|
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 };
|
|
@@ -1,7 +1,7 @@
|
|
|
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/setup.d.ts
|
|
5
|
-
declare const app: Hono<
|
|
5
|
+
declare const app: Hono<hono_types4.BlankEnv, hono_types4.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_types6 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_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_types8 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_types8.BlankEnv, hono_types8.BlankSchema, "/">;
|
|
11
11
|
//#endregion
|
|
12
12
|
export { WebhookVerificationResult, app as default, verifyWebhookSignature };
|
package/dist/slack/dispatcher.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getLogger } from "../logger.js";
|
|
2
2
|
import { findWorkspaceConnectionByTeamId } from "./services/nango.js";
|
|
3
|
-
import { getSlackClient } from "./services/client.js";
|
|
4
3
|
import { sendResponseUrlMessage } from "./services/events/utils.js";
|
|
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
7
|
import { handleMessageShortcut, handleOpenAgentSelectorModal, handleOpenFollowUpModal, handleToolApproval } from "./services/events/block-actions.js";
|
|
@@ -35,6 +35,15 @@ async function dispatchSlackEvent(eventType, payload, options, span) {
|
|
|
35
35
|
}, "Ignoring bot message");
|
|
36
36
|
return { outcome };
|
|
37
37
|
}
|
|
38
|
+
if (event?.edited) {
|
|
39
|
+
outcome = "ignored_edited_message";
|
|
40
|
+
span.setAttribute(SLACK_SPAN_KEYS.OUTCOME, outcome);
|
|
41
|
+
logger.info({
|
|
42
|
+
teamId,
|
|
43
|
+
innerEventType
|
|
44
|
+
}, "Ignoring edited message");
|
|
45
|
+
return { outcome };
|
|
46
|
+
}
|
|
38
47
|
if (event?.type === "app_mention" && event.channel && event.user && teamId) {
|
|
39
48
|
outcome = "handled";
|
|
40
49
|
span.setAttribute(SLACK_SPAN_KEYS.OUTCOME, outcome);
|
|
@@ -6,7 +6,7 @@ import { getSlackClient, getSlackTeamInfo, getSlackUserInfo } from "../services/
|
|
|
6
6
|
import { getBotTokenForTeam, setBotTokenForTeam } from "../services/workspace-tokens.js";
|
|
7
7
|
import "../services/index.js";
|
|
8
8
|
import { OpenAPIHono, z } from "@hono/zod-openapi";
|
|
9
|
-
import { createWorkAppSlackWorkspace, isUniqueConstraintError } from "@inkeep/agents-core";
|
|
9
|
+
import { createWorkAppSlackWorkspace, isUniqueConstraintError, listWorkAppSlackWorkspacesByTenant } from "@inkeep/agents-core";
|
|
10
10
|
import * as crypto$1 from "node:crypto";
|
|
11
11
|
import { createProtectedRoute, noAuth } from "@inkeep/agents-core/middleware";
|
|
12
12
|
|
|
@@ -213,6 +213,26 @@ app.openapi(createProtectedRoute({
|
|
|
213
213
|
appId: tokenData.app_id,
|
|
214
214
|
installedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
215
215
|
};
|
|
216
|
+
if (tenantId && workspaceData.teamId) {
|
|
217
|
+
let existingWorkspaces;
|
|
218
|
+
try {
|
|
219
|
+
existingWorkspaces = await listWorkAppSlackWorkspacesByTenant(runDbClient_default)(tenantId);
|
|
220
|
+
} catch (err) {
|
|
221
|
+
logger.error({
|
|
222
|
+
err,
|
|
223
|
+
tenantId
|
|
224
|
+
}, "Failed to check existing workspaces");
|
|
225
|
+
return c.redirect(`${dashboardUrl}?error=workspace_check_failed`);
|
|
226
|
+
}
|
|
227
|
+
if (existingWorkspaces.some((w) => w.slackTeamId !== workspaceData.teamId)) {
|
|
228
|
+
logger.warn({
|
|
229
|
+
tenantId,
|
|
230
|
+
newTeamId: workspaceData.teamId,
|
|
231
|
+
existingTeamIds: existingWorkspaces.map((w) => w.slackTeamId)
|
|
232
|
+
}, "Tenant already has a different Slack workspace, rejecting installation");
|
|
233
|
+
return c.redirect(`${dashboardUrl}?error=workspace_limit_reached`);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
216
236
|
if (workspaceData.teamId && workspaceData.botToken) {
|
|
217
237
|
clearWorkspaceConnectionCache(workspaceData.teamId);
|
|
218
238
|
const nangoResult = await storeWorkspaceInstallation({
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { getLogger } from "../../logger.js";
|
|
2
2
|
import runDbClient_default from "../../db/runDbClient.js";
|
|
3
3
|
import { getWorkspaceDefaultAgentFromNango } from "./nango.js";
|
|
4
|
+
import { fetchAgentsForProject } from "./events/utils.js";
|
|
4
5
|
import { findWorkAppSlackChannelAgentConfig } from "@inkeep/agents-core";
|
|
5
6
|
|
|
6
7
|
//#region src/slack/services/agent-resolution.ts
|
|
@@ -11,6 +12,35 @@ import { findWorkAppSlackChannelAgentConfig } from "@inkeep/agents-core";
|
|
|
11
12
|
* Priority: Channel default > Workspace default (all admin-controlled)
|
|
12
13
|
*/
|
|
13
14
|
const logger = getLogger("slack-agent-resolution");
|
|
15
|
+
const AGENT_NAME_CACHE_TTL_MS = 300 * 1e3;
|
|
16
|
+
const AGENT_NAME_CACHE_MAX_SIZE = 500;
|
|
17
|
+
const agentNameCache = /* @__PURE__ */ new Map();
|
|
18
|
+
async function lookupAgentName(tenantId, projectId, agentId) {
|
|
19
|
+
const cacheKey = `${tenantId}:${projectId}:${agentId}`;
|
|
20
|
+
const cached = agentNameCache.get(cacheKey);
|
|
21
|
+
if (cached && cached.expiresAt > Date.now()) return cached.name || void 0;
|
|
22
|
+
const agents = await fetchAgentsForProject(tenantId, projectId);
|
|
23
|
+
for (const agent of agents) {
|
|
24
|
+
const key = `${tenantId}:${projectId}:${agent.id}`;
|
|
25
|
+
agentNameCache.set(key, {
|
|
26
|
+
name: agent.name || null,
|
|
27
|
+
expiresAt: Date.now() + AGENT_NAME_CACHE_TTL_MS
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
if (agentNameCache.size > AGENT_NAME_CACHE_MAX_SIZE) {
|
|
31
|
+
const now = Date.now();
|
|
32
|
+
for (const [key, entry] of agentNameCache) if (entry.expiresAt <= now) agentNameCache.delete(key);
|
|
33
|
+
if (agentNameCache.size > AGENT_NAME_CACHE_MAX_SIZE) {
|
|
34
|
+
const excess = agentNameCache.size - AGENT_NAME_CACHE_MAX_SIZE;
|
|
35
|
+
const keys = agentNameCache.keys();
|
|
36
|
+
for (let i = 0; i < excess; i++) {
|
|
37
|
+
const { value } = keys.next();
|
|
38
|
+
if (value) agentNameCache.delete(value);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return agents.find((a) => a.id === agentId)?.name || void 0;
|
|
43
|
+
}
|
|
14
44
|
/**
|
|
15
45
|
* Resolve the effective agent configuration.
|
|
16
46
|
* Priority: Channel default > Workspace default
|
|
@@ -25,6 +55,7 @@ async function resolveEffectiveAgent(params) {
|
|
|
25
55
|
teamId,
|
|
26
56
|
channelId
|
|
27
57
|
}, "Resolving effective agent");
|
|
58
|
+
let result = null;
|
|
28
59
|
if (channelId) {
|
|
29
60
|
const channelConfig = await findWorkAppSlackChannelAgentConfig(runDbClient_default)(tenantId, teamId, channelId);
|
|
30
61
|
if (channelConfig?.enabled) {
|
|
@@ -33,7 +64,7 @@ async function resolveEffectiveAgent(params) {
|
|
|
33
64
|
agentId: channelConfig.agentId,
|
|
34
65
|
source: "channel"
|
|
35
66
|
}, "Resolved agent from channel config");
|
|
36
|
-
|
|
67
|
+
result = {
|
|
37
68
|
projectId: channelConfig.projectId,
|
|
38
69
|
agentId: channelConfig.agentId,
|
|
39
70
|
agentName: channelConfig.agentName || void 0,
|
|
@@ -42,27 +73,39 @@ async function resolveEffectiveAgent(params) {
|
|
|
42
73
|
};
|
|
43
74
|
}
|
|
44
75
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
76
|
+
if (!result) {
|
|
77
|
+
const workspaceConfig = await getWorkspaceDefaultAgentFromNango(teamId);
|
|
78
|
+
if (workspaceConfig?.agentId && workspaceConfig.projectId) {
|
|
79
|
+
logger.info({
|
|
80
|
+
teamId,
|
|
81
|
+
agentId: workspaceConfig.agentId,
|
|
82
|
+
source: "workspace"
|
|
83
|
+
}, "Resolved agent from workspace config");
|
|
84
|
+
result = {
|
|
85
|
+
projectId: workspaceConfig.projectId,
|
|
86
|
+
agentId: workspaceConfig.agentId,
|
|
87
|
+
agentName: workspaceConfig.agentName,
|
|
88
|
+
source: "workspace",
|
|
89
|
+
grantAccessToMembers: workspaceConfig.grantAccessToMembers ?? true
|
|
90
|
+
};
|
|
91
|
+
}
|
|
59
92
|
}
|
|
60
|
-
|
|
93
|
+
if (result && (!result.agentName || result.agentName === result.agentId)) {
|
|
94
|
+
const name = await lookupAgentName(tenantId, result.projectId, result.agentId);
|
|
95
|
+
if (name) {
|
|
96
|
+
result.agentName = name;
|
|
97
|
+
logger.debug({
|
|
98
|
+
agentId: result.agentId,
|
|
99
|
+
agentName: name
|
|
100
|
+
}, "Enriched agent config with name from manage API");
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (!result) logger.debug({
|
|
61
104
|
tenantId,
|
|
62
105
|
teamId,
|
|
63
106
|
channelId
|
|
64
107
|
}, "No agent configuration found");
|
|
65
|
-
return
|
|
108
|
+
return result;
|
|
66
109
|
}
|
|
67
110
|
/**
|
|
68
111
|
* Get all agent configuration sources for display purposes.
|
|
@@ -92,10 +135,15 @@ async function getAgentConfigSources(params) {
|
|
|
92
135
|
source: "workspace",
|
|
93
136
|
grantAccessToMembers: wsConfig.grantAccessToMembers ?? true
|
|
94
137
|
};
|
|
138
|
+
const effective = channelConfig || workspaceConfig;
|
|
139
|
+
if (effective && (!effective.agentName || effective.agentName === effective.agentId)) {
|
|
140
|
+
const name = await lookupAgentName(tenantId, effective.projectId, effective.agentId);
|
|
141
|
+
if (name) effective.agentName = name;
|
|
142
|
+
}
|
|
95
143
|
return {
|
|
96
144
|
channelConfig,
|
|
97
145
|
workspaceConfig,
|
|
98
|
-
effective
|
|
146
|
+
effective
|
|
99
147
|
};
|
|
100
148
|
}
|
|
101
149
|
|
|
@@ -44,10 +44,7 @@ function buildConversationResponseBlocks(params) {
|
|
|
44
44
|
}
|
|
45
45
|
}];
|
|
46
46
|
if (!isError) {
|
|
47
|
-
const contextBlock = createContextBlock({
|
|
48
|
-
agentName,
|
|
49
|
-
isPrivate: true
|
|
50
|
-
});
|
|
47
|
+
const contextBlock = createContextBlock({ agentName });
|
|
51
48
|
blocks.push(contextBlock);
|
|
52
49
|
blocks.push({
|
|
53
50
|
type: "actions",
|
|
@@ -2,11 +2,11 @@ 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 { extractApiErrorMessage, fetchAgentsForProject, fetchProjectsForTenant, getChannelAgentConfig, sendResponseUrlMessage } from "../events/utils.js";
|
|
5
6
|
import { resolveEffectiveAgent } from "../agent-resolution.js";
|
|
6
7
|
import { SlackStrings } from "../../i18n/strings.js";
|
|
7
8
|
import { createAlreadyLinkedMessage, createContextBlock, createCreateInkeepAccountMessage, createErrorMessage, createJwtLinkMessage, createNotLinkedMessage, createStatusMessage, createUnlinkSuccessMessage, createUpdatedHelpMessage } from "../blocks/index.js";
|
|
8
9
|
import { getSlackClient } from "../client.js";
|
|
9
|
-
import { extractApiErrorMessage, fetchAgentsForProject, fetchProjectsForTenant, getChannelAgentConfig, sendResponseUrlMessage } from "../events/utils.js";
|
|
10
10
|
import { buildAgentSelectorModal } from "../modals.js";
|
|
11
11
|
import { createInvitationInDb, deleteWorkAppSlackUserMapping, findWorkAppSlackUserMapping, findWorkAppSlackUserMappingBySlackUser, findWorkAppSlackWorkspaceByTeamId, flushTraces, getInProcessFetch, getOrganizationMemberByEmail, getPendingInvitationsByEmail, getWaitUntil, signSlackLinkToken, signSlackUserToken } from "@inkeep/agents-core";
|
|
12
12
|
|
|
@@ -412,7 +412,7 @@ async function executeAgentInBackground(payload, existingLink, targetAgent, ques
|
|
|
412
412
|
}, "Agent execution completed via Slack");
|
|
413
413
|
const contextBlock = createContextBlock({ agentName: targetAgent.name || targetAgent.id });
|
|
414
414
|
await sendResponseUrlMessage(payload.responseUrl, {
|
|
415
|
-
response_type: "
|
|
415
|
+
response_type: "in_channel",
|
|
416
416
|
text: assistantMessage,
|
|
417
417
|
blocks: [{
|
|
418
418
|
type: "section",
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { env } from "../../../env.js";
|
|
2
2
|
import { getLogger } from "../../../logger.js";
|
|
3
3
|
import { findWorkspaceConnectionByTeamId } from "../nango.js";
|
|
4
|
+
import { checkIfBotThread, classifyError, findCachedUserMapping, formatChannelContext, generateSlackConversationId, getThreadContext, getUserFriendlyErrorMessage, timedOp } from "./utils.js";
|
|
4
5
|
import { resolveEffectiveAgent } from "../agent-resolution.js";
|
|
5
6
|
import { SlackStrings } from "../../i18n/strings.js";
|
|
6
7
|
import { getSlackChannelInfo, getSlackClient, getSlackUserInfo, postMessageInThread } from "../client.js";
|
|
7
|
-
import { checkIfBotThread, classifyError, findCachedUserMapping, formatChannelContext, generateSlackConversationId, getThreadContext, getUserFriendlyErrorMessage, timedOp } from "./utils.js";
|
|
8
8
|
import { SLACK_SPAN_KEYS, SLACK_SPAN_NAMES, setSpanWithError, tracer } from "../../tracer.js";
|
|
9
9
|
import { streamAgentResponse } from "./streaming.js";
|
|
10
10
|
import { signSlackUserToken } from "@inkeep/agents-core";
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { env } from "../../../env.js";
|
|
2
2
|
import { getLogger } from "../../../logger.js";
|
|
3
3
|
import { findWorkspaceConnectionByTeamId } from "../nango.js";
|
|
4
|
+
import { fetchAgentsForProject, fetchProjectsForTenant, findCachedUserMapping, getChannelAgentConfig, sendResponseUrlMessage } from "./utils.js";
|
|
4
5
|
import { SlackStrings } from "../../i18n/strings.js";
|
|
5
6
|
import { ToolApprovalButtonValueSchema, buildToolApprovalDoneBlocks } from "../blocks/index.js";
|
|
6
7
|
import { getSlackClient } from "../client.js";
|
|
7
|
-
import { fetchAgentsForProject, fetchProjectsForTenant, findCachedUserMapping, getChannelAgentConfig, sendResponseUrlMessage } from "./utils.js";
|
|
8
8
|
import { buildAgentSelectorModal, buildFollowUpModal, buildMessageShortcutModal } from "../modals.js";
|
|
9
9
|
import { SLACK_SPAN_KEYS, SLACK_SPAN_NAMES, setSpanWithError, tracer } from "../../tracer.js";
|
|
10
10
|
import { getInProcessFetch, signSlackUserToken } from "@inkeep/agents-core";
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { env } from "../../../env.js";
|
|
2
2
|
import { getLogger } from "../../../logger.js";
|
|
3
3
|
import { findWorkspaceConnectionByTeamId } from "../nango.js";
|
|
4
|
+
import { classifyError, extractApiErrorMessage, findCachedUserMapping, generateSlackConversationId, getThreadContext, getUserFriendlyErrorMessage, markdownToMrkdwn, sendResponseUrlMessage } from "./utils.js";
|
|
4
5
|
import { SlackStrings } from "../../i18n/strings.js";
|
|
5
6
|
import { buildConversationResponseBlocks } from "../blocks/index.js";
|
|
6
7
|
import { getSlackClient } from "../client.js";
|
|
7
|
-
import { classifyError, extractApiErrorMessage, findCachedUserMapping, generateSlackConversationId, getThreadContext, getUserFriendlyErrorMessage, markdownToMrkdwn, sendResponseUrlMessage } from "./utils.js";
|
|
8
8
|
import { SLACK_SPAN_KEYS, SLACK_SPAN_NAMES, setSpanWithError, tracer } from "../../tracer.js";
|
|
9
9
|
import { getInProcessFetch, signSlackUserToken } from "@inkeep/agents-core";
|
|
10
10
|
|
|
@@ -36,13 +36,16 @@ async function handleModalSubmission(view) {
|
|
|
36
36
|
const includeContext = includeContextValue?.selected_options?.some((o) => o.value === "include_context") ?? true;
|
|
37
37
|
let agentId = metadata.selectedAgentId;
|
|
38
38
|
let projectId = metadata.selectedProjectId;
|
|
39
|
+
let agentName = null;
|
|
39
40
|
if (agentSelectValue?.selected_option?.value) try {
|
|
40
41
|
const parsed = JSON.parse(agentSelectValue.selected_option.value);
|
|
41
42
|
agentId = parsed.agentId;
|
|
42
43
|
projectId = parsed.projectId;
|
|
44
|
+
agentName = parsed.agentName || null;
|
|
43
45
|
} catch {
|
|
44
46
|
logger.warn({ value: agentSelectValue.selected_option.value }, "Failed to parse agent select value");
|
|
45
47
|
}
|
|
48
|
+
const agentDisplayName = agentName || agentId || "Agent";
|
|
46
49
|
if (!agentId || !projectId) {
|
|
47
50
|
logger.error({ metadata }, "Missing agent or project ID in modal submission");
|
|
48
51
|
if (metadata.buttonResponseUrl) await sendResponseUrlMessage(metadata.buttonResponseUrl, {
|
|
@@ -112,7 +115,7 @@ async function handleModalSubmission(view) {
|
|
|
112
115
|
});
|
|
113
116
|
span.setAttribute(SLACK_SPAN_KEYS.CONVERSATION_ID, conversationId);
|
|
114
117
|
const apiBaseUrl = env.INKEEP_AGENTS_API_URL || "http://localhost:3002";
|
|
115
|
-
const thinkingText = SlackStrings.status.thinking(
|
|
118
|
+
const thinkingText = SlackStrings.status.thinking(agentDisplayName);
|
|
116
119
|
if (metadata.buttonResponseUrl) await sendResponseUrlMessage(metadata.buttonResponseUrl, {
|
|
117
120
|
text: thinkingText,
|
|
118
121
|
response_type: "ephemeral",
|
|
@@ -139,6 +142,7 @@ async function handleModalSubmission(view) {
|
|
|
139
142
|
slackClient,
|
|
140
143
|
metadata,
|
|
141
144
|
agentId,
|
|
145
|
+
agentDisplayName,
|
|
142
146
|
projectId,
|
|
143
147
|
tenantId,
|
|
144
148
|
conversationId,
|
|
@@ -194,7 +198,8 @@ async function handleFollowUpSubmission(view) {
|
|
|
194
198
|
span.end();
|
|
195
199
|
return;
|
|
196
200
|
}
|
|
197
|
-
const { conversationId, agentId, projectId, tenantId, teamId, slackUserId, channel } = metadata;
|
|
201
|
+
const { conversationId, agentId, agentName, projectId, tenantId, teamId, slackUserId, channel } = metadata;
|
|
202
|
+
const agentDisplayName = agentName || agentId || "Agent";
|
|
198
203
|
span.setAttribute(SLACK_SPAN_KEYS.TEAM_ID, teamId);
|
|
199
204
|
span.setAttribute(SLACK_SPAN_KEYS.CHANNEL_ID, channel);
|
|
200
205
|
span.setAttribute(SLACK_SPAN_KEYS.USER_ID, slackUserId);
|
|
@@ -234,7 +239,7 @@ async function handleFollowUpSubmission(view) {
|
|
|
234
239
|
await slackClient.chat.postEphemeral({
|
|
235
240
|
channel,
|
|
236
241
|
user: slackUserId,
|
|
237
|
-
text: SlackStrings.status.thinking(
|
|
242
|
+
text: SlackStrings.status.thinking(agentDisplayName)
|
|
238
243
|
});
|
|
239
244
|
const responseText = await callAgentApi({
|
|
240
245
|
apiBaseUrl,
|
|
@@ -247,11 +252,12 @@ async function handleFollowUpSubmission(view) {
|
|
|
247
252
|
const responseBlocks = buildConversationResponseBlocks({
|
|
248
253
|
userMessage: question,
|
|
249
254
|
responseText: responseText.text,
|
|
250
|
-
agentName:
|
|
255
|
+
agentName: agentDisplayName,
|
|
251
256
|
isError: responseText.isError,
|
|
252
257
|
followUpParams: {
|
|
253
258
|
conversationId,
|
|
254
259
|
agentId,
|
|
260
|
+
agentName: agentDisplayName,
|
|
255
261
|
projectId,
|
|
256
262
|
tenantId,
|
|
257
263
|
teamId,
|
|
@@ -370,15 +376,16 @@ async function callAgentApi(params) {
|
|
|
370
376
|
});
|
|
371
377
|
}
|
|
372
378
|
async function postPrivateResponse(params) {
|
|
373
|
-
const { slackClient, metadata, agentId, projectId, tenantId, conversationId, userMessage, responseText, isError } = params;
|
|
379
|
+
const { slackClient, metadata, agentId, agentDisplayName, projectId, tenantId, conversationId, userMessage, responseText, isError } = params;
|
|
374
380
|
const responseBlocks = buildConversationResponseBlocks({
|
|
375
381
|
userMessage,
|
|
376
382
|
responseText,
|
|
377
|
-
agentName:
|
|
383
|
+
agentName: agentDisplayName,
|
|
378
384
|
isError,
|
|
379
385
|
followUpParams: {
|
|
380
386
|
conversationId,
|
|
381
387
|
agentId,
|
|
388
|
+
agentName: agentDisplayName,
|
|
382
389
|
projectId,
|
|
383
390
|
tenantId,
|
|
384
391
|
teamId: metadata.teamId,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { env } from "../../../env.js";
|
|
2
2
|
import { getLogger } from "../../../logger.js";
|
|
3
|
-
import { buildCitationsBlock, buildDataArtifactBlocks, buildDataComponentBlocks, buildSummaryBreadcrumbBlock, buildToolApprovalBlocks, buildToolApprovalExpiredBlocks, buildToolOutputErrorBlock, createContextBlock } from "../blocks/index.js";
|
|
4
3
|
import { SlackErrorType, classifyError, extractApiErrorMessage, getUserFriendlyErrorMessage } from "./utils.js";
|
|
4
|
+
import { buildCitationsBlock, buildDataArtifactBlocks, buildDataComponentBlocks, buildSummaryBreadcrumbBlock, buildToolApprovalBlocks, buildToolApprovalExpiredBlocks, buildToolOutputErrorBlock, createContextBlock } from "../blocks/index.js";
|
|
5
5
|
import { SLACK_SPAN_KEYS, SLACK_SPAN_NAMES, setSpanWithError, tracer } from "../../tracer.js";
|
|
6
6
|
import { getInProcessFetch, retryWithBackoff } from "@inkeep/agents-core";
|
|
7
7
|
|
|
@@ -193,6 +193,7 @@ async function streamAgentResponse(params) {
|
|
|
193
193
|
});
|
|
194
194
|
const pendingApprovalMessages = [];
|
|
195
195
|
const toolCallIdToName = /* @__PURE__ */ new Map();
|
|
196
|
+
const toolCallIdToInput = /* @__PURE__ */ new Map();
|
|
196
197
|
const toolErrors = [];
|
|
197
198
|
const citations = [];
|
|
198
199
|
const summaryLabels = [];
|
|
@@ -221,9 +222,9 @@ async function streamAgentResponse(params) {
|
|
|
221
222
|
continue;
|
|
222
223
|
}
|
|
223
224
|
if (data.type === "tool-approval-request" && conversationId) {
|
|
224
|
-
const toolName = data.toolName || "Tool";
|
|
225
225
|
const toolCallId = data.toolCallId;
|
|
226
|
-
const
|
|
226
|
+
const toolName = toolCallIdToName.get(toolCallId) || "Tool";
|
|
227
|
+
const input = toolCallIdToInput.get(toolCallId);
|
|
227
228
|
const buttonValue = {
|
|
228
229
|
toolCallId,
|
|
229
230
|
conversationId,
|
|
@@ -260,6 +261,7 @@ async function streamAgentResponse(params) {
|
|
|
260
261
|
}
|
|
261
262
|
if (data.type === "tool-input-available" && data.toolCallId && data.toolName) {
|
|
262
263
|
toolCallIdToName.set(String(data.toolCallId), String(data.toolName));
|
|
264
|
+
if (data.input && typeof data.input === "object") toolCallIdToInput.set(String(data.toolCallId), data.input);
|
|
263
265
|
continue;
|
|
264
266
|
}
|
|
265
267
|
if (data.type === "tool-output-denied" && data.toolCallId) {
|
|
@@ -383,6 +385,10 @@ async function streamAgentResponse(params) {
|
|
|
383
385
|
const stopBlocks = [];
|
|
384
386
|
for (const { toolName, errorText } of toolErrors) stopBlocks.push(buildToolOutputErrorBlock(toolName, errorText));
|
|
385
387
|
if (summaryLabels.length > 0) stopBlocks.push(buildSummaryBreadcrumbBlock(summaryLabels));
|
|
388
|
+
if (citations.length > 0) {
|
|
389
|
+
const citationBlocks = buildCitationsBlock(citations);
|
|
390
|
+
stopBlocks.push(...citationBlocks);
|
|
391
|
+
}
|
|
386
392
|
stopBlocks.push(createContextBlock({ agentName }));
|
|
387
393
|
try {
|
|
388
394
|
await withTimeout(streamer.stop({ blocks: stopBlocks.slice(0, 50) }), CHATSTREAM_OP_TIMEOUT_MS, "streamer.stop");
|
|
@@ -395,15 +401,6 @@ async function streamAgentResponse(params) {
|
|
|
395
401
|
responseLength: fullText.length
|
|
396
402
|
}, "Failed to finalize chatStream — content was already delivered");
|
|
397
403
|
}
|
|
398
|
-
if (citations.length > 0) {
|
|
399
|
-
const citationBlocks = buildCitationsBlock(citations);
|
|
400
|
-
if (citationBlocks.length > 0) await slackClient.chat.postMessage({
|
|
401
|
-
channel,
|
|
402
|
-
thread_ts: threadTs,
|
|
403
|
-
text: "📚 Sources",
|
|
404
|
-
blocks: citationBlocks
|
|
405
|
-
}).catch((e) => logger.warn({ error: e }, "Failed to post citations"));
|
|
406
|
-
}
|
|
407
404
|
if (thinkingMessageTs) try {
|
|
408
405
|
await slackClient.chat.delete({
|
|
409
406
|
channel,
|
|
@@ -8,12 +8,12 @@ import { AgentOption } from "../modals.js";
|
|
|
8
8
|
* Called on every @mention and /inkeep command — caching avoids redundant DB queries.
|
|
9
9
|
*/
|
|
10
10
|
declare function findCachedUserMapping(tenantId: string, slackUserId: string, teamId: string, clientId?: string): Promise<{
|
|
11
|
-
|
|
11
|
+
id: string;
|
|
12
12
|
createdAt: string;
|
|
13
13
|
updatedAt: string;
|
|
14
|
-
id: string;
|
|
15
14
|
tenantId: string;
|
|
16
15
|
clientId: string;
|
|
16
|
+
slackUserId: string;
|
|
17
17
|
slackTeamId: string;
|
|
18
18
|
slackEnterpriseId: string | null;
|
|
19
19
|
inkeepUserId: string;
|
|
@@ -137,8 +137,10 @@ declare function getThreadContext(slackClient: {
|
|
|
137
137
|
}) => Promise<{
|
|
138
138
|
user?: {
|
|
139
139
|
real_name?: string;
|
|
140
|
-
|
|
141
|
-
|
|
140
|
+
profile?: {
|
|
141
|
+
display_name?: string;
|
|
142
|
+
email?: string;
|
|
143
|
+
};
|
|
142
144
|
};
|
|
143
145
|
}>;
|
|
144
146
|
};
|
|
@@ -347,15 +347,15 @@ async function getThreadContext(slackClient, channel, threadTs, options = {}) {
|
|
|
347
347
|
try {
|
|
348
348
|
const userInfo = await slackClient.users?.info({ user: userId });
|
|
349
349
|
userNameCache.set(userId, {
|
|
350
|
-
displayName: userInfo?.user?.display_name,
|
|
350
|
+
displayName: userInfo?.user?.profile?.display_name,
|
|
351
351
|
fullName: userInfo?.user?.real_name,
|
|
352
|
-
|
|
352
|
+
email: userInfo?.user?.profile?.email
|
|
353
353
|
});
|
|
354
354
|
} catch {
|
|
355
355
|
userNameCache.set(userId, {
|
|
356
356
|
displayName: void 0,
|
|
357
357
|
fullName: void 0,
|
|
358
|
-
|
|
358
|
+
email: void 0
|
|
359
359
|
});
|
|
360
360
|
}
|
|
361
361
|
}));
|
|
@@ -365,10 +365,10 @@ async function getThreadContext(slackClient, channel, threadTs, options = {}) {
|
|
|
365
365
|
const parts = [`userId: ${userId}`];
|
|
366
366
|
if (info.displayName) parts.push(`"${info.displayName}"`);
|
|
367
367
|
if (info.fullName) parts.push(`"${info.fullName}"`);
|
|
368
|
-
if (info.
|
|
368
|
+
if (info.email) parts.push(info.email);
|
|
369
369
|
userDirectoryLines.push(`- ${parts.join(", ")}`);
|
|
370
370
|
}
|
|
371
|
-
return `${userDirectoryLines.length > 0 ? `Users in this thread (UserId - DisplayName, FullName,
|
|
371
|
+
return `${userDirectoryLines.length > 0 ? `Users in this thread (UserId - DisplayName, FullName, Email):\n${userDirectoryLines.join("\n")}\n\n` : ""}Messages in this thread:\n${messagesToProcess.map((msg, index) => {
|
|
372
372
|
const isBot = !!msg.bot_id;
|
|
373
373
|
const isParent = index === 0;
|
|
374
374
|
let role;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { clearWorkspaceConnectionCache, computeWorkspaceConnectionId, createConnectSession, deleteWorkspaceInstallation, findWorkspaceConnectionByTeamId, getConnectionAccessToken, getSlackIntegrationId, getSlackNango, getWorkspaceDefaultAgentFromNango, listWorkspaceInstallations, setWorkspaceDefaultAgent, storeWorkspaceInstallation, updateConnectionMetadata } from "./nango.js";
|
|
2
|
+
import { SlackErrorType, checkIfBotThread, classifyError, extractApiErrorMessage, fetchAgentsForProject, fetchProjectsForTenant, findCachedUserMapping, generateSlackConversationId, getChannelAgentConfig, getThreadContext, getUserFriendlyErrorMessage, getWorkspaceDefaultAgent, markdownToMrkdwn, sendResponseUrlMessage } from "./events/utils.js";
|
|
2
3
|
import { getAgentConfigSources, resolveEffectiveAgent } from "./agent-resolution.js";
|
|
3
4
|
import { ToolApprovalButtonValueSchema, buildCitationsBlock, buildConversationResponseBlocks, buildDataArtifactBlocks, buildDataComponentBlocks, buildFollowUpButton, buildSummaryBreadcrumbBlock, buildToolApprovalBlocks, buildToolApprovalDoneBlocks, buildToolApprovalExpiredBlocks, buildToolOutputErrorBlock, createAlreadyLinkedMessage, createContextBlock, createCreateInkeepAccountMessage, createErrorMessage, createJwtLinkMessage, createNotLinkedMessage, createStatusMessage, createUnlinkSuccessMessage, createUpdatedHelpMessage } from "./blocks/index.js";
|
|
4
5
|
import { checkUserIsChannelMember, getSlackChannelInfo, getSlackChannels, getSlackClient, getSlackTeamInfo, getSlackUserInfo, postMessage, postMessageInThread, revokeSlackToken } from "./client.js";
|
|
5
|
-
import { SlackErrorType, checkIfBotThread, classifyError, extractApiErrorMessage, fetchAgentsForProject, fetchProjectsForTenant, findCachedUserMapping, generateSlackConversationId, getChannelAgentConfig, getThreadContext, getUserFriendlyErrorMessage, getWorkspaceDefaultAgent, markdownToMrkdwn, sendResponseUrlMessage } from "./events/utils.js";
|
|
6
6
|
import { buildAgentSelectorModal, buildFollowUpModal, buildMessageShortcutModal } from "./modals.js";
|
|
7
7
|
import { handleAgentPickerCommand, handleCommand, handleHelpCommand, handleLinkCommand, handleQuestionCommand, handleStatusCommand, handleUnlinkCommand } from "./commands/index.js";
|
|
8
8
|
import { streamAgentResponse } from "./events/streaming.js";
|
|
@@ -29,12 +29,13 @@ function buildAgentSelectorModal(params) {
|
|
|
29
29
|
const agentOptions = agents.length > 0 ? agents.map((agent) => ({
|
|
30
30
|
text: {
|
|
31
31
|
type: "plain_text",
|
|
32
|
-
text: agent.name
|
|
32
|
+
text: agent.name ? `${agent.name} (${agent.id})` : agent.id,
|
|
33
33
|
emoji: true
|
|
34
34
|
},
|
|
35
35
|
value: JSON.stringify({
|
|
36
36
|
agentId: agent.id,
|
|
37
|
-
projectId: agent.projectId
|
|
37
|
+
projectId: agent.projectId,
|
|
38
|
+
agentName: agent.name
|
|
38
39
|
})
|
|
39
40
|
})) : [{
|
|
40
41
|
text: {
|
|
@@ -234,12 +235,13 @@ function buildMessageShortcutModal(params) {
|
|
|
234
235
|
const agentOptions = agents.length > 0 ? agents.map((agent) => ({
|
|
235
236
|
text: {
|
|
236
237
|
type: "plain_text",
|
|
237
|
-
text: agent.name
|
|
238
|
+
text: agent.name ? `${agent.name} (${agent.id})` : agent.id,
|
|
238
239
|
emoji: true
|
|
239
240
|
},
|
|
240
241
|
value: JSON.stringify({
|
|
241
242
|
agentId: agent.id,
|
|
242
|
-
projectId: agent.projectId
|
|
243
|
+
projectId: agent.projectId,
|
|
244
|
+
agentName: agent.name
|
|
243
245
|
})
|
|
244
246
|
})) : [{
|
|
245
247
|
text: {
|
package/dist/slack/tracer.d.ts
CHANGED
|
@@ -40,6 +40,6 @@ declare const SLACK_SPAN_KEYS: {
|
|
|
40
40
|
readonly AUTHORIZED: "slack.authorized";
|
|
41
41
|
readonly AUTH_SOURCE: "slack.auth_source";
|
|
42
42
|
};
|
|
43
|
-
type SlackOutcome = 'handled' | 'ignored_bot_message' | 'ignored_unknown_event' | 'ignored_no_action_match' | 'ignored_slack_retry' | 'url_verification' | 'validation_error' | 'signature_invalid' | 'error';
|
|
43
|
+
type SlackOutcome = 'handled' | 'ignored_bot_message' | 'ignored_edited_message' | 'ignored_unknown_event' | 'ignored_no_action_match' | 'ignored_slack_retry' | 'url_verification' | 'validation_error' | 'signature_invalid' | 'error';
|
|
44
44
|
//#endregion
|
|
45
45
|
export { SLACK_SPAN_KEYS, SLACK_SPAN_NAMES, SlackOutcome, setSpanWithError, tracer };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inkeep/agents-work-apps",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.53.0",
|
|
4
4
|
"description": "First party integrations for Inkeep Agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"jose": "^6.1.0",
|
|
34
34
|
"minimatch": "^10.1.1",
|
|
35
35
|
"slack-block-builder": "^2.8.0",
|
|
36
|
-
"@inkeep/agents-core": "0.
|
|
36
|
+
"@inkeep/agents-core": "0.53.0"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
39
|
"@hono/zod-openapi": "^1.1.5",
|