@render-harness/cap-slack 0.4.0 → 0.4.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/README.md CHANGED
@@ -59,6 +59,21 @@ Read tools are available when `SLACK_BOT_TOKEN` is set:
59
59
 
60
60
  User and channel lookups are cached for the lifetime of the agent process to keep enrichment cheap across turns.
61
61
 
62
+ ### Channel and user inputs
63
+
64
+ Every tool that takes a `channel` parameter (`slack.send_message`, `slack.get_thread`, `slack.get_channel_history`, `slack.get_channel_info`, `slack.add_reaction`, `slack.update_message`) accepts any of:
65
+
66
+ - A Slack channel ID — `C0AQHA6M3PS`, `G0…`, `D0…`. Used verbatim.
67
+ - A `#channel-name` — resolved to a channel ID via `conversations.list` (cached for the agent process). Requires `channels:read` and/or `groups:read` scope on the bot token. The bot must also be a **member** of the channel for write actions.
68
+ - An `@user-handle` — resolved to a user ID via `users.list` (cached) and opened as a DM channel via `conversations.open`. Requires `users:read` scope plus `im:write` (and `chat:write` for sending).
69
+ - A Slack mention literal — `<#C0AQHA6M3PS|name>` or `<@U0B4357MH7H>`. The wrapping is stripped and the inner ID is used.
70
+
71
+ `slack.get_user_info` accepts a user ID (`U0B4357MH7H`), an `@handle`, or a bare handle (`ada.lovelace`); the latter two require `users:read`.
72
+
73
+ `allowedChannels` is enforced **after** resolution, against the canonical channel ID. So `allowedChannels: ["C0AQ…"]` correctly accepts `slack.send_message({ channel: "#that-channels-name" })` because the resolver returns `C0AQ…` before the gate check fires.
74
+
75
+ If the bot lacks the lookup scope, the tool returns a clear error naming the scope (`Slack channel "#general" not found. Check the name, or ensure the bot has 'channels:read' / 'groups:read' scope and is a member of the channel.`) instead of failing silently. Agents that only ever address channels by ID don't need the read scopes; the resolver fast-paths `C…` / `G…` / `D…` / `U…` inputs without any API call.
76
+
62
77
  Set `accessMode: read_write` to enable write tools:
63
78
 
64
79
  - `slack.send_message`
@@ -67,6 +82,10 @@ Set `accessMode: read_write` to enable write tools:
67
82
 
68
83
  Use `permissions.requireApproval` for write tools if the agent should ask before posting or changing Slack messages.
69
84
 
85
+ ### Request timeouts and retries
86
+
87
+ The underlying `@slack/web-api` WebClient defaults to no per-request timeout and retries up to ten times over roughly 30 minutes, which lets a single rate-limited or transient-failure response hang a tool call indefinitely from the agent's perspective. This pack overrides those defaults with a 15-second per-request timeout and a bounded retry policy (3 retries, 0.5s → 3s backoff), so a failing call surfaces as a clear tool error within ~70 seconds worst case instead of appearing stuck. Agents using `slack.send_message` against high-traffic channels should still expect the occasional rate-limit error and either back off or use `permissions.requireApproval` to throttle posts.
88
+
70
89
  ## Test Commands
71
90
 
72
91
  ```sh
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import { WebClient } from '@slack/web-api';
6
6
 
7
7
  // package.json
8
8
  var package_default = {
9
- version: "0.4.0"};
9
+ version: "0.4.2"};
10
10
  function slackConversationId(args) {
11
11
  const digest = createHash("sha256").update(`${args.teamId}:${args.channel}:${args.threadTs}`).digest("hex").slice(0, 24);
12
12
  return `slack-${digest}`;
@@ -60,59 +60,78 @@ function objectValue(value) {
60
60
  function stringValue(value) {
61
61
  return typeof value === "string" && value.length > 0 ? value : void 0;
62
62
  }
63
+ var SLACK_REQUEST_TIMEOUT_MS = 15e3;
64
+ var BOUNDED_RETRY_CONFIG = {
65
+ retries: 3,
66
+ factor: 2,
67
+ minTimeout: 500,
68
+ maxTimeout: 3e3
69
+ };
70
+ var CHANNEL_ID_PATTERN = /^[CGD][A-Z0-9]{2,}$/;
71
+ var USER_ID_PATTERN = /^U[A-Z0-9]{2,}$/;
63
72
  function slackTools(args) {
64
- const client = new WebClient(args.botToken);
73
+ const client = new WebClient(args.botToken, {
74
+ timeout: SLACK_REQUEST_TIMEOUT_MS,
75
+ retryConfig: BOUNDED_RETRY_CONFIG
76
+ });
65
77
  const resolver = createResolver(client);
66
78
  const tools = [
67
79
  jsonTool(
68
80
  "slack.get_thread",
69
- "Read a Slack thread's messages. Responses include resolved user display names and a rewritten text_resolved field.",
81
+ "Read a Slack thread's messages. `channel` accepts a Slack ID (C\u2026/G\u2026/D\u2026), a `#channel-name`, or an `@user-handle` (opens a DM). Responses include resolved user display names and a rewritten text_resolved field.",
70
82
  objectSchema({
71
83
  channel: { type: "string" },
72
84
  thread_ts: { type: "string" }
73
85
  }),
74
86
  async (input) => {
75
87
  const { channel, thread_ts } = input;
76
- assertAllowedChannel(channel, args.allowedChannels);
77
- const resp = await client.conversations.replies({ channel, ts: thread_ts });
78
- return enrichConversationResponse(resp, channel, resolver);
88
+ const channelId = await resolver.channelInput(channel);
89
+ assertAllowedChannel(channelId, args.allowedChannels);
90
+ const resp = await client.conversations.replies({ channel: channelId, ts: thread_ts });
91
+ return enrichConversationResponse(resp, channelId, resolver);
79
92
  }
80
93
  ),
81
94
  jsonTool(
82
95
  "slack.get_channel_history",
83
- "Read recent Slack channel messages. Responses include resolved user display names and a rewritten text_resolved field.",
96
+ "Read recent Slack channel messages. `channel` accepts a Slack ID (C\u2026/G\u2026/D\u2026), a `#channel-name`, or an `@user-handle` (opens a DM). Responses include resolved user display names and a rewritten text_resolved field.",
84
97
  objectSchema({
85
98
  channel: { type: "string" },
86
99
  limit: { type: "number", optional: true }
87
100
  }),
88
101
  async (input) => {
89
102
  const { channel, limit } = input;
90
- assertAllowedChannel(channel, args.allowedChannels);
91
- const resp = await client.conversations.history({ channel, limit: limit ?? 20 });
92
- return enrichConversationResponse(resp, channel, resolver);
103
+ const channelId = await resolver.channelInput(channel);
104
+ assertAllowedChannel(channelId, args.allowedChannels);
105
+ const resp = await client.conversations.history({
106
+ channel: channelId,
107
+ limit: limit ?? 20
108
+ });
109
+ return enrichConversationResponse(resp, channelId, resolver);
93
110
  }
94
111
  ),
95
112
  jsonTool(
96
113
  "slack.get_user_info",
97
- "Resolve a Slack user ID (e.g. U0B4357MH7H) to a display name, real name, and handle.",
114
+ "Resolve a Slack user to display name, real name, and handle. Accepts a user ID (U0B4357MH7H), an `@handle`, or a bare handle.",
98
115
  objectSchema({
99
116
  user: { type: "string" }
100
117
  }),
101
118
  async (input) => {
102
119
  const { user } = input;
103
- return resolver.user(user);
120
+ const userId = await resolver.userInput(user);
121
+ return resolver.user(userId);
104
122
  }
105
123
  ),
106
124
  jsonTool(
107
125
  "slack.get_channel_info",
108
- "Resolve a Slack channel ID (e.g. C0AQHA6M3PS) to a channel name and metadata.",
126
+ "Resolve a Slack channel to name and metadata. Accepts a channel ID (C0AQHA6M3PS), a `#channel-name`, or an `@user-handle` (opens a DM).",
109
127
  objectSchema({
110
128
  channel: { type: "string" }
111
129
  }),
112
130
  async (input) => {
113
131
  const { channel } = input;
114
- assertAllowedChannel(channel, args.allowedChannels);
115
- return resolver.channel(channel);
132
+ const channelId = await resolver.channelInput(channel);
133
+ assertAllowedChannel(channelId, args.allowedChannels);
134
+ return resolver.channel(channelId);
116
135
  }
117
136
  )
118
137
  ];
