@inkeep/agents-work-apps 0.68.4 → 0.70.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.
@@ -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_types3 from "hono/types";
7
+ import * as hono_types6 from "hono/types";
8
8
 
9
9
  //#region src/github/index.d.ts
10
- declare function createGithubRoutes(): Hono<hono_types3.BlankEnv, hono_types3.BlankSchema, "/">;
11
- declare const githubRoutes: Hono<hono_types3.BlankEnv, hono_types3.BlankSchema, "/">;
10
+ declare function createGithubRoutes(): Hono<hono_types6.BlankEnv, hono_types6.BlankSchema, "/">;
11
+ declare const githubRoutes: Hono<hono_types6.BlankEnv, hono_types6.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 hono0 from "hono";
1
+ import * as hono2 from "hono";
2
2
 
3
3
  //#region src/github/mcp/auth.d.ts
4
- declare const githubMcpAuth: () => hono0.MiddlewareHandler<{
4
+ declare const githubMcpAuth: () => hono2.MiddlewareHandler<{
5
5
  Variables: {
6
6
  toolId: string;
7
7
  tenantId: string;
@@ -1,5 +1,5 @@
1
1
  import { Hono } from "hono";
2
- import * as hono_types7 from "hono/types";
2
+ import * as hono_types1 from "hono/types";
3
3
 
4
4
  //#region src/github/mcp/index.d.ts
5
5
  declare const app: Hono<{
@@ -8,6 +8,6 @@ declare const app: Hono<{
8
8
  tenantId: string;
9
9
  projectId: string;
10
10
  };
11
- }, hono_types7.BlankSchema, "/">;
11
+ }, hono_types1.BlankSchema, "/">;
12
12
  //#endregion
13
13
  export { app as default };
@@ -76,8 +76,8 @@ declare const ChangedFileSchema: z.ZodObject<{
76
76
  path: z.ZodString;
77
77
  status: z.ZodEnum<{
78
78
  added: "added";
79
- modified: "modified";
80
79
  removed: "removed";
80
+ modified: "modified";
81
81
  renamed: "renamed";
82
82
  copied: "copied";
83
83
  changed: "changed";
@@ -1,7 +1,7 @@
1
1
  import { Hono } from "hono";
2
- import * as hono_types8 from "hono/types";
2
+ import * as hono_types2 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_types2.BlankEnv, hono_types2.BlankSchema, "/">;
6
6
  //#endregion
7
7
  export { app as default };
@@ -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/tokenExchange.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 };
@@ -1,5 +1,5 @@
1
1
  import { Hono } from "hono";
2
- import * as hono_types1 from "hono/types";
2
+ import * as hono_types0 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_types1.BlankEnv, hono_types1.BlankSchema, "/">;
10
+ declare const app: Hono<hono_types0.BlankEnv, hono_types0.BlankSchema, "/">;
11
11
  //#endregion
12
12
  export { WebhookVerificationResult, app as default, verifyWebhookSignature };
@@ -1,7 +1,7 @@
1
- import * as hono2 from "hono";
1
+ import * as hono0 from "hono";
2
2
 
3
3
  //#region src/slack/mcp/auth.d.ts
4
- declare const slackMcpAuth: () => hono2.MiddlewareHandler<{
4
+ declare const slackMcpAuth: () => hono0.MiddlewareHandler<{
5
5
  Variables: {
6
6
  toolId: string;
7
7
  tenantId: string;
@@ -1,8 +1,7 @@
1
+ import { getSlackClient } from "../client.js";
2
+
1
3
  //#region src/slack/services/events/block-actions.d.ts
2
- /**
3
- * Handlers for Slack block action events (button clicks, selections, etc.)
4
- * and message shortcuts
5
- */
4
+
6
5
  /**
7
6
  * Handle tool approval/denial button clicks.
8
7
  * Called when a user clicks "Approve" or "Deny" on a tool approval message.
@@ -37,5 +36,23 @@ declare function handleMessageShortcut(params: {
37
36
  threadTs?: string;
38
37
  responseUrl?: string;
39
38
  }): Promise<void>;
39
+ /**
40
+ * Consume the SSE continuation stream returned after a durable tool approval.
41
+ * Accumulates text-delta events and posts the final result to the Slack thread.
42
+ * If the continuation triggers another tool approval (chained/delegated), posts
43
+ * the approval buttons — the next button click will recursively enter this flow.
44
+ */
45
+ declare function consumeApprovalContinuationStream(params: {
46
+ response: Response;
47
+ slackClient: ReturnType<typeof getSlackClient>;
48
+ channel: string;
49
+ threadTs?: string;
50
+ agentName: string;
51
+ conversationId: string;
52
+ projectId: string;
53
+ agentId: string;
54
+ slackUserId: string;
55
+ thinkingMessageTs?: string;
56
+ }): Promise<void>;
40
57
  //#endregion
41
- export { handleMessageShortcut, handleOpenAgentSelectorModal, handleToolApproval };
58
+ export { consumeApprovalContinuationStream, handleMessageShortcut, handleOpenAgentSelectorModal, handleToolApproval };
@@ -1,9 +1,10 @@
1
1
  import { env } from "../../../env.js";
2
2
  import { getLogger } from "../../../logger.js";
3
3
  import { findWorkspaceConnectionByTeamId } from "../nango.js";
4
- import { fetchAgentsForProject, fetchProjectsForTenant, findCachedUserMapping, getChannelAgentConfig, sendResponseUrlMessage } from "./utils.js";
4
+ import { fetchAgentsForProject, fetchProjectsForTenant, findCachedUserMapping, getChannelAgentConfig, markdownToMrkdwn, sendResponseUrlMessage } from "./utils.js";
5
+ import { lookupAgentName } from "../agent-resolution.js";
5
6
  import { SlackStrings } from "../../i18n/strings.js";
6
- import { ToolApprovalButtonValueSchema, buildToolApprovalDoneBlocks } from "../blocks/index.js";
7
+ import { ToolApprovalButtonValueSchema, buildToolApprovalBlocks, buildToolApprovalDoneBlocks, createContextBlock, createContextBlockFromText } from "../blocks/index.js";
7
8
  import { getSlackClient } from "../client.js";
8
9
  import { SLACK_SPAN_KEYS, SLACK_SPAN_NAMES, setSpanWithError, tracer } from "../../tracer.js";
9
10
  import { buildAgentSelectorModal, buildMessageShortcutModal } from "../modals.js";
@@ -64,6 +65,15 @@ async function handleToolApproval(params) {
64
65
  slackTeamId: teamId,
65
66
  slackUserId
66
67
  });
68
+ if (responseUrl) await sendResponseUrlMessage(responseUrl, {
69
+ text: approved ? `✅ Approved \`${toolName}\`` : `❌ Denied \`${toolName}\``,
70
+ replace_original: true,
71
+ blocks: buildToolApprovalDoneBlocks({
72
+ toolName,
73
+ approved,
74
+ actorUserId: slackUserId
75
+ })
76
+ }).catch((e) => logger.warn({ error: e }, "Failed to update approval message"));
67
77
  const apiUrl = env.INKEEP_AGENTS_API_URL || "http://localhost:3002";
68
78
  const approvalResponse = await getInProcessFetch()(`${apiUrl}/run/api/chat`, {
69
79
  method: "POST",
@@ -108,21 +118,38 @@ async function handleToolApproval(params) {
108
118
  span.end();
109
119
  return;
110
120
  }
111
- if (responseUrl) await sendResponseUrlMessage(responseUrl, {
112
- text: approved ? `✅ Approved \`${toolName}\`` : `❌ Denied \`${toolName}\``,
113
- replace_original: true,
114
- blocks: buildToolApprovalDoneBlocks({
115
- toolName,
116
- approved,
117
- actorUserId: slackUserId
118
- })
119
- }).catch((e) => logger.warn({ error: e }, "Failed to update approval message"));
120
121
  logger.info({
121
122
  toolCallId,
122
123
  conversationId,
123
124
  approved,
124
125
  slackUserId
125
126
  }, "Tool approval processed");
127
+ const contentType = approvalResponse.headers.get("content-type") || "";
128
+ if (approvalResponse.body && contentType.includes("text/event-stream")) {
129
+ const agentName = await lookupAgentName(tenantId, projectId, agentId) || agentId;
130
+ const thinkingText = SlackStrings.status.thinking(agentName);
131
+ const thinkingMsg = await slackClient.chat.postMessage({
132
+ channel: buttonValue.channel,
133
+ ...approvalThreadParam,
134
+ blocks: [createContextBlockFromText(thinkingText)],
135
+ text: thinkingText
136
+ }).catch((e) => {
137
+ logger.warn({ error: e }, "Failed to post thinking message");
138
+ return null;
139
+ });
140
+ await consumeApprovalContinuationStream({
141
+ response: approvalResponse,
142
+ slackClient,
143
+ channel: buttonValue.channel,
144
+ threadTs: buttonValue.threadTs,
145
+ agentName,
146
+ conversationId,
147
+ projectId,
148
+ agentId,
149
+ slackUserId,
150
+ thinkingMessageTs: thinkingMsg?.ts
151
+ });
152
+ }
126
153
  span.end();
127
154
  } catch (error) {
128
155
  if (error instanceof Error) setSpanWithError(span, error);
@@ -343,6 +370,130 @@ async function handleMessageShortcut(params) {
343
370
  }
344
371
  });
345
372
  }
373
+ const CONTINUATION_TIMEOUT_MS = 12e4;
374
+ /**
375
+ * Consume the SSE continuation stream returned after a durable tool approval.
376
+ * Accumulates text-delta events and posts the final result to the Slack thread.
377
+ * If the continuation triggers another tool approval (chained/delegated), posts
378
+ * the approval buttons — the next button click will recursively enter this flow.
379
+ */
380
+ async function consumeApprovalContinuationStream(params) {
381
+ const { response, slackClient, channel, threadTs, agentName, conversationId, projectId, agentId, slackUserId, thinkingMessageTs } = params;
382
+ const threadParam = threadTs ? { thread_ts: threadTs } : {};
383
+ if (!response.body) return;
384
+ const reader = response.body.getReader();
385
+ const decoder = new TextDecoder();
386
+ let buffer = "";
387
+ let fullText = "";
388
+ let chainedApprovalPosted = false;
389
+ const toolCallIdToName = /* @__PURE__ */ new Map();
390
+ const toolCallIdToInput = /* @__PURE__ */ new Map();
391
+ const timeoutId = setTimeout(() => {
392
+ reader.cancel().catch(() => {});
393
+ }, CONTINUATION_TIMEOUT_MS);
394
+ try {
395
+ while (true) {
396
+ const { done, value } = await reader.read();
397
+ if (done) break;
398
+ buffer += decoder.decode(value, { stream: true });
399
+ const lines = buffer.split("\n");
400
+ buffer = lines.pop() || "";
401
+ for (const line of lines) {
402
+ if (!line.startsWith("data: ")) continue;
403
+ const jsonStr = line.slice(6).trim();
404
+ if (!jsonStr || jsonStr === "[DONE]") continue;
405
+ try {
406
+ const data = JSON.parse(jsonStr);
407
+ if (data.type === "text-delta" && data.delta) fullText += data.delta;
408
+ else if (data.type === "tool-input-available" && data.toolCallId && data.toolName) {
409
+ toolCallIdToName.set(String(data.toolCallId), String(data.toolName));
410
+ if (data.input && typeof data.input === "object") toolCallIdToInput.set(String(data.toolCallId), data.input);
411
+ } else if (data.type === "tool-approval-request" && data.toolCallId && conversationId) {
412
+ const toolCallId = data.toolCallId;
413
+ const toolName = toolCallIdToName.get(toolCallId) || "Tool";
414
+ const input = toolCallIdToInput.get(toolCallId);
415
+ const buttonValue = {
416
+ toolCallId,
417
+ conversationId,
418
+ projectId,
419
+ agentId,
420
+ slackUserId,
421
+ channel,
422
+ threadTs,
423
+ toolName
424
+ };
425
+ await slackClient.chat.postMessage({
426
+ channel,
427
+ ...threadParam,
428
+ text: `Tool approval required: \`${toolName}\``,
429
+ blocks: buildToolApprovalBlocks({
430
+ toolName,
431
+ input,
432
+ buttonValue: JSON.stringify(buttonValue)
433
+ })
434
+ }).catch((e) => logger.warn({
435
+ error: e,
436
+ toolCallId
437
+ }, "Failed to post chained approval message"));
438
+ chainedApprovalPosted = true;
439
+ clearTimeout(timeoutId);
440
+ }
441
+ } catch {}
442
+ }
443
+ }
444
+ } catch (error) {
445
+ logger.warn({
446
+ error: error instanceof Error ? error.message : String(error),
447
+ channel,
448
+ threadTs,
449
+ conversationId,
450
+ agentId
451
+ }, "Error reading approval continuation stream");
452
+ if (fullText.length === 0) await slackClient.chat.postMessage({
453
+ channel,
454
+ ...threadParam,
455
+ text: "_Something went wrong while processing the tool result. Please try again._"
456
+ }).catch((e) => logger.warn({ error: e }, "Failed to post continuation error message"));
457
+ } finally {
458
+ clearTimeout(timeoutId);
459
+ reader.cancel().catch(() => {});
460
+ }
461
+ if (thinkingMessageTs) await slackClient.chat.delete({
462
+ channel,
463
+ ts: thinkingMessageTs
464
+ }).catch((e) => logger.warn({ error: e }, "Failed to delete thinking message"));
465
+ if (fullText.length > 0) {
466
+ const slackText = markdownToMrkdwn(fullText);
467
+ const SLACK_SECTION_LIMIT = 3e3;
468
+ const truncatedText = slackText.length > SLACK_SECTION_LIMIT ? `${slackText.slice(0, SLACK_SECTION_LIMIT - 3)}...` : slackText;
469
+ await slackClient.chat.postMessage({
470
+ channel,
471
+ ...threadParam,
472
+ text: slackText,
473
+ blocks: [{
474
+ type: "section",
475
+ text: {
476
+ type: "mrkdwn",
477
+ text: truncatedText
478
+ }
479
+ }, createContextBlock({ agentName })]
480
+ }).catch((e) => logger.warn({
481
+ error: e,
482
+ channel,
483
+ threadTs
484
+ }, "Failed to post approval continuation result"));
485
+ logger.info({
486
+ channel,
487
+ threadTs,
488
+ conversationId,
489
+ responseLength: fullText.length
490
+ }, "Approval continuation stream completed");
491
+ } else if (!chainedApprovalPosted) logger.warn({
492
+ channel,
493
+ threadTs,
494
+ conversationId
495
+ }, "Approval continuation stream completed with no text and no chained approval");
496
+ }
346
497
 
347
498
  //#endregion
348
- export { handleMessageShortcut, handleOpenAgentSelectorModal, handleToolApproval };
499
+ export { consumeApprovalContinuationStream, handleMessageShortcut, handleOpenAgentSelectorModal, handleToolApproval };
@@ -120,6 +120,7 @@ async function streamAgentResponse(params) {
120
120
  content: question
121
121
  }],
122
122
  stream: true,
123
+ executionMode: "durable",
123
124
  ...conversationId && { conversationId }
124
125
  }),
125
126
  signal: abortController.signal
@@ -10,9 +10,9 @@ import { AgentOption } from "../modals.js";
10
10
  */
11
11
  declare function findCachedUserMapping(tenantId: string, slackUserId: string, teamId: string, clientId?: string): Promise<{
12
12
  slackUserId: string;
13
- id: string;
14
13
  createdAt: string;
15
14
  updatedAt: string;
15
+ id: string;
16
16
  tenantId: string;
17
17
  clientId: string;
18
18
  slackTeamId: string;
@@ -1,5 +1,5 @@
1
1
  import { SlackLinkIntent } from "@inkeep/agents-core";
2
- import * as slack_block_builder0 from "slack-block-builder";
2
+ import * as slack_block_builder7 from "slack-block-builder";
3
3
 
4
4
  //#region src/slack/services/link-prompt.d.ts
5
5
  type LinkPromptResult = {
@@ -22,6 +22,6 @@ interface ResolveLinkActionParams {
22
22
  intent?: SlackLinkIntent;
23
23
  }
24
24
  declare function resolveUnlinkedUserAction(params: ResolveLinkActionParams): Promise<LinkPromptResult>;
25
- declare function buildLinkPromptMessage(result: LinkPromptResult): Readonly<slack_block_builder0.SlackMessageDto>;
25
+ declare function buildLinkPromptMessage(result: LinkPromptResult): Readonly<slack_block_builder7.SlackMessageDto>;
26
26
  //#endregion
27
27
  export { LinkPromptResult, ResolveLinkActionParams, buildLinkPromptMessage, resolveUnlinkedUserAction };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inkeep/agents-work-apps",
3
- "version": "0.68.4",
3
+ "version": "0.70.0",
4
4
  "description": "First party integrations for Inkeep Agents",
5
5
  "type": "module",
6
6
  "license": "SEE LICENSE IN LICENSE.md",
@@ -35,7 +35,7 @@
35
35
  "minimatch": "^10.2.1",
36
36
  "oxfmt": "^0.42.0",
37
37
  "slack-block-builder": "^2.8.0",
38
- "@inkeep/agents-core": "0.68.4"
38
+ "@inkeep/agents-core": "0.70.0"
39
39
  },
40
40
  "peerDependencies": {
41
41
  "@hono/zod-openapi": "^1.1.5",