@inkeep/agents-work-apps 0.50.6 → 0.52.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/github/mcp/auth.d.ts +2 -2
- package/dist/github/mcp/index.d.ts +2 -2
- package/dist/github/mcp/index.js +60 -1
- package/dist/github/mcp/schemas.d.ts +1 -1
- package/dist/github/mcp/utils.d.ts +10 -1
- package/dist/github/mcp/utils.js +87 -1
- package/dist/github/routes/setup.d.ts +2 -2
- package/dist/github/routes/tokenExchange.d.ts +2 -2
- package/dist/github/routes/webhooks.d.ts +2 -2
- package/dist/slack/dispatcher.js +24 -1
- package/dist/slack/i18n/strings.d.ts +1 -0
- package/dist/slack/i18n/strings.js +1 -0
- package/dist/slack/routes/events.js +2 -2
- package/dist/slack/routes/oauth.js +3 -4
- package/dist/slack/routes/users.js +13 -11
- package/dist/slack/routes/workspaces.js +85 -1
- package/dist/slack/services/blocks/index.d.ts +81 -1
- package/dist/slack/services/blocks/index.js +238 -19
- package/dist/slack/services/commands/index.d.ts +1 -1
- package/dist/slack/services/commands/index.js +98 -4
- package/dist/slack/services/events/app-mention.js +2 -2
- package/dist/slack/services/events/block-actions.d.ts +12 -1
- package/dist/slack/services/events/block-actions.js +126 -2
- package/dist/slack/services/events/index.d.ts +2 -2
- package/dist/slack/services/events/index.js +2 -2
- package/dist/slack/services/events/streaming.d.ts +1 -1
- package/dist/slack/services/events/streaming.js +203 -7
- package/dist/slack/services/events/utils.d.ts +2 -2
- package/dist/slack/services/events/utils.js +5 -2
- package/dist/slack/services/index.d.ts +3 -3
- package/dist/slack/services/index.js +3 -3
- package/dist/slack/services/nango.js +1 -23
- package/dist/slack/tracer.d.ts +1 -0
- package/dist/slack/tracer.js +2 -1
- package/package.json +2 -2
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as hono1 from "hono";
|
|
2
2
|
|
|
3
3
|
//#region src/github/mcp/auth.d.ts
|
|
4
|
-
declare const githubMcpAuth: () =>
|
|
4
|
+
declare const githubMcpAuth: () => hono1.MiddlewareHandler<{
|
|
5
5
|
Variables: {
|
|
6
6
|
toolId: string;
|
|
7
7
|
};
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
|
-
import * as
|
|
2
|
+
import * as hono_types9 from "hono/types";
|
|
3
3
|
|
|
4
4
|
//#region src/github/mcp/index.d.ts
|
|
5
5
|
declare const app: Hono<{
|
|
6
6
|
Variables: {
|
|
7
7
|
toolId: string;
|
|
8
8
|
};
|
|
9
|
-
},
|
|
9
|
+
}, hono_types9.BlankSchema, "/">;
|
|
10
10
|
//#endregion
|
|
11
11
|
export { app as default };
|
package/dist/github/mcp/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import runDbClient_default from "../../db/runDbClient.js";
|
|
2
2
|
import { githubMcpAuth } from "./auth.js";
|
|
3
3
|
import { ReactionContentSchema } from "./schemas.js";
|
|
4
|
-
import { commitFileChanges, commitNewFile, createIssueCommentReaction, createPullRequestReviewCommentReaction, deleteIssueCommentReaction, deletePullRequestReviewCommentReaction, 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, 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";
|
|
@@ -267,6 +267,65 @@ const getServer = async (toolId) => {
|
|
|
267
267
|
};
|
|
268
268
|
}
|
|
269
269
|
});
|
|
270
|
+
server.tool("get-changed-files-for-branch", `Get the list of files changed on a branch compared to a base ref, without requiring a pull request. Returns file paths, status, additions/deletions, and optionally patches and full file contents. ${getAvailableRepositoryString(repositoryAccess)}`, {
|
|
271
|
+
owner: z.string().describe("Repository owner name"),
|
|
272
|
+
repo: z.string().describe("Repository name"),
|
|
273
|
+
head: z.string().describe("The branch or ref to check for changes (e.g. \"feat/my-feature\")"),
|
|
274
|
+
base: z.string().optional().describe("The base branch or ref to compare against (defaults to the repository default branch)"),
|
|
275
|
+
file_paths: z.array(z.string()).optional().describe("Optional list of file path glob patterns to filter results"),
|
|
276
|
+
include_contents: z.boolean().default(false).describe("Whether to include full file contents for each changed file"),
|
|
277
|
+
include_patch: z.boolean().default(false).describe("Whether to include the patch/diff text for each changed file")
|
|
278
|
+
}, async ({ owner, repo, head, base, file_paths, include_contents, include_patch }) => {
|
|
279
|
+
try {
|
|
280
|
+
let githubClient;
|
|
281
|
+
try {
|
|
282
|
+
githubClient = getGitHubClientFromRepo(owner, repo, installationIdMap);
|
|
283
|
+
} catch (error) {
|
|
284
|
+
return {
|
|
285
|
+
content: [{
|
|
286
|
+
type: "text",
|
|
287
|
+
text: `Error accessing GitHub: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
288
|
+
}],
|
|
289
|
+
isError: true
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
const baseRef = base ?? (await githubClient.rest.repos.get({
|
|
293
|
+
owner,
|
|
294
|
+
repo
|
|
295
|
+
})).data.default_branch;
|
|
296
|
+
const files = await fetchBranchChangedFiles(githubClient, owner, repo, baseRef, head, {
|
|
297
|
+
pathFilters: file_paths ?? [],
|
|
298
|
+
includeContents: include_contents,
|
|
299
|
+
includePatch: include_patch
|
|
300
|
+
});
|
|
301
|
+
if (files.length === 0) return { content: [{
|
|
302
|
+
type: "text",
|
|
303
|
+
text: `No changed files found between ${baseRef} and ${head} in ${owner}/${repo}.${file_paths?.length ? `\n\nFilters applied: ${file_paths.join(", ")}` : ""}`
|
|
304
|
+
}] };
|
|
305
|
+
const output = await formatFileDiff(0, files, include_contents);
|
|
306
|
+
return { content: [{
|
|
307
|
+
type: "text",
|
|
308
|
+
text: `## Changed files: ${baseRef}...${head} in ${owner}/${repo}\n\nFound ${files.length} changed file(s).\n\n` + output
|
|
309
|
+
}] };
|
|
310
|
+
} catch (error) {
|
|
311
|
+
if (error instanceof Error && "status" in error) {
|
|
312
|
+
if (error.status === 404) return {
|
|
313
|
+
content: [{
|
|
314
|
+
type: "text",
|
|
315
|
+
text: `Repository ${owner}/${repo} not found, or one of the refs ("${base ?? "default"}", "${head}") does not exist.`
|
|
316
|
+
}],
|
|
317
|
+
isError: true
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
return {
|
|
321
|
+
content: [{
|
|
322
|
+
type: "text",
|
|
323
|
+
text: `Error fetching changed files: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
324
|
+
}],
|
|
325
|
+
isError: true
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
});
|
|
270
329
|
server.tool("create-branch", `Create a new branch in a repository. ${getAvailableRepositoryString(repositoryAccess)}`, {
|
|
271
330
|
owner: z.string().describe("Repository owner name"),
|
|
272
331
|
repo: z.string().describe("Repository name"),
|
|
@@ -152,6 +152,15 @@ declare function fetchPrFiles(octokit: Octokit, owner: string, repo: string, prN
|
|
|
152
152
|
* Get file-based diffs with all commit messages that impacted each file.
|
|
153
153
|
*/
|
|
154
154
|
declare function fetchPrFileDiffs(octokit: Octokit, owner: string, repo: string, prNumber: number): Promise<ChangedFile[]>;
|
|
155
|
+
/**
|
|
156
|
+
* Fetch files changed on a branch compared to a base ref, without requiring a PR.
|
|
157
|
+
* Uses the GitHub Compare API (`repos.compareCommitsWithBasehead`).
|
|
158
|
+
*/
|
|
159
|
+
declare function fetchBranchChangedFiles(octokit: Octokit, owner: string, repo: string, base: string, head: string, options?: {
|
|
160
|
+
pathFilters?: string[];
|
|
161
|
+
includeContents?: boolean;
|
|
162
|
+
includePatch?: boolean;
|
|
163
|
+
}): Promise<ChangedFile[]>;
|
|
155
164
|
/**
|
|
156
165
|
* Fetch all PR comments (both issue comments and review comments)
|
|
157
166
|
*/
|
|
@@ -247,4 +256,4 @@ declare function listIssueCommentReactions(octokit: Octokit, owner: string, repo
|
|
|
247
256
|
declare function listPullRequestReviewCommentReactions(octokit: Octokit, owner: string, repo: string, commentId: number): Promise<ReactionDetail[]>;
|
|
248
257
|
declare function formatFileDiff(pullRequestNumber: number, files: ChangedFile[], includeContents?: boolean): Promise<string>;
|
|
249
258
|
//#endregion
|
|
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 };
|
|
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 };
|
package/dist/github/mcp/utils.js
CHANGED
|
@@ -265,6 +265,92 @@ async function fetchPrFileDiffs(octokit, owner, repo, prNumber) {
|
|
|
265
265
|
}
|
|
266
266
|
}
|
|
267
267
|
/**
|
|
268
|
+
* Fetch files changed on a branch compared to a base ref, without requiring a PR.
|
|
269
|
+
* Uses the GitHub Compare API (`repos.compareCommitsWithBasehead`).
|
|
270
|
+
*/
|
|
271
|
+
async function fetchBranchChangedFiles(octokit, owner, repo, base, head, options = {}) {
|
|
272
|
+
const { pathFilters = [], includeContents = false, includePatch = false } = options;
|
|
273
|
+
logger.info({
|
|
274
|
+
owner,
|
|
275
|
+
repo,
|
|
276
|
+
base,
|
|
277
|
+
head,
|
|
278
|
+
pathFilters,
|
|
279
|
+
includeContents,
|
|
280
|
+
includePatch
|
|
281
|
+
}, `Fetching changed files between ${base}...${head}`);
|
|
282
|
+
const totalCommits = (await octokit.rest.repos.compareCommitsWithBasehead({
|
|
283
|
+
owner,
|
|
284
|
+
repo,
|
|
285
|
+
basehead: `${base}...${head}`,
|
|
286
|
+
per_page: 1
|
|
287
|
+
})).data.total_commits;
|
|
288
|
+
const collectedFiles = [];
|
|
289
|
+
let page = 1;
|
|
290
|
+
const perPage = 100;
|
|
291
|
+
while (true) {
|
|
292
|
+
const files = (await octokit.rest.repos.compareCommitsWithBasehead({
|
|
293
|
+
owner,
|
|
294
|
+
repo,
|
|
295
|
+
basehead: `${base}...${head}`,
|
|
296
|
+
per_page: perPage,
|
|
297
|
+
page
|
|
298
|
+
})).data.files ?? [];
|
|
299
|
+
if (files.length === 0) break;
|
|
300
|
+
for (const file of files) {
|
|
301
|
+
if (pathFilters.length > 0 && !pathFilters.some((filter) => minimatch(file.filename, filter))) continue;
|
|
302
|
+
collectedFiles.push({
|
|
303
|
+
commit_messages: [],
|
|
304
|
+
path: file.filename,
|
|
305
|
+
status: file.status,
|
|
306
|
+
additions: file.additions,
|
|
307
|
+
deletions: file.deletions,
|
|
308
|
+
patch: includePatch ? file.patch : void 0,
|
|
309
|
+
previousPath: file.previous_filename
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
if (files.length < perPage) break;
|
|
313
|
+
page++;
|
|
314
|
+
}
|
|
315
|
+
if (includeContents) {
|
|
316
|
+
const BATCH_SIZE = 10;
|
|
317
|
+
const filesToFetch = collectedFiles.filter((f) => f.status !== "removed");
|
|
318
|
+
for (let i = 0; i < filesToFetch.length; i += BATCH_SIZE) {
|
|
319
|
+
const batch = filesToFetch.slice(i, i + BATCH_SIZE);
|
|
320
|
+
await Promise.all(batch.map(async (changedFile) => {
|
|
321
|
+
try {
|
|
322
|
+
const { data: content } = await octokit.rest.repos.getContent({
|
|
323
|
+
owner,
|
|
324
|
+
repo,
|
|
325
|
+
path: changedFile.path,
|
|
326
|
+
ref: head
|
|
327
|
+
});
|
|
328
|
+
if ("content" in content && content.encoding === "base64") changedFile.contents = Buffer.from(content.content, "base64").toString("utf-8");
|
|
329
|
+
} catch (error) {
|
|
330
|
+
logger.warn({
|
|
331
|
+
owner,
|
|
332
|
+
repo,
|
|
333
|
+
base,
|
|
334
|
+
head,
|
|
335
|
+
file: changedFile.path
|
|
336
|
+
}, `Failed to fetch contents for ${changedFile.path}: ${error}`);
|
|
337
|
+
}
|
|
338
|
+
}));
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
logger.info({
|
|
342
|
+
owner,
|
|
343
|
+
repo,
|
|
344
|
+
base,
|
|
345
|
+
head,
|
|
346
|
+
totalCommits,
|
|
347
|
+
pathFilters,
|
|
348
|
+
includeContents,
|
|
349
|
+
fileCount: collectedFiles.length
|
|
350
|
+
}, `Found ${collectedFiles.length} changed files between ${base}...${head} (${totalCommits} commits)`);
|
|
351
|
+
return collectedFiles;
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
268
354
|
* Fetch all PR comments (both issue comments and review comments)
|
|
269
355
|
*/
|
|
270
356
|
async function fetchComments(octokit, owner, repo, prNumber) {
|
|
@@ -694,4 +780,4 @@ async function formatFileDiff(pullRequestNumber, files, includeContents = false)
|
|
|
694
780
|
}
|
|
695
781
|
|
|
696
782
|
//#endregion
|
|
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 };
|
|
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 };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
|
-
import * as
|
|
2
|
+
import * as hono_types3 from "hono/types";
|
|
3
3
|
|
|
4
4
|
//#region src/github/routes/setup.d.ts
|
|
5
|
-
declare const app: Hono<
|
|
5
|
+
declare const app: Hono<hono_types3.BlankEnv, hono_types3.BlankSchema, "/">;
|
|
6
6
|
//#endregion
|
|
7
7
|
export { app as default };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
|
-
import * as
|
|
2
|
+
import * as hono_types5 from "hono/types";
|
|
3
3
|
|
|
4
4
|
//#region src/github/routes/tokenExchange.d.ts
|
|
5
|
-
declare const app: Hono<
|
|
5
|
+
declare const app: Hono<hono_types5.BlankEnv, hono_types5.BlankSchema, "/">;
|
|
6
6
|
//#endregion
|
|
7
7
|
export { app as default };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
|
-
import * as
|
|
2
|
+
import * as hono_types7 from "hono/types";
|
|
3
3
|
|
|
4
4
|
//#region src/github/routes/webhooks.d.ts
|
|
5
5
|
interface WebhookVerificationResult {
|
|
@@ -7,6 +7,6 @@ interface WebhookVerificationResult {
|
|
|
7
7
|
error?: string;
|
|
8
8
|
}
|
|
9
9
|
declare function verifyWebhookSignature(payload: string, signature: string | undefined, secret: string): WebhookVerificationResult;
|
|
10
|
-
declare const app: Hono<
|
|
10
|
+
declare const app: Hono<hono_types7.BlankEnv, hono_types7.BlankSchema, "/">;
|
|
11
11
|
//#endregion
|
|
12
12
|
export { WebhookVerificationResult, app as default, verifyWebhookSignature };
|
package/dist/slack/dispatcher.js
CHANGED
|
@@ -4,7 +4,7 @@ import { getSlackClient } from "./services/client.js";
|
|
|
4
4
|
import { sendResponseUrlMessage } from "./services/events/utils.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 } from "./services/events/block-actions.js";
|
|
7
|
+
import { handleMessageShortcut, handleOpenAgentSelectorModal, handleOpenFollowUpModal, handleToolApproval } from "./services/events/block-actions.js";
|
|
8
8
|
import { handleFollowUpSubmission, handleModalSubmission } from "./services/events/modal-submission.js";
|
|
9
9
|
import "./services/events/index.js";
|
|
10
10
|
import "./services/index.js";
|
|
@@ -176,6 +176,29 @@ async function dispatchSlackEvent(eventType, payload, options, span) {
|
|
|
176
176
|
}
|
|
177
177
|
})());
|
|
178
178
|
}
|
|
179
|
+
if ((action.action_id === "tool_approval_approve" || action.action_id === "tool_approval_deny") && action.value) {
|
|
180
|
+
anyHandled = true;
|
|
181
|
+
const approved = action.action_id === "tool_approval_approve";
|
|
182
|
+
const slackUserId = payload.user?.id || "";
|
|
183
|
+
logger.info({
|
|
184
|
+
teamId,
|
|
185
|
+
actionId: action.action_id,
|
|
186
|
+
approved
|
|
187
|
+
}, `Handling block_action: ${action.action_id}`);
|
|
188
|
+
registerBackgroundWork(handleToolApproval({
|
|
189
|
+
actionValue: action.value,
|
|
190
|
+
approved,
|
|
191
|
+
teamId,
|
|
192
|
+
slackUserId,
|
|
193
|
+
responseUrl
|
|
194
|
+
}).catch((err) => {
|
|
195
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
196
|
+
logger.error({
|
|
197
|
+
errorMessage,
|
|
198
|
+
actionId: action.action_id
|
|
199
|
+
}, "Failed to handle tool approval");
|
|
200
|
+
}).finally(() => flushTraces()));
|
|
201
|
+
}
|
|
179
202
|
if (action.action_id === "open_follow_up_modal" && action.value && triggerId) {
|
|
180
203
|
anyHandled = true;
|
|
181
204
|
logger.info({
|
|
@@ -45,6 +45,7 @@ declare const SlackStrings: {
|
|
|
45
45
|
};
|
|
46
46
|
readonly status: {
|
|
47
47
|
readonly thinking: (agentName: string) => string;
|
|
48
|
+
readonly readingThread: (agentName: string) => string;
|
|
48
49
|
readonly noAgentsAvailable: "No agents available";
|
|
49
50
|
readonly noProjectsConfigured: "No projects configured. Set up projects in the dashboard.";
|
|
50
51
|
};
|
|
@@ -41,6 +41,7 @@ const SlackStrings = {
|
|
|
41
41
|
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
42
|
status: {
|
|
43
43
|
thinking: (agentName) => `_${agentName} is thinking..._`,
|
|
44
|
+
readingThread: (agentName) => `_${agentName} is reading this thread..._`,
|
|
44
45
|
noAgentsAvailable: "No agents available",
|
|
45
46
|
noProjectsConfigured: "No projects configured. Set up projects in the dashboard."
|
|
46
47
|
},
|
|
@@ -70,7 +70,7 @@ app.post("/events", async (c) => {
|
|
|
70
70
|
retryReason
|
|
71
71
|
}, "Acknowledging Slack retry without re-processing");
|
|
72
72
|
span.end();
|
|
73
|
-
return c.
|
|
73
|
+
return c.body(null, 200);
|
|
74
74
|
});
|
|
75
75
|
const waitUntil = await getWaitUntil();
|
|
76
76
|
return tracer.startActiveSpan(SLACK_SPAN_NAMES.WEBHOOK, async (span) => {
|
|
@@ -128,7 +128,7 @@ app.post("/events", async (c) => {
|
|
|
128
128
|
return c.json(result.response);
|
|
129
129
|
}
|
|
130
130
|
span.end();
|
|
131
|
-
return c.
|
|
131
|
+
return c.body(null, 200);
|
|
132
132
|
} catch (error) {
|
|
133
133
|
outcome = "error";
|
|
134
134
|
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 } from "@inkeep/agents-core";
|
|
9
|
+
import { createWorkAppSlackWorkspace, isUniqueConstraintError } 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
|
|
|
@@ -96,6 +96,7 @@ app.openapi(createProtectedRoute({
|
|
|
96
96
|
"chat:write",
|
|
97
97
|
"chat:write.public",
|
|
98
98
|
"commands",
|
|
99
|
+
"files:write",
|
|
99
100
|
"groups:history",
|
|
100
101
|
"groups:read",
|
|
101
102
|
"im:history",
|
|
@@ -252,8 +253,7 @@ app.openapi(createProtectedRoute({
|
|
|
252
253
|
tenantId
|
|
253
254
|
}, "Persisted workspace installation to database");
|
|
254
255
|
} catch (dbError) {
|
|
255
|
-
|
|
256
|
-
if (dbErrorMessage.includes("duplicate key") || dbErrorMessage.includes("unique constraint")) logger.info({
|
|
256
|
+
if (isUniqueConstraintError(dbError)) logger.info({
|
|
257
257
|
teamId: workspaceData.teamId,
|
|
258
258
|
tenantId
|
|
259
259
|
}, "Workspace already exists in database");
|
|
@@ -261,7 +261,6 @@ app.openapi(createProtectedRoute({
|
|
|
261
261
|
const pgCode = dbError && typeof dbError === "object" && "code" in dbError ? dbError.code : void 0;
|
|
262
262
|
logger.error({
|
|
263
263
|
err: dbError,
|
|
264
|
-
dbErrorMessage,
|
|
265
264
|
pgCode,
|
|
266
265
|
teamId: workspaceData.teamId,
|
|
267
266
|
tenantId,
|
|
@@ -3,7 +3,7 @@ import runDbClient_default from "../../db/runDbClient.js";
|
|
|
3
3
|
import { createConnectSession } from "../services/nango.js";
|
|
4
4
|
import "../services/index.js";
|
|
5
5
|
import { OpenAPIHono, z } from "@hono/zod-openapi";
|
|
6
|
-
import { createWorkAppSlackUserMapping, deleteWorkAppSlackUserMapping, findWorkAppSlackUserMapping, findWorkAppSlackUserMappingByInkeepUserId, verifySlackLinkToken } from "@inkeep/agents-core";
|
|
6
|
+
import { createWorkAppSlackUserMapping, deleteWorkAppSlackUserMapping, findWorkAppSlackUserMapping, findWorkAppSlackUserMappingByInkeepUserId, isUniqueConstraintError, verifySlackLinkToken } from "@inkeep/agents-core";
|
|
7
7
|
import { createProtectedRoute, inheritedWorkAppsAuth } from "@inkeep/agents-core/middleware";
|
|
8
8
|
|
|
9
9
|
//#region src/slack/routes/users.ts
|
|
@@ -131,12 +131,15 @@ app.openapi(createProtectedRoute({
|
|
|
131
131
|
tenantId
|
|
132
132
|
});
|
|
133
133
|
}
|
|
134
|
-
if (existingLink)
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
134
|
+
if (existingLink) {
|
|
135
|
+
logger.info({
|
|
136
|
+
slackUserId,
|
|
137
|
+
existingUserId: existingLink.inkeepUserId,
|
|
138
|
+
newUserId: inkeepUserId,
|
|
139
|
+
tenantId
|
|
140
|
+
}, "Slack user already linked, updating to new user");
|
|
141
|
+
await deleteWorkAppSlackUserMapping(runDbClient_default)(tenantId, slackUserId, teamId, "work-apps-slack");
|
|
142
|
+
}
|
|
140
143
|
const slackUserMapping = await createWorkAppSlackUserMapping(runDbClient_default)({
|
|
141
144
|
tenantId,
|
|
142
145
|
clientId: "work-apps-slack",
|
|
@@ -162,10 +165,9 @@ app.openapi(createProtectedRoute({
|
|
|
162
165
|
tenantId
|
|
163
166
|
});
|
|
164
167
|
} catch (error) {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
return c.json({ error: "This Slack account is already linked to an Inkeep account." }, 409);
|
|
168
|
+
if (isUniqueConstraintError(error)) {
|
|
169
|
+
logger.info({ userId: body.userId }, "Concurrent link resolved — mapping already exists");
|
|
170
|
+
return c.json({ success: true });
|
|
169
171
|
}
|
|
170
172
|
logger.error({
|
|
171
173
|
error,
|
|
@@ -5,7 +5,7 @@ import { getSlackChannels, getSlackClient, revokeSlackToken } from "../services/
|
|
|
5
5
|
import "../services/index.js";
|
|
6
6
|
import { requireChannelMemberOrAdmin, requireWorkspaceAdmin } from "../middleware/permissions.js";
|
|
7
7
|
import { OpenAPIHono, z } from "@hono/zod-openapi";
|
|
8
|
-
import { deleteAllWorkAppSlackChannelAgentConfigsByTeam, deleteAllWorkAppSlackUserMappingsByTeam, deleteWorkAppSlackChannelAgentConfig, deleteWorkAppSlackWorkspaceByNangoConnectionId, findWorkAppSlackChannelAgentConfig, listWorkAppSlackChannelAgentConfigsByTeam, listWorkAppSlackUserMappingsByTeam, upsertWorkAppSlackChannelAgentConfig } from "@inkeep/agents-core";
|
|
8
|
+
import { deleteAllWorkAppSlackChannelAgentConfigsByTeam, deleteAllWorkAppSlackUserMappingsByTeam, deleteWorkAppSlackChannelAgentConfig, deleteWorkAppSlackWorkspaceByNangoConnectionId, findWorkAppSlackChannelAgentConfig, findWorkAppSlackWorkspaceByTeamId, listWorkAppSlackChannelAgentConfigsByTeam, listWorkAppSlackUserMappingsByTeam, updateWorkAppSlackWorkspace, upsertWorkAppSlackChannelAgentConfig } from "@inkeep/agents-core";
|
|
9
9
|
import { createProtectedRoute, inheritedWorkAppsAuth } from "@inkeep/agents-core/middleware";
|
|
10
10
|
|
|
11
11
|
//#region src/slack/routes/workspaces.ts
|
|
@@ -48,6 +48,7 @@ const ChannelAgentConfigSchema = z.object({
|
|
|
48
48
|
grantAccessToMembers: z.boolean().optional()
|
|
49
49
|
});
|
|
50
50
|
const WorkspaceSettingsSchema = z.object({ defaultAgent: ChannelAgentConfigSchema.optional() });
|
|
51
|
+
const JoinFromWorkspaceSettingsSchema = z.object({ shouldAllowJoinFromWorkspace: z.boolean() });
|
|
51
52
|
app.openapi(createProtectedRoute({
|
|
52
53
|
method: "get",
|
|
53
54
|
path: "/",
|
|
@@ -222,6 +223,89 @@ app.openapi(createProtectedRoute({
|
|
|
222
223
|
}
|
|
223
224
|
return c.json({ success: true });
|
|
224
225
|
});
|
|
226
|
+
app.openapi(createProtectedRoute({
|
|
227
|
+
method: "get",
|
|
228
|
+
path: "/{teamId}/join-from-workspace",
|
|
229
|
+
summary: "Get Join From Workspace Setting",
|
|
230
|
+
description: "Get the join from workspace setting for the workspace",
|
|
231
|
+
operationId: "slack-get-join-from-workspace",
|
|
232
|
+
tags: [
|
|
233
|
+
"Work Apps",
|
|
234
|
+
"Slack",
|
|
235
|
+
"Workspaces"
|
|
236
|
+
],
|
|
237
|
+
permission: inheritedWorkAppsAuth(),
|
|
238
|
+
request: { params: z.object({ teamId: z.string() }) },
|
|
239
|
+
responses: {
|
|
240
|
+
200: {
|
|
241
|
+
description: "Join from workspace setting",
|
|
242
|
+
content: { "application/json": { schema: JoinFromWorkspaceSettingsSchema } }
|
|
243
|
+
},
|
|
244
|
+
404: { description: "Workspace not found" }
|
|
245
|
+
}
|
|
246
|
+
}), async (c) => {
|
|
247
|
+
const { teamId } = c.req.valid("param");
|
|
248
|
+
const sessionTenantId = c.get("tenantId");
|
|
249
|
+
if (!sessionTenantId) return c.json({ error: "Unauthorized" }, 401);
|
|
250
|
+
const workspace = await findWorkAppSlackWorkspaceByTeamId(runDbClient_default)(sessionTenantId, teamId);
|
|
251
|
+
if (!workspace) return c.json({ shouldAllowJoinFromWorkspace: false });
|
|
252
|
+
return c.json({ shouldAllowJoinFromWorkspace: workspace.shouldAllowJoinFromWorkspace ?? false });
|
|
253
|
+
});
|
|
254
|
+
app.openapi(createProtectedRoute({
|
|
255
|
+
method: "put",
|
|
256
|
+
path: "/{teamId}/join-from-workspace",
|
|
257
|
+
summary: "Update Join From Workspace Setting",
|
|
258
|
+
description: "Enable or disable join from workspace for the workspace",
|
|
259
|
+
operationId: "slack-update-join-from-workspace",
|
|
260
|
+
tags: [
|
|
261
|
+
"Work Apps",
|
|
262
|
+
"Slack",
|
|
263
|
+
"Workspaces"
|
|
264
|
+
],
|
|
265
|
+
permission: requireWorkspaceAdmin(),
|
|
266
|
+
request: {
|
|
267
|
+
params: z.object({ teamId: z.string() }),
|
|
268
|
+
body: { content: { "application/json": { schema: JoinFromWorkspaceSettingsSchema } } }
|
|
269
|
+
},
|
|
270
|
+
responses: {
|
|
271
|
+
200: {
|
|
272
|
+
description: "Join from workspace setting updated",
|
|
273
|
+
content: { "application/json": { schema: z.object({ success: z.boolean() }) } }
|
|
274
|
+
},
|
|
275
|
+
401: { description: "Unauthorized" },
|
|
276
|
+
404: { description: "Workspace not found" },
|
|
277
|
+
500: { description: "Failed to update setting" }
|
|
278
|
+
}
|
|
279
|
+
}), async (c) => {
|
|
280
|
+
const { teamId } = c.req.valid("param");
|
|
281
|
+
const { shouldAllowJoinFromWorkspace } = c.req.valid("json");
|
|
282
|
+
const sessionTenantId = c.get("tenantId");
|
|
283
|
+
if (!sessionTenantId) return c.json({ error: "Unauthorized" }, 401);
|
|
284
|
+
const workspace = await findWorkAppSlackWorkspaceByTeamId(runDbClient_default)(sessionTenantId, teamId);
|
|
285
|
+
if (!workspace) return c.json({ error: "Workspace not found" }, 404);
|
|
286
|
+
try {
|
|
287
|
+
if (!await updateWorkAppSlackWorkspace(runDbClient_default)(workspace.id, { shouldAllowJoinFromWorkspace })) {
|
|
288
|
+
logger.error({
|
|
289
|
+
teamId,
|
|
290
|
+
shouldAllowJoinFromWorkspace
|
|
291
|
+
}, "Failed to update join from workspace setting");
|
|
292
|
+
return c.json({ error: "Failed to update setting" }, 500);
|
|
293
|
+
}
|
|
294
|
+
logger.info({
|
|
295
|
+
teamId,
|
|
296
|
+
shouldAllowJoinFromWorkspace,
|
|
297
|
+
workspaceId: workspace.id
|
|
298
|
+
}, "Updated workspace join from workspace settings");
|
|
299
|
+
return c.json({ success: true });
|
|
300
|
+
} catch (error) {
|
|
301
|
+
logger.error({
|
|
302
|
+
teamId,
|
|
303
|
+
shouldAllowJoinFromWorkspace,
|
|
304
|
+
error
|
|
305
|
+
}, "Failed to update join from workspace setting");
|
|
306
|
+
return c.json({ error: "Failed to update setting" }, 500);
|
|
307
|
+
}
|
|
308
|
+
});
|
|
225
309
|
app.openapi(createProtectedRoute({
|
|
226
310
|
method: "delete",
|
|
227
311
|
path: "/{teamId}",
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
1
2
|
import * as slack_block_builder0 from "slack-block-builder";
|
|
2
3
|
|
|
3
4
|
//#region src/slack/services/blocks/index.d.ts
|
|
@@ -63,6 +64,85 @@ interface AgentConfigSources {
|
|
|
63
64
|
} | null;
|
|
64
65
|
}
|
|
65
66
|
declare function createStatusMessage(email: string, linkedAt: string, dashboardUrl: string, agentConfigs: AgentConfigSources): Readonly<slack_block_builder0.SlackMessageDto>;
|
|
67
|
+
interface ToolApprovalButtonValue {
|
|
68
|
+
toolCallId: string;
|
|
69
|
+
conversationId: string;
|
|
70
|
+
projectId: string;
|
|
71
|
+
agentId: string;
|
|
72
|
+
slackUserId: string;
|
|
73
|
+
channel: string;
|
|
74
|
+
threadTs: string;
|
|
75
|
+
toolName: string;
|
|
76
|
+
}
|
|
77
|
+
declare const ToolApprovalButtonValueSchema: z.ZodObject<{
|
|
78
|
+
toolCallId: z.ZodString;
|
|
79
|
+
conversationId: z.ZodString;
|
|
80
|
+
projectId: z.ZodString;
|
|
81
|
+
agentId: z.ZodString;
|
|
82
|
+
slackUserId: z.ZodString;
|
|
83
|
+
channel: z.ZodString;
|
|
84
|
+
threadTs: z.ZodString;
|
|
85
|
+
toolName: z.ZodString;
|
|
86
|
+
}, z.core.$strip>;
|
|
87
|
+
declare function buildToolApprovalBlocks(params: {
|
|
88
|
+
toolName: string;
|
|
89
|
+
input?: Record<string, unknown>;
|
|
90
|
+
buttonValue: string;
|
|
91
|
+
}): any[];
|
|
92
|
+
declare function buildToolApprovalDoneBlocks(params: {
|
|
93
|
+
toolName: string;
|
|
94
|
+
approved: boolean;
|
|
95
|
+
actorUserId: string;
|
|
96
|
+
}): {
|
|
97
|
+
type: string;
|
|
98
|
+
elements: {
|
|
99
|
+
type: string;
|
|
100
|
+
text: string;
|
|
101
|
+
}[];
|
|
102
|
+
}[];
|
|
103
|
+
declare function buildToolApprovalExpiredBlocks(params: {
|
|
104
|
+
toolName: string;
|
|
105
|
+
}): {
|
|
106
|
+
type: string;
|
|
107
|
+
elements: {
|
|
108
|
+
type: string;
|
|
109
|
+
text: string;
|
|
110
|
+
}[];
|
|
111
|
+
}[];
|
|
112
|
+
declare function buildToolOutputErrorBlock(toolName: string, errorText: string): {
|
|
113
|
+
type: "context";
|
|
114
|
+
elements: {
|
|
115
|
+
type: "mrkdwn";
|
|
116
|
+
text: string;
|
|
117
|
+
}[];
|
|
118
|
+
};
|
|
119
|
+
declare function buildSummaryBreadcrumbBlock(labels: string[]): {
|
|
120
|
+
type: "context";
|
|
121
|
+
elements: {
|
|
122
|
+
type: "mrkdwn";
|
|
123
|
+
text: string;
|
|
124
|
+
}[];
|
|
125
|
+
};
|
|
126
|
+
declare function buildDataComponentBlocks(component: {
|
|
127
|
+
id: string;
|
|
128
|
+
data: Record<string, unknown>;
|
|
129
|
+
}): {
|
|
130
|
+
blocks: any[];
|
|
131
|
+
overflowJson?: string;
|
|
132
|
+
componentType?: string;
|
|
133
|
+
};
|
|
134
|
+
declare function buildDataArtifactBlocks(artifact: {
|
|
135
|
+
data: Record<string, unknown>;
|
|
136
|
+
}): {
|
|
137
|
+
blocks: any[];
|
|
138
|
+
overflowContent?: string;
|
|
139
|
+
artifactName?: string;
|
|
140
|
+
};
|
|
141
|
+
declare function buildCitationsBlock(citations: Array<{
|
|
142
|
+
title?: string;
|
|
143
|
+
url?: string;
|
|
144
|
+
}>): any[];
|
|
66
145
|
declare function createJwtLinkMessage(linkUrl: string, expiresInMinutes: number): Readonly<slack_block_builder0.SlackMessageDto>;
|
|
146
|
+
declare function createCreateInkeepAccountMessage(acceptUrl: string, expiresInMinutes: number): Readonly<slack_block_builder0.SlackMessageDto>;
|
|
67
147
|
//#endregion
|
|
68
|
-
export { AgentConfigSources, ContextBlockParams, FollowUpButtonParams, buildConversationResponseBlocks, buildFollowUpButton, createAlreadyLinkedMessage, createContextBlock, createErrorMessage, createJwtLinkMessage, createNotLinkedMessage, createStatusMessage, createUnlinkSuccessMessage, createUpdatedHelpMessage };
|
|
148
|
+
export { AgentConfigSources, ContextBlockParams, FollowUpButtonParams, ToolApprovalButtonValue, ToolApprovalButtonValueSchema, buildCitationsBlock, buildConversationResponseBlocks, buildDataArtifactBlocks, buildDataComponentBlocks, buildFollowUpButton, buildSummaryBreadcrumbBlock, buildToolApprovalBlocks, buildToolApprovalDoneBlocks, buildToolApprovalExpiredBlocks, buildToolOutputErrorBlock, createAlreadyLinkedMessage, createContextBlock, createCreateInkeepAccountMessage, createErrorMessage, createJwtLinkMessage, createNotLinkedMessage, createStatusMessage, createUnlinkSuccessMessage, createUpdatedHelpMessage };
|