@inkeep/agents-work-apps 0.53.2 → 0.53.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/github/mcp/index.js +61 -1
  2. package/dist/github/mcp/utils.d.ts +18 -1
  3. package/dist/github/mcp/utils.js +52 -16
  4. package/dist/github/routes/setup.d.ts +2 -2
  5. package/dist/github/routes/tokenExchange.d.ts +2 -2
  6. package/dist/github/routes/webhooks.d.ts +2 -2
  7. package/dist/slack/dispatcher.js +54 -40
  8. package/dist/slack/i18n/strings.d.ts +6 -5
  9. package/dist/slack/i18n/strings.js +7 -10
  10. package/dist/slack/routes/events.js +1 -1
  11. package/dist/slack/routes/workspaces.js +3 -3
  12. package/dist/slack/services/blocks/index.d.ts +3 -35
  13. package/dist/slack/services/blocks/index.js +5 -42
  14. package/dist/slack/services/client.d.ts +21 -1
  15. package/dist/slack/services/client.js +43 -1
  16. package/dist/slack/services/commands/index.js +42 -104
  17. package/dist/slack/services/events/app-mention.js +8 -31
  18. package/dist/slack/services/events/block-actions.d.ts +1 -11
  19. package/dist/slack/services/events/block-actions.js +6 -49
  20. package/dist/slack/services/events/direct-message.d.ts +11 -0
  21. package/dist/slack/services/events/direct-message.js +148 -0
  22. package/dist/slack/services/events/execution.d.ts +20 -0
  23. package/dist/slack/services/events/execution.js +46 -0
  24. package/dist/slack/services/events/index.d.ts +5 -3
  25. package/dist/slack/services/events/index.js +5 -3
  26. package/dist/slack/services/events/modal-submission.d.ts +1 -21
  27. package/dist/slack/services/events/modal-submission.js +14 -294
  28. package/dist/slack/services/events/streaming.d.ts +1 -1
  29. package/dist/slack/services/events/streaming.js +69 -70
  30. package/dist/slack/services/events/utils.d.ts +2 -14
  31. package/dist/slack/services/events/utils.js +2 -13
  32. package/dist/slack/services/index.d.ts +8 -6
  33. package/dist/slack/services/index.js +9 -7
  34. package/dist/slack/services/modals.d.ts +1 -18
  35. package/dist/slack/services/modals.js +1 -48
  36. package/dist/slack/services/resume-intent.js +43 -3
  37. package/dist/slack/socket-mode.js +1 -1
  38. package/dist/slack/tracer.d.ts +2 -4
  39. package/dist/slack/tracer.js +1 -3
  40. package/package.json +2 -2
@@ -1,7 +1,7 @@
1
1
  import runDbClient_default from "../../db/runDbClient.js";
2
2
  import { githubMcpAuth } from "./auth.js";
3
3
  import { ReactionContentSchema } from "./schemas.js";
4
- import { commitFileChanges, commitNewFile, createIssueCommentReaction, createPullRequestReviewCommentReaction, deleteIssueCommentReaction, deletePullRequestReviewCommentReaction, fetchBranchChangedFiles, fetchComments, fetchPrFileDiffs, fetchPrFiles, fetchPrInfo, formatFileDiff, generatePrMarkdown, getGitHubClientFromRepo, listIssueCommentReactions, listIssueReactions, listPullRequestReviewCommentReactions, visualizeUpdateOperations } from "./utils.js";
4
+ import { commitFileChanges, commitNewFile, createIssueCommentReaction, createPullRequestReviewCommentReaction, deleteIssueCommentReaction, deletePullRequestReviewCommentReaction, fetchBranchChangedFiles, fetchComments, fetchPrFileDiffs, fetchPrFiles, fetchPrInfo, formatFileDiff, generatePrMarkdown, getGitHubClientFromRepo, listIssueCommentReactions, listIssueReactions, listPullRequestReviewCommentReactions, moveFile, visualizeUpdateOperations } from "./utils.js";
5
5
  import { z } from "@hono/zod-openapi";
6
6
  import { getMcpToolRepositoryAccessWithDetails } from "@inkeep/agents-core";
7
7
  import { Hono } from "hono";
@@ -519,6 +519,66 @@ const getServer = async (toolId) => {
519
519
  };
520
520
  }
521
521
  });
