@inkeep/agents-work-apps 0.49.0 → 0.50.1

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.
@@ -4,10 +4,10 @@ import "./routes/setup.js";
4
4
  import "./routes/tokenExchange.js";
5
5
  import { WebhookVerificationResult, verifyWebhookSignature } from "./routes/webhooks.js";
6
6
  import { Hono } from "hono";
7
- import * as hono_types6 from "hono/types";
7
+ import * as hono_types0 from "hono/types";
8
8
 
9
9
  //#region src/github/index.d.ts
10
- declare function createGithubRoutes(): Hono<hono_types6.BlankEnv, hono_types6.BlankSchema, "/">;
11
- declare const githubRoutes: Hono<hono_types6.BlankEnv, hono_types6.BlankSchema, "/">;
10
+ declare function createGithubRoutes(): Hono<hono_types0.BlankEnv, hono_types0.BlankSchema, "/">;
11
+ declare const githubRoutes: Hono<hono_types0.BlankEnv, hono_types0.BlankSchema, "/">;
12
12
  //#endregion
13
13
  export { GenerateInstallationAccessTokenResult, GenerateTokenError, GenerateTokenResult, GitHubAppConfig, InstallationAccessToken, InstallationInfo, LookupInstallationError, LookupInstallationForRepoResult, LookupInstallationResult, WebhookVerificationResult, clearConfigCache, createAppJwt, createGithubRoutes, determineStatus, fetchInstallationDetails, fetchInstallationRepositories, generateInstallationAccessToken, getGitHubAppConfig, getGitHubAppName, getStateSigningSecret, getWebhookSecret, githubRoutes, isGitHubAppConfigured, isGitHubAppNameConfigured, isStateSigningConfigured, isWebhookConfigured, lookupInstallationForRepo, validateGitHubAppConfigOnStartup, validateGitHubInstallFlowConfigOnStartup, validateGitHubWebhookConfigOnStartup, verifyWebhookSignature };
@@ -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_types0 from "hono/types";
2
+ import * as hono_types3 from "hono/types";
3
3
 
4
4
  //#region src/github/mcp/index.d.ts
5
5
  declare const app: Hono<{
6
6
  Variables: {
7
7
  toolId: string;
8
8
  };
9
- }, hono_types0.BlankSchema, "/">;
9
+ }, hono_types3.BlankSchema, "/">;
10
10
  //#endregion
11
11
  export { app as default };
@@ -1,12 +1,13 @@
1
1
  import runDbClient_default from "../../db/runDbClient.js";
2
2
  import { githubMcpAuth } from "./auth.js";
3
- import { commitFileChanges, commitNewFile, fetchComments, fetchPrFileDiffs, fetchPrFiles, fetchPrInfo, formatFileDiff, generatePrMarkdown, getGitHubClientFromRepo, visualizeUpdateOperations } from "./utils.js";
3
+ import { ReactionContentSchema } from "./schemas.js";
4
+ import { commitFileChanges, commitNewFile, createIssueCommentReaction, createPullRequestReviewCommentReaction, deleteIssueCommentReaction, deletePullRequestReviewCommentReaction, fetchComments, fetchPrFileDiffs, fetchPrFiles, fetchPrInfo, formatFileDiff, generatePrMarkdown, getGitHubClientFromRepo, listIssueCommentReactions, listPullRequestReviewCommentReactions, visualizeUpdateOperations } from "./utils.js";
5
+ import { z } from "@hono/zod-openapi";
4
6
  import { getMcpToolRepositoryAccessWithDetails } from "@inkeep/agents-core";
5
7
  import { Hono } from "hono";
6
8
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
7
9
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
8
10
  import { toFetchResponse, toReqRes } from "fetch-to-node";
9
- import { z } from "zod/v3";
10
11
 
11
12
  //#region src/github/mcp/index.ts
12
13
  const updateOperationSchema = z.object({
@@ -606,6 +607,157 @@ const getServer = async (toolId) => {
606
607
  };
607
608
  }
608
609
  });
