@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.
@@ -1,7 +1,7 @@
1
- import * as hono1 from "hono";
1
+ import * as hono0 from "hono";
2
2
 
3
3
  //#region src/github/mcp/auth.d.ts
4
- declare const githubMcpAuth: () => hono1.MiddlewareHandler<{
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 hono_types9 from "hono/types";
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
- }, hono_types9.BlankSchema, "/">;
9
+ }, hono_types0.BlankSchema, "/">;
10
10
  //#endregion
11
11
  export { app as default };
@@ -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-comment-reaction", `Add a reaction to a comment on a pull request. Supports general pull request comments and inline PR review comments. ${getAvailableRepositoryString(repositoryAccess)}`, {
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
- comment_id: z.number().describe("The ID of the comment to react to"),
673
- comment_type: z.enum(["issue_comment", "review_comment"]).describe("The type of comment: \"issue_comment\" for general pull request comments, \"review_comment\" for inline PR review comments"),
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, comment_id, comment_type, reaction }) => {
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 ${comment_type} comment ${comment_id} in ${owner}/${repo}\n\nReaction ID: ${(comment_type === "issue_comment" ? await createIssueCommentReaction(githubClient, owner, repo, comment_id, reaction) : await createPullRequestReviewCommentReaction(githubClient, owner, repo, comment_id, reaction)).id}`
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: `Comment ${comment_id} not found in ${owner}/${repo}.`
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 comment exists.`
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 comment ${comment_id} in ${owner}/${repo}. Your GitHub App may not have sufficient permissions to create reactions.`
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-comment-reaction", `Remove a reaction from a comment on a pull request. Requires the reaction ID (returned when adding a reaction or available from comment data). ${getAvailableRepositoryString(repositoryAccess)}`, {
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
- comment_id: z.number().describe("The ID of the comment the reaction belongs to"),
731
- comment_type: z.enum(["issue_comment", "review_comment"]).describe("The type of comment: \"issue_comment\" for general pull request comments, \"review_comment\" for inline PR review comments"),
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, comment_id, comment_type, reaction_id }) => {
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 (comment_type === "issue_comment") await deleteIssueCommentReaction(githubClient, owner, repo, comment_id, reaction_id);
748
- else await deletePullRequestReviewCommentReaction(githubClient, owner, repo, comment_id, reaction_id);
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 ${comment_type} comment ${comment_id} in ${owner}/${repo}`
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: `Comment ${comment_id} or reaction ${reaction_id} not found in ${owner}/${repo}.`
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-comment-reactions", `List all reactions on a comment, including each reaction's ID (needed for removal). Supports both general issue/PR comments and inline PR review comments. ${getAvailableRepositoryString(repositoryAccess)}`, {
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
- comment_id: z.number().describe("The ID of the comment to list reactions for"),
776
- comment_type: z.enum(["issue_comment", "review_comment"]).describe("The type of comment: \"issue_comment\" for general pull request comments, \"review_comment\" for inline PR review comments")
777
- }, async ({ owner, repo, comment_id, comment_type }) => {
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
- const reactions = comment_type === "issue_comment" ? await listIssueCommentReactions(githubClient, owner, repo, comment_id) : await listPullRequestReviewCommentReactions(githubClient, owner, repo, comment_id);
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 ${comment_type} comment ${comment_id} in ${owner}/${repo}.`
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 ${comment_type} comment ${comment_id} in ${owner}/${repo}:\n\n${formatted}`
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: `Comment ${comment_id} not found in ${owner}/${repo}.`
842
+ text: `Target ${target_id} (${target_type}) not found in ${owner}/${repo}.`
807
843
  }],
808
844
  isError: true
809
845
  };
@@ -76,8 +76,8 @@ declare const ChangedFileSchema: z.ZodObject<{
76
76
  path: z.ZodString;
77
77
  status: z.ZodEnum<{
78
78
  added: "added";
79
- removed: "removed";
80
79
  modified: "modified";
80
+ removed: "removed";
81
81
  renamed: "renamed";
82
82
  copied: "copied";
83
83
  changed: "changed";
@@ -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 };
@@ -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 hono_types3 from "hono/types";
2
+ import * as hono_types4 from "hono/types";
3
3
 
4
4
  //#region src/github/routes/setup.d.ts
5
- declare const app: Hono<hono_types3.BlankEnv, hono_types3.BlankSchema, "/">;
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 hono_types5 from "hono/types";
2
+ import * as hono_types6 from "hono/types";
3
3
 
4
4
  //#region src/github/routes/tokenExchange.d.ts
5
- declare const app: Hono<hono_types5.BlankEnv, hono_types5.BlankSchema, "/">;
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 hono_types7 from "hono/types";
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<hono_types7.BlankEnv, hono_types7.BlankSchema, "/">;
10
+ declare const app: Hono<hono_types8.BlankEnv, hono_types8.BlankSchema, "/">;
11
11
  //#endregion
12
12
  export { WebhookVerificationResult, app as default, verifyWebhookSignature };
@@ -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
- return {
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
- const workspaceConfig = await getWorkspaceDefaultAgentFromNango(teamId);
46
- if (workspaceConfig?.agentId && workspaceConfig.projectId) {
47
- logger.info({
48
- teamId,
49
- agentId: workspaceConfig.agentId,
50
- source: "workspace"
51
- }, "Resolved agent from workspace config");
52
- return {
53
- projectId: workspaceConfig.projectId,
54
- agentId: workspaceConfig.agentId,
55
- agentName: workspaceConfig.agentName,
56
- source: "workspace",
57
- grantAccessToMembers: workspaceConfig.grantAccessToMembers ?? true
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
- logger.debug({
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 null;
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: channelConfig || workspaceConfig
146
+ effective
99
147
  };
100
148
  }
101
149
 
@@ -17,6 +17,7 @@ declare function createContextBlock(params: ContextBlockParams): {
17
17
  interface FollowUpButtonParams {
18
18
  conversationId: string;
19
19
  agentId: string;
20
+ agentName?: string;
20
21
  projectId: string;
21
22
  tenantId: string;
22
23
  teamId: string;
@@ -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: "ephemeral",
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(agentId);
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(agentId)
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: agentId,
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: agentId,
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 input = data.input;
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
- slackUserId: string;
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
- display_name?: string;
141
- name?: string;
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
- name: userInfo?.user?.name
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
- name: void 0
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.name) parts.push(`"${info.name}"`);
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, Name):\n${userDirectoryLines.join("\n")}\n\n` : ""}Messages in this thread:\n${messagesToProcess.map((msg, index) => {
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";
@@ -24,6 +24,7 @@ interface ModalMetadata {
24
24
  interface FollowUpModalMetadata {
25
25
  conversationId: string;
26
26
  agentId: string;
27
+ agentName?: string;
27
28
  projectId: string;
28
29
  tenantId: string;
29
30
  teamId: string;
@@ -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 || agent.id,
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 || agent.id,
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: {
@@ -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.52.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.52.0"
36
+ "@inkeep/agents-core": "0.53.0"
37
37
  },
38
38
  "peerDependencies": {
39
39
  "@hono/zod-openapi": "^1.1.5",