@inkeep/agents-work-apps 0.0.0-dev-20260224030137 → 0.0.0-dev-20260224031202

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/env.d.ts CHANGED
@@ -14,11 +14,11 @@ declare const envSchema: z.ZodObject<{
14
14
  pentest: "pentest";
15
15
  }>>;
16
16
  LOG_LEVEL: z.ZodDefault<z.ZodEnum<{
17
- error: "error";
18
17
  trace: "trace";
19
18
  debug: "debug";
20
19
  info: "info";
21
20
  warn: "warn";
21
+ error: "error";
22
22
  }>>;
23
23
  INKEEP_AGENTS_RUN_DATABASE_URL: z.ZodOptional<z.ZodString>;
24
24
  INKEEP_AGENTS_MANAGE_UI_URL: z.ZodOptional<z.ZodString>;
@@ -44,7 +44,7 @@ declare const envSchema: z.ZodObject<{
44
44
  declare const env: {
45
45
  NODE_ENV: "development" | "production" | "test";
46
46
  ENVIRONMENT: "development" | "production" | "test" | "pentest";
47
- LOG_LEVEL: "error" | "trace" | "debug" | "info" | "warn";
47
+ LOG_LEVEL: "trace" | "debug" | "info" | "warn" | "error";
48
48
  INKEEP_AGENTS_RUN_DATABASE_URL?: string | undefined;
49
49
  INKEEP_AGENTS_MANAGE_UI_URL?: string | undefined;
50
50
  GITHUB_APP_ID?: string | undefined;
@@ -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_types0 from "hono/types";
7
+ import * as hono_types5 from "hono/types";
8
8
 
9
9
  //#region src/github/index.d.ts
10
- declare function createGithubRoutes(): Hono<hono_types0.BlankEnv, hono_types0.BlankSchema, "/">;
11
- declare const githubRoutes: Hono<hono_types0.BlankEnv, hono_types0.BlankSchema, "/">;
10
+ declare function createGithubRoutes(): Hono<hono_types5.BlankEnv, hono_types5.BlankSchema, "/">;
11
+ declare const githubRoutes: Hono<hono_types5.BlankEnv, hono_types5.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 };
@@ -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_types3 from "hono/types";
2
+ import * as hono_types1 from "hono/types";
3
3
 
4
4
  //#region src/github/routes/setup.d.ts
5
- declare const app: Hono<hono_types3.BlankEnv, hono_types3.BlankSchema, "/">;
5
+ declare const app: Hono<hono_types1.BlankEnv, hono_types1.BlankSchema, "/">;
6
6
  //#endregion
7
7
  export { app as default };
@@ -1,7 +1,7 @@
1
1
  import { Hono } from "hono";
2
- import * as hono_types7 from "hono/types";
2
+ import * as hono_types0 from "hono/types";
3
3
 
4
4
  //#region src/github/routes/tokenExchange.d.ts
5
- declare const app: Hono<hono_types7.BlankEnv, hono_types7.BlankSchema, "/">;
5
+ declare const app: Hono<hono_types0.BlankEnv, hono_types0.BlankSchema, "/">;
6
6
  //#endregion
7
7
  export { app as default };
@@ -1,5 +1,5 @@
1
1
  import { Hono } from "hono";
2
- import * as hono_types5 from "hono/types";
2
+ import * as hono_types3 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_types5.BlankEnv, hono_types5.BlankSchema, "/">;
10
+ declare const app: Hono<hono_types3.BlankEnv, hono_types3.BlankSchema, "/">;
11
11
  //#endregion
12
12
  export { WebhookVerificationResult, app as default, verifyWebhookSignature };
@@ -1,7 +1,8 @@
1
+ import { env } from "../../env.js";
1
2
  import { getLogger } from "../../logger.js";
2
3
  import runDbClient_default from "../../db/runDbClient.js";
3
4
  import { getWorkspaceDefaultAgentFromNango } from "./nango.js";
4
- import { findWorkAppSlackChannelAgentConfig } from "@inkeep/agents-core";
5
+ import { InternalServices, findWorkAppSlackChannelAgentConfig, generateInternalServiceToken, getInProcessFetch } from "@inkeep/agents-core";
5
6
 
6
7
  //#region src/slack/services/agent-resolution.ts
7
8
  /**
@@ -11,6 +12,75 @@ import { findWorkAppSlackChannelAgentConfig } from "@inkeep/agents-core";
11
12
  * Priority: Channel default > Workspace default (all admin-controlled)
12
13
  */
13
14
  const logger = getLogger("slack-agent-resolution");
15
+ const AGENT_NAME_CACHE_TTL_MS = 300 * 1e3;
16
+ const AGENT_NAME_CACHE_MAX_SIZE = 500;
17
+ const agentNameCache = /* @__PURE__ */ new Map();
18
+ async function lookupAgentName(tenantId, projectId, agentId) {
19
+ const cacheKey = `${tenantId}:${projectId}:${agentId}`;
20
+ const cached = agentNameCache.get(cacheKey);
21
+ if (cached && cached.expiresAt > Date.now()) return cached.name || void 0;
22
+ try {
23
+ const apiUrl = env.INKEEP_AGENTS_API_URL || "http://localhost:3002";
24
+ const token = await generateInternalServiceToken({
25
+ serviceId: InternalServices.INKEEP_AGENTS_MANAGE_API,
26
+ tenantId,
27
+ projectId
28
+ });
29
+ const controller = new AbortController();
30
+ const timeout = setTimeout(() => controller.abort(), 5e3);
31
+ try {
32
+ const response = await getInProcessFetch()(`${apiUrl}/manage/tenants/${tenantId}/projects/${projectId}/agents?limit=50`, {
33
+ method: "GET",
34
+ headers: {
35
+ Authorization: `Bearer ${token}`,
36
+ "Content-Type": "application/json",
37
+ "x-inkeep-project-id": projectId
38
+ },
39
+ signal: controller.signal
40
+ });
41
+ if (!response.ok) {
42
+ logger.warn({
43
+ status: response.status,
44
+ tenantId,
45
+ projectId,
46
+ agentId
47
+ }, "Failed to fetch agents for name lookup");
48
+ return;
49
+ }
50
+ const result = await response.json();
51
+ for (const agent of result.data) {
52
+ const key = `${tenantId}:${projectId}:${agent.id}`;
53
+ agentNameCache.set(key, {
54
+ name: agent.name || null,
55
+ expiresAt: Date.now() + AGENT_NAME_CACHE_TTL_MS
56
+ });
57
+ }
58
+ if (agentNameCache.size > AGENT_NAME_CACHE_MAX_SIZE) {
59
+ const now = Date.now();
60
+ for (const [key, entry] of agentNameCache) if (entry.expiresAt <= now) agentNameCache.delete(key);
61
+ if (agentNameCache.size > AGENT_NAME_CACHE_MAX_SIZE) {
62
+ const excess = agentNameCache.size - AGENT_NAME_CACHE_MAX_SIZE;
63
+ const keys = agentNameCache.keys();
64
+ for (let i = 0; i < excess; i++) {
65
+ const { value } = keys.next();
66
+ if (value) agentNameCache.delete(value);
67
+ }
68
+ }
69
+ }
70
+ return result.data.find((a) => a.id === agentId)?.name || void 0;
71
+ } finally {
72
+ clearTimeout(timeout);
73
+ }
74
+ } catch (error) {
75
+ logger.warn({
76
+ error,
77
+ tenantId,
78
+ projectId,
79
+ agentId
80
+ }, "Failed to look up agent name");
81
+ return;
82
+ }
83
+ }
14
84
  /**
15
85
  * Resolve the effective agent configuration.
16
86
  * Priority: Channel default > Workspace default
@@ -25,6 +95,7 @@ async function resolveEffectiveAgent(params) {
25
95
  teamId,
26
96
  channelId
27
97
  }, "Resolving effective agent");
98
+ let result = null;
28
99
  if (channelId) {
29
100
  const channelConfig = await findWorkAppSlackChannelAgentConfig(runDbClient_default)(tenantId, teamId, channelId);
30
101
  if (channelConfig?.enabled) {
@@ -33,7 +104,7 @@ async function resolveEffectiveAgent(params) {
33
104
  agentId: channelConfig.agentId,
34
105
  source: "channel"
35
106
  }, "Resolved agent from channel config");
36
- return {
107
+ result = {
37
108
  projectId: channelConfig.projectId,
38
109
  agentId: channelConfig.agentId,
39
110
  agentName: channelConfig.agentName || void 0,
@@ -42,27 +113,39 @@ async function resolveEffectiveAgent(params) {
42
113
  };
43
114
  }
44
115
  }
45
- const workspaceConfig = await getWorkspaceDefaultAgentFromNango(teamId);
46
- if (workspaceConfig?.agentId && workspaceConfig.projectId) {
47
- logger.info({
48
- teamId,
49
- agentId: workspaceConfig.agentId,
50
- source: "workspace"
51
- }, "Resolved agent from workspace config");
52
- return {
53
- projectId: workspaceConfig.projectId,
54
- agentId: workspaceConfig.agentId,
55
- agentName: workspaceConfig.agentName,
56
- source: "workspace",
57
- grantAccessToMembers: workspaceConfig.grantAccessToMembers ?? true
58
- };
116
+ if (!result) {
117
+ const workspaceConfig = await getWorkspaceDefaultAgentFromNango(teamId);
118
+ if (workspaceConfig?.agentId && workspaceConfig.projectId) {
119
+ logger.info({
120
+ teamId,
121
+ agentId: workspaceConfig.agentId,
122
+ source: "workspace"
123
+ }, "Resolved agent from workspace config");
124
+ result = {
125
+ projectId: workspaceConfig.projectId,
126
+ agentId: workspaceConfig.agentId,
127
+ agentName: workspaceConfig.agentName,
128
+ source: "workspace",
129
+ grantAccessToMembers: workspaceConfig.grantAccessToMembers ?? true
130
+ };
131
+ }
59
132
  }
60
- logger.debug({
133
+ if (result && (!result.agentName || result.agentName === result.agentId)) {
134
+ const name = await lookupAgentName(tenantId, result.projectId, result.agentId);
135
+ if (name) {
136
+ result.agentName = name;
137
+ logger.debug({
138
+ agentId: result.agentId,
139
+ agentName: name
140
+ }, "Enriched agent config with name from manage API");
141
+ }
142
+ }
143
+ if (!result) logger.debug({
61
144
  tenantId,
62
145
  teamId,
63
146
  channelId
64
147
  }, "No agent configuration found");
65
- return null;
148
+ return result;
66
149
  }
67
150
  /**
68
151
  * Get all agent configuration sources for display purposes.
@@ -92,10 +175,15 @@ async function getAgentConfigSources(params) {
92
175
  source: "workspace",
93
176
  grantAccessToMembers: wsConfig.grantAccessToMembers ?? true
94
177
  };
178
+ const effective = channelConfig || workspaceConfig;
179
+ if (effective && (!effective.agentName || effective.agentName === effective.agentId)) {
180
+ const name = await lookupAgentName(tenantId, effective.projectId, effective.agentId);
181
+ if (name) effective.agentName = name;
182
+ }
95
183
  return {
96
184
  channelConfig,
97
185
  workspaceConfig,
98
- effective: channelConfig || workspaceConfig
186
+ effective
99
187
  };
100
188
  }
101
189
 
@@ -17,6 +17,7 @@ declare function createContextBlock(params: ContextBlockParams): {
17
17
  interface FollowUpButtonParams {
18
18
  conversationId: string;
19
19
  agentId: string;
20
+ agentName?: string;
20
21
  projectId: string;
21
22
  tenantId: string;
22
23
  teamId: string;
@@ -44,10 +44,7 @@ function buildConversationResponseBlocks(params) {
44
44
  }
45
45
  }];
46
46
  if (!isError) {
47
- const contextBlock = createContextBlock({
48
- agentName,
49
- isPrivate: true
50
- });
47
+ const contextBlock = createContextBlock({ agentName });
51
48
  blocks.push(contextBlock);
52
49
  blocks.push({
53
50
  type: "actions",
@@ -36,13 +36,16 @@ async function handleModalSubmission(view) {
36
36
  const includeContext = includeContextValue?.selected_options?.some((o) => o.value === "include_context") ?? true;
37
37
  let agentId = metadata.selectedAgentId;
38
38
  let projectId = metadata.selectedProjectId;
39
+ let agentName = null;
39
40
  if (agentSelectValue?.selected_option?.value) try {
40
41
  const parsed = JSON.parse(agentSelectValue.selected_option.value);
41
42
  agentId = parsed.agentId;
42
43
  projectId = parsed.projectId;
44
+ agentName = parsed.agentName || null;
43
45
  } catch {
44
46
  logger.warn({ value: agentSelectValue.selected_option.value }, "Failed to parse agent select value");
45
47
  }
48
+ const agentDisplayName = agentName || agentId || "Agent";
46
49
  if (!agentId || !projectId) {
47
50
  logger.error({ metadata }, "Missing agent or project ID in modal submission");
48
51
  if (metadata.buttonResponseUrl) await sendResponseUrlMessage(metadata.buttonResponseUrl, {
@@ -112,7 +115,7 @@ async function handleModalSubmission(view) {
112
115
  });
113
116
  span.setAttribute(SLACK_SPAN_KEYS.CONVERSATION_ID, conversationId);
114
117
  const apiBaseUrl = env.INKEEP_AGENTS_API_URL || "http://localhost:3002";
115
- const thinkingText = SlackStrings.status.thinking(agentId);
118
+ const thinkingText = SlackStrings.status.thinking(agentDisplayName);
116
119
  if (metadata.buttonResponseUrl) await sendResponseUrlMessage(metadata.buttonResponseUrl, {
117
120
  text: thinkingText,
118
121
  response_type: "ephemeral",
@@ -139,6 +142,7 @@ async function handleModalSubmission(view) {
139
142
  slackClient,
140
143
  metadata,
141
144
  agentId,
145
+ agentDisplayName,
142
146
  projectId,
143
147
  tenantId,
144
148
  conversationId,
@@ -194,7 +198,8 @@ async function handleFollowUpSubmission(view) {
194
198
  span.end();
195
199
  return;
196
200
  }
197
- const { conversationId, agentId, projectId, tenantId, teamId, slackUserId, channel } = metadata;
201
+ const { conversationId, agentId, agentName, projectId, tenantId, teamId, slackUserId, channel } = metadata;
202
+ const agentDisplayName = agentName || agentId || "Agent";
198
203
  span.setAttribute(SLACK_SPAN_KEYS.TEAM_ID, teamId);
199
204
  span.setAttribute(SLACK_SPAN_KEYS.CHANNEL_ID, channel);
200
205
  span.setAttribute(SLACK_SPAN_KEYS.USER_ID, slackUserId);
@@ -234,7 +239,7 @@ async function handleFollowUpSubmission(view) {
234
239
  await slackClient.chat.postEphemeral({
235
240
  channel,
236
241
  user: slackUserId,
237
- text: SlackStrings.status.thinking(agentId)
242
+ text: SlackStrings.status.thinking(agentDisplayName)
238
243
  });
239
244
  const responseText = await callAgentApi({
240
245
  apiBaseUrl,
@@ -247,11 +252,12 @@ async function handleFollowUpSubmission(view) {
247
252
  const responseBlocks = buildConversationResponseBlocks({
248
253
  userMessage: question,
249
254
  responseText: responseText.text,
250
- agentName: agentId,
255
+ agentName: agentDisplayName,
251
256
  isError: responseText.isError,
252
257
  followUpParams: {
253
258
  conversationId,
254
259
  agentId,
260
+ agentName: agentDisplayName,
255
261
  projectId,
256
262
  tenantId,
257
263
  teamId,
@@ -370,15 +376,16 @@ async function callAgentApi(params) {
370
376
  });
371
377
  }
372
378
  async function postPrivateResponse(params) {
373
- const { slackClient, metadata, agentId, projectId, tenantId, conversationId, userMessage, responseText, isError } = params;
379
+ const { slackClient, metadata, agentId, agentDisplayName, projectId, tenantId, conversationId, userMessage, responseText, isError } = params;
374
380
  const responseBlocks = buildConversationResponseBlocks({
375
381
  userMessage,
376
382
  responseText,
377
- agentName: agentId,
383
+ agentName: agentDisplayName,
378
384
  isError,
379
385
  followUpParams: {
380
386
  conversationId,
381
387
  agentId,
388
+ agentName: agentDisplayName,
382
389
  projectId,
383
390
  tenantId,
384
391
  teamId: metadata.teamId,
@@ -11,9 +11,9 @@ declare function findCachedUserMapping(tenantId: string, slackUserId: string, te
11
11
  id: string;
12
12
  createdAt: string;
13
13
  updatedAt: string;
14
- slackUserId: 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;
@@ -24,6 +24,7 @@ interface ModalMetadata {
24
24
  interface FollowUpModalMetadata {
25
25
  conversationId: string;
26
26
  agentId: string;
27
+ agentName?: string;
27
28
  projectId: string;
28
29
  tenantId: string;
29
30
  teamId: string;
@@ -34,7 +34,8 @@ function buildAgentSelectorModal(params) {
34
34
  },
35
35
  value: JSON.stringify({
36
36
  agentId: agent.id,
37
- projectId: agent.projectId
37
+ projectId: agent.projectId,
38
+ agentName: agent.name
38
39
  })
39
40
  })) : [{
40
41
  text: {
@@ -239,7 +240,8 @@ function buildMessageShortcutModal(params) {
239
240
  },
240
241
  value: JSON.stringify({
241
242
  agentId: agent.id,
242
- projectId: agent.projectId
243
+ projectId: agent.projectId,
244
+ agentName: agent.name
243
245
  })
244
246
  })) : [{
245
247
  text: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inkeep/agents-work-apps",
3
- "version": "0.0.0-dev-20260224030137",
3
+ "version": "0.0.0-dev-20260224031202",
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-20260224030137"
36
+ "@inkeep/agents-core": "0.0.0-dev-20260224031202"
37
37
  },
38
38
  "peerDependencies": {
39
39
  "@hono/zod-openapi": "^1.1.5",