@inkeep/agents-work-apps 0.0.0-dev-20260225215837 → 0.0.0-dev-20260225221013

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,11 +1,11 @@
1
1
  import { Hono } from "hono";
2
- import * as hono_types3 from "hono/types";
2
+ import * as hono_types5 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_types3.BlankSchema, "/">;
9
+ }, hono_types5.BlankSchema, "/">;
10
10
  //#endregion
11
11
  export { app as default };
@@ -1,7 +1,7 @@
1
1
  import { Hono } from "hono";
2
- import * as hono_types8 from "hono/types";
2
+ import * as hono_types3 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_types3.BlankEnv, hono_types3.BlankSchema, "/">;
6
6
  //#endregion
7
7
  export { app as default };
@@ -1,7 +1,7 @@
1
1
  import { env } from "../../env.js";
2
2
  import { getLogger } from "../../logger.js";
3
- import runDbClient_default from "../../db/runDbClient.js";
4
3
  import { getStateSigningSecret, isStateSigningConfigured } from "../config.js";
4
+ import runDbClient_default from "../../db/runDbClient.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";
@@ -1,7 +1,7 @@
1
1
  import { Hono } from "hono";
2
- import * as hono_types4 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_types4.BlankEnv, hono_types4.BlankSchema, "/">;
5
+ declare const app: Hono<hono_types6.BlankEnv, hono_types6.BlankSchema, "/">;
6
6
  //#endregion
7
7
  export { app as default };
@@ -1,6 +1,6 @@
1
1
  import { getLogger } from "../../logger.js";
2
- import runDbClient_default from "../../db/runDbClient.js";
3
2
  import { isGitHubAppConfigured } from "../config.js";
3
+ import runDbClient_default from "../../db/runDbClient.js";
4
4
  import { generateInstallationAccessToken, lookupInstallationForRepo } from "../installation.js";
5
5
  import { validateOidcToken } from "../oidcToken.js";
6
6
  import { checkProjectRepositoryAccess, getInstallationByGitHubId } from "@inkeep/agents-core";
@@ -1,5 +1,5 @@
1
1
  import { Hono } from "hono";
2
- import * as hono_types6 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_types6.BlankEnv, hono_types6.BlankSchema, "/">;
10
+ declare const app: Hono<hono_types8.BlankEnv, hono_types8.BlankSchema, "/">;
11
11
  //#endregion
12
12
  export { WebhookVerificationResult, app as default, verifyWebhookSignature };
@@ -1,6 +1,6 @@
1
1
  import { getLogger } from "../../logger.js";
2
- import runDbClient_default from "../../db/runDbClient.js";
3
2
  import { getWebhookSecret, isWebhookConfigured } from "../config.js";
3
+ import runDbClient_default from "../../db/runDbClient.js";
4
4
  import { addRepositories, deleteInstallation, getInstallationByGitHubId, removeRepositories, updateInstallationStatusByGitHubId } from "@inkeep/agents-core";
5
5
  import { Hono } from "hono";
6
6
  import { createHmac, timingSafeEqual } from "node:crypto";
@@ -1,3 +1,4 @@
1
+ import { escapeSlackLinkText, escapeSlackMrkdwn } from "../events/utils.js";
1
2
  import { SlackStrings } from "../../i18n/strings.js";
2
3
  import { z } from "zod";
3
4
  import { Blocks, Elements, Md, Message } from "slack-block-builder";
@@ -12,7 +13,7 @@ function createContextBlock(params) {
12
13
  type: "context",
13
14
  elements: [{
14
15
  type: "mrkdwn",
15
- text: SlackStrings.context.poweredBy(agentName)
16
+ text: SlackStrings.context.poweredBy(escapeSlackMrkdwn(agentName))
16
17
  }]
17
18
  };
18
19
  }
@@ -73,15 +74,16 @@ function buildToolApprovalBlocks(params) {
73
74
  type: "section",
74
75
  text: {
75
76
  type: "mrkdwn",
76
- text: `*Approval required - \`${toolName}\`*`
77
+ text: `*Approval required - \`${escapeSlackMrkdwn(toolName)}\`*`
77
78
  }
78
79
  }];