522
+ server.tool("move-file", `Move (rename) a file within a repository in a single atomic commit. The file content is preserved and the old path is deleted. ${getAvailableRepositoryString(repositoryAccess)}`, {
523
+ owner: z.string().describe("Repository owner name"),
524
+ repo: z.string().describe("Repository name"),
525
+ branch_name: z.string().describe("Branch to commit the move to"),
526
+ source_path: z.string().describe("Current path of the file (relative to repository root)"),
527
+ destination_path: z.string().describe("New path for the file (relative to repository root)"),
528
+ commit_message: z.string().describe("Commit message for the move")
529
+ }, async ({ owner, repo, branch_name, source_path, destination_path, commit_message }) => {
530
+ try {
531
+ let githubClient;
532
+ try {
533
+ githubClient = getGitHubClientFromRepo(owner, repo, installationIdMap);
534
+ } catch (error) {
535
+ return {
536
+ content: [{
537
+ type: "text",
538
+ text: `Error accessing GitHub: ${error instanceof Error ? error.message : "Unknown error"}`
539
+ }],
540
+ isError: true
541
+ };
542
+ }
543
+ return { content: [{
544
+ type: "text",
545
+ text: `Successfully moved "${source_path}" to "${destination_path}" in ${owner}/${repo} on branch "${branch_name}"\n\nCommit SHA: ${await moveFile({
546
+ githubClient,
547
+ owner,
548
+ repo,
549
+ sourcePath: source_path,
550
+ destinationPath: destination_path,
551
+ branchName: branch_name,
552
+ commitMessage: commit_message
553
+ })}`
554
+ }] };
555
+ } catch (error) {
556
+ if (error instanceof Error && "status" in error) {
557
+ const apiError = error;
558
+ if (apiError.status === 404) return {
559
+ content: [{
560
+ type: "text",
561
+ text: `Repository ${owner}/${repo}, branch "${branch_name}", or source file "${source_path}" not found.`
562
+ }],
563
+ isError: true
564
+ };
565
+ if (apiError.status === 422) return {
566
+ content: [{
567
+ type: "text",
568
+ text: `Invalid move operation. The destination path "${destination_path}" may already exist or the path is invalid.`
569
+ }],
570
+ isError: true
571
+ };
572
+ }
573
+ return {
574
+ content: [{
575
+ type: "text",
576
+ text: `Error moving file: ${error instanceof Error ? error.message : "Unknown error"}`
577
+ }],
578
+ isError: true
579
+ };
580
+ }
581
+ });
522
582
  server.tool("create-pull-request", `Create a pull request in a repository. ${getAvailableRepositoryString(repositoryAccess)}`, {
523
583
  owner: z.string().describe("Repository owner name"),
524
584
  repo: z.string().describe("Repository name"),
@@ -236,6 +236,23 @@ declare function commitNewFile({
236
236
  content: string;
237
237
  commitMessage: string;
238
238
  }): Promise<string>;
239
+ declare function moveFile({
240
+ githubClient,
241
+ owner,
242
+ repo,
243
+ sourcePath,
244
+ destinationPath,
245
+ branchName,
246
+ commitMessage
247
+ }: {
248
+ githubClient: Octokit;
249
+ owner: string;
250
+ repo: string;
251
+ sourcePath: string;
252
+ destinationPath: string;
253
+ branchName: string;
254
+ commitMessage: string;
255
+ }): Promise<string>;
239
256
  declare function createIssueCommentReaction(octokit: Octokit, owner: string, repo: string, commentId: number, content: ReactionContent): Promise<{
240
257
  id: number;
241
258
  content: string;
@@ -257,4 +274,4 @@ declare function listPullRequestReviewCommentReactions(octokit: Octokit, owner:
257
274
  declare function listIssueReactions(octokit: Octokit, owner: string, repo: string, issueNumber: number): Promise<ReactionDetail[]>;
258
275
  declare function formatFileDiff(pullRequestNumber: number, files: ChangedFile[], includeContents?: boolean): Promise<string>;
259
276
  //#endregion
260
- export { CommitData, LLMUpdateOperation, PullCommit, ReactionDetail, applyOperation, applyOperations, commitFileChanges, commitNewFile, createIssueCommentReaction, createPullRequestReviewCommentReaction, deleteIssueCommentReaction, deletePullRequestReviewCommentReaction, fetchBranchChangedFiles, fetchComments, fetchCommitDetails, fetchPrCommits, fetchPrFileDiffs, fetchPrFiles, fetchPrInfo, formatFileDiff, generatePrMarkdown, getFilePathsInRepo, getGitHubClientFromInstallationId, getGitHubClientFromRepo, listIssueCommentReactions, listIssueReactions, listPullRequestReviewCommentReactions, validateLineNumbers, visualizeUpdateOperations };
277
+ export { CommitData, LLMUpdateOperation, PullCommit, ReactionDetail, applyOperation, applyOperations, commitFileChanges, commitNewFile, createIssueCommentReaction, createPullRequestReviewCommentReaction, deleteIssueCommentReaction, deletePullRequestReviewCommentReaction, fetchBranchChangedFiles, fetchComments, fetchCommitDetails, fetchPrCommits, fetchPrFileDiffs, fetchPrFiles, fetchPrInfo, formatFileDiff, generatePrMarkdown, getFilePathsInRepo, getGitHubClientFromInstallationId, getGitHubClientFromRepo, listIssueCommentReactions, listIssueReactions, listPullRequestReviewCommentReactions, moveFile, validateLineNumbers, visualizeUpdateOperations };
@@ -615,33 +615,22 @@ function visualizeUpdateOperations(fileContent, operations) {
615
615
  return `Error applying operations: ${error instanceof Error ? error.message : "Unknown error"}`;
616
616
  }
617
617
  }
618
- async function commitContent({ githubClient, owner, repo, filePath, branchName, content, commitMessage }) {
618
+ async function commitTreeEntries({ githubClient, owner, repo, branchName, treeEntries, commitMessage }) {
619
619
  const currentSha = (await githubClient.rest.git.getRef({
620
620
  owner,
621
621
  repo,
622
622
  ref: `heads/${branchName}`
623
623
  })).data.object.sha;
624
- const currentTreeSha = (await githubClient.rest.git.getCommit({
624
+ const currentCommit = await githubClient.rest.git.getCommit({
625
625
  owner,
626
626
  repo,
627
627
  commit_sha: currentSha
628
- })).data.tree.sha;
629
- const blob = await githubClient.rest.git.createBlob({
630
- owner,
631
- repo,
632
- content: Buffer.from(content).toString("base64"),
633
- encoding: "base64"
634
628
  });
635
629
  const newTree = await githubClient.rest.git.createTree({
636
630
  owner,
637
631
  repo,
638
- base_tree: currentTreeSha,
639
- tree: [{
640
- path: filePath,
641
- mode: "100644",
642
- type: "blob",
643
- sha: blob.data.sha
644
- }]
632
+ base_tree: currentCommit.data.tree.sha,
633
+ tree: treeEntries
645
634
  });
646
635
  const newCommit = await githubClient.rest.git.createCommit({
647
636
  owner,
@@ -658,6 +647,26 @@ async function commitContent({ githubClient, owner, repo, filePath, branchName,
658
647
  });
659
648
  return newCommit.data.sha;
660
649
  }
650
+ async function commitContent({ githubClient, owner, repo, filePath, branchName, content, commitMessage }) {
651
+ return commitTreeEntries({
652
+ githubClient,
653
+ owner,
654
+ repo,
655
+ branchName,
656
+ treeEntries: [{
657
+ path: filePath,
658
+ mode: "100644",
659
+ type: "blob",
660
+ sha: (await githubClient.rest.git.createBlob({
661
+ owner,
662
+ repo,
663
+ content: Buffer.from(content).toString("base64"),
664
+ encoding: "base64"
665
+ })).data.sha
666
+ }],
667
+ commitMessage
668
+ });
669
+ }
661
670
  async function commitFileChanges({ githubClient, owner, repo, fileContent, filePath, branchName, operations, commitMessage }) {
662
671
  try {
663
672
  return await commitContent({
@@ -688,6 +697,33 @@ async function commitNewFile({ githubClient, owner, repo, filePath, branchName,
688
697
  throw new Error(`Failed to commit new file: ${error instanceof Error ? error.message : "Unknown error"}`);
689
698
  }
690
699
  }
700
+ async function moveFile({ githubClient, owner, repo, sourcePath, destinationPath, branchName, commitMessage }) {
701
+ const fileResponse = await githubClient.rest.repos.getContent({
702
+ owner,
703
+ repo,
704
+ path: sourcePath,
705
+ ref: branchName
706
+ });
707
+ if (!("sha" in fileResponse.data) || Array.isArray(fileResponse.data)) throw new Error(`Source path "${sourcePath}" is not a file`);
708
+ return commitTreeEntries({
709
+ githubClient,
710
+ owner,
711
+ repo,
712
+ branchName,
713
+ treeEntries: [{
714
+ path: destinationPath,
715
+ mode: "100644",
716
+ type: "blob",
717
+ sha: fileResponse.data.sha
718
+ }, {
719
+ path: sourcePath,
720
+ mode: "100644",
721
+ type: "blob",
722
+ sha: null
723
+ }],
724
+ commitMessage
725
+ });
726
+ }
691
727
  async function createIssueCommentReaction(octokit, owner, repo, commentId, content) {
692
728
  const { data } = await octokit.rest.reactions.createForIssueComment({
693
729
  owner,
@@ -795,4 +831,4 @@ async function formatFileDiff(pullRequestNumber, files, includeContents = false)
795
831
  }
796
832
 
797
833
  //#endregion
798
- export { applyOperation, applyOperations, commitFileChanges, commitNewFile, createIssueCommentReaction, createPullRequestReviewCommentReaction, deleteIssueCommentReaction, deletePullRequestReviewCommentReaction, fetchBranchChangedFiles, fetchComments, fetchCommitDetails, fetchPrCommits, fetchPrFileDiffs, fetchPrFiles, fetchPrInfo, formatFileDiff, generatePrMarkdown, getFilePathsInRepo, getGitHubClientFromInstallationId, getGitHubClientFromRepo, listIssueCommentReactions, listIssueReactions, listPullRequestReviewCommentReactions, validateLineNumbers, visualizeUpdateOperations };
834
+ export { applyOperation, applyOperations, commitFileChanges, commitNewFile, createIssueCommentReaction, createPullRequestReviewCommentReaction, deleteIssueCommentReaction, deletePullRequestReviewCommentReaction, fetchBranchChangedFiles, fetchComments, fetchCommitDetails, fetchPrCommits, fetchPrFileDiffs, fetchPrFiles, fetchPrInfo, formatFileDiff, generatePrMarkdown, getFilePathsInRepo, getGitHubClientFromInstallationId, getGitHubClientFromRepo, listIssueCommentReactions, listIssueReactions, listPullRequestReviewCommentReactions, moveFile, validateLineNumbers, visualizeUpdateOperations };
@@ -1,7 +1,7 @@
1
1
  import { Hono } from "hono";
2
- import * as hono_types8 from "hono/types";
2
+ import * as hono_types6 from "hono/types";
3
3
 
4
4
  //#region src/github/routes/setup.d.ts
5
- declare const app: Hono<hono_types8.BlankEnv, hono_types8.BlankSchema, "/">;
5
+ declare const app: Hono<hono_types6.BlankEnv, hono_types6.BlankSchema, "/">;
6
6
  //#endregion
7
7
  export { app as default };
@@ -1,7 +1,7 @@
1
1
  import { Hono } from "hono";
2
- import * as hono_types4 from "hono/types";
2
+ import * as hono_types8 from "hono/types";
3
3
 
4
4
  //#region src/github/routes/tokenExchange.d.ts
5
- declare const app: Hono<hono_types4.BlankEnv, hono_types4.BlankSchema, "/">;
5
+ declare const app: Hono<hono_types8.BlankEnv, hono_types8.BlankSchema, "/">;
6
6
  //#endregion
7
7
  export { app as default };
@@ -1,5 +1,5 @@
1
1
  import { Hono } from "hono";
2
- import * as hono_types6 from "hono/types";
2
+ import * as hono_types4 from "hono/types";
3
3
 
4
4
  //#region src/github/routes/webhooks.d.ts
5
5
  interface WebhookVerificationResult {
@@ -7,6 +7,6 @@ interface WebhookVerificationResult {
7
7
  error?: string;
8
8
  }
9
9
  declare function verifyWebhookSignature(payload: string, signature: string | undefined, secret: string): WebhookVerificationResult;
10
- declare const app: Hono<hono_types6.BlankEnv, hono_types6.BlankSchema, "/">;
10
+ declare const app: Hono<hono_types4.BlankEnv, hono_types4.BlankSchema, "/">;
11
11
  //#endregion
12
12
  export { WebhookVerificationResult, app as default, verifyWebhookSignature };
@@ -4,18 +4,44 @@ import { sendResponseUrlMessage } from "./services/events/utils.js";
4
4
  import { getSlackClient } from "./services/client.js";
5
5
  import { SLACK_SPAN_KEYS } from "./tracer.js";
6
6
  import { handleAppMention } from "./services/events/app-mention.js";
7
- import { handleMessageShortcut, handleOpenAgentSelectorModal, handleOpenFollowUpModal, handleToolApproval } from "./services/events/block-actions.js";
8
- import { handleFollowUpSubmission, handleModalSubmission } from "./services/events/modal-submission.js";
7
+ import { handleMessageShortcut, handleOpenAgentSelectorModal, handleToolApproval } from "./services/events/block-actions.js";
8
+ import { handleDirectMessage } from "./services/events/direct-message.js";
9
+ import { handleModalSubmission } from "./services/events/modal-submission.js";
9
10
  import "./services/events/index.js";
10
11
  import "./services/index.js";
11
12
  import { flushTraces } from "@inkeep/agents-core";
12
13
 
13
14
  //#region src/slack/dispatcher.ts
14
15
  const logger = getLogger("slack-dispatcher");
16
+ /**
17
+ * Simple in-memory deduplication for Slack events.
18
+ * Slack may deliver the same event more than once (Socket Mode reconnections,
19
+ * edge cases in the Events API). We track recent event IDs to prevent
20
+ * duplicate processing. Entries expire after 5 minutes.
21
+ */
22
+ const DEDUP_TTL_MS = 300 * 1e3;
23
+ const recentEventIds = /* @__PURE__ */ new Map();
24
+ function isDuplicateEvent(eventId) {
25
+ if (!eventId) return false;
26
+ const now = Date.now();
27
+ if (recentEventIds.has(eventId)) return true;
28
+ recentEventIds.set(eventId, now);
29
+ if (recentEventIds.size > 500) {
30
+ for (const [id, ts] of recentEventIds) if (now - ts > DEDUP_TTL_MS) recentEventIds.delete(id);
31
+ }
32
+ return false;
33
+ }
15
34
  async function dispatchSlackEvent(eventType, payload, options, span) {
16
35
  const { registerBackgroundWork } = options;
17
36
  let outcome = "ignored_unknown_event";
18
37
  if (eventType === "event_callback") {
38
+ const eventId = payload.event_id;
39
+ if (isDuplicateEvent(eventId)) {
40
+ outcome = "ignored_duplicate_event";
41
+ span.setAttribute(SLACK_SPAN_KEYS.OUTCOME, outcome);
42
+ logger.info({ eventId }, "Ignoring duplicate event");
43
+ return { outcome };
44
+ }
19
45
  const teamId = payload.team_id;
20
46
  const event = payload.event;
21
47
  const innerEventType = event?.type || "unknown";
@@ -44,7 +70,7 @@ async function dispatchSlackEvent(eventType, payload, options, span) {
44
70
  }, "Ignoring edited message");
45
71
  return { outcome };
46
72
  }
47
- if (event?.type === "app_mention" && event.channel && event.user && teamId) {
73
+ if (event?.type === "app_mention" && event.channel_type !== "im" && event.channel && event.user && teamId) {
48
74
  outcome = "handled";
49
75
  span.setAttribute(SLACK_SPAN_KEYS.OUTCOME, outcome);
50
76
  const question = (event.text || "").replace(/<@[A-Z0-9]+>/g, "").trim();
@@ -80,6 +106,31 @@ async function dispatchSlackEvent(eventType, payload, options, span) {
80
106
  channel: event.channel,
81
107
  dispatchedAt
82
108
  }, "app_mention work registered");
109
+ } else if (event?.type === "message" && event.channel_type === "im" && event.channel && event.user && teamId) {
110
+ outcome = "handled";
111
+ span.setAttribute(SLACK_SPAN_KEYS.OUTCOME, outcome);
112
+ if (event.thread_ts) span.setAttribute(SLACK_SPAN_KEYS.THREAD_TS, event.thread_ts);
113
+ if (event.ts) span.setAttribute(SLACK_SPAN_KEYS.MESSAGE_TS, event.ts);
114
+ logger.info({
115
+ userId: event.user,
116
+ channel: event.channel,
117
+ teamId
118
+ }, "Handling event: message.im");
119
+ registerBackgroundWork(handleDirectMessage({
120
+ slackUserId: event.user,
121
+ channel: event.channel,
122
+ text: event.text || "",
123
+ threadTs: event.thread_ts,
124
+ messageTs: event.ts || "",
125
+ teamId
126
+ }).catch((err) => {
127
+ const errorMessage = err instanceof Error ? err.message : String(err);
128
+ const errorStack = err instanceof Error ? err.stack : void 0;
129
+ logger.error({
130
+ errorMessage,
131
+ errorStack
132
+ }, "Failed to handle direct message (outer catch)");
133
+ }).finally(() => flushTraces()));
83
134
  } else {
84
135
  outcome = "ignored_unknown_event";
85
136
  span.setAttribute(SLACK_SPAN_KEYS.OUTCOME, outcome);
@@ -209,29 +260,6 @@ async function dispatchSlackEvent(eventType, payload, options, span) {
209
260
  }, "Failed to handle tool approval");
210
261
  }).finally(() => flushTraces()));
211
262
  }
212
- if (action.action_id === "open_follow_up_modal" && action.value && triggerId) {
213
- anyHandled = true;
214
- logger.info({
215
- teamId,
216
- actionId: action.action_id
217
- }, "Handling block_action: open_follow_up_modal");
218
- registerBackgroundWork(handleOpenFollowUpModal({
219
- triggerId,
220
- actionValue: action.value,
221
- teamId,
222
- responseUrl: responseUrl || void 0
223
- }).catch(async (err) => {
224
- const errorMessage = err instanceof Error ? err.message : String(err);
225
- logger.error({
226
- errorMessage,
227
- actionId: action.action_id
228
- }, "Failed to open follow-up modal");
229
- if (responseUrl) await sendResponseUrlMessage(responseUrl, {
230
- text: "Sorry, something went wrong while opening the follow-up form. Please try again.",
231
- response_type: "ephemeral"
232
- }).catch((e) => logger.warn({ error: e }, "Failed to send error notification via response URL"));
233
- }).finally(() => flushTraces()));
234
- }
235
263
  }
236
264
  outcome = anyHandled ? "handled" : "ignored_no_action_match";
237
265
  span.setAttribute(SLACK_SPAN_KEYS.OUTCOME, outcome);
@@ -338,20 +366,6 @@ async function dispatchSlackEvent(eventType, payload, options, span) {
338
366
  }).finally(() => flushTraces()));
339
367
  return { outcome };
340
368
  }
341
- if (callbackId === "follow_up_modal") {
342
- const view = payload.view;
343
- outcome = "handled";
344
- span.setAttribute(SLACK_SPAN_KEYS.OUTCOME, outcome);
345
- logger.info({ callbackId }, "Handling view_submission: follow_up_modal");
346
- registerBackgroundWork(handleFollowUpSubmission(view).catch((err) => {
347
- const errorMessage = err instanceof Error ? err.message : String(err);
348
- logger.error({
349
- errorMessage,
350
- callbackId
351
- }, "Failed to handle follow-up submission");
352
- }).finally(() => flushTraces()));
353
- return { outcome };
354
- }
355
369
  outcome = "ignored_unknown_event";
356
370
  span.setAttribute(SLACK_SPAN_KEYS.OUTCOME, outcome);
357
371
  logger.info({ callbackId }, `Ignoring unhandled view_submission: ${callbackId}`);
@@ -9,7 +9,6 @@ declare const SlackStrings: {
9
9
  readonly buttons: {
10
10
  readonly triggerAgent: "Trigger Agent";
11
11
  readonly send: "Send";
12
- readonly followUp: "Follow Up";
13
12
  readonly cancel: "Cancel";
14
13
  readonly openDashboard: "Open Dashboard";
15
14
  };
@@ -17,7 +16,6 @@ declare const SlackStrings: {
17
16
  readonly triggerAgent: "Trigger Agent";
18
17
  readonly triggerAgentThread: "Trigger Agent (Thread)";
19
18
  readonly askAboutMessage: "Ask About Message";
20
- readonly followUp: "Follow Up";
21
19
  };
22
20
  readonly labels: {
23
21
  readonly project: "Project";
@@ -38,7 +36,6 @@ declare const SlackStrings: {
38
36
  };
39
37
  readonly context: {
40
38
  readonly poweredBy: (agentName: string) => string;
41
- readonly privateResponse: "_Private response_";
42
39
  };
43
40
  readonly usage: {
44
41
  readonly mentionEmpty: string;
@@ -50,13 +47,17 @@ declare const SlackStrings: {
50
47
  readonly noProjectsConfigured: "No projects configured. Set up projects in the dashboard.";
51
48
  };
52
49
  readonly errors: {
53
- readonly generic: "Something went wrong. Please try again.";
50
+ readonly generic: "Something went wrong processing your request. Please try again.";
54
51
  readonly failedToOpenSelector: "Failed to open agent selector. Please try again.";
52
+ readonly noAgentConfigured: "No agent is configured for this workspace. Ask your admin to set up a default agent in the Inkeep dashboard.";
53
+ };
54
+ readonly linkPrompt: {
55
+ readonly intro: "To get started, let's connect your Inkeep account with Slack.";
55
56
  };
56
57
  readonly help: {
57
58
  readonly title: "Inkeep — How to Use";
58
59
  readonly publicSection: string;
59
- readonly privateSection: string;
60
+ readonly slashSection: string;
60
61
  readonly otherCommands: string;
61
62
  readonly docsLink: "<https://docs.inkeep.com/talk-to-your-agents/slack/overview|Learn more>";
62
63
  };
@@ -9,15 +9,13 @@ const SlackStrings = {
9
9
  buttons: {
10
10
  triggerAgent: "Trigger Agent",
11
11
  send: "Send",
12
- followUp: "Follow Up",
13
12
  cancel: "Cancel",
14
13
  openDashboard: "Open Dashboard"
15
14
  },
16
15
  modals: {
17
16
  triggerAgent: "Trigger Agent",
18
17
  triggerAgentThread: "Trigger Agent (Thread)",
19
- askAboutMessage: "Ask About Message",
20
- followUp: "Follow Up"
18
+ askAboutMessage: "Ask About Message"
21
19
  },
22
20
  labels: {
23
21
  project: "Project",
@@ -34,10 +32,7 @@ const SlackStrings = {
34
32
  additionalInstructionsMessage: "Additional instructions or question about this message..."
35
33
  },
36
34
  visibility: { includeThreadContext: "Include thread context" },
37
- context: {
38
- poweredBy: (agentName) => `Powered by *${agentName}* via Inkeep`,
39
- privateResponse: "_Private response_"
40
- },
35
+ context: { poweredBy: (agentName) => `Powered by *${agentName}* via Inkeep` },
41
36
  usage: { mentionEmpty: "*Include a message to use your Inkeep agent:*\n\n• `@Inkeep <message>` — Message the default agent (reply appears in a thread)\n• `@Inkeep <message>` in a thread — Includes thread as context\n• `@Inkeep` in a thread — Uses the full thread as context\n\nUse `/inkeep help` for all available commands." },
42
37
  status: {
43
38
  thinking: (agentName) => `_${agentName} is thinking..._`,
@@ -46,13 +41,15 @@ const SlackStrings = {
46
41
  noProjectsConfigured: "No projects configured. Set up projects in the dashboard."
47
42
  },
48
43
  errors: {
49
- generic: "Something went wrong. Please try again.",
50
- failedToOpenSelector: "Failed to open agent selector. Please try again."
44
+ generic: "Something went wrong processing your request. Please try again.",
45
+ failedToOpenSelector: "Failed to open agent selector. Please try again.",
46
+ noAgentConfigured: "No agent is configured for this workspace. Ask your admin to set up a default agent in the Inkeep dashboard."
51
47
  },
48
+ linkPrompt: { intro: "To get started, let's connect your Inkeep account with Slack." },
52
49
  help: {
53
50
  title: "Inkeep — How to Use",
54
51
  publicSection: "*Public* — visible to everyone in the channel\n\n• `@Inkeep <message>` — Message the default agent in this channel\n• `@Inkeep <message>` in a thread — Includes thread as context\n• `@Inkeep` in a thread — Uses the full thread as context",
55
- privateSection: "*Private* — only visible to you\n\n• `/inkeep <message>` — Message the default agent in this channel\n• `/inkeep` — Open the agent picker to choose an agent and write a prompt",
52
+ slashSection: "*Slash Commands* — visible to everyone in the channel\n\n• `/inkeep <message>` — Message the default agent in this channel\n• `/inkeep` — Open the agent picker to choose an agent and write a prompt",
56
53
  otherCommands: "*Other Commands*\n\n• `/inkeep status` — Check your connection and agent config\n• `/inkeep link` / `/inkeep unlink` — Manage account connection\n• `/inkeep help` — Show this message",
57
54
  docsLink: "<https://docs.inkeep.com/talk-to-your-agents/slack/overview|Learn more>"
58
55
  },
@@ -3,8 +3,8 @@ import { getLogger } from "../../logger.js";
3
3
  import runDbClient_default from "../../db/runDbClient.js";
4
4
  import { findWorkspaceConnectionByTeamId, getSlackIntegrationId, getSlackNango, updateConnectionMetadata } from "../services/nango.js";
5
5
  import { getSlackClient, getSlackUserInfo } from "../services/client.js";
6
- import { handleCommand } from "../services/commands/index.js";
7
6
  import { SLACK_SPAN_KEYS, SLACK_SPAN_NAMES, tracer } from "../tracer.js";
7
+ import { handleCommand } from "../services/commands/index.js";
8
8
  import { parseSlackCommandBody, parseSlackEventBody, verifySlackRequest } from "../services/security.js";
9
9
  import "../services/index.js";
10
10
  import { dispatchSlackEvent } from "../dispatcher.js";
@@ -1,7 +1,7 @@
1
1
  import { getLogger } from "../../logger.js";
2
2
  import runDbClient_default from "../../db/runDbClient.js";
3
3
  import { clearWorkspaceConnectionCache, computeWorkspaceConnectionId, deleteWorkspaceInstallation, findWorkspaceConnectionByTeamId, getWorkspaceDefaultAgentFromNango, listWorkspaceInstallations, setWorkspaceDefaultAgent } from "../services/nango.js";
4
- import { getSlackChannels, getSlackClient, revokeSlackToken } from "../services/client.js";
4
+ import { getBotMemberChannels, getSlackChannels, getSlackClient, revokeSlackToken } from "../services/client.js";
5
5
  import "../services/index.js";
6
6
  import { requireChannelMemberOrAdmin, requireWorkspaceAdmin } from "../middleware/permissions.js";
7
7
  import { OpenAPIHono, z } from "@hono/zod-openapi";
@@ -380,7 +380,7 @@ app.openapi(createProtectedRoute({
380
380
  method: "get",
381
381
  path: "/{teamId}/channels",
382
382
  summary: "List Channels",
383
- description: "List Slack channels in the workspace that the bot can see",
383
+ description: "List Slack channels where the bot is a member",
384
384
  operationId: "slack-list-channels",
385
385
  tags: [
386
386
  "Work Apps",
@@ -422,7 +422,7 @@ app.openapi(createProtectedRoute({
422
422
  const tenantId = workspace.tenantId;
423
423
  const slackClient = getSlackClient(workspace.botToken);
424
424
  try {
425
- const channels = await getSlackChannels(slackClient, limit);
425
+ const channels = await getBotMemberChannels(slackClient, limit);
426
426
  let channelConfigs = [];
427
427
  try {
428
428
  channelConfigs = await listWorkAppSlackChannelAgentConfigsByTeam(runDbClient_default)(tenantId, teamId);
@@ -5,7 +5,6 @@ import * as slack_block_builder0 from "slack-block-builder";
5
5
  declare function createErrorMessage(message: string): Readonly<slack_block_builder0.SlackMessageDto>;
6
6
  interface ContextBlockParams {
7
7
  agentName: string;
8
- isPrivate?: boolean;
9
8
  }
10
9
  declare function createContextBlock(params: ContextBlockParams): {
11
10
  type: "context";
@@ -14,37 +13,6 @@ declare function createContextBlock(params: ContextBlockParams): {
14
13
  text: string;
15
14
  }[];
16
15
  };
17
- interface FollowUpButtonParams {
18
- conversationId: string;
19
- agentId: string;
20
- agentName?: string;
21
- projectId: string;
22
- tenantId: string;
23
- teamId: string;
24
- slackUserId: string;
25
- channel: string;
26
- }
27
- declare function buildFollowUpButton(params: FollowUpButtonParams): {
28
- type: "button";
29
- text: {
30
- type: "plain_text";
31
- text: "Follow Up";
32
- emoji: boolean;
33
- };
34
- action_id: string;
35
- value: string;
36
- }[];
37
- /**
38
- * Build Block Kit blocks for a private conversational response.
39
- * Shows the user's message, a divider, the agent response, context, and a Follow Up button.
40
- */
41
- declare function buildConversationResponseBlocks(params: {
42
- userMessage: string;
43
- responseText: string;
44
- agentName: string;
45
- isError: boolean;
46
- followUpParams: FollowUpButtonParams;
47
- }): any[];
48
16
  declare function createUpdatedHelpMessage(): Readonly<slack_block_builder0.SlackMessageDto>;
49
17
  declare function createAlreadyLinkedMessage(email: string, linkedAt: string, dashboardUrl: string): Readonly<slack_block_builder0.SlackMessageDto>;
50
18
  declare function createUnlinkSuccessMessage(): Readonly<slack_block_builder0.SlackMessageDto>;
@@ -79,7 +47,7 @@ interface ToolApprovalButtonValue {
79
47
  agentId: string;
80
48
  slackUserId: string;
81
49
  channel: string;
82
- threadTs: string;
50
+ threadTs?: string;
83
51
  toolName: string;
84
52
  }
85
53
  declare const ToolApprovalButtonValueSchema: z.ZodObject<{
@@ -89,7 +57,7 @@ declare const ToolApprovalButtonValueSchema: z.ZodObject<{
89
57
  agentId: z.ZodString;
90
58
  slackUserId: z.ZodString;
91
59
  channel: z.ZodString;
92
- threadTs: z.ZodString;
60
+ threadTs: z.ZodOptional<z.ZodString>;
93
61
  toolName: z.ZodString;
94
62
  }, z.core.$strip>;
95
63
  declare function buildToolApprovalBlocks(params: {
@@ -152,4 +120,4 @@ declare function buildCitationsBlock(citations: Array<{
152
120
  }>): any[];
153
121
  declare function createCreateInkeepAccountMessage(acceptUrl: string, expiresInMinutes: number): Readonly<slack_block_builder0.SlackMessageDto>;
154
122
  //#endregion
155
- export { AgentConfigSources, ContextBlockParams, FollowUpButtonParams, ToolApprovalButtonValue, ToolApprovalButtonValueSchema, buildCitationsBlock, buildConversationResponseBlocks, buildDataArtifactBlocks, buildDataComponentBlocks, buildFollowUpButton, buildSummaryBreadcrumbBlock, buildToolApprovalBlocks, buildToolApprovalDoneBlocks, buildToolApprovalExpiredBlocks, buildToolOutputErrorBlock, createAlreadyLinkedMessage, createContextBlock, createCreateInkeepAccountMessage, createErrorMessage, createNotLinkedMessage, createSmartLinkMessage, createStatusMessage, createUnlinkSuccessMessage, createUpdatedHelpMessage };
123
+ export { AgentConfigSources, ContextBlockParams, ToolApprovalButtonValue, ToolApprovalButtonValueSchema, buildCitationsBlock, buildDataArtifactBlocks, buildDataComponentBlocks, buildSummaryBreadcrumbBlock, buildToolApprovalBlocks, buildToolApprovalDoneBlocks, buildToolApprovalExpiredBlocks, buildToolOutputErrorBlock, createAlreadyLinkedMessage, createContextBlock, createCreateInkeepAccountMessage, createErrorMessage, createNotLinkedMessage, createSmartLinkMessage, createStatusMessage, createUnlinkSuccessMessage, createUpdatedHelpMessage };
@@ -7,54 +7,17 @@ function createErrorMessage(message) {
7
7
  return Message().blocks(Blocks.Section().text(message)).buildToObject();
8
8
  }
9
9
  function createContextBlock(params) {
10
- const { agentName, isPrivate = false } = params;
11
- let text = SlackStrings.context.poweredBy(agentName);
12
- if (isPrivate) text = `${SlackStrings.context.privateResponse} • ${text}`;
10
+ const { agentName } = params;
13
11
  return {
14
12
  type: "context",
15
13
  elements: [{
16
14
  type: "mrkdwn",
17
- text
15
+ text: SlackStrings.context.poweredBy(agentName)
18
16
  }]
19
17
  };
20
18
  }
21
- function buildFollowUpButton(params) {
22
- return [{
23
- type: "button",
24
- text: {
25
- type: "plain_text",
26
- text: SlackStrings.buttons.followUp,
27
- emoji: true
28
- },
29
- action_id: "open_follow_up_modal",
30
- value: JSON.stringify(params)
31
- }];
32
- }
33
- /**
34
- * Build Block Kit blocks for a private conversational response.
35
- * Shows the user's message, a divider, the agent response, context, and a Follow Up button.
36
- */
37
- function buildConversationResponseBlocks(params) {
38
- const { responseText, agentName, isError, followUpParams } = params;
39
- const blocks = [{
40
- type: "section",
41
- text: {
42
- type: "mrkdwn",
43
- text: responseText
44
- }
45
- }];
46
- if (!isError) {
47
- const contextBlock = createContextBlock({ agentName });
48
- blocks.push(contextBlock);
49
- blocks.push({
50
- type: "actions",
51
- elements: buildFollowUpButton(followUpParams)
52
- });
53
- }
54
- return blocks;
55
- }
56
19
  function createUpdatedHelpMessage() {
57
- return Message().blocks(Blocks.Header().text(SlackStrings.help.title), Blocks.Section().text(SlackStrings.help.publicSection), Blocks.Divider(), Blocks.Section().text(SlackStrings.help.privateSection), Blocks.Divider(), Blocks.Section().text(SlackStrings.help.otherCommands), Blocks.Divider(), Blocks.Context().elements(SlackStrings.help.docsLink)).buildToObject();
20
+ return Message().blocks(Blocks.Header().text(SlackStrings.help.title), Blocks.Section().text(SlackStrings.help.publicSection), Blocks.Divider(), Blocks.Section().text(SlackStrings.help.slashSection), Blocks.Divider(), Blocks.Section().text(SlackStrings.help.otherCommands), Blocks.Divider(), Blocks.Context().elements(SlackStrings.help.docsLink)).buildToObject();
58
21
  }
59
22
  function createAlreadyLinkedMessage(email, linkedAt, dashboardUrl) {
60
23
  return Message().blocks(Blocks.Section().text(Md.bold("Already linked") + "\n\nYour Slack account is connected to Inkeep.\n\n" + Md.bold("Account:") + ` ${email}\n` + Md.bold("Linked:") + ` ${new Date(linkedAt).toLocaleDateString()}\n\nTo switch accounts, first run \`/inkeep unlink\``), Blocks.Actions().elements(Elements.Button().text(SlackStrings.buttons.openDashboard).url(dashboardUrl).actionId("open_dashboard"))).buildToObject();
@@ -101,7 +64,7 @@ const ToolApprovalButtonValueSchema = z.object({
101
64
  agentId: z.string(),
102
65
  slackUserId: z.string(),
103
66
  channel: z.string(),
104
- threadTs: z.string(),
67
+ threadTs: z.string().optional(),
105
68
  toolName: z.string()
106
69
  });
107
70
  function buildToolApprovalBlocks(params) {
@@ -324,4 +287,4 @@ function createCreateInkeepAccountMessage(acceptUrl, expiresInMinutes) {
324
287
  }
325
288
 
326
289
  //#endregion
327
- export { ToolApprovalButtonValueSchema, buildCitationsBlock, buildConversationResponseBlocks, buildDataArtifactBlocks, buildDataComponentBlocks, buildFollowUpButton, buildSummaryBreadcrumbBlock, buildToolApprovalBlocks, buildToolApprovalDoneBlocks, buildToolApprovalExpiredBlocks, buildToolOutputErrorBlock, createAlreadyLinkedMessage, createContextBlock, createCreateInkeepAccountMessage, createErrorMessage, createNotLinkedMessage, createSmartLinkMessage, createStatusMessage, createUnlinkSuccessMessage, createUpdatedHelpMessage };
290
+ export { ToolApprovalButtonValueSchema, buildCitationsBlock, buildDataArtifactBlocks, buildDataComponentBlocks, buildSummaryBreadcrumbBlock, buildToolApprovalBlocks, buildToolApprovalDoneBlocks, buildToolApprovalExpiredBlocks, buildToolOutputErrorBlock, createAlreadyLinkedMessage, createContextBlock, createCreateInkeepAccountMessage, createErrorMessage, createNotLinkedMessage, createSmartLinkMessage, createStatusMessage, createUnlinkSuccessMessage, createUpdatedHelpMessage };