@@ -120,7 +139,7 @@ function slackTools(args) {
120
139
  tools.push(
121
140
  jsonTool(
122
141
  "slack.send_message",
123
- "Send a Slack message, optionally as a thread reply.",
142
+ "Send a Slack message, optionally as a thread reply. `channel` accepts a Slack ID (C\u2026/G\u2026/D\u2026), a `#channel-name`, or an `@user-handle` (opens a DM).",
124
143
  objectSchema({
125
144
  channel: { type: "string" },
126
145
  text: { type: "string" },
@@ -128,9 +147,10 @@ function slackTools(args) {
128
147
  }),
129
148
  async (input) => {
130
149
  const { channel, text, thread_ts } = input;
131
- assertAllowedChannel(channel, args.allowedChannels);
150
+ const channelId = await resolver.channelInput(channel);
151
+ assertAllowedChannel(channelId, args.allowedChannels);
132
152
  return client.chat.postMessage({
133
- channel,
153
+ channel: channelId,
134
154
  text,
135
155
  ...thread_ts ? { thread_ts } : {}
136
156
  });
@@ -138,7 +158,7 @@ function slackTools(args) {
138
158
  ),
139
159
  jsonTool(
140
160
  "slack.add_reaction",
141
- "Add a reaction to a Slack message.",
161
+ "Add a reaction to a Slack message. `channel` accepts a Slack ID, a `#channel-name`, or an `@user-handle`.",
142
162
  objectSchema({
143
163
  channel: { type: "string" },
144
164
  ts: { type: "string" },
@@ -146,13 +166,14 @@ function slackTools(args) {
146
166
  }),
147
167
  async (input) => {
148
168
  const { channel, ts, name } = input;
149
- assertAllowedChannel(channel, args.allowedChannels);
150
- return client.reactions.add({ channel, timestamp: ts, name });
169
+ const channelId = await resolver.channelInput(channel);
170
+ assertAllowedChannel(channelId, args.allowedChannels);
171
+ return client.reactions.add({ channel: channelId, timestamp: ts, name });
151
172
  }
152
173
  ),
153
174
  jsonTool(
154
175
  "slack.update_message",
155
- "Update a Slack message.",
176
+ "Update a Slack message. `channel` accepts a Slack ID, a `#channel-name`, or an `@user-handle`.",
156
177
  objectSchema({
157
178
  channel: { type: "string" },
158
179
  ts: { type: "string" },
@@ -160,8 +181,9 @@ function slackTools(args) {
160
181
  }),
161
182
  async (input) => {
162
183
  const { channel, ts, text } = input;
163
- assertAllowedChannel(channel, args.allowedChannels);
164
- return client.chat.update({ channel, ts, text });
184
+ const channelId = await resolver.channelInput(channel);
185
+ assertAllowedChannel(channelId, args.allowedChannels);
186
+ return client.chat.update({ channel: channelId, ts, text });
165
187
  }
166
188
  )
167
189
  );
@@ -198,6 +220,120 @@ function createResolver(client) {
198
220
  const channels = /* @__PURE__ */ new Map();
199
221
  const pendingUsers = /* @__PURE__ */ new Map();
200
222
  const pendingChannels = /* @__PURE__ */ new Map();
223
+ let channelsIndex = null;
224
+ let usersIndex = null;
225
+ const dmByUserId = /* @__PURE__ */ new Map();
226
+ const ensureChannelsIndex = () => {
227
+ if (channelsIndex) return channelsIndex;
228
+ channelsIndex = (async () => {
229
+ const byName = /* @__PURE__ */ new Map();
230
+ let cursor;
231
+ do {
232
+ const resp = await client.conversations.list({
233
+ limit: 1e3,
234
+ types: "public_channel,private_channel",
235
+ exclude_archived: true,
236
+ ...cursor ? { cursor } : {}
237
+ });
238
+ for (const c of resp.channels ?? []) {
239
+ if (typeof c.id === "string" && typeof c.name === "string") {
240
+ byName.set(c.name.toLowerCase(), c.id);
241
+ if (!channels.has(c.id)) channels.set(c.id, { id: c.id, name: c.name });
242
+ }
243
+ }
244
+ cursor = typeof resp.response_metadata?.next_cursor === "string" && resp.response_metadata.next_cursor.length > 0 ? resp.response_metadata.next_cursor : void 0;
245
+ } while (cursor);
246
+ return byName;
247
+ })().catch((err) => {
248
+ channelsIndex = null;
249
+ throw err;
250
+ });
251
+ return channelsIndex;
252
+ };
253
+ const ensureUsersIndex = () => {
254
+ if (usersIndex) return usersIndex;
255
+ usersIndex = (async () => {
256
+ const byHandle = /* @__PURE__ */ new Map();
257
+ let cursor;
258
+ do {
259
+ const resp = await client.users.list({
260
+ limit: 200,
261
+ ...cursor ? { cursor } : {}
262
+ });
263
+ for (const u of resp.members ?? []) {
264
+ if (typeof u.id !== "string" || u.deleted === true) continue;
265
+ for (const candidate of [
266
+ u.name,
267
+ u.real_name,
268
+ u.profile?.display_name,
269
+ u.profile?.display_name_normalized
270
+ ]) {
271
+ if (typeof candidate === "string" && candidate.length > 0) {
272
+ byHandle.set(candidate.toLowerCase(), u.id);
273
+ }
274
+ }
275
+ if (!users.has(u.id)) {
276
+ users.set(u.id, buildResolvedUser(u.id, u));
277
+ }
278
+ }
279
+ cursor = typeof resp.response_metadata?.next_cursor === "string" && resp.response_metadata.next_cursor.length > 0 ? resp.response_metadata.next_cursor : void 0;
280
+ } while (cursor);
281
+ return byHandle;
282
+ })().catch((err) => {
283
+ usersIndex = null;
284
+ throw err;
285
+ });
286
+ return usersIndex;
287
+ };
288
+ const openDmFor = async (userId) => {
289
+ const cached = dmByUserId.get(userId);
290
+ if (cached) return cached;
291
+ const resp = await client.conversations.open({ users: userId });
292
+ const channelId = resp.channel?.id;
293
+ if (typeof channelId !== "string") {
294
+ throw new Error(`conversations.open for ${userId} returned no channel id`);
295
+ }
296
+ dmByUserId.set(userId, channelId);
297
+ return channelId;
298
+ };
299
+ const resolveUserInput = async (input) => {
300
+ const trimmed = input.trim();
301
+ if (trimmed.length === 0) throw new Error("Slack user input is empty");
302
+ const mention = /^<@(U[A-Z0-9]+)(?:\|[^>]+)?>$/.exec(trimmed);
303
+ if (mention?.[1]) return mention[1];
304
+ if (USER_ID_PATTERN.test(trimmed)) return trimmed;
305
+ const handle = trimmed.startsWith("@") ? trimmed.slice(1) : trimmed;
306
+ if (handle.length === 0) throw new Error("Slack user input has no handle after '@'");
307
+ const index = await ensureUsersIndex();
308
+ const id = index.get(handle.toLowerCase());
309
+ if (!id) {
310
+ throw new Error(
311
+ `Slack user "${input}" not found. Check the handle, or ensure the bot has 'users:read' scope.`
312
+ );
313
+ }
314
+ return id;
315
+ };
316
+ const resolveChannelInput = async (input) => {
317
+ const trimmed = input.trim();
318
+ if (trimmed.length === 0) throw new Error("Slack channel input is empty");
319
+ const mention = /^<#([CGD][A-Z0-9]+)(?:\|[^>]+)?>$/.exec(trimmed);
320
+ if (mention?.[1]) return mention[1];
321
+ if (CHANNEL_ID_PATTERN.test(trimmed)) return trimmed;
322
+ if (trimmed.startsWith("@")) {
323
+ const userId = await resolveUserInput(trimmed);
324
+ return openDmFor(userId);
325
+ }
326
+ const name = trimmed.startsWith("#") ? trimmed.slice(1) : trimmed;
327
+ if (name.length === 0) throw new Error("Slack channel input has no name after '#'");
328
+ const index = await ensureChannelsIndex();
329
+ const id = index.get(name.toLowerCase());
330
+ if (!id) {
331
+ throw new Error(
332
+ `Slack channel "${input}" not found. Check the name, or ensure the bot has 'channels:read' / 'groups:read' scope and is a member of the channel.`
333
+ );
334
+ }
335
+ return id;
336
+ };
201
337
  return {
202
338
  async user(id) {
203
339
  const cached = users.get(id);
@@ -244,7 +380,9 @@ function createResolver(client) {
244
380
  })();
245
381
  pendingChannels.set(id, fetchOne);
246
382
  return fetchOne;
247
- }
383
+ },
384
+ channelInput: resolveChannelInput,
385
+ userInput: resolveUserInput
248
386
  };
249
387
  }
250
388
  function buildResolvedUser(id, raw) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../package.json","../src/convid.ts","../src/normalize.ts","../src/tools.ts","../src/verify.ts","../src/index.ts"],"names":["stringValue","createHash"],"mappings":"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","file":"index.js","sourcesContent":["{\n \"name\": \"@render-harness/cap-slack\",\n \"version\": \"0.4.0\",\n \"description\": \"Slack Events and Web API capability pack for the Render agent harness.\",\n \"type\": \"module\",\n \"license\": \"MIT\",\n \"main\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\"\n },\n \"./package.json\": \"./package.json\"\n },\n \"files\": [\n \"dist\"\n ],\n \"keywords\": [\n \"render-harness-cap\",\n \"render-harness\",\n \"slack\"\n ],\n \"renderHarness\": {\n \"gallery\": {\n \"label\": \"Slack\",\n \"envHint\": \"SLACK_BOT_TOKEN\"\n }\n },\n \"scripts\": {\n \"build\": \"tsup\",\n \"typecheck\": \"tsc --noEmit\",\n \"test\": \"vitest run --passWithNoTests\"\n },\n \"dependencies\": {\n \"@render-harness/registry\": \"workspace:*\",\n \"@slack/web-api\": \"^7.12.0\"\n },\n \"devDependencies\": {\n \"@render-harness/core\": \"workspace:*\",\n \"@types/node\": \"^25.6.2\",\n \"tsup\": \"^8.5.1\",\n \"typescript\": \"^6.0.3\",\n \"vitest\": \"^4.1.5\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/render-lab/render-agent-harness.git\",\n \"directory\": \"packages/capabilities/cap-slack\"\n }\n}\n","import { createHash } from \"node:crypto\";\n\nexport function slackConversationId(args: {\n teamId: string;\n channel: string;\n threadTs: string;\n}): string {\n const digest = createHash(\"sha256\")\n .update(`${args.teamId}:${args.channel}:${args.threadTs}`)\n .digest(\"hex\")\n .slice(0, 24);\n return `slack-${digest}`;\n}\n","export type SlackNormalizedEvent =\n | { kind: \"challenge\"; challenge: string }\n | { kind: \"noop\"; reason: string }\n | {\n kind: \"message\";\n text: string;\n teamId: string;\n channel: string;\n ts: string;\n threadTs: string;\n eventId: string;\n userId?: string;\n };\n\nexport interface SlackNormalizeConfig {\n includeEdits?: boolean;\n allowedChannels?: string[];\n}\n\nexport function normalizeSlackEvent(\n body: unknown,\n cfg: SlackNormalizeConfig = {},\n): SlackNormalizedEvent {\n if (!body || typeof body !== \"object\") return { kind: \"noop\", reason: \"invalid_body\" };\n const payload = body as Record<string, unknown>;\n if (payload.type === \"url_verification\") {\n const challenge = stringValue(payload.challenge);\n return challenge\n ? { kind: \"challenge\", challenge }\n : { kind: \"noop\", reason: \"missing_challenge\" };\n }\n if (payload.type !== \"event_callback\") return { kind: \"noop\", reason: \"unsupported_type\" };\n\n const event = objectValue(payload.event);\n if (!event) return { kind: \"noop\", reason: \"missing_event\" };\n const eventType = stringValue(event.type);\n if (eventType !== \"app_mention\" && eventType !== \"message\") {\n return { kind: \"noop\", reason: \"unsupported_event\" };\n }\n if (event.bot_id || event.subtype === \"bot_message\")\n return { kind: \"noop\", reason: \"bot_message\" };\n if (event.subtype === \"message_changed\" && !cfg.includeEdits) {\n return { kind: \"noop\", reason: \"edit_ignored\" };\n }\n\n const channel = stringValue(event.channel);\n if (!channel) return { kind: \"noop\", reason: \"missing_channel\" };\n if (cfg.allowedChannels?.length && !cfg.allowedChannels.includes(channel)) {\n return { kind: \"noop\", reason: \"channel_not_allowed\" };\n }\n\n const teamId = stringValue(payload.team_id) ?? stringValue(event.team);\n const ts = stringValue(event.ts);\n const text = stringValue(event.text) ?? \"\";\n const eventId =\n stringValue(payload.event_id) ?? `${teamId ?? \"team\"}-${channel}-${ts ?? \"event\"}`;\n if (!teamId || !ts || text.trim().length === 0) return { kind: \"noop\", reason: \"missing_fields\" };\n const userId = stringValue(event.user);\n\n return {\n kind: \"message\",\n text,\n teamId,\n channel,\n ts,\n threadTs: stringValue(event.thread_ts) ?? ts,\n eventId,\n ...(userId ? { userId } : {}),\n };\n}\n\nfunction objectValue(value: unknown): Record<string, unknown> | null {\n return value && typeof value === \"object\" && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : null;\n}\n\nfunction stringValue(value: unknown): string | undefined {\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n","import type { LocalToolHandler } from \"@render-harness/core\";\nimport { WebClient } from \"@slack/web-api\";\n\nexport type SlackAccessMode = \"read\" | \"read_write\";\n\ninterface ResolvedUser {\n id: string;\n name?: string;\n real_name?: string;\n display_name?: string;\n is_bot?: boolean;\n}\n\ninterface ResolvedChannel {\n id: string;\n name?: string;\n is_private?: boolean;\n is_archived?: boolean;\n}\n\ninterface SlackResolver {\n user: (id: string) => Promise<ResolvedUser>;\n channel: (id: string) => Promise<ResolvedChannel>;\n}\n\nexport function slackTools(args: {\n botToken: string;\n accessMode: SlackAccessMode;\n allowedChannels?: string[];\n}): LocalToolHandler[] {\n const client = new WebClient(args.botToken);\n const resolver = createResolver(client);\n\n const tools: LocalToolHandler[] = [\n jsonTool(\n \"slack.get_thread\",\n \"Read a Slack thread's messages. Responses include resolved user display names and a rewritten text_resolved field.\",\n objectSchema({\n channel: { type: \"string\" },\n thread_ts: { type: \"string\" },\n }),\n async (input) => {\n const { channel, thread_ts } = input as { channel: string; thread_ts: string };\n assertAllowedChannel(channel, args.allowedChannels);\n const resp = await client.conversations.replies({ channel, ts: thread_ts });\n return enrichConversationResponse(resp, channel, resolver);\n },\n ),\n jsonTool(\n \"slack.get_channel_history\",\n \"Read recent Slack channel messages. Responses include resolved user display names and a rewritten text_resolved field.\",\n objectSchema({\n channel: { type: \"string\" },\n limit: { type: \"number\", optional: true },\n }),\n async (input) => {\n const { channel, limit } = input as { channel: string; limit?: number };\n assertAllowedChannel(channel, args.allowedChannels);\n const resp = await client.conversations.history({ channel, limit: limit ?? 20 });\n return enrichConversationResponse(resp, channel, resolver);\n },\n ),\n jsonTool(\n \"slack.get_user_info\",\n \"Resolve a Slack user ID (e.g. U0B4357MH7H) to a display name, real name, and handle.\",\n objectSchema({\n user: { type: \"string\" },\n }),\n async (input) => {\n const { user } = input as { user: string };\n return resolver.user(user);\n },\n ),\n jsonTool(\n \"slack.get_channel_info\",\n \"Resolve a Slack channel ID (e.g. C0AQHA6M3PS) to a channel name and metadata.\",\n objectSchema({\n channel: { type: \"string\" },\n }),\n async (input) => {\n const { channel } = input as { channel: string };\n assertAllowedChannel(channel, args.allowedChannels);\n return resolver.channel(channel);\n },\n ),\n ];\n\n if (args.accessMode === \"read_write\") {\n tools.push(\n jsonTool(\n \"slack.send_message\",\n \"Send a Slack message, optionally as a thread reply.\",\n objectSchema({\n channel: { type: \"string\" },\n text: { type: \"string\" },\n thread_ts: { type: \"string\", optional: true },\n }),\n async (input) => {\n const { channel, text, thread_ts } = input as {\n channel: string;\n text: string;\n thread_ts?: string;\n };\n assertAllowedChannel(channel, args.allowedChannels);\n return client.chat.postMessage({\n channel,\n text,\n ...(thread_ts ? { thread_ts } : {}),\n });\n },\n ),\n jsonTool(\n \"slack.add_reaction\",\n \"Add a reaction to a Slack message.\",\n objectSchema({\n channel: { type: \"string\" },\n ts: { type: \"string\" },\n name: { type: \"string\" },\n }),\n async (input) => {\n const { channel, ts, name } = input as { channel: string; ts: string; name: string };\n assertAllowedChannel(channel, args.allowedChannels);\n return client.reactions.add({ channel, timestamp: ts, name });\n },\n ),\n jsonTool(\n \"slack.update_message\",\n \"Update a Slack message.\",\n objectSchema({\n channel: { type: \"string\" },\n ts: { type: \"string\" },\n text: { type: \"string\" },\n }),\n async (input) => {\n const { channel, ts, text } = input as { channel: string; ts: string; text: string };\n assertAllowedChannel(channel, args.allowedChannels);\n return client.chat.update({ channel, ts, text });\n },\n ),\n );\n }\n\n return tools;\n}\n\nfunction jsonTool(\n name: string,\n description: string,\n inputSchema: Record<string, unknown>,\n call: (input: unknown) => Promise<unknown>,\n): LocalToolHandler {\n return {\n definition: { name, description, inputSchema, source: \"pack:cap-slack\" },\n handler: async ({ input }) => {\n try {\n return { content: JSON.stringify(await call(input), null, 2) };\n } catch (err) {\n return { content: err instanceof Error ? err.message : String(err), isError: true };\n }\n },\n };\n}\n\nfunction assertAllowedChannel(channel: string, allowedChannels: string[] | undefined): void {\n if (allowedChannels?.length && !allowedChannels.includes(channel)) {\n throw new Error(`Slack channel \"${channel}\" is not allowed by cap-slack config`);\n }\n}\n\nfunction objectSchema(properties: Record<string, unknown>) {\n return {\n type: \"object\",\n additionalProperties: false,\n properties,\n required: Object.entries(properties)\n .filter(([, value]) => !(value as { optional?: boolean }).optional)\n .map(([key]) => key),\n };\n}\n\nfunction createResolver(client: WebClient): SlackResolver {\n const users = new Map<string, ResolvedUser>();\n const channels = new Map<string, ResolvedChannel>();\n const pendingUsers = new Map<string, Promise<ResolvedUser>>();\n const pendingChannels = new Map<string, Promise<ResolvedChannel>>();\n\n return {\n async user(id: string): Promise<ResolvedUser> {\n const cached = users.get(id);\n if (cached) return cached;\n const pending = pendingUsers.get(id);\n if (pending) return pending;\n const fetchOne = (async (): Promise<ResolvedUser> => {\n try {\n const resp = await client.users.info({ user: id });\n const raw = (resp as unknown as { user?: Record<string, unknown> }).user;\n const info = buildResolvedUser(id, raw);\n users.set(id, info);\n return info;\n } catch {\n const info: ResolvedUser = { id };\n users.set(id, info);\n return info;\n } finally {\n pendingUsers.delete(id);\n }\n })();\n pendingUsers.set(id, fetchOne);\n return fetchOne;\n },\n async channel(id: string): Promise<ResolvedChannel> {\n const cached = channels.get(id);\n if (cached) return cached;\n const pending = pendingChannels.get(id);\n if (pending) return pending;\n const fetchOne = (async (): Promise<ResolvedChannel> => {\n try {\n const resp = await client.conversations.info({ channel: id });\n const raw = (resp as unknown as { channel?: Record<string, unknown> }).channel;\n const info = buildResolvedChannel(id, raw);\n channels.set(id, info);\n return info;\n } catch {\n const info: ResolvedChannel = { id };\n channels.set(id, info);\n return info;\n } finally {\n pendingChannels.delete(id);\n }\n })();\n pendingChannels.set(id, fetchOne);\n return fetchOne;\n },\n };\n}\n\nfunction buildResolvedUser(id: string, raw: Record<string, unknown> | undefined): ResolvedUser {\n if (!raw) return { id };\n const profile = (raw.profile as Record<string, unknown> | undefined) ?? undefined;\n const info: ResolvedUser = { id };\n const name = typeof raw.name === \"string\" ? raw.name : undefined;\n if (name) info.name = name;\n const realName =\n typeof raw.real_name === \"string\"\n ? raw.real_name\n : typeof profile?.real_name === \"string\"\n ? (profile.real_name as string)\n : undefined;\n if (realName) info.real_name = realName;\n const displayName =\n typeof profile?.display_name === \"string\" && (profile.display_name as string).length > 0\n ? (profile.display_name as string)\n : undefined;\n if (displayName) info.display_name = displayName;\n if (raw.is_bot === true) info.is_bot = true;\n return info;\n}\n\nfunction buildResolvedChannel(\n id: string,\n raw: Record<string, unknown> | undefined,\n): ResolvedChannel {\n if (!raw) return { id };\n const info: ResolvedChannel = { id };\n if (typeof raw.name === \"string\") info.name = raw.name;\n if (raw.is_private === true) info.is_private = true;\n if (raw.is_archived === true) info.is_archived = true;\n return info;\n}\n\nfunction displayLabel(user: ResolvedUser | undefined): string | undefined {\n return user?.display_name ?? user?.real_name ?? user?.name;\n}\n\nconst USER_MENTION = /<@(U[A-Z0-9]+)(?:\\|[^>]+)?>/g;\nconst CHANNEL_MENTION = /<#(C[A-Z0-9]+)(?:\\|([^>]+))?>/g;\n\nasync function enrichConversationResponse(\n resp: unknown,\n channelId: string,\n resolver: SlackResolver,\n): Promise<unknown> {\n if (!resp || typeof resp !== \"object\") return resp;\n const body = resp as Record<string, unknown>;\n const rawMessages = Array.isArray(body.messages) ? (body.messages as unknown[]) : [];\n const userIds = new Set<string>();\n const channelIds = new Set<string>([channelId]);\n for (const msg of rawMessages) {\n if (!msg || typeof msg !== \"object\") continue;\n const m = msg as Record<string, unknown>;\n if (typeof m.user === \"string\") userIds.add(m.user);\n if (typeof m.text === \"string\") collectMentions(m.text, userIds, channelIds);\n }\n\n const [resolvedUserList, resolvedChannelList] = await Promise.all([\n Promise.all([...userIds].map(async (id) => [id, await resolver.user(id)] as const)),\n Promise.all([...channelIds].map(async (id) => [id, await resolver.channel(id)] as const)),\n ]);\n const usersById = new Map<string, ResolvedUser>(resolvedUserList);\n const channelsById = new Map<string, ResolvedChannel>(resolvedChannelList);\n\n const enrichedMessages = rawMessages.map((msg) => {\n if (!msg || typeof msg !== \"object\") return msg;\n const m = msg as Record<string, unknown>;\n const out: Record<string, unknown> = { ...m };\n if (typeof m.user === \"string\") {\n const label = displayLabel(usersById.get(m.user));\n if (label) out.user_display_name = label;\n }\n if (typeof m.text === \"string\") {\n out.text_resolved = rewriteMentions(m.text, usersById, channelsById);\n }\n return out;\n });\n\n const resolvedUsers: Record<string, ResolvedUser> = {};\n for (const [id, info] of usersById) resolvedUsers[id] = info;\n const channelInfo = channelsById.get(channelId);\n\n return {\n ...body,\n messages: enrichedMessages,\n resolved_users: resolvedUsers,\n ...(channelInfo ? { resolved_channel: channelInfo } : {}),\n };\n}\n\nfunction collectMentions(text: string, userIds: Set<string>, channelIds: Set<string>): void {\n for (const match of text.matchAll(USER_MENTION)) {\n const id = match[1];\n if (id) userIds.add(id);\n }\n for (const match of text.matchAll(CHANNEL_MENTION)) {\n const id = match[1];\n if (id) channelIds.add(id);\n }\n}\n\nfunction rewriteMentions(\n text: string,\n users: Map<string, ResolvedUser>,\n channels: Map<string, ResolvedChannel>,\n): string {\n return text\n .replace(USER_MENTION, (_match, id: string) => {\n const label = displayLabel(users.get(id));\n return label ? `@${label}` : `<@${id}>`;\n })\n .replace(CHANNEL_MENTION, (_match, id: string, fallbackName?: string) => {\n const name = channels.get(id)?.name ?? fallbackName;\n return name ? `#${name}` : `<#${id}>`;\n });\n}\n","import { createHmac, timingSafeEqual } from \"node:crypto\";\n\nconst FIVE_MINUTES_SECONDS = 60 * 5;\n\nexport function verifySlackSignature(args: {\n rawBody: string;\n signingSecret: string;\n signature: string | null;\n timestamp: string | null;\n nowSeconds?: number;\n}): boolean {\n if (!args.signature?.startsWith(\"v0=\") || !args.timestamp) return false;\n const ts = Number(args.timestamp);\n if (!Number.isFinite(ts)) return false;\n const now = args.nowSeconds ?? Math.floor(Date.now() / 1000);\n if (Math.abs(now - ts) > FIVE_MINUTES_SECONDS) return false;\n\n const base = `v0:${args.timestamp}:${args.rawBody}`;\n const expected = `v0=${createHmac(\"sha256\", args.signingSecret).update(base).digest(\"hex\")}`;\n const expectedBuf = Buffer.from(expected, \"utf8\");\n const actualBuf = Buffer.from(args.signature, \"utf8\");\n if (expectedBuf.length !== actualBuf.length) return false;\n return timingSafeEqual(expectedBuf, actualBuf);\n}\n","import { createHash } from \"node:crypto\";\nimport type { LocalToolHandler } from \"@render-harness/core\";\nimport { type ConnectorContribution, definePack, type PackContext } from \"@render-harness/registry\";\nimport pkg from \"../package.json\" with { type: \"json\" };\nimport { slackConversationId } from \"./convid.js\";\nimport { normalizeSlackEvent, type SlackNormalizeConfig } from \"./normalize.js\";\nimport { type SlackAccessMode, slackTools } from \"./tools.js\";\nimport { verifySlackSignature } from \"./verify.js\";\n\ninterface SlackConfig extends SlackNormalizeConfig {\n agent?: string;\n userId?: string;\n signingSecretEnv?: string;\n botTokenEnv?: string;\n accessMode?: SlackAccessMode;\n}\n\nconst DEFAULT_SIGNING_SECRET_ENV = \"SLACK_SIGNING_SECRET\";\nconst DEFAULT_BOT_TOKEN_ENV = \"SLACK_BOT_TOKEN\";\n\nconst pack = definePack({\n name: \"cap-slack\",\n version: pkg.version,\n envSchema: [\n {\n name: DEFAULT_SIGNING_SECRET_ENV,\n required: true,\n secret: true,\n description: \"Slack signing secret used to verify Events API requests.\",\n },\n {\n name: DEFAULT_BOT_TOKEN_ENV,\n required: true,\n secret: true,\n description: \"Slack bot token used for read tools and optional write tools.\",\n },\n ],\n localTools(ctx: PackContext): LocalToolHandler[] {\n const cfg = readConfig(ctx.config);\n const botToken = ctx.env(cfg.botTokenEnv);\n if (!botToken) return [];\n return slackTools({\n botToken,\n accessMode: cfg.accessMode,\n ...(cfg.allowedChannels ? { allowedChannels: cfg.allowedChannels } : {}),\n });\n },\n connectors(ctx: PackContext): ConnectorContribution[] {\n const cfg = readConfig(ctx.config);\n return [\n {\n key: \"slack\",\n webhook: async (req, webCtx) => {\n const rawBody = await req.text();\n const signingSecret = ctx.env(cfg.signingSecretEnv);\n if (!signingSecret) {\n return json({ error: \"missing_secret\", env: cfg.signingSecretEnv }, 500);\n }\n if (\n !verifySlackSignature({\n rawBody,\n signingSecret,\n signature: req.headers.get(\"x-slack-signature\"),\n timestamp: req.headers.get(\"x-slack-request-timestamp\"),\n })\n ) {\n return json({ error: \"invalid_signature\" }, 401);\n }\n\n const parsed = parseBody(rawBody);\n const normalized = normalizeSlackEvent(parsed, cfg);\n if (normalized.kind === \"challenge\") return json({ challenge: normalized.challenge });\n if (normalized.kind === \"noop\")\n return json({ ok: true, skipped: true, reason: normalized.reason });\n\n const agent = webCtx.resolveAgent(cfg.agent);\n const conversationId = slackConversationId({\n teamId: normalized.teamId,\n channel: normalized.channel,\n threadTs: normalized.threadTs,\n });\n const result = await webCtx.enqueueIntoConversation({\n conversationId,\n agentName: agent.name,\n agentVersion: agent.version,\n userId: cfg.userId ?? \"cap-slack\",\n runId: `slack-${hash(normalized.eventId)}`,\n title: `Slack ${normalized.channel}/${normalized.threadTs}`,\n initialContent: [{ type: \"text\", text: normalized.text }],\n metadata: {\n connector: \"cap-slack\",\n teamId: normalized.teamId,\n channel: normalized.channel,\n threadTs: normalized.threadTs,\n ts: normalized.ts,\n ...(normalized.userId ? { userId: normalized.userId } : {}),\n },\n });\n return json(result, result.status === \"enqueued\" ? 202 : 200);\n },\n },\n ];\n },\n});\n\nexport default pack;\n\ntype ResolvedSlackConfig = Required<\n Pick<SlackConfig, \"signingSecretEnv\" | \"botTokenEnv\" | \"accessMode\" | \"includeEdits\">\n> &\n Omit<SlackConfig, \"signingSecretEnv\" | \"botTokenEnv\" | \"accessMode\" | \"includeEdits\">;\n\nfunction readConfig(raw: Record<string, unknown>): ResolvedSlackConfig {\n const cfg: ResolvedSlackConfig = {\n signingSecretEnv: stringValue(raw.signingSecretEnv) ?? DEFAULT_SIGNING_SECRET_ENV,\n botTokenEnv: stringValue(raw.botTokenEnv) ?? DEFAULT_BOT_TOKEN_ENV,\n accessMode: raw.accessMode === \"read_write\" ? \"read_write\" : \"read\",\n includeEdits: raw.includeEdits === true,\n };\n const agent = stringValue(raw.agent);\n if (agent) cfg.agent = agent;\n const userId = stringValue(raw.userId);\n if (userId) cfg.userId = userId;\n const allowedChannels = stringArray(raw.allowedChannels);\n if (allowedChannels) cfg.allowedChannels = allowedChannels;\n return cfg;\n}\n\nfunction parseBody(rawBody: string): unknown {\n try {\n return JSON.parse(rawBody);\n } catch {\n return null;\n }\n}\n\nfunction hash(value: string): string {\n return createHash(\"sha256\").update(value).digest(\"hex\").slice(0, 32);\n}\n\nfunction json(body: unknown, status = 200): Response {\n return Response.json(body, { status });\n}\n\nfunction stringValue(value: unknown): string | undefined {\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n\nfunction stringArray(value: unknown): string[] | undefined {\n return Array.isArray(value)\n ? value.filter((item): item is string => typeof item === \"string\")\n : undefined;\n}\n"]}
1
+ {"version":3,"sources":["../package.json","../src/convid.ts","../src/normalize.ts","../src/tools.ts","../src/verify.ts","../src/index.ts"],"names":["stringValue","createHash"],"mappings":";;;;;;;AAAA,IAAA,eAAA,GAAA;AAAA,EAEE,OAAA,EAAW,OAmDb,CAAA;ACnDO,SAAS,oBAAoB,IAAA,EAIzB;AACT,EAAA,MAAM,MAAA,GAAS,WAAW,QAAQ,CAAA,CAC/B,OAAO,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,CAAA,EAAI,IAAA,CAAK,OAAO,CAAA,CAAA,EAAI,IAAA,CAAK,QAAQ,CAAA,CAAE,CAAA,CACxD,OAAO,KAAK,CAAA,CACZ,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA;AACd,EAAA,OAAO,SAAS,MAAM,CAAA,CAAA;AACxB;;;ACOO,SAAS,mBAAA,CACd,IAAA,EACA,GAAA,GAA4B,EAAC,EACP;AACtB,EAAA,IAAI,CAAC,IAAA,IAAQ,OAAO,IAAA,KAAS,QAAA,SAAiB,EAAE,IAAA,EAAM,MAAA,EAAQ,MAAA,EAAQ,cAAA,EAAe;AACrF,EAAA,MAAM,OAAA,GAAU,IAAA;AAChB,EAAA,IAAI,OAAA,CAAQ,SAAS,kBAAA,EAAoB;AACvC,IAAA,MAAM,SAAA,GAAY,WAAA,CAAY,OAAA,CAAQ,SAAS,CAAA;AAC/C,IAAA,OAAO,SAAA,GACH,EAAE,IAAA,EAAM,WAAA,EAAa,SAAA,KACrB,EAAE,IAAA,EAAM,MAAA,EAAQ,MAAA,EAAQ,mBAAA,EAAoB;AAAA,EAClD;AACA,EAAA,IAAI,OAAA,CAAQ,SAAS,gBAAA,EAAkB,OAAO,EAAE,IAAA,EAAM,MAAA,EAAQ,QAAQ,kBAAA,EAAmB;AAEzF,EAAA,MAAM,KAAA,GAAQ,WAAA,CAAY,OAAA,CAAQ,KAAK,CAAA;AACvC,EAAA,IAAI,CAAC,KAAA,EAAO,OAAO,EAAE,IAAA,EAAM,MAAA,EAAQ,QAAQ,eAAA,EAAgB;AAC3D,EAAA,MAAM,SAAA,GAAY,WAAA,CAAY,KAAA,CAAM,IAAI,CAAA;AACxC,EAAA,IAAI,SAAA,KAAc,aAAA,IAAiB,SAAA,KAAc,SAAA,EAAW;AAC1D,IAAA,OAAO,EAAE,IAAA,EAAM,MAAA,EAAQ,MAAA,EAAQ,mBAAA,EAAoB;AAAA,EACrD;AACA,EAAA,IAAI,KAAA,CAAM,MAAA,IAAU,KAAA,CAAM,OAAA,KAAY,aAAA;AACpC,IAAA,OAAO,EAAE,IAAA,EAAM,MAAA,EAAQ,MAAA,EAAQ,aAAA,EAAc;AAC/C,EAAA,IAAI,KAAA,CAAM,OAAA,KAAY,iBAAA,IAAqB,CAAC,IAAI,YAAA,EAAc;AAC5D,IAAA,OAAO,EAAE,IAAA,EAAM,MAAA,EAAQ,MAAA,EAAQ,cAAA,EAAe;AAAA,EAChD;AAEA,EAAA,MAAM,OAAA,GAAU,WAAA,CAAY,KAAA,CAAM,OAAO,CAAA;AACzC,EAAA,IAAI,CAAC,OAAA,EAAS,OAAO,EAAE,IAAA,EAAM,MAAA,EAAQ,QAAQ,iBAAA,EAAkB;AAC/D,EAAA,IAAI,GAAA,CAAI,iBAAiB,MAAA,IAAU,CAAC,IAAI,eAAA,CAAgB,QAAA,CAAS,OAAO,CAAA,EAAG;AACzE,IAAA,OAAO,EAAE,IAAA,EAAM,MAAA,EAAQ,MAAA,EAAQ,qBAAA,EAAsB;AAAA,EACvD;AAEA,EAAA,MAAM,SAAS,WAAA,CAAY,OAAA,CAAQ,OAAO,CAAA,IAAK,WAAA,CAAY,MAAM,IAAI,CAAA;AACrE,EAAA,MAAM,EAAA,GAAK,WAAA,CAAY,KAAA,CAAM,EAAE,CAAA;AAC/B,EAAA,MAAM,IAAA,GAAO,WAAA,CAAY,KAAA,CAAM,IAAI,CAAA,IAAK,EAAA;AACxC,EAAA,MAAM,OAAA,GACJ,WAAA,CAAY,OAAA,CAAQ,QAAQ,CAAA,IAAK,CAAA,EAAG,MAAA,IAAU,MAAM,CAAA,CAAA,EAAI,OAAO,CAAA,CAAA,EAAI,EAAA,IAAM,OAAO,CAAA,CAAA;AAClF,EAAA,IAAI,CAAC,MAAA,IAAU,CAAC,EAAA,IAAM,KAAK,IAAA,EAAK,CAAE,MAAA,KAAW,CAAA,EAAG,OAAO,EAAE,IAAA,EAAM,MAAA,EAAQ,QAAQ,gBAAA,EAAiB;AAChG,EAAA,MAAM,MAAA,GAAS,WAAA,CAAY,KAAA,CAAM,IAAI,CAAA;AAErC,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,SAAA;AAAA,IACN,IAAA;AAAA,IACA,MAAA;AAAA,IACA,OAAA;AAAA,IACA,EAAA;AAAA,IACA,QAAA,EAAU,WAAA,CAAY,KAAA,CAAM,SAAS,CAAA,IAAK,EAAA;AAAA,IAC1C,OAAA;AAAA,IACA,GAAI,MAAA,GAAS,EAAE,MAAA,KAAW;AAAC,GAC7B;AACF;AAEA,SAAS,YAAY,KAAA,EAAgD;AACnE,EAAA,OAAO,KAAA,IAAS,OAAO,KAAA,KAAU,QAAA,IAAY,CAAC,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,GAC5D,KAAA,GACD,IAAA;AACN;AAEA,SAAS,YAAY,KAAA,EAAoC;AACvD,EAAA,OAAO,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,CAAM,MAAA,GAAS,IAAI,KAAA,GAAQ,MAAA;AACjE;ACrEA,IAAM,wBAAA,GAA2B,IAAA;AACjC,IAAM,oBAAA,GAAqC;AAAA,EACzC,OAAA,EAAS,CAAA;AAAA,EACT,MAAA,EAAQ,CAAA;AAAA,EACR,UAAA,EAAY,GAAA;AAAA,EACZ,UAAA,EAAY;AACd,CAAA;AA2CA,IAAM,kBAAA,GAAqB,qBAAA;AAC3B,IAAM,eAAA,GAAkB,iBAAA;AAEjB,SAAS,WAAW,IAAA,EAIJ;AACrB,EAAA,MAAM,MAAA,GAAS,IAAI,SAAA,CAAU,IAAA,CAAK,QAAA,EAAU;AAAA,IAC1C,OAAA,EAAS,wBAAA;AAAA,IACT,WAAA,EAAa;AAAA,GACd,CAAA;AACD,EAAA,MAAM,QAAA,GAAW,eAAe,MAAM,CAAA;AAEtC,EAAA,MAAM,KAAA,GAA4B;AAAA,IAChC,QAAA;AAAA,MACE,kBAAA;AAAA,MACA,kOAAA;AAAA,MACA,YAAA,CAAa;AAAA,QACX,OAAA,EAAS,EAAE,IAAA,EAAM,QAAA,EAAS;AAAA,QAC1B,SAAA,EAAW,EAAE,IAAA,EAAM,QAAA;AAAS,OAC7B,CAAA;AAAA,MACD,OAAO,KAAA,KAAU;AACf,QAAA,MAAM,EAAE,OAAA,EAAS,SAAA,EAAU,GAAI,KAAA;AAC/B,QAAA,MAAM,SAAA,GAAY,MAAM,QAAA,CAAS,YAAA,CAAa,OAAO,CAAA;AACrD,QAAA,oBAAA,CAAqB,SAAA,EAAW,KAAK,eAAe,CAAA;AACpD,QAAA,MAAM,IAAA,GAAO,MAAM,MAAA,CAAO,aAAA,CAAc,OAAA,CAAQ,EAAE,OAAA,EAAS,SAAA,EAAW,EAAA,EAAI,SAAA,EAAW,CAAA;AACrF,QAAA,OAAO,0BAAA,CAA2B,IAAA,EAAM,SAAA,EAAW,QAAQ,CAAA;AAAA,MAC7D;AAAA,KACF;AAAA,IACA,QAAA;AAAA,MACE,2BAAA;AAAA,MACA,sOAAA;AAAA,MACA,YAAA,CAAa;AAAA,QACX,OAAA,EAAS,EAAE,IAAA,EAAM,QAAA,EAAS;AAAA,QAC1B,KAAA,EAAO,EAAE,IAAA,EAAM,QAAA,EAAU,UAAU,IAAA;AAAK,OACzC,CAAA;AAAA,MACD,OAAO,KAAA,KAAU;AACf,QAAA,MAAM,EAAE,OAAA,EAAS,KAAA,EAAM,GAAI,KAAA;AAC3B,QAAA,MAAM,SAAA,GAAY,MAAM,QAAA,CAAS,YAAA,CAAa,OAAO,CAAA;AACrD,QAAA,oBAAA,CAAqB,SAAA,EAAW,KAAK,eAAe,CAAA;AACpD,QAAA,MAAM,IAAA,GAAO,MAAM,MAAA,CAAO,aAAA,CAAc,OAAA,CAAQ;AAAA,UAC9C,OAAA,EAAS,SAAA;AAAA,UACT,OAAO,KAAA,IAAS;AAAA,SACjB,CAAA;AACD,QAAA,OAAO,0BAAA,CAA2B,IAAA,EAAM,SAAA,EAAW,QAAQ,CAAA;AAAA,MAC7D;AAAA,KACF;AAAA,IACA,QAAA;AAAA,MACE,qBAAA;AAAA,MACA,+HAAA;AAAA,MACA,YAAA,CAAa;AAAA,QACX,IAAA,EAAM,EAAE,IAAA,EAAM,QAAA;AAAS,OACxB,CAAA;AAAA,MACD,OAAO,KAAA,KAAU;AACf,QAAA,MAAM,EAAE,MAAK,GAAI,KAAA;AACjB,QAAA,MAAM,MAAA,GAAS,MAAM,QAAA,CAAS,SAAA,CAAU,IAAI,CAAA;AAC5C,QAAA,OAAO,QAAA,CAAS,KAAK,MAAM,CAAA;AAAA,MAC7B;AAAA,KACF;AAAA,IACA,QAAA;AAAA,MACE,wBAAA;AAAA,MACA,yIAAA;AAAA,MACA,YAAA,CAAa;AAAA,QACX,OAAA,EAAS,EAAE,IAAA,EAAM,QAAA;AAAS,OAC3B,CAAA;AAAA,MACD,OAAO,KAAA,KAAU;AACf,QAAA,MAAM,EAAE,SAAQ,GAAI,KAAA;AACpB,QAAA,MAAM,SAAA,GAAY,MAAM,QAAA,CAAS,YAAA,CAAa,OAAO,CAAA;AACrD,QAAA,oBAAA,CAAqB,SAAA,EAAW,KAAK,eAAe,CAAA;AACpD,QAAA,OAAO,QAAA,CAAS,QAAQ,SAAS,CAAA;AAAA,MACnC;AAAA;AACF,GACF;AAEA,EAAA,IAAI,IAAA,CAAK,eAAe,YAAA,EAAc;AACpC,IAAA,KAAA,CAAM,IAAA;AAAA,MACJ,QAAA;AAAA,QACE,oBAAA;AAAA,QACA,mKAAA;AAAA,QACA,YAAA,CAAa;AAAA,UACX,OAAA,EAAS,EAAE,IAAA,EAAM,QAAA,EAAS;AAAA,UAC1B,IAAA,EAAM,EAAE,IAAA,EAAM,QAAA,EAAS;AAAA,UACvB,SAAA,EAAW,EAAE,IAAA,EAAM,QAAA,EAAU,UAAU,IAAA;AAAK,SAC7C,CAAA;AAAA,QACD,OAAO,KAAA,KAAU;AACf,UAAA,MAAM,EAAE,OAAA,EAAS,IAAA,EAAM,SAAA,EAAU,GAAI,KAAA;AAKrC,UAAA,MAAM,SAAA,GAAY,MAAM,QAAA,CAAS,YAAA,CAAa,OAAO,CAAA;AACrD,UAAA,oBAAA,CAAqB,SAAA,EAAW,KAAK,eAAe,CAAA;AACpD,UAAA,OAAO,MAAA,CAAO,KAAK,WAAA,CAAY;AAAA,YAC7B,OAAA,EAAS,SAAA;AAAA,YACT,IAAA;AAAA,YACA,GAAI,SAAA,GAAY,EAAE,SAAA,KAAc;AAAC,WAClC,CAAA;AAAA,QACH;AAAA,OACF;AAAA,MACA,QAAA;AAAA,QACE,oBAAA;AAAA,QACA,2GAAA;AAAA,QACA,YAAA,CAAa;AAAA,UACX,OAAA,EAAS,EAAE,IAAA,EAAM,QAAA,EAAS;AAAA,UAC1B,EAAA,EAAI,EAAE,IAAA,EAAM,QAAA,EAAS;AAAA,UACrB,IAAA,EAAM,EAAE,IAAA,EAAM,QAAA;AAAS,SACxB,CAAA;AAAA,QACD,OAAO,KAAA,KAAU;AACf,UAAA,MAAM,EAAE,OAAA,EAAS,EAAA,EAAI,IAAA,EAAK,GAAI,KAAA;AAC9B,UAAA,MAAM,SAAA,GAAY,MAAM,QAAA,CAAS,YAAA,CAAa,OAAO,CAAA;AACrD,UAAA,oBAAA,CAAqB,SAAA,EAAW,KAAK,eAAe,CAAA;AACpD,UAAA,OAAO,MAAA,CAAO,UAAU,GAAA,CAAI,EAAE,SAAS,SAAA,EAAW,SAAA,EAAW,EAAA,EAAI,IAAA,EAAM,CAAA;AAAA,QACzE;AAAA,OACF;AAAA,MACA,QAAA;AAAA,QACE,sBAAA;AAAA,QACA,gGAAA;AAAA,QACA,YAAA,CAAa;AAAA,UACX,OAAA,EAAS,EAAE,IAAA,EAAM,QAAA,EAAS;AAAA,UAC1B,EAAA,EAAI,EAAE,IAAA,EAAM,QAAA,EAAS;AAAA,UACrB,IAAA,EAAM,EAAE,IAAA,EAAM,QAAA;AAAS,SACxB,CAAA;AAAA,QACD,OAAO,KAAA,KAAU;AACf,UAAA,MAAM,EAAE,OAAA,EAAS,EAAA,EAAI,IAAA,EAAK,GAAI,KAAA;AAC9B,UAAA,MAAM,SAAA,GAAY,MAAM,QAAA,CAAS,YAAA,CAAa,OAAO,CAAA;AACrD,UAAA,oBAAA,CAAqB,SAAA,EAAW,KAAK,eAAe,CAAA;AACpD,UAAA,OAAO,MAAA,CAAO,KAAK,MAAA,CAAO,EAAE,SAAS,SAAA,EAAW,EAAA,EAAI,MAAM,CAAA;AAAA,QAC5D;AAAA;AACF,KACF;AAAA,EACF;AAEA,EAAA,OAAO,KAAA;AACT;AAEA,SAAS,QAAA,CACP,IAAA,EACA,WAAA,EACA,WAAA,EACA,IAAA,EACkB;AAClB,EAAA,OAAO;AAAA,IACL,YAAY,EAAE,IAAA,EAAM,WAAA,EAAa,WAAA,EAAa,QAAQ,gBAAA,EAAiB;AAAA,IACvE,OAAA,EAAS,OAAO,EAAE,KAAA,EAAM,KAAM;AAC5B,MAAA,IAAI;AACF,QAAA,OAAO,EAAE,OAAA,EAAS,IAAA,CAAK,SAAA,CAAU,MAAM,KAAK,KAAK,CAAA,EAAG,IAAA,EAAM,CAAC,CAAA,EAAE;AAAA,MAC/D,SAAS,GAAA,EAAK;AACZ,QAAA,OAAO,EAAE,OAAA,EAAS,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,UAAU,MAAA,CAAO,GAAG,CAAA,EAAG,OAAA,EAAS,IAAA,EAAK;AAAA,MACpF;AAAA,IACF;AAAA,GACF;AACF;AAEA,SAAS,oBAAA,CAAqB,SAAiB,eAAA,EAA6C;AAC1F,EAAA,IAAI,iBAAiB,MAAA,IAAU,CAAC,eAAA,CAAgB,QAAA,CAAS,OAAO,CAAA,EAAG;AACjE,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,eAAA,EAAkB,OAAO,CAAA,oCAAA,CAAsC,CAAA;AAAA,EACjF;AACF;AAEA,SAAS,aAAa,UAAA,EAAqC;AACzD,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,QAAA;AAAA,IACN,oBAAA,EAAsB,KAAA;AAAA,IACtB,UAAA;AAAA,IACA,QAAA,EAAU,OAAO,OAAA,CAAQ,UAAU,EAChC,MAAA,CAAO,CAAC,GAAG,KAAK,MAAM,CAAE,KAAA,CAAiC,QAAQ,CAAA,CACjE,GAAA,CAAI,CAAC,CAAC,GAAG,MAAM,GAAG;AAAA,GACvB;AACF;AAEA,SAAS,eAAe,MAAA,EAAkC;AACxD,EAAA,MAAM,KAAA,uBAAY,GAAA,EAA0B;AAC5C,EAAA,MAAM,QAAA,uBAAe,GAAA,EAA6B;AAClD,EAAA,MAAM,YAAA,uBAAmB,GAAA,EAAmC;AAC5D,EAAA,MAAM,eAAA,uBAAsB,GAAA,EAAsC;AAQlE,EAAA,IAAI,aAAA,GAAqD,IAAA;AACzD,EAAA,IAAI,UAAA,GAAkD,IAAA;AACtD,EAAA,MAAM,UAAA,uBAAiB,GAAA,EAAoB;AAE3C,EAAA,MAAM,sBAAsB,MAAoC;AAC9D,IAAA,IAAI,eAAe,OAAO,aAAA;AAC1B,IAAA,aAAA,GAAA,CAAiB,YAAY;AAC3B,MAAA,MAAM,MAAA,uBAAa,GAAA,EAAoB;AACvC,MAAA,IAAI,MAAA;AACJ,MAAA,GAAG;AACD,QAAA,MAAM,IAAA,GAAQ,MAAM,MAAA,CAAO,aAAA,CAAc,IAAA,CAAK;AAAA,UAC5C,KAAA,EAAO,GAAA;AAAA,UACP,KAAA,EAAO,gCAAA;AAAA,UACP,gBAAA,EAAkB,IAAA;AAAA,UAClB,GAAI,MAAA,GAAS,EAAE,MAAA,KAAW;AAAC,SAC5B,CAAA;AAID,QAAA,KAAA,MAAW,CAAA,IAAK,IAAA,CAAK,QAAA,IAAY,EAAC,EAAG;AACnC,UAAA,IAAI,OAAO,CAAA,CAAE,EAAA,KAAO,YAAY,OAAO,CAAA,CAAE,SAAS,QAAA,EAAU;AAC1D,YAAA,MAAA,CAAO,IAAI,CAAA,CAAE,IAAA,CAAK,WAAA,EAAY,EAAG,EAAE,EAAE,CAAA;AAGrC,YAAA,IAAI,CAAC,QAAA,CAAS,GAAA,CAAI,CAAA,CAAE,EAAE,GAAG,QAAA,CAAS,GAAA,CAAI,CAAA,CAAE,EAAA,EAAI,EAAE,EAAA,EAAI,CAAA,CAAE,IAAI,IAAA,EAAM,CAAA,CAAE,MAAM,CAAA;AAAA,UACxE;AAAA,QACF;AACA,QAAA,MAAA,GACE,OAAO,IAAA,CAAK,iBAAA,EAAmB,WAAA,KAAgB,QAAA,IAC/C,IAAA,CAAK,iBAAA,CAAkB,WAAA,CAAY,MAAA,GAAS,CAAA,GACxC,IAAA,CAAK,iBAAA,CAAkB,WAAA,GACvB,MAAA;AAAA,MACR,CAAA,QAAS,MAAA;AACT,MAAA,OAAO,MAAA;AAAA,IACT,CAAA,GAAG,CAAE,KAAA,CAAM,CAAC,GAAA,KAAQ;AAGlB,MAAA,aAAA,GAAgB,IAAA;AAChB,MAAA,MAAM,GAAA;AAAA,IACR,CAAC,CAAA;AACD,IAAA,OAAO,aAAA;AAAA,EACT,CAAA;AAEA,EAAA,MAAM,mBAAmB,MAAoC;AAC3D,IAAA,IAAI,YAAY,OAAO,UAAA;AACvB,IAAA,UAAA,GAAA,CAAc,YAAY;AACxB,MAAA,MAAM,QAAA,uBAAe,GAAA,EAAoB;AACzC,MAAA,IAAI,MAAA;AACJ,MAAA,GAAG;AACD,QAAA,MAAM,IAAA,GAAQ,MAAM,MAAA,CAAO,KAAA,CAAM,IAAA,CAAK;AAAA,UACpC,KAAA,EAAO,GAAA;AAAA,UACP,GAAI,MAAA,GAAS,EAAE,MAAA,KAAW;AAAC,SAC5B,CAAA;AAUD,QAAA,KAAA,MAAW,CAAA,IAAK,IAAA,CAAK,OAAA,IAAW,EAAC,EAAG;AAClC,UAAA,IAAI,OAAO,CAAA,CAAE,EAAA,KAAO,QAAA,IAAY,CAAA,CAAE,YAAY,IAAA,EAAM;AAIpD,UAAA,KAAA,MAAW,SAAA,IAAa;AAAA,YACtB,CAAA,CAAE,IAAA;AAAA,YACF,CAAA,CAAE,SAAA;AAAA,YACF,EAAE,OAAA,EAAS,YAAA;AAAA,YACX,EAAE,OAAA,EAAS;AAAA,WACb,EAAG;AACD,YAAA,IAAI,OAAO,SAAA,KAAc,QAAA,IAAY,SAAA,CAAU,SAAS,CAAA,EAAG;AACzD,cAAA,QAAA,CAAS,GAAA,CAAI,SAAA,CAAU,WAAA,EAAY,EAAG,EAAE,EAAE,CAAA;AAAA,YAC5C;AAAA,UACF;AACA,UAAA,IAAI,CAAC,KAAA,CAAM,GAAA,CAAI,CAAA,CAAE,EAAE,CAAA,EAAG;AACpB,YAAA,KAAA,CAAM,IAAI,CAAA,CAAE,EAAA,EAAI,kBAAkB,CAAA,CAAE,EAAA,EAAI,CAAuC,CAAC,CAAA;AAAA,UAClF;AAAA,QACF;AACA,QAAA,MAAA,GACE,OAAO,IAAA,CAAK,iBAAA,EAAmB,WAAA,KAAgB,QAAA,IAC/C,IAAA,CAAK,iBAAA,CAAkB,WAAA,CAAY,MAAA,GAAS,CAAA,GACxC,IAAA,CAAK,iBAAA,CAAkB,WAAA,GACvB,MAAA;AAAA,MACR,CAAA,QAAS,MAAA;AACT,MAAA,OAAO,QAAA;AAAA,IACT,CAAA,GAAG,CAAE,KAAA,CAAM,CAAC,GAAA,KAAQ;AAClB,MAAA,UAAA,GAAa,IAAA;AACb,MAAA,MAAM,GAAA;AAAA,IACR,CAAC,CAAA;AACD,IAAA,OAAO,UAAA;AAAA,EACT,CAAA;AAEA,EAAA,MAAM,SAAA,GAAY,OAAO,MAAA,KAAoC;AAC3D,IAAA,MAAM,MAAA,GAAS,UAAA,CAAW,GAAA,CAAI,MAAM,CAAA;AACpC,IAAA,IAAI,QAAQ,OAAO,MAAA;AACnB,IAAA,MAAM,IAAA,GAAQ,MAAM,MAAA,CAAO,aAAA,CAAc,KAAK,EAAE,KAAA,EAAO,QAAQ,CAAA;AAG/D,IAAA,MAAM,SAAA,GAAY,KAAK,OAAA,EAAS,EAAA;AAChC,IAAA,IAAI,OAAO,cAAc,QAAA,EAAU;AACjC,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,uBAAA,EAA0B,MAAM,CAAA,uBAAA,CAAyB,CAAA;AAAA,IAC3E;AACA,IAAA,UAAA,CAAW,GAAA,CAAI,QAAQ,SAAS,CAAA;AAChC,IAAA,OAAO,SAAA;AAAA,EACT,CAAA;AAEA,EAAA,MAAM,gBAAA,GAAmB,OAAO,KAAA,KAAmC;AACjE,IAAA,MAAM,OAAA,GAAU,MAAM,IAAA,EAAK;AAC3B,IAAA,IAAI,QAAQ,MAAA,KAAW,CAAA,EAAG,MAAM,IAAI,MAAM,2BAA2B,CAAA;AAErE,IAAA,MAAM,OAAA,GAAU,+BAAA,CAAgC,IAAA,CAAK,OAAO,CAAA;AAC5D,IAAA,IAAI,OAAA,GAAU,CAAC,CAAA,EAAG,OAAO,QAAQ,CAAC,CAAA;AAClC,IAAA,IAAI,eAAA,CAAgB,IAAA,CAAK,OAAO,CAAA,EAAG,OAAO,OAAA;AAC1C,IAAA,MAAM,MAAA,GAAS,QAAQ,UAAA,CAAW,GAAG,IAAI,OAAA,CAAQ,KAAA,CAAM,CAAC,CAAA,GAAI,OAAA;AAC5D,IAAA,IAAI,OAAO,MAAA,KAAW,CAAA,EAAG,MAAM,IAAI,MAAM,0CAA0C,CAAA;AACnF,IAAA,MAAM,KAAA,GAAQ,MAAM,gBAAA,EAAiB;AACrC,IAAA,MAAM,EAAA,GAAK,KAAA,CAAM,GAAA,CAAI,MAAA,CAAO,aAAa,CAAA;AACzC,IAAA,IAAI,CAAC,EAAA,EAAI;AACP,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,eAAe,KAAK,CAAA,wEAAA;AAAA,OACtB;AAAA,IACF;AACA,IAAA,OAAO,EAAA;AAAA,EACT,CAAA;AAEA,EAAA,MAAM,mBAAA,GAAsB,OAAO,KAAA,KAAmC;AACpE,IAAA,MAAM,OAAA,GAAU,MAAM,IAAA,EAAK;AAC3B,IAAA,IAAI,QAAQ,MAAA,KAAW,CAAA,EAAG,MAAM,IAAI,MAAM,8BAA8B,CAAA;AAExE,IAAA,MAAM,OAAA,GAAU,mCAAA,CAAoC,IAAA,CAAK,OAAO,CAAA;AAChE,IAAA,IAAI,OAAA,GAAU,CAAC,CAAA,EAAG,OAAO,QAAQ,CAAC,CAAA;AAClC,IAAA,IAAI,kBAAA,CAAmB,IAAA,CAAK,OAAO,CAAA,EAAG,OAAO,OAAA;AAC7C,IAAA,IAAI,OAAA,CAAQ,UAAA,CAAW,GAAG,CAAA,EAAG;AAC3B,MAAA,MAAM,MAAA,GAAS,MAAM,gBAAA,CAAiB,OAAO,CAAA;AAC7C,MAAA,OAAO,UAAU,MAAM,CAAA;AAAA,IACzB;AACA,IAAA,MAAM,IAAA,GAAO,QAAQ,UAAA,CAAW,GAAG,IAAI,OAAA,CAAQ,KAAA,CAAM,CAAC,CAAA,GAAI,OAAA;AAC1D,IAAA,IAAI,KAAK,MAAA,KAAW,CAAA,EAAG,MAAM,IAAI,MAAM,2CAA2C,CAAA;AAClF,IAAA,MAAM,KAAA,GAAQ,MAAM,mBAAA,EAAoB;AACxC,IAAA,MAAM,EAAA,GAAK,KAAA,CAAM,GAAA,CAAI,IAAA,CAAK,aAAa,CAAA;AACvC,IAAA,IAAI,CAAC,EAAA,EAAI;AACP,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,kBAAkB,KAAK,CAAA,wHAAA;AAAA,OACzB;AAAA,IACF;AACA,IAAA,OAAO,EAAA;AAAA,EACT,CAAA;AAEA,EAAA,OAAO;AAAA,IACL,MAAM,KAAK,EAAA,EAAmC;AAC5C,MAAA,MAAM,MAAA,GAAS,KAAA,CAAM,GAAA,CAAI,EAAE,CAAA;AAC3B,MAAA,IAAI,QAAQ,OAAO,MAAA;AACnB,MAAA,MAAM,OAAA,GAAU,YAAA,CAAa,GAAA,CAAI,EAAE,CAAA;AACnC,MAAA,IAAI,SAAS,OAAO,OAAA;AACpB,MAAA,MAAM,YAAY,YAAmC;AACnD,QAAA,IAAI;AACF,UAAA,MAAM,IAAA,GAAO,MAAM,MAAA,CAAO,KAAA,CAAM,KAAK,EAAE,IAAA,EAAM,IAAI,CAAA;AACjD,UAAA,MAAM,MAAO,IAAA,CAAuD,IAAA;AACpE,UAAA,MAAM,IAAA,GAAO,iBAAA,CAAkB,EAAA,EAAI,GAAG,CAAA;AACtC,UAAA,KAAA,CAAM,GAAA,CAAI,IAAI,IAAI,CAAA;AAClB,UAAA,OAAO,IAAA;AAAA,QACT,CAAA,CAAA,MAAQ;AACN,UAAA,MAAM,IAAA,GAAqB,EAAE,EAAA,EAAG;AAChC,UAAA,KAAA,CAAM,GAAA,CAAI,IAAI,IAAI,CAAA;AAClB,UAAA,OAAO,IAAA;AAAA,QACT,CAAA,SAAE;AACA,UAAA,YAAA,CAAa,OAAO,EAAE,CAAA;AAAA,QACxB;AAAA,MACF,CAAA,GAAG;AACH,MAAA,YAAA,CAAa,GAAA,CAAI,IAAI,QAAQ,CAAA;AAC7B,MAAA,OAAO,QAAA;AAAA,IACT,CAAA;AAAA,IACA,MAAM,QAAQ,EAAA,EAAsC;AAClD,MAAA,MAAM,MAAA,GAAS,QAAA,CAAS,GAAA,CAAI,EAAE,CAAA;AAC9B,MAAA,IAAI,QAAQ,OAAO,MAAA;AACnB,MAAA,MAAM,OAAA,GAAU,eAAA,CAAgB,GAAA,CAAI,EAAE,CAAA;AACtC,MAAA,IAAI,SAAS,OAAO,OAAA;AACpB,MAAA,MAAM,YAAY,YAAsC;AACtD,QAAA,IAAI;AACF,UAAA,MAAM,IAAA,GAAO,MAAM,MAAA,CAAO,aAAA,CAAc,KAAK,EAAE,OAAA,EAAS,IAAI,CAAA;AAC5D,UAAA,MAAM,MAAO,IAAA,CAA0D,OAAA;AACvE,UAAA,MAAM,IAAA,GAAO,oBAAA,CAAqB,EAAA,EAAI,GAAG,CAAA;AACzC,UAAA,QAAA,CAAS,GAAA,CAAI,IAAI,IAAI,CAAA;AACrB,UAAA,OAAO,IAAA;AAAA,QACT,CAAA,CAAA,MAAQ;AACN,UAAA,MAAM,IAAA,GAAwB,EAAE,EAAA,EAAG;AACnC,UAAA,QAAA,CAAS,GAAA,CAAI,IAAI,IAAI,CAAA;AACrB,UAAA,OAAO,IAAA;AAAA,QACT,CAAA,SAAE;AACA,UAAA,eAAA,CAAgB,OAAO,EAAE,CAAA;AAAA,QAC3B;AAAA,MACF,CAAA,GAAG;AACH,MAAA,eAAA,CAAgB,GAAA,CAAI,IAAI,QAAQ,CAAA;AAChC,MAAA,OAAO,QAAA;AAAA,IACT,CAAA;AAAA,IACA,YAAA,EAAc,mBAAA;AAAA,IACd,SAAA,EAAW;AAAA,GACb;AACF;AAEA,SAAS,iBAAA,CAAkB,IAAY,GAAA,EAAwD;AAC7F,EAAA,IAAI,CAAC,GAAA,EAAK,OAAO,EAAE,EAAA,EAAG;AACtB,EAAA,MAAM,OAAA,GAAW,IAAI,OAAA,IAAmD,MAAA;AACxE,EAAA,MAAM,IAAA,GAAqB,EAAE,EAAA,EAAG;AAChC,EAAA,MAAM,OAAO,OAAO,GAAA,CAAI,IAAA,KAAS,QAAA,GAAW,IAAI,IAAA,GAAO,MAAA;AACvD,EAAA,IAAI,IAAA,OAAW,IAAA,GAAO,IAAA;AACtB,EAAA,MAAM,QAAA,GACJ,OAAO,GAAA,CAAI,SAAA,KAAc,QAAA,GACrB,GAAA,CAAI,SAAA,GACJ,OAAO,OAAA,EAAS,SAAA,KAAc,QAAA,GAC3B,OAAA,CAAQ,SAAA,GACT,MAAA;AACR,EAAA,IAAI,QAAA,OAAe,SAAA,GAAY,QAAA;AAC/B,EAAA,MAAM,WAAA,GACJ,OAAO,OAAA,EAAS,YAAA,KAAiB,QAAA,IAAa,QAAQ,YAAA,CAAwB,MAAA,GAAS,CAAA,GAClF,OAAA,CAAQ,YAAA,GACT,MAAA;AACN,EAAA,IAAI,WAAA,OAAkB,YAAA,GAAe,WAAA;AACrC,EAAA,IAAI,GAAA,CAAI,MAAA,KAAW,IAAA,EAAM,IAAA,CAAK,MAAA,GAAS,IAAA;AACvC,EAAA,OAAO,IAAA;AACT;AAEA,SAAS,oBAAA,CACP,IACA,GAAA,EACiB;AACjB,EAAA,IAAI,CAAC,GAAA,EAAK,OAAO,EAAE,EAAA,EAAG;AACtB,EAAA,MAAM,IAAA,GAAwB,EAAE,EAAA,EAAG;AACnC,EAAA,IAAI,OAAO,GAAA,CAAI,IAAA,KAAS,QAAA,EAAU,IAAA,CAAK,OAAO,GAAA,CAAI,IAAA;AAClD,EAAA,IAAI,GAAA,CAAI,UAAA,KAAe,IAAA,EAAM,IAAA,CAAK,UAAA,GAAa,IAAA;AAC/C,EAAA,IAAI,GAAA,CAAI,WAAA,KAAgB,IAAA,EAAM,IAAA,CAAK,WAAA,GAAc,IAAA;AACjD,EAAA,OAAO,IAAA;AACT;AAEA,SAAS,aAAa,IAAA,EAAoD;AACxE,EAAA,OAAO,IAAA,EAAM,YAAA,IAAgB,IAAA,EAAM,SAAA,IAAa,IAAA,EAAM,IAAA;AACxD;AAEA,IAAM,YAAA,GAAe,8BAAA;AACrB,IAAM,eAAA,GAAkB,gCAAA;AAExB,eAAe,0BAAA,CACb,IAAA,EACA,SAAA,EACA,QAAA,EACkB;AAClB,EAAA,IAAI,CAAC,IAAA,IAAQ,OAAO,IAAA,KAAS,UAAU,OAAO,IAAA;AAC9C,EAAA,MAAM,IAAA,GAAO,IAAA;AACb,EAAA,MAAM,WAAA,GAAc,MAAM,OAAA,CAAQ,IAAA,CAAK,QAAQ,CAAA,GAAK,IAAA,CAAK,WAAyB,EAAC;AACnF,EAAA,MAAM,OAAA,uBAAc,GAAA,EAAY;AAChC,EAAA,MAAM,UAAA,mBAAa,IAAI,GAAA,CAAY,CAAC,SAAS,CAAC,CAAA;AAC9C,EAAA,KAAA,MAAW,OAAO,WAAA,EAAa;AAC7B,IAAA,IAAI,CAAC,GAAA,IAAO,OAAO,GAAA,KAAQ,QAAA,EAAU;AACrC,IAAA,MAAM,CAAA,GAAI,GAAA;AACV,IAAA,IAAI,OAAO,CAAA,CAAE,IAAA,KAAS,UAAU,OAAA,CAAQ,GAAA,CAAI,EAAE,IAAI,CAAA;AAClD,IAAA,IAAI,OAAO,EAAE,IAAA,KAAS,QAAA,kBAA0B,CAAA,CAAE,IAAA,EAAM,SAAS,UAAU,CAAA;AAAA,EAC7E;AAEA,EAAA,MAAM,CAAC,gBAAA,EAAkB,mBAAmB,CAAA,GAAI,MAAM,QAAQ,GAAA,CAAI;AAAA,IAChE,QAAQ,GAAA,CAAI,CAAC,GAAG,OAAO,EAAE,GAAA,CAAI,OAAO,EAAA,KAAO,CAAC,IAAI,MAAM,QAAA,CAAS,KAAK,EAAE,CAAC,CAAU,CAAC,CAAA;AAAA,IAClF,QAAQ,GAAA,CAAI,CAAC,GAAG,UAAU,EAAE,GAAA,CAAI,OAAO,EAAA,KAAO,CAAC,IAAI,MAAM,QAAA,CAAS,QAAQ,EAAE,CAAC,CAAU,CAAC;AAAA,GACzF,CAAA;AACD,EAAA,MAAM,SAAA,GAAY,IAAI,GAAA,CAA0B,gBAAgB,CAAA;AAChE,EAAA,MAAM,YAAA,GAAe,IAAI,GAAA,CAA6B,mBAAmB,CAAA;AAEzE,EAAA,MAAM,gBAAA,GAAmB,WAAA,CAAY,GAAA,CAAI,CAAC,GAAA,KAAQ;AAChD,IAAA,IAAI,CAAC,GAAA,IAAO,OAAO,GAAA,KAAQ,UAAU,OAAO,GAAA;AAC5C,IAAA,MAAM,CAAA,GAAI,GAAA;AACV,IAAA,MAAM,GAAA,GAA+B,EAAE,GAAG,CAAA,EAAE;AAC5C,IAAA,IAAI,OAAO,CAAA,CAAE,IAAA,KAAS,QAAA,EAAU;AAC9B,MAAA,MAAM,QAAQ,YAAA,CAAa,SAAA,CAAU,GAAA,CAAI,CAAA,CAAE,IAAI,CAAC,CAAA;AAChD,MAAA,IAAI,KAAA,MAAW,iBAAA,GAAoB,KAAA;AAAA,IACrC;AACA,IAAA,IAAI,OAAO,CAAA,CAAE,IAAA,KAAS,QAAA,EAAU;AAC9B,MAAA,GAAA,CAAI,aAAA,GAAgB,eAAA,CAAgB,CAAA,CAAE,IAAA,EAAM,WAAW,YAAY,CAAA;AAAA,IACrE;AACA,IAAA,OAAO,GAAA;AAAA,EACT,CAAC,CAAA;AAED,EAAA,MAAM,gBAA8C,EAAC;AACrD,EAAA,KAAA,MAAW,CAAC,EAAA,EAAI,IAAI,KAAK,SAAA,EAAW,aAAA,CAAc,EAAE,CAAA,GAAI,IAAA;AACxD,EAAA,MAAM,WAAA,GAAc,YAAA,CAAa,GAAA,CAAI,SAAS,CAAA;AAE9C,EAAA,OAAO;AAAA,IACL,GAAG,IAAA;AAAA,IACH,QAAA,EAAU,gBAAA;AAAA,IACV,cAAA,EAAgB,aAAA;AAAA,IAChB,GAAI,WAAA,GAAc,EAAE,gBAAA,EAAkB,WAAA,KAAgB;AAAC,GACzD;AACF;AAEA,SAAS,eAAA,CAAgB,IAAA,EAAc,OAAA,EAAsB,UAAA,EAA+B;AAC1F,EAAA,KAAA,MAAW,KAAA,IAAS,IAAA,CAAK,QAAA,CAAS,YAAY,CAAA,EAAG;AAC/C,IAAA,MAAM,EAAA,GAAK,MAAM,CAAC,CAAA;AAClB,IAAA,IAAI,EAAA,EAAI,OAAA,CAAQ,GAAA,CAAI,EAAE,CAAA;AAAA,EACxB;AACA,EAAA,KAAA,MAAW,KAAA,IAAS,IAAA,CAAK,QAAA,CAAS,eAAe,CAAA,EAAG;AAClD,IAAA,MAAM,EAAA,GAAK,MAAM,CAAC,CAAA;AAClB,IAAA,IAAI,EAAA,EAAI,UAAA,CAAW,GAAA,CAAI,EAAE,CAAA;AAAA,EAC3B;AACF;AAEA,SAAS,eAAA,CACP,IAAA,EACA,KAAA,EACA,QAAA,EACQ;AACR,EAAA,OAAO,IAAA,CACJ,OAAA,CAAQ,YAAA,EAAc,CAAC,QAAQ,EAAA,KAAe;AAC7C,IAAA,MAAM,KAAA,GAAQ,YAAA,CAAa,KAAA,CAAM,GAAA,CAAI,EAAE,CAAC,CAAA;AACxC,IAAA,OAAO,KAAA,GAAQ,CAAA,CAAA,EAAI,KAAK,CAAA,CAAA,GAAK,KAAK,EAAE,CAAA,CAAA,CAAA;AAAA,EACtC,CAAC,CAAA,CACA,OAAA,CAAQ,iBAAiB,CAAC,MAAA,EAAQ,IAAY,YAAA,KAA0B;AACvE,IAAA,MAAM,IAAA,GAAO,QAAA,CAAS,GAAA,CAAI,EAAE,GAAG,IAAA,IAAQ,YAAA;AACvC,IAAA,OAAO,IAAA,GAAO,CAAA,CAAA,EAAI,IAAI,CAAA,CAAA,GAAK,KAAK,EAAE,CAAA,CAAA,CAAA;AAAA,EACpC,CAAC,CAAA;AACL;AC/iBA,IAAM,uBAAuB,EAAA,GAAK,CAAA;AAE3B,SAAS,qBAAqB,IAAA,EAMzB;AACV,EAAA,IAAI,CAAC,KAAK,SAAA,EAAW,UAAA,CAAW,KAAK,CAAA,IAAK,CAAC,IAAA,CAAK,SAAA,EAAW,OAAO,KAAA;AAClE,EAAA,MAAM,EAAA,GAAK,MAAA,CAAO,IAAA,CAAK,SAAS,CAAA;AAChC,EAAA,IAAI,CAAC,MAAA,CAAO,QAAA,CAAS,EAAE,GAAG,OAAO,KAAA;AACjC,EAAA,MAAM,GAAA,GAAM,KAAK,UAAA,IAAc,IAAA,CAAK,MAAM,IAAA,CAAK,GAAA,KAAQ,GAAI,CAAA;AAC3D,EAAA,IAAI,KAAK,GAAA,CAAI,GAAA,GAAM,EAAE,CAAA,GAAI,sBAAsB,OAAO,KAAA;AAEtD,EAAA,MAAM,OAAO,CAAA,GAAA,EAAM,IAAA,CAAK,SAAS,CAAA,CAAA,EAAI,KAAK,OAAO,CAAA,CAAA;AACjD,EAAA,MAAM,QAAA,GAAW,CAAA,GAAA,EAAM,UAAA,CAAW,QAAA,EAAU,IAAA,CAAK,aAAa,CAAA,CAAE,MAAA,CAAO,IAAI,CAAA,CAAE,MAAA,CAAO,KAAK,CAAC,CAAA,CAAA;AAC1F,EAAA,MAAM,WAAA,GAAc,MAAA,CAAO,IAAA,CAAK,QAAA,EAAU,MAAM,CAAA;AAChD,EAAA,MAAM,SAAA,GAAY,MAAA,CAAO,IAAA,CAAK,IAAA,CAAK,WAAW,MAAM,CAAA;AACpD,EAAA,IAAI,WAAA,CAAY,MAAA,KAAW,SAAA,CAAU,MAAA,EAAQ,OAAO,KAAA;AACpD,EAAA,OAAO,eAAA,CAAgB,aAAa,SAAS,CAAA;AAC/C;;;ACNA,IAAM,0BAAA,GAA6B,sBAAA;AACnC,IAAM,qBAAA,GAAwB,iBAAA;AAE9B,IAAM,OAAO,UAAA,CAAW;AAAA,EACtB,IAAA,EAAM,WAAA;AAAA,EACN,SAAS,eAAA,CAAI,OAAA;AAAA,EACb,SAAA,EAAW;AAAA,IACT;AAAA,MACE,IAAA,EAAM,0BAAA;AAAA,MACN,QAAA,EAAU,IAAA;AAAA,MACV,MAAA,EAAQ,IAAA;AAAA,MACR,WAAA,EAAa;AAAA,KACf;AAAA,IACA;AAAA,MACE,IAAA,EAAM,qBAAA;AAAA,MACN,QAAA,EAAU,IAAA;AAAA,MACV,MAAA,EAAQ,IAAA;AAAA,MACR,WAAA,EAAa;AAAA;AACf,GACF;AAAA,EACA,WAAW,GAAA,EAAsC;AAC/C,IAAA,MAAM,GAAA,GAAM,UAAA,CAAW,GAAA,CAAI,MAAM,CAAA;AACjC,IAAA,MAAM,QAAA,GAAW,GAAA,CAAI,GAAA,CAAI,GAAA,CAAI,WAAW,CAAA;AACxC,IAAA,IAAI,CAAC,QAAA,EAAU,OAAO,EAAC;AACvB,IAAA,OAAO,UAAA,CAAW;AAAA,MAChB,QAAA;AAAA,MACA,YAAY,GAAA,CAAI,UAAA;AAAA,MAChB,GAAI,IAAI,eAAA,GAAkB,EAAE,iBAAiB,GAAA,CAAI,eAAA,KAAoB;AAAC,KACvE,CAAA;AAAA,EACH,CAAA;AAAA,EACA,WAAW,GAAA,EAA2C;AACpD,IAAA,MAAM,GAAA,GAAM,UAAA,CAAW,GAAA,CAAI,MAAM,CAAA;AACjC,IAAA,OAAO;AAAA,MACL;AAAA,QACE,GAAA,EAAK,OAAA;AAAA,QACL,OAAA,EAAS,OAAO,GAAA,EAAK,MAAA,KAAW;AAC9B,UAAA,MAAM,OAAA,GAAU,MAAM,GAAA,CAAI,IAAA,EAAK;AAC/B,UAAA,MAAM,aAAA,GAAgB,GAAA,CAAI,GAAA,CAAI,GAAA,CAAI,gBAAgB,CAAA;AAClD,UAAA,IAAI,CAAC,aAAA,EAAe;AAClB,YAAA,OAAO,IAAA,CAAK,EAAE,KAAA,EAAO,gBAAA,EAAkB,KAAK,GAAA,CAAI,gBAAA,IAAoB,GAAG,CAAA;AAAA,UACzE;AACA,UAAA,IACE,CAAC,oBAAA,CAAqB;AAAA,YACpB,OAAA;AAAA,YACA,aAAA;AAAA,YACA,SAAA,EAAW,GAAA,CAAI,OAAA,CAAQ,GAAA,CAAI,mBAAmB,CAAA;AAAA,YAC9C,SAAA,EAAW,GAAA,CAAI,OAAA,CAAQ,GAAA,CAAI,2BAA2B;AAAA,WACvD,CAAA,EACD;AACA,YAAA,OAAO,IAAA,CAAK,EAAE,KAAA,EAAO,mBAAA,IAAuB,GAAG,CAAA;AAAA,UACjD;AAEA,UAAA,MAAM,MAAA,GAAS,UAAU,OAAO,CAAA;AAChC,UAAA,MAAM,UAAA,GAAa,mBAAA,CAAoB,MAAA,EAAQ,GAAG,CAAA;AAClD,UAAA,IAAI,UAAA,CAAW,SAAS,WAAA,EAAa,OAAO,KAAK,EAAE,SAAA,EAAW,UAAA,CAAW,SAAA,EAAW,CAAA;AACpF,UAAA,IAAI,WAAW,IAAA,KAAS,MAAA;AACtB,YAAA,OAAO,IAAA,CAAK,EAAE,EAAA,EAAI,IAAA,EAAM,SAAS,IAAA,EAAM,MAAA,EAAQ,UAAA,CAAW,MAAA,EAAQ,CAAA;AAEpE,UAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,YAAA,CAAa,GAAA,CAAI,KAAK,CAAA;AAC3C,UAAA,MAAM,iBAAiB,mBAAA,CAAoB;AAAA,YACzC,QAAQ,UAAA,CAAW,MAAA;AAAA,YACnB,SAAS,UAAA,CAAW,OAAA;AAAA,YACpB,UAAU,UAAA,CAAW;AAAA,WACtB,CAAA;AACD,UAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,uBAAA,CAAwB;AAAA,YAClD,cAAA;AAAA,YACA,WAAW,KAAA,CAAM,IAAA;AAAA,YACjB,cAAc,KAAA,CAAM,OAAA;AAAA,YACpB,MAAA,EAAQ,IAAI,MAAA,IAAU,WAAA;AAAA,YACtB,KAAA,EAAO,CAAA,MAAA,EAAS,IAAA,CAAK,UAAA,CAAW,OAAO,CAAC,CAAA,CAAA;AAAA,YACxC,OAAO,CAAA,MAAA,EAAS,UAAA,CAAW,OAAO,CAAA,CAAA,EAAI,WAAW,QAAQ,CAAA,CAAA;AAAA,YACzD,cAAA,EAAgB,CAAC,EAAE,IAAA,EAAM,QAAQ,IAAA,EAAM,UAAA,CAAW,MAAM,CAAA;AAAA,YACxD,QAAA,EAAU;AAAA,cACR,SAAA,EAAW,WAAA;AAAA,cACX,QAAQ,UAAA,CAAW,MAAA;AAAA,cACnB,SAAS,UAAA,CAAW,OAAA;AAAA,cACpB,UAAU,UAAA,CAAW,QAAA;AAAA,cACrB,IAAI,UAAA,CAAW,EAAA;AAAA,cACf,GAAI,WAAW,MAAA,GAAS,EAAE,QAAQ,UAAA,CAAW,MAAA,KAAW;AAAC;AAC3D,WACD,CAAA;AACD,UAAA,OAAO,KAAK,MAAA,EAAQ,MAAA,CAAO,MAAA,KAAW,UAAA,GAAa,MAAM,GAAG,CAAA;AAAA,QAC9D;AAAA;AACF,KACF;AAAA,EACF;AACF,CAAC,CAAA;AAED,IAAO,WAAA,GAAQ;AAOf,SAAS,WAAW,GAAA,EAAmD;AACrE,EAAA,MAAM,GAAA,GAA2B;AAAA,IAC/B,gBAAA,EAAkBA,YAAAA,CAAY,GAAA,CAAI,gBAAgB,CAAA,IAAK,0BAAA;AAAA,IACvD,WAAA,EAAaA,YAAAA,CAAY,GAAA,CAAI,WAAW,CAAA,IAAK,qBAAA;AAAA,IAC7C,UAAA,EAAY,GAAA,CAAI,UAAA,KAAe,YAAA,GAAe,YAAA,GAAe,MAAA;AAAA,IAC7D,YAAA,EAAc,IAAI,YAAA,KAAiB;AAAA,GACrC;AACA,EAAA,MAAM,KAAA,GAAQA,YAAAA,CAAY,GAAA,CAAI,KAAK,CAAA;AACnC,EAAA,IAAI,KAAA,MAAW,KAAA,GAAQ,KAAA;AACvB,EAAA,MAAM,MAAA,GAASA,YAAAA,CAAY,GAAA,CAAI,MAAM,CAAA;AACrC,EAAA,IAAI,MAAA,MAAY,MAAA,GAAS,MAAA;AACzB,EAAA,MAAM,eAAA,GAAkB,WAAA,CAAY,GAAA,CAAI,eAAe,CAAA;AACvD,EAAA,IAAI,eAAA,MAAqB,eAAA,GAAkB,eAAA;AAC3C,EAAA,OAAO,GAAA;AACT;AAEA,SAAS,UAAU,OAAA,EAA0B;AAC3C,EAAA,IAAI;AACF,IAAA,OAAO,IAAA,CAAK,MAAM,OAAO,CAAA;AAAA,EAC3B,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,IAAA;AAAA,EACT;AACF;AAEA,SAAS,KAAK,KAAA,EAAuB;AACnC,EAAA,OAAOC,UAAAA,CAAW,QAAQ,CAAA,CAAE,MAAA,CAAO,KAAK,CAAA,CAAE,MAAA,CAAO,KAAK,CAAA,CAAE,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA;AACrE;AAEA,SAAS,IAAA,CAAK,IAAA,EAAe,MAAA,GAAS,GAAA,EAAe;AACnD,EAAA,OAAO,QAAA,CAAS,IAAA,CAAK,IAAA,EAAM,EAAE,QAAQ,CAAA;AACvC;AAEA,SAASD,aAAY,KAAA,EAAoC;AACvD,EAAA,OAAO,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,CAAM,MAAA,GAAS,IAAI,KAAA,GAAQ,MAAA;AACjE;AAEA,SAAS,YAAY,KAAA,EAAsC;AACzD,EAAA,OAAO,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,GACtB,KAAA,CAAM,MAAA,CAAO,CAAC,IAAA,KAAyB,OAAO,IAAA,KAAS,QAAQ,CAAA,GAC/D,MAAA;AACN","file":"index.js","sourcesContent":["{\n \"name\": \"@render-harness/cap-slack\",\n \"version\": \"0.4.2\",\n \"description\": \"Slack Events and Web API capability pack for the Render agent harness.\",\n \"type\": \"module\",\n \"license\": \"MIT\",\n \"main\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\"\n },\n \"./package.json\": \"./package.json\"\n },\n \"files\": [\n \"dist\"\n ],\n \"keywords\": [\n \"render-harness-cap\",\n \"render-harness\",\n \"slack\"\n ],\n \"renderHarness\": {\n \"gallery\": {\n \"label\": \"Slack\",\n \"envHint\": \"SLACK_BOT_TOKEN\"\n }\n },\n \"scripts\": {\n \"build\": \"tsup\",\n \"typecheck\": \"tsc --noEmit\",\n \"test\": \"vitest run --passWithNoTests\"\n },\n \"dependencies\": {\n \"@render-harness/registry\": \"workspace:*\",\n \"@slack/web-api\": \"^7.12.0\"\n },\n \"devDependencies\": {\n \"@render-harness/core\": \"workspace:*\",\n \"@types/node\": \"^25.6.2\",\n \"tsup\": \"^8.5.1\",\n \"typescript\": \"^6.0.3\",\n \"vitest\": \"^4.1.5\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/render-lab/render-agent-harness.git\",\n \"directory\": \"packages/capabilities/cap-slack\"\n }\n}\n","import { createHash } from \"node:crypto\";\n\nexport function slackConversationId(args: {\n teamId: string;\n channel: string;\n threadTs: string;\n}): string {\n const digest = createHash(\"sha256\")\n .update(`${args.teamId}:${args.channel}:${args.threadTs}`)\n .digest(\"hex\")\n .slice(0, 24);\n return `slack-${digest}`;\n}\n","export type SlackNormalizedEvent =\n | { kind: \"challenge\"; challenge: string }\n | { kind: \"noop\"; reason: string }\n | {\n kind: \"message\";\n text: string;\n teamId: string;\n channel: string;\n ts: string;\n threadTs: string;\n eventId: string;\n userId?: string;\n };\n\nexport interface SlackNormalizeConfig {\n includeEdits?: boolean;\n allowedChannels?: string[];\n}\n\nexport function normalizeSlackEvent(\n body: unknown,\n cfg: SlackNormalizeConfig = {},\n): SlackNormalizedEvent {\n if (!body || typeof body !== \"object\") return { kind: \"noop\", reason: \"invalid_body\" };\n const payload = body as Record<string, unknown>;\n if (payload.type === \"url_verification\") {\n const challenge = stringValue(payload.challenge);\n return challenge\n ? { kind: \"challenge\", challenge }\n : { kind: \"noop\", reason: \"missing_challenge\" };\n }\n if (payload.type !== \"event_callback\") return { kind: \"noop\", reason: \"unsupported_type\" };\n\n const event = objectValue(payload.event);\n if (!event) return { kind: \"noop\", reason: \"missing_event\" };\n const eventType = stringValue(event.type);\n if (eventType !== \"app_mention\" && eventType !== \"message\") {\n return { kind: \"noop\", reason: \"unsupported_event\" };\n }\n if (event.bot_id || event.subtype === \"bot_message\")\n return { kind: \"noop\", reason: \"bot_message\" };\n if (event.subtype === \"message_changed\" && !cfg.includeEdits) {\n return { kind: \"noop\", reason: \"edit_ignored\" };\n }\n\n const channel = stringValue(event.channel);\n if (!channel) return { kind: \"noop\", reason: \"missing_channel\" };\n if (cfg.allowedChannels?.length && !cfg.allowedChannels.includes(channel)) {\n return { kind: \"noop\", reason: \"channel_not_allowed\" };\n }\n\n const teamId = stringValue(payload.team_id) ?? stringValue(event.team);\n const ts = stringValue(event.ts);\n const text = stringValue(event.text) ?? \"\";\n const eventId =\n stringValue(payload.event_id) ?? `${teamId ?? \"team\"}-${channel}-${ts ?? \"event\"}`;\n if (!teamId || !ts || text.trim().length === 0) return { kind: \"noop\", reason: \"missing_fields\" };\n const userId = stringValue(event.user);\n\n return {\n kind: \"message\",\n text,\n teamId,\n channel,\n ts,\n threadTs: stringValue(event.thread_ts) ?? ts,\n eventId,\n ...(userId ? { userId } : {}),\n };\n}\n\nfunction objectValue(value: unknown): Record<string, unknown> | null {\n return value && typeof value === \"object\" && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : null;\n}\n\nfunction stringValue(value: unknown): string | undefined {\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n","import type { LocalToolHandler } from \"@render-harness/core\";\nimport { type RetryOptions, WebClient } from \"@slack/web-api\";\n\nexport type SlackAccessMode = \"read\" | \"read_write\";\n\n// @slack/web-api defaults to `tenRetriesInAboutThirtyMinutes` and no per-request\n// timeout, which means a single rate-limited or transient-failure response can\n// hang a tool call for up to 30 minutes with no surfaced progress. We replace\n// that with a bounded retry policy + 15s per-request timeout so the agent gets a\n// clear error within ~70s worst case instead of appearing stuck forever.\nconst SLACK_REQUEST_TIMEOUT_MS = 15_000;\nconst BOUNDED_RETRY_CONFIG: RetryOptions = {\n retries: 3,\n factor: 2,\n minTimeout: 500,\n maxTimeout: 3_000,\n};\n\ninterface ResolvedUser {\n id: string;\n name?: string;\n real_name?: string;\n display_name?: string;\n is_bot?: boolean;\n}\n\ninterface ResolvedChannel {\n id: string;\n name?: string;\n is_private?: boolean;\n is_archived?: boolean;\n}\n\ninterface SlackResolver {\n /** Look up a user by ID. Backed by `users.info`, cached for the agent process. */\n user: (id: string) => Promise<ResolvedUser>;\n /** Look up a channel by ID. Backed by `conversations.info`, cached for the agent process. */\n channel: (id: string) => Promise<ResolvedChannel>;\n /**\n * Resolve a channel input — either a raw Slack ID (`C…` / `G…` / `D…`),\n * a `#channel-name` string, or an `@user-handle` string (which opens or\n * reuses a DM channel). Returns the canonical channel ID for `chat.*` /\n * `conversations.*` calls. Lazy-fetches `conversations.list` and\n * `users.list` and caches by name/handle for subsequent lookups.\n */\n channelInput: (input: string) => Promise<string>;\n /**\n * Resolve a user input — either a raw `U…` ID or an `@handle` string\n * (with or without the leading `@`). Returns the user ID. Lazy-fetches\n * `users.list` and caches by handle.\n */\n userInput: (input: string) => Promise<string>;\n}\n\n// Slack channel/user/DM IDs are always `<prefix><A-Z0-9>+`; channel names\n// are restricted to lowercase letters / digits / `-` / `_`, so an input\n// that matches one of these patterns is unambiguously an ID. The {2,}\n// lower bound is intentionally loose so short test fixtures (`C123`) work\n// the same as real-world ids (`C0AQHA6M3PS`).\nconst CHANNEL_ID_PATTERN = /^[CGD][A-Z0-9]{2,}$/;\nconst USER_ID_PATTERN = /^U[A-Z0-9]{2,}$/;\n\nexport function slackTools(args: {\n botToken: string;\n accessMode: SlackAccessMode;\n allowedChannels?: string[];\n}): LocalToolHandler[] {\n const client = new WebClient(args.botToken, {\n timeout: SLACK_REQUEST_TIMEOUT_MS,\n retryConfig: BOUNDED_RETRY_CONFIG,\n });\n const resolver = createResolver(client);\n\n const tools: LocalToolHandler[] = [\n jsonTool(\n \"slack.get_thread\",\n \"Read a Slack thread's messages. `channel` accepts a Slack ID (C…/G…/D…), a `#channel-name`, or an `@user-handle` (opens a DM). Responses include resolved user display names and a rewritten text_resolved field.\",\n objectSchema({\n channel: { type: \"string\" },\n thread_ts: { type: \"string\" },\n }),\n async (input) => {\n const { channel, thread_ts } = input as { channel: string; thread_ts: string };\n const channelId = await resolver.channelInput(channel);\n assertAllowedChannel(channelId, args.allowedChannels);\n const resp = await client.conversations.replies({ channel: channelId, ts: thread_ts });\n return enrichConversationResponse(resp, channelId, resolver);\n },\n ),\n jsonTool(\n \"slack.get_channel_history\",\n \"Read recent Slack channel messages. `channel` accepts a Slack ID (C…/G…/D…), a `#channel-name`, or an `@user-handle` (opens a DM). Responses include resolved user display names and a rewritten text_resolved field.\",\n objectSchema({\n channel: { type: \"string\" },\n limit: { type: \"number\", optional: true },\n }),\n async (input) => {\n const { channel, limit } = input as { channel: string; limit?: number };\n const channelId = await resolver.channelInput(channel);\n assertAllowedChannel(channelId, args.allowedChannels);\n const resp = await client.conversations.history({\n channel: channelId,\n limit: limit ?? 20,\n });\n return enrichConversationResponse(resp, channelId, resolver);\n },\n ),\n jsonTool(\n \"slack.get_user_info\",\n \"Resolve a Slack user to display name, real name, and handle. Accepts a user ID (U0B4357MH7H), an `@handle`, or a bare handle.\",\n objectSchema({\n user: { type: \"string\" },\n }),\n async (input) => {\n const { user } = input as { user: string };\n const userId = await resolver.userInput(user);\n return resolver.user(userId);\n },\n ),\n jsonTool(\n \"slack.get_channel_info\",\n \"Resolve a Slack channel to name and metadata. Accepts a channel ID (C0AQHA6M3PS), a `#channel-name`, or an `@user-handle` (opens a DM).\",\n objectSchema({\n channel: { type: \"string\" },\n }),\n async (input) => {\n const { channel } = input as { channel: string };\n const channelId = await resolver.channelInput(channel);\n assertAllowedChannel(channelId, args.allowedChannels);\n return resolver.channel(channelId);\n },\n ),\n ];\n\n if (args.accessMode === \"read_write\") {\n tools.push(\n jsonTool(\n \"slack.send_message\",\n \"Send a Slack message, optionally as a thread reply. `channel` accepts a Slack ID (C…/G…/D…), a `#channel-name`, or an `@user-handle` (opens a DM).\",\n objectSchema({\n channel: { type: \"string\" },\n text: { type: \"string\" },\n thread_ts: { type: \"string\", optional: true },\n }),\n async (input) => {\n const { channel, text, thread_ts } = input as {\n channel: string;\n text: string;\n thread_ts?: string;\n };\n const channelId = await resolver.channelInput(channel);\n assertAllowedChannel(channelId, args.allowedChannels);\n return client.chat.postMessage({\n channel: channelId,\n text,\n ...(thread_ts ? { thread_ts } : {}),\n });\n },\n ),\n jsonTool(\n \"slack.add_reaction\",\n \"Add a reaction to a Slack message. `channel` accepts a Slack ID, a `#channel-name`, or an `@user-handle`.\",\n objectSchema({\n channel: { type: \"string\" },\n ts: { type: \"string\" },\n name: { type: \"string\" },\n }),\n async (input) => {\n const { channel, ts, name } = input as { channel: string; ts: string; name: string };\n const channelId = await resolver.channelInput(channel);\n assertAllowedChannel(channelId, args.allowedChannels);\n return client.reactions.add({ channel: channelId, timestamp: ts, name });\n },\n ),\n jsonTool(\n \"slack.update_message\",\n \"Update a Slack message. `channel` accepts a Slack ID, a `#channel-name`, or an `@user-handle`.\",\n objectSchema({\n channel: { type: \"string\" },\n ts: { type: \"string\" },\n text: { type: \"string\" },\n }),\n async (input) => {\n const { channel, ts, text } = input as { channel: string; ts: string; text: string };\n const channelId = await resolver.channelInput(channel);\n assertAllowedChannel(channelId, args.allowedChannels);\n return client.chat.update({ channel: channelId, ts, text });\n },\n ),\n );\n }\n\n return tools;\n}\n\nfunction jsonTool(\n name: string,\n description: string,\n inputSchema: Record<string, unknown>,\n call: (input: unknown) => Promise<unknown>,\n): LocalToolHandler {\n return {\n definition: { name, description, inputSchema, source: \"pack:cap-slack\" },\n handler: async ({ input }) => {\n try {\n return { content: JSON.stringify(await call(input), null, 2) };\n } catch (err) {\n return { content: err instanceof Error ? err.message : String(err), isError: true };\n }\n },\n };\n}\n\nfunction assertAllowedChannel(channel: string, allowedChannels: string[] | undefined): void {\n if (allowedChannels?.length && !allowedChannels.includes(channel)) {\n throw new Error(`Slack channel \"${channel}\" is not allowed by cap-slack config`);\n }\n}\n\nfunction objectSchema(properties: Record<string, unknown>) {\n return {\n type: \"object\",\n additionalProperties: false,\n properties,\n required: Object.entries(properties)\n .filter(([, value]) => !(value as { optional?: boolean }).optional)\n .map(([key]) => key),\n };\n}\n\nfunction createResolver(client: WebClient): SlackResolver {\n const users = new Map<string, ResolvedUser>();\n const channels = new Map<string, ResolvedChannel>();\n const pendingUsers = new Map<string, Promise<ResolvedUser>>();\n const pendingChannels = new Map<string, Promise<ResolvedChannel>>();\n\n // Name → ID indexes for reverse lookup. Populated lazily by\n // `ensureChannelsIndex` / `ensureUsersIndex` (which page through\n // `conversations.list` / `users.list` once and cache the full map for\n // the lifetime of this resolver — i.e. the agent process). DM channel\n // ids opened via `conversations.open` are written back into `dmByUserId`\n // so repeated `@handle` sends only hit the API once.\n let channelsIndex: Promise<Map<string, string>> | null = null;\n let usersIndex: Promise<Map<string, string>> | null = null;\n const dmByUserId = new Map<string, string>();\n\n const ensureChannelsIndex = (): Promise<Map<string, string>> => {\n if (channelsIndex) return channelsIndex;\n channelsIndex = (async () => {\n const byName = new Map<string, string>();\n let cursor: string | undefined;\n do {\n const resp = (await client.conversations.list({\n limit: 1000,\n types: \"public_channel,private_channel\",\n exclude_archived: true,\n ...(cursor ? { cursor } : {}),\n })) as unknown as {\n channels?: Array<{ id?: unknown; name?: unknown }>;\n response_metadata?: { next_cursor?: unknown };\n };\n for (const c of resp.channels ?? []) {\n if (typeof c.id === \"string\" && typeof c.name === \"string\") {\n byName.set(c.name.toLowerCase(), c.id);\n // Also warm the by-ID resolver cache so a later\n // `slack.get_channel_info` lookup is a hit.\n if (!channels.has(c.id)) channels.set(c.id, { id: c.id, name: c.name });\n }\n }\n cursor =\n typeof resp.response_metadata?.next_cursor === \"string\" &&\n resp.response_metadata.next_cursor.length > 0\n ? resp.response_metadata.next_cursor\n : undefined;\n } while (cursor);\n return byName;\n })().catch((err) => {\n // Reset on failure so a subsequent lookup retries (e.g. once\n // `channels:read` scope is added without the agent restarting).\n channelsIndex = null;\n throw err;\n });\n return channelsIndex;\n };\n\n const ensureUsersIndex = (): Promise<Map<string, string>> => {\n if (usersIndex) return usersIndex;\n usersIndex = (async () => {\n const byHandle = new Map<string, string>();\n let cursor: string | undefined;\n do {\n const resp = (await client.users.list({\n limit: 200,\n ...(cursor ? { cursor } : {}),\n })) as unknown as {\n members?: Array<{\n id?: unknown;\n name?: unknown;\n real_name?: unknown;\n profile?: { display_name?: unknown; display_name_normalized?: unknown };\n deleted?: unknown;\n }>;\n response_metadata?: { next_cursor?: unknown };\n };\n for (const u of resp.members ?? []) {\n if (typeof u.id !== \"string\" || u.deleted === true) continue;\n // Index every name variant the agent might use. Slack handles\n // are unique, but display names + real names can collide — last\n // writer wins, which is acceptable for an LLM hint.\n for (const candidate of [\n u.name,\n u.real_name,\n u.profile?.display_name,\n u.profile?.display_name_normalized,\n ]) {\n if (typeof candidate === \"string\" && candidate.length > 0) {\n byHandle.set(candidate.toLowerCase(), u.id);\n }\n }\n if (!users.has(u.id)) {\n users.set(u.id, buildResolvedUser(u.id, u as unknown as Record<string, unknown>));\n }\n }\n cursor =\n typeof resp.response_metadata?.next_cursor === \"string\" &&\n resp.response_metadata.next_cursor.length > 0\n ? resp.response_metadata.next_cursor\n : undefined;\n } while (cursor);\n return byHandle;\n })().catch((err) => {\n usersIndex = null;\n throw err;\n });\n return usersIndex;\n };\n\n const openDmFor = async (userId: string): Promise<string> => {\n const cached = dmByUserId.get(userId);\n if (cached) return cached;\n const resp = (await client.conversations.open({ users: userId })) as unknown as {\n channel?: { id?: unknown };\n };\n const channelId = resp.channel?.id;\n if (typeof channelId !== \"string\") {\n throw new Error(`conversations.open for ${userId} returned no channel id`);\n }\n dmByUserId.set(userId, channelId);\n return channelId;\n };\n\n const resolveUserInput = async (input: string): Promise<string> => {\n const trimmed = input.trim();\n if (trimmed.length === 0) throw new Error(\"Slack user input is empty\");\n // Accept Slack's `<@U123>` mention wrapping verbatim.\n const mention = /^<@(U[A-Z0-9]+)(?:\\|[^>]+)?>$/.exec(trimmed);\n if (mention?.[1]) return mention[1];\n if (USER_ID_PATTERN.test(trimmed)) return trimmed;\n const handle = trimmed.startsWith(\"@\") ? trimmed.slice(1) : trimmed;\n if (handle.length === 0) throw new Error(\"Slack user input has no handle after '@'\");\n const index = await ensureUsersIndex();\n const id = index.get(handle.toLowerCase());\n if (!id) {\n throw new Error(\n `Slack user \"${input}\" not found. Check the handle, or ensure the bot has 'users:read' scope.`,\n );\n }\n return id;\n };\n\n const resolveChannelInput = async (input: string): Promise<string> => {\n const trimmed = input.trim();\n if (trimmed.length === 0) throw new Error(\"Slack channel input is empty\");\n // Accept Slack's `<#C123|name>` mention wrapping verbatim.\n const mention = /^<#([CGD][A-Z0-9]+)(?:\\|[^>]+)?>$/.exec(trimmed);\n if (mention?.[1]) return mention[1];\n if (CHANNEL_ID_PATTERN.test(trimmed)) return trimmed;\n if (trimmed.startsWith(\"@\")) {\n const userId = await resolveUserInput(trimmed);\n return openDmFor(userId);\n }\n const name = trimmed.startsWith(\"#\") ? trimmed.slice(1) : trimmed;\n if (name.length === 0) throw new Error(\"Slack channel input has no name after '#'\");\n const index = await ensureChannelsIndex();\n const id = index.get(name.toLowerCase());\n if (!id) {\n throw new Error(\n `Slack channel \"${input}\" not found. Check the name, or ensure the bot has 'channels:read' / 'groups:read' scope and is a member of the channel.`,\n );\n }\n return id;\n };\n\n return {\n async user(id: string): Promise<ResolvedUser> {\n const cached = users.get(id);\n if (cached) return cached;\n const pending = pendingUsers.get(id);\n if (pending) return pending;\n const fetchOne = (async (): Promise<ResolvedUser> => {\n try {\n const resp = await client.users.info({ user: id });\n const raw = (resp as unknown as { user?: Record<string, unknown> }).user;\n const info = buildResolvedUser(id, raw);\n users.set(id, info);\n return info;\n } catch {\n const info: ResolvedUser = { id };\n users.set(id, info);\n return info;\n } finally {\n pendingUsers.delete(id);\n }\n })();\n pendingUsers.set(id, fetchOne);\n return fetchOne;\n },\n async channel(id: string): Promise<ResolvedChannel> {\n const cached = channels.get(id);\n if (cached) return cached;\n const pending = pendingChannels.get(id);\n if (pending) return pending;\n const fetchOne = (async (): Promise<ResolvedChannel> => {\n try {\n const resp = await client.conversations.info({ channel: id });\n const raw = (resp as unknown as { channel?: Record<string, unknown> }).channel;\n const info = buildResolvedChannel(id, raw);\n channels.set(id, info);\n return info;\n } catch {\n const info: ResolvedChannel = { id };\n channels.set(id, info);\n return info;\n } finally {\n pendingChannels.delete(id);\n }\n })();\n pendingChannels.set(id, fetchOne);\n return fetchOne;\n },\n channelInput: resolveChannelInput,\n userInput: resolveUserInput,\n };\n}\n\nfunction buildResolvedUser(id: string, raw: Record<string, unknown> | undefined): ResolvedUser {\n if (!raw) return { id };\n const profile = (raw.profile as Record<string, unknown> | undefined) ?? undefined;\n const info: ResolvedUser = { id };\n const name = typeof raw.name === \"string\" ? raw.name : undefined;\n if (name) info.name = name;\n const realName =\n typeof raw.real_name === \"string\"\n ? raw.real_name\n : typeof profile?.real_name === \"string\"\n ? (profile.real_name as string)\n : undefined;\n if (realName) info.real_name = realName;\n const displayName =\n typeof profile?.display_name === \"string\" && (profile.display_name as string).length > 0\n ? (profile.display_name as string)\n : undefined;\n if (displayName) info.display_name = displayName;\n if (raw.is_bot === true) info.is_bot = true;\n return info;\n}\n\nfunction buildResolvedChannel(\n id: string,\n raw: Record<string, unknown> | undefined,\n): ResolvedChannel {\n if (!raw) return { id };\n const info: ResolvedChannel = { id };\n if (typeof raw.name === \"string\") info.name = raw.name;\n if (raw.is_private === true) info.is_private = true;\n if (raw.is_archived === true) info.is_archived = true;\n return info;\n}\n\nfunction displayLabel(user: ResolvedUser | undefined): string | undefined {\n return user?.display_name ?? user?.real_name ?? user?.name;\n}\n\nconst USER_MENTION = /<@(U[A-Z0-9]+)(?:\\|[^>]+)?>/g;\nconst CHANNEL_MENTION = /<#(C[A-Z0-9]+)(?:\\|([^>]+))?>/g;\n\nasync function enrichConversationResponse(\n resp: unknown,\n channelId: string,\n resolver: SlackResolver,\n): Promise<unknown> {\n if (!resp || typeof resp !== \"object\") return resp;\n const body = resp as Record<string, unknown>;\n const rawMessages = Array.isArray(body.messages) ? (body.messages as unknown[]) : [];\n const userIds = new Set<string>();\n const channelIds = new Set<string>([channelId]);\n for (const msg of rawMessages) {\n if (!msg || typeof msg !== \"object\") continue;\n const m = msg as Record<string, unknown>;\n if (typeof m.user === \"string\") userIds.add(m.user);\n if (typeof m.text === \"string\") collectMentions(m.text, userIds, channelIds);\n }\n\n const [resolvedUserList, resolvedChannelList] = await Promise.all([\n Promise.all([...userIds].map(async (id) => [id, await resolver.user(id)] as const)),\n Promise.all([...channelIds].map(async (id) => [id, await resolver.channel(id)] as const)),\n ]);\n const usersById = new Map<string, ResolvedUser>(resolvedUserList);\n const channelsById = new Map<string, ResolvedChannel>(resolvedChannelList);\n\n const enrichedMessages = rawMessages.map((msg) => {\n if (!msg || typeof msg !== \"object\") return msg;\n const m = msg as Record<string, unknown>;\n const out: Record<string, unknown> = { ...m };\n if (typeof m.user === \"string\") {\n const label = displayLabel(usersById.get(m.user));\n if (label) out.user_display_name = label;\n }\n if (typeof m.text === \"string\") {\n out.text_resolved = rewriteMentions(m.text, usersById, channelsById);\n }\n return out;\n });\n\n const resolvedUsers: Record<string, ResolvedUser> = {};\n for (const [id, info] of usersById) resolvedUsers[id] = info;\n const channelInfo = channelsById.get(channelId);\n\n return {\n ...body,\n messages: enrichedMessages,\n resolved_users: resolvedUsers,\n ...(channelInfo ? { resolved_channel: channelInfo } : {}),\n };\n}\n\nfunction collectMentions(text: string, userIds: Set<string>, channelIds: Set<string>): void {\n for (const match of text.matchAll(USER_MENTION)) {\n const id = match[1];\n if (id) userIds.add(id);\n }\n for (const match of text.matchAll(CHANNEL_MENTION)) {\n const id = match[1];\n if (id) channelIds.add(id);\n }\n}\n\nfunction rewriteMentions(\n text: string,\n users: Map<string, ResolvedUser>,\n channels: Map<string, ResolvedChannel>,\n): string {\n return text\n .replace(USER_MENTION, (_match, id: string) => {\n const label = displayLabel(users.get(id));\n return label ? `@${label}` : `<@${id}>`;\n })\n .replace(CHANNEL_MENTION, (_match, id: string, fallbackName?: string) => {\n const name = channels.get(id)?.name ?? fallbackName;\n return name ? `#${name}` : `<#${id}>`;\n });\n}\n","import { createHmac, timingSafeEqual } from \"node:crypto\";\n\nconst FIVE_MINUTES_SECONDS = 60 * 5;\n\nexport function verifySlackSignature(args: {\n rawBody: string;\n signingSecret: string;\n signature: string | null;\n timestamp: string | null;\n nowSeconds?: number;\n}): boolean {\n if (!args.signature?.startsWith(\"v0=\") || !args.timestamp) return false;\n const ts = Number(args.timestamp);\n if (!Number.isFinite(ts)) return false;\n const now = args.nowSeconds ?? Math.floor(Date.now() / 1000);\n if (Math.abs(now - ts) > FIVE_MINUTES_SECONDS) return false;\n\n const base = `v0:${args.timestamp}:${args.rawBody}`;\n const expected = `v0=${createHmac(\"sha256\", args.signingSecret).update(base).digest(\"hex\")}`;\n const expectedBuf = Buffer.from(expected, \"utf8\");\n const actualBuf = Buffer.from(args.signature, \"utf8\");\n if (expectedBuf.length !== actualBuf.length) return false;\n return timingSafeEqual(expectedBuf, actualBuf);\n}\n","import { createHash } from \"node:crypto\";\nimport type { LocalToolHandler } from \"@render-harness/core\";\nimport { type ConnectorContribution, definePack, type PackContext } from \"@render-harness/registry\";\nimport pkg from \"../package.json\" with { type: \"json\" };\nimport { slackConversationId } from \"./convid.js\";\nimport { normalizeSlackEvent, type SlackNormalizeConfig } from \"./normalize.js\";\nimport { type SlackAccessMode, slackTools } from \"./tools.js\";\nimport { verifySlackSignature } from \"./verify.js\";\n\ninterface SlackConfig extends SlackNormalizeConfig {\n agent?: string;\n userId?: string;\n signingSecretEnv?: string;\n botTokenEnv?: string;\n accessMode?: SlackAccessMode;\n}\n\nconst DEFAULT_SIGNING_SECRET_ENV = \"SLACK_SIGNING_SECRET\";\nconst DEFAULT_BOT_TOKEN_ENV = \"SLACK_BOT_TOKEN\";\n\nconst pack = definePack({\n name: \"cap-slack\",\n version: pkg.version,\n envSchema: [\n {\n name: DEFAULT_SIGNING_SECRET_ENV,\n required: true,\n secret: true,\n description: \"Slack signing secret used to verify Events API requests.\",\n },\n {\n name: DEFAULT_BOT_TOKEN_ENV,\n required: true,\n secret: true,\n description: \"Slack bot token used for read tools and optional write tools.\",\n },\n ],\n localTools(ctx: PackContext): LocalToolHandler[] {\n const cfg = readConfig(ctx.config);\n const botToken = ctx.env(cfg.botTokenEnv);\n if (!botToken) return [];\n return slackTools({\n botToken,\n accessMode: cfg.accessMode,\n ...(cfg.allowedChannels ? { allowedChannels: cfg.allowedChannels } : {}),\n });\n },\n connectors(ctx: PackContext): ConnectorContribution[] {\n const cfg = readConfig(ctx.config);\n return [\n {\n key: \"slack\",\n webhook: async (req, webCtx) => {\n const rawBody = await req.text();\n const signingSecret = ctx.env(cfg.signingSecretEnv);\n if (!signingSecret) {\n return json({ error: \"missing_secret\", env: cfg.signingSecretEnv }, 500);\n }\n if (\n !verifySlackSignature({\n rawBody,\n signingSecret,\n signature: req.headers.get(\"x-slack-signature\"),\n timestamp: req.headers.get(\"x-slack-request-timestamp\"),\n })\n ) {\n return json({ error: \"invalid_signature\" }, 401);\n }\n\n const parsed = parseBody(rawBody);\n const normalized = normalizeSlackEvent(parsed, cfg);\n if (normalized.kind === \"challenge\") return json({ challenge: normalized.challenge });\n if (normalized.kind === \"noop\")\n return json({ ok: true, skipped: true, reason: normalized.reason });\n\n const agent = webCtx.resolveAgent(cfg.agent);\n const conversationId = slackConversationId({\n teamId: normalized.teamId,\n channel: normalized.channel,\n threadTs: normalized.threadTs,\n });\n const result = await webCtx.enqueueIntoConversation({\n conversationId,\n agentName: agent.name,\n agentVersion: agent.version,\n userId: cfg.userId ?? \"cap-slack\",\n runId: `slack-${hash(normalized.eventId)}`,\n title: `Slack ${normalized.channel}/${normalized.threadTs}`,\n initialContent: [{ type: \"text\", text: normalized.text }],\n metadata: {\n connector: \"cap-slack\",\n teamId: normalized.teamId,\n channel: normalized.channel,\n threadTs: normalized.threadTs,\n ts: normalized.ts,\n ...(normalized.userId ? { userId: normalized.userId } : {}),\n },\n });\n return json(result, result.status === \"enqueued\" ? 202 : 200);\n },\n },\n ];\n },\n});\n\nexport default pack;\n\ntype ResolvedSlackConfig = Required<\n Pick<SlackConfig, \"signingSecretEnv\" | \"botTokenEnv\" | \"accessMode\" | \"includeEdits\">\n> &\n Omit<SlackConfig, \"signingSecretEnv\" | \"botTokenEnv\" | \"accessMode\" | \"includeEdits\">;\n\nfunction readConfig(raw: Record<string, unknown>): ResolvedSlackConfig {\n const cfg: ResolvedSlackConfig = {\n signingSecretEnv: stringValue(raw.signingSecretEnv) ?? DEFAULT_SIGNING_SECRET_ENV,\n botTokenEnv: stringValue(raw.botTokenEnv) ?? DEFAULT_BOT_TOKEN_ENV,\n accessMode: raw.accessMode === \"read_write\" ? \"read_write\" : \"read\",\n includeEdits: raw.includeEdits === true,\n };\n const agent = stringValue(raw.agent);\n if (agent) cfg.agent = agent;\n const userId = stringValue(raw.userId);\n if (userId) cfg.userId = userId;\n const allowedChannels = stringArray(raw.allowedChannels);\n if (allowedChannels) cfg.allowedChannels = allowedChannels;\n return cfg;\n}\n\nfunction parseBody(rawBody: string): unknown {\n try {\n return JSON.parse(rawBody);\n } catch {\n return null;\n }\n}\n\nfunction hash(value: string): string {\n return createHash(\"sha256\").update(value).digest(\"hex\").slice(0, 32);\n}\n\nfunction json(body: unknown, status = 200): Response {\n return Response.json(body, { status });\n}\n\nfunction stringValue(value: unknown): string | undefined {\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n\nfunction stringArray(value: unknown): string[] | undefined {\n return Array.isArray(value)\n ? value.filter((item): item is string => typeof item === \"string\")\n : undefined;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@render-harness/cap-slack",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "Slack Events and Web API capability pack for the Render agent harness.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -29,14 +29,14 @@
29
29
  },
30
30
  "dependencies": {
31
31
  "@slack/web-api": "^7.12.0",
32
- "@render-harness/registry": "0.4.0"
32
+ "@render-harness/registry": "0.4.1"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@types/node": "^25.6.2",
36
36
  "tsup": "^8.5.1",
37
37
  "typescript": "^6.0.3",
38
38
  "vitest": "^4.1.5",
39
- "@render-harness/core": "0.4.0"
39
+ "@render-harness/core": "0.4.1"
40
40
  },
41
41
  "publishConfig": {
42
42
  "access": "public"