79
80
  if (input && Object.keys(input).length > 0) {
80
81
  const fields = Object.entries(input).slice(0, 10).map(([k, v]) => {
81
82
  const val = typeof v === "object" ? JSON.stringify(v) : String(v ?? "");
83
+ const truncated = val.length > 80 ? `${val.slice(0, 80)}…` : val;
82
84
  return {
83
85
  type: "mrkdwn",
84
- text: `*${k}:*\n${val.length > 80 ? `${val.slice(0, 80)}…` : val}`
86
+ text: `*${escapeSlackMrkdwn(k)}:*\n${escapeSlackMrkdwn(truncated)}`
85
87
  };
86
88
  });
87
89
  blocks.push({
@@ -121,7 +123,7 @@ function buildToolApprovalDoneBlocks(params) {
121
123
  type: "context",
122
124
  elements: [{
123
125
  type: "mrkdwn",
124
- text: approved ? `✅ Approved \`${toolName}\` · <@${actorUserId}>` : `❌ Denied \`${toolName}\` · <@${actorUserId}>`
126
+ text: approved ? `✅ Approved \`${escapeSlackMrkdwn(toolName)}\` · <@${actorUserId}>` : `❌ Denied \`${escapeSlackMrkdwn(toolName)}\` · <@${actorUserId}>`
125
127
  }]
126
128
  }];
127
129
  }
@@ -130,16 +132,17 @@ function buildToolApprovalExpiredBlocks(params) {
130
132
  type: "context",
131
133
  elements: [{
132
134
  type: "mrkdwn",
133
- text: `⏱️ Expired · \`${params.toolName}\``
135
+ text: `⏱️ Expired · \`${escapeSlackMrkdwn(params.toolName)}\``
134
136
  }]
135
137
  }];
136
138
  }
137
139
  function buildToolOutputErrorBlock(toolName, errorText) {
140
+ const truncated = errorText.length > 100 ? `${errorText.slice(0, 100)}…` : errorText;
138
141
  return {
139
142
  type: "context",
140
143
  elements: [{
141
144
  type: "mrkdwn",
142
- text: `⚠️ *${toolName}* · failed: ${errorText.length > 100 ? `${errorText.slice(0, 100)}…` : errorText}`
145
+ text: `⚠️ *${escapeSlackMrkdwn(toolName)}* · failed: ${escapeSlackMrkdwn(truncated)}`
143
146
  }]
144
147
  };
145
148
  }
@@ -148,7 +151,7 @@ function buildSummaryBreadcrumbBlock(labels) {
148
151
  type: "context",
149
152
  elements: [{
150
153
  type: "mrkdwn",
151
- text: labels.join(" → ")
154
+ text: labels.map(escapeSlackMrkdwn).join(" → ")
152
155
  }]
153
156
  };
154
157
  }