610
+ 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)}`, {
611
+ owner: z.string().describe("Repository owner name"),
612
+ repo: z.string().describe("Repository name"),
613
+ comment_id: z.number().describe("The ID of the comment to react to"),
614
+ 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"),
615
+ reaction: ReactionContentSchema.describe("The reaction emoji to add: +1, -1, laugh, hooray, confused, heart, rocket, or eyes")
616
+ }, async ({ owner, repo, comment_id, comment_type, reaction }) => {
617
+ try {
618
+ let githubClient;
619
+ try {
620
+ githubClient = getGitHubClientFromRepo(owner, repo, installationIdMap);
621
+ } catch (error) {
622
+ return {
623
+ content: [{
624
+ type: "text",
625
+ text: `Error accessing GitHub: ${error instanceof Error ? error.message : "Unknown error"}`
626
+ }],
627
+ isError: true
628
+ };
629
+ }
630
+ return { content: [{
631
+ type: "text",
632
+ 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}`
633
+ }] };
634
+ } catch (error) {
635
+ if (error instanceof Error && "status" in error) {
636
+ const apiError = error;
637
+ if (apiError.status === 404) return {
638
+ content: [{
639
+ type: "text",
640
+ text: `Comment ${comment_id} not found in ${owner}/${repo}.`
641
+ }],
642
+ isError: true
643
+ };
644
+ if (apiError.status === 422) return {
645
+ content: [{
646
+ type: "text",
647
+ text: `Invalid reaction. Ensure the reaction type is valid and the comment exists.`
648
+ }],
649
+ isError: true
650
+ };
651
+ if (apiError.status === 403) return {
652
+ content: [{
653
+ type: "text",
654
+ text: `Access denied when adding reaction to comment ${comment_id} in ${owner}/${repo}. Your GitHub App may not have sufficient permissions to create reactions.`
655
+ }],
656
+ isError: true
657
+ };
658
+ }
659
+ return {
660
+ content: [{
661
+ type: "text",
662
+ text: `Error adding reaction: ${error instanceof Error ? error.message : "Unknown error"}`
663
+ }],
664
+ isError: true
665
+ };
666
+ }
667
+ });
668
+ 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)}`, {
669
+ owner: z.string().describe("Repository owner name"),
670
+ repo: z.string().describe("Repository name"),
671
+ comment_id: z.number().describe("The ID of the comment the reaction belongs to"),
672
+ 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"),
673
+ reaction_id: z.number().describe("The ID of the reaction to remove")
674
+ }, async ({ owner, repo, comment_id, comment_type, reaction_id }) => {
675
+ try {
676
+ let githubClient;
677
+ try {
678
+ githubClient = getGitHubClientFromRepo(owner, repo, installationIdMap);
679
+ } catch (error) {
680
+ return {
681
+ content: [{
682
+ type: "text",
683
+ text: `Error accessing GitHub: ${error instanceof Error ? error.message : "Unknown error"}`
684
+ }],
685
+ isError: true
686
+ };
687
+ }
688
+ if (comment_type === "issue_comment") await deleteIssueCommentReaction(githubClient, owner, repo, comment_id, reaction_id);
689
+ else await deletePullRequestReviewCommentReaction(githubClient, owner, repo, comment_id, reaction_id);
690
+ return { content: [{
691
+ type: "text",
692
+ text: `Successfully removed reaction ${reaction_id} from ${comment_type} comment ${comment_id} in ${owner}/${repo}`
693
+ }] };
694
+ } catch (error) {
695
+ if (error instanceof Error && "status" in error) {
696
+ if (error.status === 404) return {
697
+ content: [{
698
+ type: "text",
699
+ text: `Comment ${comment_id} or reaction ${reaction_id} not found in ${owner}/${repo}.`
700
+ }],
701
+ isError: true
702
+ };
703
+ }
704
+ return {
705
+ content: [{
706
+ type: "text",
707
+ text: `Error removing reaction: ${error instanceof Error ? error.message : "Unknown error"}`
708
+ }],
709
+ isError: true
710
+ };
711
+ }
712
+ });
713
+ 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)}`, {
714
+ owner: z.string().describe("Repository owner name"),
715
+ repo: z.string().describe("Repository name"),
716
+ comment_id: z.number().describe("The ID of the comment to list reactions for"),
717
+ 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")
718
+ }, async ({ owner, repo, comment_id, comment_type }) => {
719
+ try {
720
+ let githubClient;
721
+ try {
722
+ githubClient = getGitHubClientFromRepo(owner, repo, installationIdMap);
723
+ } catch (error) {
724
+ return {
725
+ content: [{
726
+ type: "text",
727
+ text: `Error accessing GitHub: ${error instanceof Error ? error.message : "Unknown error"}`
728
+ }],
729
+ isError: true
730
+ };
731
+ }
732
+ const reactions = comment_type === "issue_comment" ? await listIssueCommentReactions(githubClient, owner, repo, comment_id) : await listPullRequestReviewCommentReactions(githubClient, owner, repo, comment_id);
733
+ if (reactions.length === 0) return { content: [{
734
+ type: "text",
735
+ text: `No reactions found on ${comment_type} comment ${comment_id} in ${owner}/${repo}.`
736
+ }] };
737
+ const formatted = reactions.map((r) => `• ${r.content} by @${r.user} (reaction_id: ${r.id})`).join("\n");
738
+ return { content: [{
739
+ type: "text",
740
+ text: `Found ${reactions.length} reaction(s) on ${comment_type} comment ${comment_id} in ${owner}/${repo}:\n\n${formatted}`
741
+ }] };
742
+ } catch (error) {
743
+ if (error instanceof Error && "status" in error) {
744
+ if (error.status === 404) return {
745
+ content: [{
746
+ type: "text",
747
+ text: `Comment ${comment_id} not found in ${owner}/${repo}.`
748
+ }],
749
+ isError: true
750
+ };
751
+ }
752
+ return {
753
+ content: [{
754
+ type: "text",
755
+ text: `Error listing reactions: ${error instanceof Error ? error.message : "Unknown error"}`
756
+ }],
757
+ isError: true
758
+ };
759
+ }
760
+ });
609
761
  server.tool("visualize-update-operations", `Apply a list of operations to a piece of documentation and return a mapping of line number to line content. ${getAvailableRepositoryString(repositoryAccess)}`, {
610
762
  owner: z.string().describe("Repository owner name"),
611
763
  repo: z.string().describe("Repository name"),
@@ -11,6 +11,46 @@ declare const RepositorySchema: z.ZodObject<{
11
11
  url: z.ZodString;
12
12
  defaultBranch: z.ZodString;
13
13
  }, z.core.$strip>;
14
+ declare const ReactionContentSchema: z.ZodEnum<{
15
+ "+1": "+1";
16
+ [-1]: "-1";
17
+ laugh: "laugh";
18
+ hooray: "hooray";
19
+ confused: "confused";
20
+ heart: "heart";
21
+ rocket: "rocket";
22
+ eyes: "eyes";
23
+ }>;
24
+ declare const ReactionSchema: z.ZodObject<{
25
+ id: z.ZodNumber;
26
+ user: z.ZodString;
27
+ content: z.ZodEnum<{
28
+ "+1": "+1";
29
+ [-1]: "-1";
30
+ laugh: "laugh";
31
+ hooray: "hooray";
32
+ confused: "confused";
33
+ heart: "heart";
34
+ rocket: "rocket";
35
+ eyes: "eyes";
36
+ }>;
37
+ createdAt: z.ZodString;
38
+ }, z.core.$strip>;
39
+ declare const ReactionsSchema: z.ZodArray<z.ZodObject<{
40
+ id: z.ZodNumber;
41
+ user: z.ZodString;
42
+ content: z.ZodEnum<{
43
+ "+1": "+1";
44
+ [-1]: "-1";
45
+ laugh: "laugh";
46
+ hooray: "hooray";
47
+ confused: "confused";
48
+ heart: "heart";
49
+ rocket: "rocket";
50
+ eyes: "eyes";
51
+ }>;
52
+ createdAt: z.ZodString;
53
+ }, z.core.$strip>>;
14
54
  declare const PullRequestSchema: z.ZodObject<{
15
55
  number: z.ZodNumber;
16
56
  title: z.ZodString;
@@ -36,8 +76,8 @@ declare const ChangedFileSchema: z.ZodObject<{
36
76
  path: z.ZodString;
37
77
  status: z.ZodEnum<{
38
78
  added: "added";
39
- removed: "removed";
40
79
  modified: "modified";
80
+ removed: "removed";
41
81
  renamed: "renamed";
42
82
  copied: "copied";
43
83
  changed: "changed";
@@ -73,6 +113,21 @@ declare const CommentSchema: z.ZodObject<{
73
113
  DISMISSED: "DISMISSED";
74
114
  PENDING: "PENDING";
75
115
  }>>;
116
+ reactions: z.ZodOptional<z.ZodArray<z.ZodObject<{
117
+ id: z.ZodNumber;
118
+ user: z.ZodString;
119
+ content: z.ZodEnum<{
120
+ "+1": "+1";
121
+ [-1]: "-1";
122
+ laugh: "laugh";
123
+ hooray: "hooray";
124
+ confused: "confused";
125
+ heart: "heart";
126
+ rocket: "rocket";
127
+ eyes: "eyes";
128
+ }>;
129
+ createdAt: z.ZodString;
130
+ }, z.core.$strip>>>;
76
131
  }, z.core.$strip>;
77
132
  declare const GitHubEventSchema: z.ZodObject<{
78
133
  type: z.ZodString;
@@ -83,6 +138,9 @@ type Repository = z.infer<typeof RepositorySchema>;
83
138
  type PullRequest = z.infer<typeof PullRequestSchema>;
84
139
  type ChangedFile = z.infer<typeof ChangedFileSchema>;
85
140
  type Comment = z.infer<typeof CommentSchema>;
141
+ type Reaction = z.infer<typeof ReactionSchema>;
142
+ type Reactions = z.infer<typeof ReactionsSchema>;
86
143
  type GitHubEvent = z.infer<typeof GitHubEventSchema>;
144
+ type ReactionContent = z.infer<typeof ReactionContentSchema>;
87
145
  //#endregion
88
- export { ChangedFile, ChangedFileSchema, Comment, CommentSchema, GitHubEvent, GitHubEventSchema, GitHubUser, GitHubUserSchema, PullRequest, PullRequestSchema, Repository, RepositorySchema };
146
+ export { ChangedFile, ChangedFileSchema, Comment, CommentSchema, GitHubEvent, GitHubEventSchema, GitHubUser, GitHubUserSchema, PullRequest, PullRequestSchema, Reaction, ReactionContent, ReactionContentSchema, ReactionSchema, Reactions, ReactionsSchema, Repository, RepositorySchema };
@@ -9,6 +9,23 @@ const RepositorySchema = z.object({
9
9
  url: z.string().url(),
10
10
  defaultBranch: z.string()
11
11
  });
12
+ const ReactionContentSchema = z.enum([
13
+ "+1",
14
+ "-1",
15
+ "laugh",
16
+ "hooray",
17
+ "confused",
18
+ "heart",
19
+ "rocket",
20
+ "eyes"
21
+ ]);
22
+ const ReactionSchema = z.object({
23
+ id: z.number(),
24
+ user: z.string(),
25
+ content: ReactionContentSchema,
26
+ createdAt: z.string()
27
+ });
28
+ const ReactionsSchema = z.array(ReactionSchema);
12
29
  const PullRequestSchema = z.object({
13
30
  number: z.number(),
14
31
  title: z.string(),
@@ -66,7 +83,8 @@ const CommentSchema = z.object({
66
83
  "COMMENTED",
67
84
  "DISMISSED",
68
85
  "PENDING"
69
- ]).optional()
86
+ ]).optional(),
87
+ reactions: ReactionsSchema.optional()
70
88
  });
71
89
  const GitHubEventSchema = z.object({
72
90
  type: z.string(),
@@ -74,4 +92,4 @@ const GitHubEventSchema = z.object({
74
92
  });
75
93
 
76
94
  //#endregion
77
- export { ChangedFileSchema, CommentSchema, GitHubEventSchema, GitHubUserSchema, PullRequestSchema, RepositorySchema };
95
+ export { ChangedFileSchema, CommentSchema, GitHubEventSchema, GitHubUserSchema, PullRequestSchema, ReactionContentSchema, ReactionSchema, ReactionsSchema, RepositorySchema };
@@ -1,4 +1,4 @@
1
- import { ChangedFile, Comment, PullRequest } from "./schemas.js";
1
+ import { ChangedFile, Comment, PullRequest, ReactionContent } from "./schemas.js";
2
2
  import { Octokit } from "@octokit/rest";
3
3
 
4
4
  //#region src/github/mcp/utils.d.ts
@@ -227,6 +227,24 @@ declare function commitNewFile({
227
227
  content: string;
228
228
  commitMessage: string;
229
229
  }): Promise<string>;
230
+ declare function createIssueCommentReaction(octokit: Octokit, owner: string, repo: string, commentId: number, content: ReactionContent): Promise<{
231
+ id: number;
232
+ content: string;
233
+ }>;
234
+ declare function deleteIssueCommentReaction(octokit: Octokit, owner: string, repo: string, commentId: number, reactionId: number): Promise<void>;
235
+ declare function createPullRequestReviewCommentReaction(octokit: Octokit, owner: string, repo: string, commentId: number, content: ReactionContent): Promise<{
236
+ id: number;
237
+ content: string;
238
+ }>;
239
+ declare function deletePullRequestReviewCommentReaction(octokit: Octokit, owner: string, repo: string, commentId: number, reactionId: number): Promise<void>;
240
+ interface ReactionDetail {
241
+ id: number;
242
+ content: ReactionContent;
243
+ user: string;
244
+ createdAt: string;
245
+ }
246
+ declare function listIssueCommentReactions(octokit: Octokit, owner: string, repo: string, commentId: number): Promise<ReactionDetail[]>;
247
+ declare function listPullRequestReviewCommentReactions(octokit: Octokit, owner: string, repo: string, commentId: number): Promise<ReactionDetail[]>;
230
248
  declare function formatFileDiff(pullRequestNumber: number, files: ChangedFile[], includeContents?: boolean): Promise<string>;
231
249
  //#endregion
232
- export { CommitData, LLMUpdateOperation, PullCommit, applyOperation, applyOperations, commitFileChanges, commitNewFile, fetchComments, fetchCommitDetails, fetchPrCommits, fetchPrFileDiffs, fetchPrFiles, fetchPrInfo, formatFileDiff, generatePrMarkdown, getFilePathsInRepo, getGitHubClientFromInstallationId, getGitHubClientFromRepo, validateLineNumbers, visualizeUpdateOperations };
250
+ export { CommitData, LLMUpdateOperation, PullCommit, ReactionDetail, applyOperation, applyOperations, commitFileChanges, commitNewFile, createIssueCommentReaction, createPullRequestReviewCommentReaction, deleteIssueCommentReaction, deletePullRequestReviewCommentReaction, fetchComments, fetchCommitDetails, fetchPrCommits, fetchPrFileDiffs, fetchPrFiles, fetchPrInfo, formatFileDiff, generatePrMarkdown, getFilePathsInRepo, getGitHubClientFromInstallationId, getGitHubClientFromRepo, listIssueCommentReactions, listPullRequestReviewCommentReactions, validateLineNumbers, visualizeUpdateOperations };
@@ -39,6 +39,42 @@ function mapUser(user) {
39
39
  return { login: user.login };
40
40
  }
41
41
  /**
42
+ * Fetch detailed reactions for an issue comment (with user attribution)
43
+ */
44
+ async function fetchIssueCommentReactions(octokit, owner, repo, commentId) {
45
+ const reactions = [];
46
+ for await (const response of octokit.paginate.iterator(octokit.rest.reactions.listForIssueComment, {
47
+ owner,
48
+ repo,
49
+ comment_id: commentId,
50
+ per_page: 100
51
+ })) for (const reaction of response.data) reactions.push({
52
+ id: reaction.id,
53
+ user: reaction.user?.login ?? "[deleted]",
54
+ content: reaction.content,
55
+ createdAt: reaction.created_at
56
+ });
57
+ return reactions;
58
+ }
59
+ /**
60
+ * Fetch detailed reactions for a pull request review comment (with user attribution)
61
+ */
62
+ async function fetchReviewCommentReactions(octokit, owner, repo, commentId) {
63
+ const reactions = [];
64
+ for await (const response of octokit.paginate.iterator(octokit.rest.reactions.listForPullRequestReviewComment, {
65
+ owner,
66
+ repo,
67
+ comment_id: commentId,
68
+ per_page: 100
69
+ })) for (const reaction of response.data) reactions.push({
70
+ id: reaction.id,
71
+ user: reaction.user?.login ?? "[deleted]",
72
+ content: reaction.content,
73
+ createdAt: reaction.created_at
74
+ });
75
+ return reactions;
76
+ }
77
+ /**
42
78
  * Fetch pull request details
43
79
  */
44
80
  async function fetchPrInfo(octokit, owner, repo, prNumber) {
@@ -241,6 +277,18 @@ async function fetchComments(octokit, owner, repo, prNumber) {
241
277
  issue_number: prNumber,
242
278
  per_page: 100
243
279
  })) for (const comment of response.data) {
280
+ let reactions;
281
+ if (comment.reactions && comment.reactions.total_count > 0) try {
282
+ reactions = await fetchIssueCommentReactions(octokit, owner, repo, comment.id);
283
+ } catch (error) {
284
+ logger.warn({
285
+ owner,
286
+ repo,
287
+ prNumber,
288
+ commentId: comment.id
289
+ }, `Failed to fetch issue comment reactions: ${error}`);
290
+ reactions = void 0;
291
+ }
244
292
  if (!comment.user) continue;
245
293
  results.push({
246
294
  id: comment.id,
@@ -248,7 +296,8 @@ async function fetchComments(octokit, owner, repo, prNumber) {
248
296
  author: mapUser(comment.user),
249
297
  createdAt: comment.created_at,
250
298
  updatedAt: comment.updated_at,
251
- type: "issue"
299
+ type: "issue",
300
+ reactions
252
301
  });
253
302
  }
254
303
  return results;
@@ -262,6 +311,18 @@ async function fetchComments(octokit, owner, repo, prNumber) {
262
311
  per_page: 100
263
312
  })) for (const comment of response.data) {
264
313
  const isSuggestion = /```suggestion\b/.test(comment.body);
314
+ let reactions;
315
+ if (comment.reactions && comment.reactions.total_count > 0) try {
316
+ reactions = await fetchReviewCommentReactions(octokit, owner, repo, comment.id);
317
+ } catch (error) {
318
+ logger.warn({
319
+ owner,
320
+ repo,
321
+ prNumber,
322
+ commentId: comment.id
323
+ }, `Failed to fetch review comment reactions: ${error}`);
324
+ reactions = void 0;
325
+ }
265
326
  results.push({
266
327
  id: comment.id,
267
328
  body: comment.body,
@@ -272,7 +333,8 @@ async function fetchComments(octokit, owner, repo, prNumber) {
272
333
  path: comment.path,
273
334
  line: comment.line || comment.original_line,
274
335
  diffHunk: comment.diff_hunk,
275
- isSuggestion
336
+ isSuggestion,
337
+ reactions
276
338
  });
277
339
  }
278
340
  return results;
@@ -331,34 +393,24 @@ function generatePrMarkdown(pr, fileDiffs, comments, owner, repo) {
331
393
  markdown += "</files>\n\n";
332
394
  if (comments.length > 0) {
333
395
  markdown += "<comments>\n";
334
- const reviewSummaries = comments.filter((c) => c.type === "review_summary");
335
- const issueComments = comments.filter((c) => c.type === "issue");
336
- const reviewComments = comments.filter((c) => c.type === "review");
337
- for (const review of reviewSummaries) {
338
- markdown += `[${review.state}] @${review.author.login} (${new Date(review.createdAt).toLocaleDateString()})\n`;
339
- if (review.body) markdown += `${review.body}\n`;
340
- markdown += "\n";
341
- }
342
- for (const comment of issueComments) {
343
- markdown += `@${comment.author.login} (${new Date(comment.createdAt).toLocaleDateString()})\n`;
344
- markdown += `${comment.body}\n\n`;
345
- }
346
- if (reviewComments.length > 0) {
347
- const commentsByFile = /* @__PURE__ */ new Map();
348
- for (const comment of reviewComments) {
349
- const path = comment.path || "unknown";
350
- const existing = commentsByFile.get(path) || [];
351
- existing.push(comment);
352
- commentsByFile.set(path, existing);
353
- }
354
- for (const [path, fileComments] of commentsByFile) {
355
- markdown += `${path}:\n`;
356
- for (const comment of fileComments) {
357
- const lineInfo = comment.line ? ` line ${comment.line}` : "";
358
- markdown += ` @${comment.author.login}${lineInfo} (${new Date(comment.createdAt).toLocaleDateString()})\n`;
359
- markdown += ` ${comment.body}\n\n`;
360
- }
396
+ const sorted = [...comments].sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
397
+ for (const comment of sorted) {
398
+ const date = new Date(comment.createdAt).toLocaleDateString();
399
+ const author = comment.author.login;
400
+ if (comment.type === "review_summary") markdown += `[review_summary] user:${author} comment_id:${comment.id} (${date})\n`;
401
+ else if (comment.type === "review") {
402
+ const lineInfo = comment.line ? `:${comment.line}` : "";
403
+ markdown += `[review_comment] user:${author} on ${comment.path}${lineInfo} comment_id:${comment.id} (${date})\n`;
404
+ } else markdown += `[issue_comment] user:${author} comment_id:${comment.id} (${date})\n`;
405
+ markdown += `${comment.body}\n`;
406
+ if (comment.reactions && comment.reactions.length > 0) {
407
+ const reactionCounts = /* @__PURE__ */ new Map();
408
+ for (const r of comment.reactions) reactionCounts.set(r.content, (reactionCounts.get(r.content) || 0) + 1);
409
+ const parts = [];
410
+ for (const [emoji, count] of reactionCounts) parts.push(count > 1 ? `${emoji} x${count}` : emoji);
411
+ markdown += `reactions: ${parts.join(", ")}\n`;
361
412
  }
413
+ markdown += "\n";
362
414
  }
363
415
  markdown += "</comments>\n";
364
416
  }
@@ -550,6 +602,76 @@ async function commitNewFile({ githubClient, owner, repo, filePath, branchName,
550
602
  throw new Error(`Failed to commit new file: ${error instanceof Error ? error.message : "Unknown error"}`);
551
603
  }
552
604
  }
605
+ async function createIssueCommentReaction(octokit, owner, repo, commentId, content) {
606
+ const { data } = await octokit.rest.reactions.createForIssueComment({
607
+ owner,
608
+ repo,
609
+ comment_id: commentId,
610
+ content
611
+ });
612
+ return {
613
+ id: data.id,
614
+ content: data.content
615
+ };
616
+ }
617
+ async function deleteIssueCommentReaction(octokit, owner, repo, commentId, reactionId) {
618
+ await octokit.rest.reactions.deleteForIssueComment({
619
+ owner,
620
+ repo,
621
+ comment_id: commentId,
622
+ reaction_id: reactionId
623
+ });
624
+ }
625
+ async function createPullRequestReviewCommentReaction(octokit, owner, repo, commentId, content) {
626
+ const { data } = await octokit.rest.reactions.createForPullRequestReviewComment({
627
+ owner,
628
+ repo,
629
+ comment_id: commentId,
630
+ content
631
+ });
632
+ return {
633
+ id: data.id,
634
+ content: data.content
635
+ };
636
+ }
637
+ async function deletePullRequestReviewCommentReaction(octokit, owner, repo, commentId, reactionId) {
638
+ await octokit.rest.reactions.deleteForPullRequestComment({
639
+ owner,
640
+ repo,
641
+ comment_id: commentId,
642
+ reaction_id: reactionId
643
+ });
644
+ }
645
+ async function listIssueCommentReactions(octokit, owner, repo, commentId) {
646
+ const reactions = [];
647
+ for await (const response of octokit.paginate.iterator(octokit.rest.reactions.listForIssueComment, {
648
+ owner,
649
+ repo,
650
+ comment_id: commentId,
651
+ per_page: 100
652
+ })) for (const r of response.data) reactions.push({
653
+ id: r.id,
654
+ content: r.content,
655
+ user: r.user?.login ?? "unknown",
656
+ createdAt: r.created_at
657
+ });
658
+ return reactions;
659
+ }
660
+ async function listPullRequestReviewCommentReactions(octokit, owner, repo, commentId) {
661
+ const reactions = [];
662
+ for await (const response of octokit.paginate.iterator(octokit.rest.reactions.listForPullRequestReviewComment, {
663
+ owner,
664
+ repo,
665
+ comment_id: commentId,
666
+ per_page: 100
667
+ })) for (const r of response.data) reactions.push({
668
+ id: r.id,
669
+ content: r.content,
670
+ user: r.user?.login ?? "unknown",
671
+ createdAt: r.created_at
672
+ });
673
+ return reactions;
674
+ }
553
675
  async function formatFileDiff(pullRequestNumber, files, includeContents = false) {
554
676
  let output = `## File Patches for PR #${pullRequestNumber}\n\n`;
555
677
  output += `Found ${files.length} file(s) matching the requested paths.\n\n`;
@@ -572,4 +694,4 @@ async function formatFileDiff(pullRequestNumber, files, includeContents = false)
572
694
  }
573
695
 
574
696
  //#endregion
575
- export { applyOperation, applyOperations, commitFileChanges, commitNewFile, fetchComments, fetchCommitDetails, fetchPrCommits, fetchPrFileDiffs, fetchPrFiles, fetchPrInfo, formatFileDiff, generatePrMarkdown, getFilePathsInRepo, getGitHubClientFromInstallationId, getGitHubClientFromRepo, validateLineNumbers, visualizeUpdateOperations };
697
+ export { applyOperation, applyOperations, commitFileChanges, commitNewFile, createIssueCommentReaction, createPullRequestReviewCommentReaction, deleteIssueCommentReaction, deletePullRequestReviewCommentReaction, fetchComments, fetchCommitDetails, fetchPrCommits, fetchPrFileDiffs, fetchPrFiles, fetchPrInfo, formatFileDiff, generatePrMarkdown, getFilePathsInRepo, getGitHubClientFromInstallationId, getGitHubClientFromRepo, listIssueCommentReactions, listPullRequestReviewCommentReactions, validateLineNumbers, visualizeUpdateOperations };
@@ -1,7 +1,7 @@
1
1
  import { Hono } from "hono";
2
- import * as hono_types0 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_types0.BlankEnv, hono_types0.BlankSchema, "/">;
5
+ declare const app: Hono<hono_types4.BlankEnv, hono_types4.BlankSchema, "/">;
6
6
  //#endregion
7
7
  export { app as default };
@@ -5,8 +5,8 @@ import { getStateSigningSecret, isStateSigningConfigured } from "../config.js";
5
5
  import { createAppJwt, determineStatus, fetchInstallationDetails, fetchInstallationRepositories } from "../installation.js";
6
6
  import { createInstallation, generateId, getInstallationByGitHubId, listProjectsMetadata, setProjectAccessMode, syncRepositories, updateInstallationStatusByGitHubId } from "@inkeep/agents-core";
7
7
  import { Hono } from "hono";
8
- import { jwtVerify } from "jose";
9
8
  import { z } from "zod";
9
+ import { jwtVerify } from "jose";
10
10
 
11
11
  //#region src/github/routes/setup.ts
12
12
  const logger = getLogger("github-setup");
@@ -1,7 +1,7 @@
1
1
  import { Hono } from "hono";
2
- import * as hono_types2 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_types2.BlankEnv, hono_types2.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_types4 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_types4.BlankEnv, hono_types4.BlankSchema, "/">;
10
+ declare const app: Hono<hono_types8.BlankEnv, hono_types8.BlankSchema, "/">;
11
11
  //#endregion
12
12
  export { WebhookVerificationResult, app as default, verifyWebhookSignature };
@@ -89,7 +89,7 @@ function createJwtLinkMessage(linkUrl, expiresInMinutes) {
89
89
  • Get personalized responses from AI agents
90
90
  • Set your own default agent preferences`), Blocks.Section().text(`${Md.bold("How to link:")}\n1. Click the button below
91
91
  2. Sign in to Inkeep (or create an account)
92
- 3. Done! Come back here and start asking questions`), Blocks.Actions().elements(Elements.Button().text("🔗 Link Account").url(linkUrl).actionId("link_account").primary()), Blocks.Context().elements(`${Md.emoji("clock")} This link expires in ${expiresInMinutes} minutes`)).buildToObject();
92
+ 3. Done! Come back here and start asking questions`), Blocks.Actions().elements(Elements.Button().text("🔗 Link Account").url(linkUrl).actionId("link_account").primary()), Blocks.Context().elements(`This link expires in ${expiresInMinutes} minutes`)).buildToObject();
93
93
  }
94
94
 
95
95
  //#endregion
@@ -8,9 +8,9 @@ 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
+ id: string;
11
12
  createdAt: string;
12
13
  updatedAt: string;
13
- id: string;
14
14
  tenantId: string;
15
15
  clientId: string;
16
16
  slackUserId: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inkeep/agents-work-apps",
3
- "version": "0.49.0",
3
+ "version": "0.50.1",
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.49.0"
36
+ "@inkeep/agents-core": "0.50.1"
37
37
  },
38
38
  "peerDependencies": {
39
39
  "@hono/zod-openapi": "^1.1.5",