@inkeep/agents-work-apps 0.48.0 → 0.48.2
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/index.d.ts +3 -3
- package/dist/github/mcp/auth.d.ts +2 -2
- package/dist/github/mcp/index.d.ts +2 -2
- package/dist/github/mcp/schemas.d.ts +1 -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/routes/events.js +13 -0
- package/dist/slack/services/client.js +2 -2
- package/dist/slack/services/events/app-mention.js +13 -6
- package/dist/slack/services/events/streaming.js +64 -9
- package/dist/slack/tracer.d.ts +3 -1
- package/dist/slack/tracer.js +3 -1
- package/package.json +2 -2
package/dist/github/index.d.ts
CHANGED
|
@@ -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
|
|
7
|
+
import * as hono_types0 from "hono/types";
|
|
8
8
|
|
|
9
9
|
//#region src/github/index.d.ts
|
|
10
|
-
declare function createGithubRoutes(): Hono<
|
|
11
|
-
declare const githubRoutes: Hono<
|
|
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
|
|
1
|
+
import * as hono0 from "hono";
|
|
2
2
|
|
|
3
3
|
//#region src/github/mcp/auth.d.ts
|
|
4
|
-
declare const githubMcpAuth: () =>
|
|
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
|
|
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
|
-
},
|
|
9
|
+
}, hono_types3.BlankSchema, "/">;
|
|
10
10
|
//#endregion
|
|
11
11
|
export { app as default };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
|
-
import * as
|
|
2
|
+
import * as hono_types4 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_types4.BlankEnv, hono_types4.BlankSchema, "/">;
|
|
6
6
|
//#endregion
|
|
7
7
|
export { app as default };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
|
-
import * as
|
|
2
|
+
import * as hono_types6 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_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
|
|
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<
|
|
10
|
+
declare const app: Hono<hono_types8.BlankEnv, hono_types8.BlankSchema, "/">;
|
|
11
11
|
//#endregion
|
|
12
12
|
export { WebhookVerificationResult, app as default, verifyWebhookSignature };
|
|
@@ -63,6 +63,19 @@ app.post("/commands", async (c) => {
|
|
|
63
63
|
return c.json(response);
|
|
64
64
|
});
|
|
65
65
|
app.post("/events", async (c) => {
|
|
66
|
+
const retryNum = c.req.header("x-slack-retry-num");
|
|
67
|
+
const retryReason = c.req.header("x-slack-retry-reason");
|
|
68
|
+
if (retryNum) return tracer.startActiveSpan(`${SLACK_SPAN_NAMES.WEBHOOK} retry`, (span) => {
|
|
69
|
+
span.setAttribute(SLACK_SPAN_KEYS.OUTCOME, "ignored_slack_retry");
|
|
70
|
+
span.setAttribute("slack.retry_num", retryNum);
|
|
71
|
+
if (retryReason) span.setAttribute("slack.retry_reason", retryReason);
|
|
72
|
+
logger.info({
|
|
73
|
+
retryNum,
|
|
74
|
+
retryReason
|
|
75
|
+
}, "Acknowledging Slack retry without re-processing");
|
|
76
|
+
span.end();
|
|
77
|
+
return c.json({ ok: true });
|
|
78
|
+
});
|
|
66
79
|
return tracer.startActiveSpan(SLACK_SPAN_NAMES.WEBHOOK, async (span) => {
|
|
67
80
|
let outcome = "ignored_unknown_event";
|
|
68
81
|
try {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getLogger } from "../../logger.js";
|
|
2
|
-
import { WebClient
|
|
2
|
+
import { WebClient } from "@slack/web-api";
|
|
3
3
|
|
|
4
4
|
//#region src/slack/services/client.ts
|
|
5
5
|
/**
|
|
@@ -29,7 +29,7 @@ async function paginateSlack({ fetchPage, extractItems, getNextCursor, limit })
|
|
|
29
29
|
* @returns Configured Slack WebClient instance
|
|
30
30
|
*/
|
|
31
31
|
function getSlackClient(token) {
|
|
32
|
-
return new WebClient(token
|
|
32
|
+
return new WebClient(token);
|
|
33
33
|
}
|
|
34
34
|
/**
|
|
35
35
|
* Fetch user profile information from Slack.
|
|
@@ -71,6 +71,7 @@ async function handleAppMention(params) {
|
|
|
71
71
|
const replyThreadTs = threadTs || messageTs;
|
|
72
72
|
const isInThread = Boolean(threadTs && threadTs !== messageTs);
|
|
73
73
|
const hasQuery = Boolean(text && text.trim().length > 0);
|
|
74
|
+
let thinkingMessageTs;
|
|
74
75
|
try {
|
|
75
76
|
const [agentConfig, existingLink] = await Promise.all([resolveChannelAgentConfig(teamId, channel, workspaceConnection), findCachedUserMapping(tenantId, slackUserId, teamId)]);
|
|
76
77
|
if (!agentConfig) {
|
|
@@ -167,11 +168,11 @@ _Using: ${agentDisplayName}_`
|
|
|
167
168
|
slackTeamId: teamId,
|
|
168
169
|
slackUserId
|
|
169
170
|
});
|
|
170
|
-
|
|
171
|
+
thinkingMessageTs = (await slackClient.chat.postMessage({
|
|
171
172
|
channel,
|
|
172
173
|
thread_ts: threadTs,
|
|
173
174
|
text: `_${agentDisplayName} is reading this thread..._`
|
|
174
|
-
});
|
|
175
|
+
})).ts || void 0;
|
|
175
176
|
const conversationId$1 = generateSlackConversationId({
|
|
176
177
|
teamId,
|
|
177
178
|
threadTs,
|
|
@@ -203,7 +204,7 @@ Respond naturally as if you're joining the conversation to help.`;
|
|
|
203
204
|
slackClient,
|
|
204
205
|
channel,
|
|
205
206
|
threadTs,
|
|
206
|
-
thinkingMessageTs:
|
|
207
|
+
thinkingMessageTs: thinkingMessageTs || "",
|
|
207
208
|
slackUserId,
|
|
208
209
|
teamId,
|
|
209
210
|
jwtToken: slackUserToken$1,
|
|
@@ -227,11 +228,11 @@ Respond naturally as if you're joining the conversation to help.`;
|
|
|
227
228
|
slackTeamId: teamId,
|
|
228
229
|
slackUserId
|
|
229
230
|
});
|
|
230
|
-
|
|
231
|
+
thinkingMessageTs = (await slackClient.chat.postMessage({
|
|
231
232
|
channel,
|
|
232
233
|
thread_ts: replyThreadTs,
|
|
233
234
|
text: `_${agentDisplayName} is preparing a response..._`
|
|
234
|
-
});
|
|
235
|
+
})).ts || void 0;
|
|
235
236
|
const conversationId = generateSlackConversationId({
|
|
236
237
|
teamId,
|
|
237
238
|
threadTs: replyThreadTs,
|
|
@@ -249,7 +250,7 @@ Respond naturally as if you're joining the conversation to help.`;
|
|
|
249
250
|
slackClient,
|
|
250
251
|
channel,
|
|
251
252
|
threadTs: replyThreadTs,
|
|
252
|
-
thinkingMessageTs:
|
|
253
|
+
thinkingMessageTs: thinkingMessageTs || "",
|
|
253
254
|
slackUserId,
|
|
254
255
|
teamId,
|
|
255
256
|
jwtToken: slackUserToken,
|
|
@@ -268,6 +269,12 @@ Respond naturally as if you're joining the conversation to help.`;
|
|
|
268
269
|
teamId
|
|
269
270
|
}, "Failed in app mention handler");
|
|
270
271
|
if (error instanceof Error) setSpanWithError(span, error);
|
|
272
|
+
if (thinkingMessageTs) try {
|
|
273
|
+
await slackClient.chat.delete({
|
|
274
|
+
channel,
|
|
275
|
+
ts: thinkingMessageTs
|
|
276
|
+
});
|
|
277
|
+
} catch {}
|
|
271
278
|
const userMessage = getUserFriendlyErrorMessage(classifyError(error));
|
|
272
279
|
try {
|
|
273
280
|
await slackClient.chat.postEphemeral({
|
|
@@ -14,6 +14,8 @@ import { SLACK_SPAN_KEYS, SLACK_SPAN_NAMES, setSpanWithError, tracer } from "../
|
|
|
14
14
|
const logger = getLogger("slack-streaming");
|
|
15
15
|
const STREAM_TIMEOUT_MS = 12e4;
|
|
16
16
|
const CHATSTREAM_OP_TIMEOUT_MS = 1e4;
|
|
17
|
+
/** Shorter timeout for best-effort cleanup in error paths to bound total error handling time. */
|
|
18
|
+
const CLEANUP_TIMEOUT_MS = 3e3;
|
|
17
19
|
/**
|
|
18
20
|
* Wrap a promise with a timeout to prevent indefinite blocking on Slack API calls.
|
|
19
21
|
*/
|
|
@@ -78,12 +80,12 @@ async function streamAgentResponse(params) {
|
|
|
78
80
|
} catch (fetchError) {
|
|
79
81
|
clearTimeout(timeoutId);
|
|
80
82
|
if (fetchError.name === "AbortError") {
|
|
81
|
-
const errorType = SlackErrorType.TIMEOUT;
|
|
82
|
-
const errorMessage = getUserFriendlyErrorMessage(errorType, agentName);
|
|
83
|
+
const errorType$1 = SlackErrorType.TIMEOUT;
|
|
84
|
+
const errorMessage$1 = getUserFriendlyErrorMessage(errorType$1, agentName);
|
|
83
85
|
await slackClient.chat.postMessage({
|
|
84
86
|
channel,
|
|
85
87
|
thread_ts: threadTs,
|
|
86
|
-
text: errorMessage
|
|
88
|
+
text: errorMessage$1
|
|
87
89
|
});
|
|
88
90
|
if (thinkingMessageTs) try {
|
|
89
91
|
await slackClient.chat.delete({
|
|
@@ -94,13 +96,30 @@ async function streamAgentResponse(params) {
|
|
|
94
96
|
span.end();
|
|
95
97
|
return {
|
|
96
98
|
success: false,
|
|
97
|
-
errorType,
|
|
98
|
-
errorMessage
|
|
99
|
+
errorType: errorType$1,
|
|
100
|
+
errorMessage: errorMessage$1
|
|
99
101
|
};
|
|
100
102
|
}
|
|
103
|
+
const errorType = classifyError(fetchError);
|
|
104
|
+
const errorMessage = getUserFriendlyErrorMessage(errorType, agentName);
|
|
105
|
+
await slackClient.chat.postMessage({
|
|
106
|
+
channel,
|
|
107
|
+
thread_ts: threadTs,
|
|
108
|
+
text: errorMessage
|
|
109
|
+
}).catch((e) => logger.warn({ error: e }, "Failed to send fetch error notification"));
|
|
110
|
+
if (thinkingMessageTs) try {
|
|
111
|
+
await slackClient.chat.delete({
|
|
112
|
+
channel,
|
|
113
|
+
ts: thinkingMessageTs
|
|
114
|
+
});
|
|
115
|
+
} catch {}
|
|
101
116
|
if (fetchError instanceof Error) setSpanWithError(span, fetchError);
|
|
102
117
|
span.end();
|
|
103
|
-
|
|
118
|
+
return {
|
|
119
|
+
success: false,
|
|
120
|
+
errorType,
|
|
121
|
+
errorMessage
|
|
122
|
+
};
|
|
104
123
|
}
|
|
105
124
|
if (!response.ok) {
|
|
106
125
|
clearTimeout(timeoutId);
|
|
@@ -167,6 +186,7 @@ async function streamAgentResponse(params) {
|
|
|
167
186
|
thread_ts: threadTs
|
|
168
187
|
});
|
|
169
188
|
try {
|
|
189
|
+
let agentCompleted = false;
|
|
170
190
|
while (true) {
|
|
171
191
|
const { done, value } = await reader.read();
|
|
172
192
|
if (done) break;
|
|
@@ -179,7 +199,13 @@ async function streamAgentResponse(params) {
|
|
|
179
199
|
if (!jsonStr || jsonStr === "[DONE]") continue;
|
|
180
200
|
try {
|
|
181
201
|
const data = JSON.parse(jsonStr);
|
|
182
|
-
if (data.type === "data-operation")
|
|
202
|
+
if (data.type === "data-operation") {
|
|
203
|
+
if (data.data?.type === "completion") {
|
|
204
|
+
agentCompleted = true;
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
183
209
|
if (data.type === "text-start" || data.type === "text-end") continue;
|
|
184
210
|
if (data.type === "text-delta" && data.delta) {
|
|
185
211
|
fullText += data.delta;
|
|
@@ -194,10 +220,21 @@ async function streamAgentResponse(params) {
|
|
|
194
220
|
}
|
|
195
221
|
} catch {}
|
|
196
222
|
}
|
|
223
|
+
if (agentCompleted) break;
|
|
197
224
|
}
|
|
198
225
|
clearTimeout(timeoutId);
|
|
199
226
|
const contextBlock = createContextBlock({ agentName });
|
|
200
|
-
|
|
227
|
+
try {
|
|
228
|
+
await withTimeout(streamer.stop({ blocks: [contextBlock] }), CHATSTREAM_OP_TIMEOUT_MS, "streamer.stop");
|
|
229
|
+
} catch (stopError) {
|
|
230
|
+
span.setAttribute(SLACK_SPAN_KEYS.STREAM_FINALIZATION_FAILED, true);
|
|
231
|
+
logger.warn({
|
|
232
|
+
stopError,
|
|
233
|
+
channel,
|
|
234
|
+
threadTs,
|
|
235
|
+
responseLength: fullText.length
|
|
236
|
+
}, "Failed to finalize chatStream — content was already delivered");
|
|
237
|
+
}
|
|
201
238
|
if (thinkingMessageTs) try {
|
|
202
239
|
await slackClient.chat.delete({
|
|
203
240
|
channel,
|
|
@@ -218,8 +255,26 @@ async function streamAgentResponse(params) {
|
|
|
218
255
|
} catch (streamError) {
|
|
219
256
|
clearTimeout(timeoutId);
|
|
220
257
|
if (streamError instanceof Error) setSpanWithError(span, streamError);
|
|
258
|
+
if (fullText.length > 0) {
|
|
259
|
+
span.setAttribute(SLACK_SPAN_KEYS.CONTENT_ALREADY_DELIVERED, true);
|
|
260
|
+
logger.warn({
|
|
261
|
+
streamError,
|
|
262
|
+
channel,
|
|
263
|
+
threadTs,
|
|
264
|
+
responseLength: fullText.length
|
|
265
|
+
}, "Error during Slack streaming after content was already delivered — suppressing user-facing error");
|
|
266
|
+
await withTimeout(streamer.stop(), CLEANUP_TIMEOUT_MS, "streamer.stop-cleanup").catch((e) => logger.warn({ error: e }, "Failed to stop streamer during error cleanup"));
|
|
267
|
+
if (thinkingMessageTs) try {
|
|
268
|
+
await slackClient.chat.delete({
|
|
269
|
+
channel,
|
|
270
|
+
ts: thinkingMessageTs
|
|
271
|
+
});
|
|
272
|
+
} catch {}
|
|
273
|
+
span.end();
|
|
274
|
+
return { success: true };
|
|
275
|
+
}
|
|
221
276
|
logger.error({ streamError }, "Error during Slack streaming");
|
|
222
|
-
await withTimeout(streamer.stop(),
|
|
277
|
+
await withTimeout(streamer.stop(), CLEANUP_TIMEOUT_MS, "streamer.stop-cleanup").catch((e) => logger.warn({ error: e }, "Failed to stop streamer during error cleanup"));
|
|
223
278
|
if (thinkingMessageTs) try {
|
|
224
279
|
await slackClient.chat.delete({
|
|
225
280
|
channel,
|
package/dist/slack/tracer.d.ts
CHANGED
|
@@ -34,7 +34,9 @@ declare const SLACK_SPAN_KEYS: {
|
|
|
34
34
|
readonly IS_BOT_MESSAGE: "slack.is_bot_message";
|
|
35
35
|
readonly HAS_QUERY: "slack.has_query";
|
|
36
36
|
readonly IS_IN_THREAD: "slack.is_in_thread";
|
|
37
|
+
readonly STREAM_FINALIZATION_FAILED: "slack.stream_finalization_failed";
|
|
38
|
+
readonly CONTENT_ALREADY_DELIVERED: "slack.content_already_delivered";
|
|
37
39
|
};
|
|
38
|
-
type SlackOutcome = 'handled' | 'ignored_bot_message' | 'ignored_unknown_event' | 'ignored_no_action_match' | 'url_verification' | 'validation_error' | 'signature_invalid' | 'error';
|
|
40
|
+
type SlackOutcome = 'handled' | 'ignored_bot_message' | 'ignored_unknown_event' | 'ignored_no_action_match' | 'ignored_slack_retry' | 'url_verification' | 'validation_error' | 'signature_invalid' | 'error';
|
|
39
41
|
//#endregion
|
|
40
42
|
export { SLACK_SPAN_KEYS, SLACK_SPAN_NAMES, SlackOutcome, setSpanWithError, tracer };
|
package/dist/slack/tracer.js
CHANGED
|
@@ -32,7 +32,9 @@ const SLACK_SPAN_KEYS = {
|
|
|
32
32
|
OUTCOME: "slack.outcome",
|
|
33
33
|
IS_BOT_MESSAGE: "slack.is_bot_message",
|
|
34
34
|
HAS_QUERY: "slack.has_query",
|
|
35
|
-
IS_IN_THREAD: "slack.is_in_thread"
|
|
35
|
+
IS_IN_THREAD: "slack.is_in_thread",
|
|
36
|
+
STREAM_FINALIZATION_FAILED: "slack.stream_finalization_failed",
|
|
37
|
+
CONTENT_ALREADY_DELIVERED: "slack.content_already_delivered"
|
|
36
38
|
};
|
|
37
39
|
|
|
38
40
|
//#endregion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inkeep/agents-work-apps",
|
|
3
|
-
"version": "0.48.
|
|
3
|
+
"version": "0.48.2",
|
|
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.48.
|
|
36
|
+
"@inkeep/agents-core": "0.48.2"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
39
|
"@hono/zod-openapi": "^1.1.5",
|