@@ -179,9 +182,10 @@ function buildDataComponentBlocks(component) {
179
182
  if (Object.keys(payload).length > 0) if (isFlatRecord(payload)) {
180
183
  const fields = Object.entries(payload).slice(0, 10).map(([k, v]) => {
181
184
  const val = String(v ?? "");
185
+ const truncated = val.length > 80 ? `${val.slice(0, 80)}…` : val;
182
186
  return {
183
187
  type: "mrkdwn",
184
- text: `*${k}*\n${val.length > 80 ? `${val.slice(0, 80)}…` : val}`
188
+ text: `*${escapeSlackMrkdwn(k)}*\n${escapeSlackMrkdwn(truncated)}`
185
189
  };
186
190
  });
187
191
  blocks.push({
@@ -203,7 +207,7 @@ function buildDataComponentBlocks(component) {
203
207
  type: "context",
204
208
  elements: [{
205
209
  type: "mrkdwn",
206
- text: `data component · type: ${componentType}`
210
+ text: `data component · type: ${escapeSlackMrkdwn(componentType)}`
207
211
  }]
208
212
  });
209
213
  return {
@@ -219,7 +223,8 @@ function buildDataArtifactBlocks(artifact) {
219
223
  const MAX_SOURCES = 10;
220
224
  const lines = sourcesArray.slice(0, MAX_SOURCES).map((s) => {
221
225
  const url = s.url || s.href;
222
- const title = s.title || s.name || url;
226
+ const rawTitle = s.title || s.name || url;
227
+ const title = rawTitle ? escapeSlackLinkText(rawTitle) : rawTitle;
223
228
  return url ? `• <${url}|${title}>` : null;
224
229
  }).filter((l) => l !== null);
225
230
  if (lines.length > 0) {
@@ -247,7 +252,7 @@ function buildDataArtifactBlocks(artifact) {
247
252
  type: "context",
248
253
  elements: [{
249
254
  type: "mrkdwn",
250
- text: `type: ${artifactType}`
255
+ text: `type: ${escapeSlackMrkdwn(artifactType)}`
251
256
  }]
252
257
  });
253
258
  let overflowContent;
@@ -267,10 +272,11 @@ function buildDataArtifactBlocks(artifact) {
267
272
  }
268
273
  function buildCitationsBlock(citations) {
269
274
  const MAX_CITATIONS = 10;
270
- const lines = citations.slice(0, MAX_CITATIONS).map((c) => {
275
+ const lines = citations.slice(0, MAX_CITATIONS).map((c, i) => {
271
276
  const url = c.url;
272
- const title = c.title || url;
273
- return url ? `• <${url}|${title}>` : null;
277
+ const rawTitle = c.title || url;
278
+ const title = rawTitle ? escapeSlackLinkText(rawTitle) : rawTitle;
279
+ return url ? `<${url}|[${i + 1}] ${title}>` : null;
274
280
  }).filter((l) => l !== null);
275
281
  if (lines.length === 0) return [];
276
282
  const suffix = citations.length > MAX_CITATIONS ? `\n_and ${citations.length - MAX_CITATIONS} more_` : "";
@@ -349,10 +349,14 @@ async function streamAgentResponse(params) {
349
349
  const artifactData = data.data;
350
350
  if (typeof artifactData.type === "string" && artifactData.type.toLowerCase() === "citation") {
351
351
  const summary = artifactData.artifactSummary;
352
- if (summary?.url && !citations.some((c) => c.url === summary.url)) citations.push({
353
- title: summary.title,
354
- url: summary.url
355
- });
352
+ if (summary?.url && !citations.some((c) => c.url === summary.url)) {
353
+ citations.push({
354
+ title: summary.title,
355
+ url: summary.url
356
+ });
357
+ const citationIndex = citations.length;
358
+ if (fullText.length > 0) await withTimeout(streamer.append({ markdown_text: `<${summary.url}|[${citationIndex}]>` }), CHATSTREAM_OP_TIMEOUT_MS, "streamer.append").catch((e) => logger.warn({ error: e }, "Failed to append inline citation"));
359
+ }
356
360
  } else if (richMessageCount < MAX_RICH_MESSAGES) {
357
361
  const { blocks, overflowContent, artifactName } = buildDataArtifactBlocks({ data: artifactData });
358
362
  if (overflowContent) {
@@ -8,12 +8,12 @@ import { AgentOption } from "../modals.js";
8
8
  * Called on every @mention and /inkeep command — caching avoids redundant DB queries.
9
9
  */
10
10
  declare function findCachedUserMapping(tenantId: string, slackUserId: string, teamId: string, clientId?: string): Promise<{
11
- slackUserId: string;
12
- id: string;
13
11
  createdAt: string;
14
12
  updatedAt: string;
13
+ id: string;
15
14
  tenantId: string;
16
15
  clientId: string;
16
+ slackUserId: string;
17
17
  slackTeamId: string;
18
18
  slackEnterpriseId: string | null;
19
19
  inkeepUserId: string;
@@ -22,6 +22,18 @@ declare function findCachedUserMapping(tenantId: string, slackUserId: string, te
22
22
  linkedAt: string;
23
23
  lastUsedAt: string | null;
24
24
  } | null>;
25
+ /**
26
+ * Escape special characters in Slack mrkdwn link display text.
27
+ * In Slack's <url|text> format, `>` terminates the link, `<` opens a new one,
28
+ * and `&` begins an HTML entity — all must be escaped.
29
+ */
30
+ declare function escapeSlackLinkText(text: string): string;
31
+ /**
32
+ * Escape special characters in Slack mrkdwn text.
33
+ * In Slack's mrkdwn, `&`, `<`, and `>` are treated as HTML entities/tags
34
+ * and must be escaped in all dynamic mrkdwn text fields.
35
+ */
36
+ declare function escapeSlackMrkdwn(text: string): string;
25
37
  /**
26
38
  * Convert standard Markdown to Slack's mrkdwn format
27
39
  *
@@ -167,4 +179,4 @@ declare function formatChannelContext(channelInfo: {
167
179
  name?: string;
168
180
  } | null): string;
169
181
  //#endregion
170
- export { ProjectOption, SlackAttachment, SlackErrorType, checkIfBotThread, classifyError, extractApiErrorMessage, fetchAgentsForProject, fetchProjectsForTenant, findCachedUserMapping, formatAttachments, formatChannelContext, formatChannelLabel, generateSlackConversationId, getChannelAgentConfig, getThreadContext, getUserFriendlyErrorMessage, getWorkspaceDefaultAgent, markdownToMrkdwn, resolveChannelAgentConfig, sendResponseUrlMessage, timedOp };
182
+ export { ProjectOption, SlackAttachment, SlackErrorType, checkIfBotThread, classifyError, escapeSlackLinkText, escapeSlackMrkdwn, extractApiErrorMessage, fetchAgentsForProject, fetchProjectsForTenant, findCachedUserMapping, formatAttachments, formatChannelContext, formatChannelLabel, generateSlackConversationId, getChannelAgentConfig, getThreadContext, getUserFriendlyErrorMessage, getWorkspaceDefaultAgent, markdownToMrkdwn, resolveChannelAgentConfig, sendResponseUrlMessage, timedOp };
@@ -50,6 +50,22 @@ async function findCachedUserMapping(tenantId, slackUserId, teamId, clientId = "
50
50
  return mapping;
51
51
  }
52
52
  /**
53
+ * Escape special characters in Slack mrkdwn link display text.
54
+ * In Slack's <url|text> format, `>` terminates the link, `<` opens a new one,
55
+ * and `&` begins an HTML entity — all must be escaped.
56
+ */
57
+ function escapeSlackLinkText(text) {
58
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
59
+ }
60
+ /**
61
+ * Escape special characters in Slack mrkdwn text.
62
+ * In Slack's mrkdwn, `&`, `<`, and `>` are treated as HTML entities/tags
63
+ * and must be escaped in all dynamic mrkdwn text fields.
64
+ */
65
+ function escapeSlackMrkdwn(text) {
66
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
67
+ }
68
+ /**
53
69
  * Convert standard Markdown to Slack's mrkdwn format
54
70
  *
55
71
  * Key differences:
@@ -63,7 +79,7 @@ function markdownToMrkdwn(markdown) {
63
79
  if (!markdown) return markdown;
64
80
  let result = markdown;
65
81
  result = result.replace(/^#{1,6}\s+(.+)$/gm, "*$1*");
66
- result = result.replace(/\[([^\]]+)\]\(([^)]+)\)/g, "<$2|$1>");
82
+ result = result.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_, text, url) => `<${url}|${escapeSlackLinkText(text)}>`);
67
83
  result = result.replace(/\*\*([^*]+)\*\*/g, "*$1*");
68
84
  result = result.replace(/__([^_]+)__/g, "*$1*");
69
85
  result = result.replace(/~~([^~]+)~~/g, "~$1~");
@@ -426,4 +442,4 @@ function formatChannelContext(channelInfo) {
426
442
  }
427
443
 
428
444
  //#endregion
429
- export { SlackErrorType, checkIfBotThread, classifyError, extractApiErrorMessage, fetchAgentsForProject, fetchProjectsForTenant, findCachedUserMapping, formatAttachments, formatChannelContext, formatChannelLabel, generateSlackConversationId, getChannelAgentConfig, getThreadContext, getUserFriendlyErrorMessage, getWorkspaceDefaultAgent, markdownToMrkdwn, resolveChannelAgentConfig, sendResponseUrlMessage, timedOp };
445
+ export { SlackErrorType, checkIfBotThread, classifyError, escapeSlackLinkText, escapeSlackMrkdwn, extractApiErrorMessage, fetchAgentsForProject, fetchProjectsForTenant, findCachedUserMapping, formatAttachments, formatChannelContext, formatChannelLabel, generateSlackConversationId, getChannelAgentConfig, getThreadContext, getUserFriendlyErrorMessage, getWorkspaceDefaultAgent, markdownToMrkdwn, resolveChannelAgentConfig, sendResponseUrlMessage, timedOp };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inkeep/agents-work-apps",
3
- "version": "0.0.0-dev-20260225215837",
3
+ "version": "0.0.0-dev-20260225221013",
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.0.0-dev-20260225215837"
36
+ "@inkeep/agents-core": "0.0.0-dev-20260225221013"
37
37
  },
38
38
  "peerDependencies": {
39
39
  "@hono/zod-openapi": "^1.1.5",