@opentag/slack 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/events.d.ts +38 -1
- package/dist/events.d.ts.map +1 -1
- package/dist/index.js +423 -190
- package/dist/index.js.map +1 -1
- package/dist/ingress.d.ts.map +1 -1
- package/dist/normalize.d.ts.map +1 -1
- package/dist/render.d.ts +37 -1
- package/dist/render.d.ts.map +1 -1
- package/dist/socket-mode.d.ts +2 -2
- package/dist/socket-mode.d.ts.map +1 -1
- package/package.json +3 -3
package/dist/events.d.ts
CHANGED
|
@@ -38,6 +38,43 @@ export type SlackEventEnvelope = {
|
|
|
38
38
|
user_id?: string;
|
|
39
39
|
}>;
|
|
40
40
|
};
|
|
41
|
+
export type SlackInteractiveBlockAction = {
|
|
42
|
+
type?: string;
|
|
43
|
+
action_id?: string;
|
|
44
|
+
block_id?: string;
|
|
45
|
+
value?: string;
|
|
46
|
+
action_ts?: string;
|
|
47
|
+
};
|
|
48
|
+
export type SlackInteractivePayload = {
|
|
49
|
+
type: "block_actions";
|
|
50
|
+
api_app_id?: string;
|
|
51
|
+
team?: {
|
|
52
|
+
id?: string;
|
|
53
|
+
domain?: string;
|
|
54
|
+
};
|
|
55
|
+
user?: {
|
|
56
|
+
id?: string;
|
|
57
|
+
username?: string;
|
|
58
|
+
name?: string;
|
|
59
|
+
};
|
|
60
|
+
channel?: {
|
|
61
|
+
id?: string;
|
|
62
|
+
name?: string;
|
|
63
|
+
};
|
|
64
|
+
message?: {
|
|
65
|
+
ts?: string;
|
|
66
|
+
thread_ts?: string;
|
|
67
|
+
};
|
|
68
|
+
container?: {
|
|
69
|
+
type?: string;
|
|
70
|
+
channel_id?: string;
|
|
71
|
+
message_ts?: string;
|
|
72
|
+
thread_ts?: string;
|
|
73
|
+
};
|
|
74
|
+
trigger_id?: string;
|
|
75
|
+
actions?: SlackInteractiveBlockAction[];
|
|
76
|
+
};
|
|
77
|
+
export type SlackIngressPayload = SlackEventEnvelope | SlackInteractivePayload;
|
|
41
78
|
export type SlackAppRuntimeConfig = {
|
|
42
79
|
agentId: string;
|
|
43
80
|
appId?: string;
|
|
@@ -65,6 +102,6 @@ export type SlackEventProcessorResult = {
|
|
|
65
102
|
body: string;
|
|
66
103
|
};
|
|
67
104
|
export declare function createSlackEventProcessor(input: SlackEventProcessorInput): {
|
|
68
|
-
process(payload:
|
|
105
|
+
process(payload: SlackIngressPayload, slackApp: SlackAppRuntimeConfig): Promise<SlackEventProcessorResult>;
|
|
69
106
|
};
|
|
70
107
|
//# sourceMappingURL=events.d.ts.map
|
package/dist/events.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAC5E,OAAO,EAAwE,KAAK,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -3,15 +3,22 @@ import { parseThreadActionCommand } from "@opentag/core";
|
|
|
3
3
|
|
|
4
4
|
// src/normalize.ts
|
|
5
5
|
import { commandFromRawText } from "@opentag/core";
|
|
6
|
+
var LEADING_MENTION_RUN = /^(?:<@[^>]+>\s*)+/;
|
|
7
|
+
var MENTION_TOKEN = /<@([^>]+)>/g;
|
|
6
8
|
function stripSlackAppMention(text2, botUserId) {
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
const run = text2.match(LEADING_MENTION_RUN);
|
|
10
|
+
if (!run) return null;
|
|
11
|
+
const leadingRun = run[0];
|
|
12
|
+
if (botUserId) {
|
|
13
|
+
const mentionedIds = leadingRun.match(MENTION_TOKEN) ?? [];
|
|
14
|
+
const botIsMentioned = mentionedIds.some((token) => {
|
|
15
|
+
const id = token.slice(2, -1).split("|")[0] ?? "";
|
|
16
|
+
return id.toLowerCase() === botUserId.toLowerCase();
|
|
17
|
+
});
|
|
18
|
+
if (!botIsMentioned) return null;
|
|
13
19
|
}
|
|
14
|
-
|
|
20
|
+
const stripped = text2.slice(leadingRun.length).trim();
|
|
21
|
+
return stripped.length > 0 ? stripped : null;
|
|
15
22
|
}
|
|
16
23
|
function encodeSlackThreadKey(input) {
|
|
17
24
|
return `${input.teamId}|${input.channelId}|${input.threadTs}`;
|
|
@@ -23,18 +30,27 @@ function parseSlackThreadKey(threadKey) {
|
|
|
23
30
|
}
|
|
24
31
|
return { teamId, channelId, threadTs };
|
|
25
32
|
}
|
|
26
|
-
|
|
33
|
+
var UNKNOWN_WRITE_VERB_PATTERN = /\b(add|append|apply|change|commit|create|delete|edit|fix|modify|open\s+a?\s*pr|pull\s+request|remove|update|write)\b/i;
|
|
34
|
+
var REPO_WRITE_TARGET_PATTERN = /\b(repo|repository|code|file|files|branch|commit|diff|patch|readme|pr|pull\s+request|package\.json|pnpm|npm|test|build)\b|(?:^|\s)[./\w-]+\.(?:cjs|css|gitignore|go|html|js|json|jsx|lock|md|mjs|py|rb|rs|sh|toml|ts|tsx|txt|yaml|yml)\b|(?:^|[\s`'"(])(?:[./\w-]+\/)?(?:Dockerfile|Makefile|Procfile|Rakefile|Gemfile|Brewfile|Justfile|Taskfile|\.dockerignore|\.env(?:\.[\w-]+)?|\.gitignore|\.npmrc)(?=$|[\s`'",.):])/i;
|
|
35
|
+
function commandLooksRepoWriteCapable(command) {
|
|
36
|
+
return UNKNOWN_WRITE_VERB_PATTERN.test(command.rawText) && REPO_WRITE_TARGET_PATTERN.test(command.rawText);
|
|
37
|
+
}
|
|
38
|
+
function permissionsForCommand(command) {
|
|
27
39
|
const permissions = [
|
|
28
40
|
{
|
|
29
41
|
scope: "chat:postMessage",
|
|
30
42
|
reason: "reply in the originating Slack thread"
|
|
31
43
|
},
|
|
44
|
+
{
|
|
45
|
+
scope: "reactions:write",
|
|
46
|
+
reason: "mark the originating Slack message as received without posting a thread reply"
|
|
47
|
+
},
|
|
32
48
|
{
|
|
33
49
|
scope: "runner:local",
|
|
34
50
|
reason: "execute the run on a paired local daemon"
|
|
35
51
|
}
|
|
36
52
|
];
|
|
37
|
-
if (intent === "fix" || intent === "run") {
|
|
53
|
+
if (command.intent === "fix" || command.intent === "run" || command.intent === "unknown" && commandLooksRepoWriteCapable(command)) {
|
|
38
54
|
permissions.push(
|
|
39
55
|
{
|
|
40
56
|
scope: "repo:read",
|
|
@@ -129,7 +145,7 @@ function normalizeSlackAppMention(input) {
|
|
|
129
145
|
},
|
|
130
146
|
...contextPointersForCommand(command)
|
|
131
147
|
],
|
|
132
|
-
permissions:
|
|
148
|
+
permissions: permissionsForCommand(command),
|
|
133
149
|
callback: {
|
|
134
150
|
provider: "slack",
|
|
135
151
|
uri: input.callbackUri ?? "https://slack.com/api/chat.postMessage",
|
|
@@ -153,6 +169,290 @@ function normalizeSlackAppMention(input) {
|
|
|
153
169
|
};
|
|
154
170
|
}
|
|
155
171
|
|
|
172
|
+
// src/render.ts
|
|
173
|
+
import { suggestedActionCandidatesFromResult } from "@opentag/core";
|
|
174
|
+
var MAX_SLACK_SUGGESTED_ACTION_CANDIDATES = 20;
|
|
175
|
+
function buildSlackSuggestedActionButtonValue(input) {
|
|
176
|
+
return JSON.stringify(input);
|
|
177
|
+
}
|
|
178
|
+
function parseSlackSuggestedActionButtonValue(value) {
|
|
179
|
+
try {
|
|
180
|
+
const parsed = JSON.parse(value);
|
|
181
|
+
if (parsed.version !== 1 || typeof parsed.command !== "string" || parsed.command.trim().length === 0 || typeof parsed.proposalId !== "string" || parsed.proposalId.length === 0 || typeof parsed.intentId !== "string" || parsed.intentId.length === 0) {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
return {
|
|
185
|
+
version: 1,
|
|
186
|
+
command: parsed.command.trim(),
|
|
187
|
+
proposalId: parsed.proposalId,
|
|
188
|
+
intentId: parsed.intentId
|
|
189
|
+
};
|
|
190
|
+
} catch {
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
function escapeSlackText(text2) {
|
|
195
|
+
return text2.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
196
|
+
}
|
|
197
|
+
function markdownToSlackMrkdwn(text2) {
|
|
198
|
+
const links = [];
|
|
199
|
+
const withoutLinks = text2.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_match, label, url) => {
|
|
200
|
+
const token = `\0SLACK_LINK_${links.length}\0`;
|
|
201
|
+
links.push(`<${url}|${escapeSlackText(label)}>`);
|
|
202
|
+
return token;
|
|
203
|
+
});
|
|
204
|
+
const converted = escapeSlackText(withoutLinks).replace(/\*\*(.+?)\*\*/g, "*$1*").replace(/__(.+?)__/g, "*$1*");
|
|
205
|
+
return links.reduce((output, link, index) => output.replace(`\0SLACK_LINK_${index}\0`, link), converted);
|
|
206
|
+
}
|
|
207
|
+
function renderSlackAcknowledgement(runId) {
|
|
208
|
+
void runId;
|
|
209
|
+
return "Working on it.";
|
|
210
|
+
}
|
|
211
|
+
function slackSourceReceiptReactionName(state) {
|
|
212
|
+
if (state === "received") return "eyes";
|
|
213
|
+
return "eyes";
|
|
214
|
+
}
|
|
215
|
+
function createSlackReactionPayload(input) {
|
|
216
|
+
return {
|
|
217
|
+
channel: input.channelId,
|
|
218
|
+
timestamp: input.messageTs,
|
|
219
|
+
name: input.name
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
function nextActionSummary(result) {
|
|
223
|
+
if (!result.nextAction) return void 0;
|
|
224
|
+
if (typeof result.nextAction === "string") return result.nextAction;
|
|
225
|
+
return result.nextAction.summary;
|
|
226
|
+
}
|
|
227
|
+
function stringParam(params, key) {
|
|
228
|
+
const value = params?.[key];
|
|
229
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
230
|
+
}
|
|
231
|
+
function stringArrayParam(params, key) {
|
|
232
|
+
const value = params?.[key];
|
|
233
|
+
if (!Array.isArray(value)) return [];
|
|
234
|
+
return value.filter((item) => typeof item === "string" && item.length > 0);
|
|
235
|
+
}
|
|
236
|
+
function renderVerificationParams(params) {
|
|
237
|
+
const value = params?.["verification"];
|
|
238
|
+
if (!Array.isArray(value)) return [];
|
|
239
|
+
return value.map((item) => {
|
|
240
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) return void 0;
|
|
241
|
+
const command = item["command"];
|
|
242
|
+
const outcome = item["outcome"];
|
|
243
|
+
return typeof command === "string" && typeof outcome === "string" ? ` - \`${command}\`: ${outcome}` : void 0;
|
|
244
|
+
}).filter((line) => Boolean(line));
|
|
245
|
+
}
|
|
246
|
+
function renderSuggestedActionDetails(params, action) {
|
|
247
|
+
if (action !== "create_pull_request") return [];
|
|
248
|
+
const lines = [];
|
|
249
|
+
const title = stringParam(params, "title");
|
|
250
|
+
const head = stringParam(params, "head") ?? stringParam(params, "branch");
|
|
251
|
+
const base = stringParam(params, "base") ?? stringParam(params, "baseBranch");
|
|
252
|
+
const changedFiles = stringArrayParam(params, "changedFiles");
|
|
253
|
+
const risks = stringArrayParam(params, "risks");
|
|
254
|
+
const verification = renderVerificationParams(params);
|
|
255
|
+
if (title) lines.push(` Title: ${markdownToSlackMrkdwn(title)}`);
|
|
256
|
+
if (head || base) lines.push(` Branch: \`${head ?? "unknown"}\` -> \`${base ?? "main"}\``);
|
|
257
|
+
if (changedFiles.length > 0) lines.push(` Changed files: ${changedFiles.map((file) => `\`${file}\``).join(", ")}`);
|
|
258
|
+
if (risks.length > 0) {
|
|
259
|
+
lines.push(" Risks:");
|
|
260
|
+
for (const risk of risks) {
|
|
261
|
+
lines.push(` - ${markdownToSlackMrkdwn(risk)}`);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
if (verification.length > 0) {
|
|
265
|
+
lines.push(" Verification:");
|
|
266
|
+
lines.push(...verification);
|
|
267
|
+
}
|
|
268
|
+
return lines;
|
|
269
|
+
}
|
|
270
|
+
function truncateSlackText(text2, maxLength) {
|
|
271
|
+
const normalized = text2.replace(/\s+/g, " ").trim();
|
|
272
|
+
if (normalized.length <= maxLength) return normalized;
|
|
273
|
+
return `${normalized.slice(0, Math.max(0, maxLength - 1)).trimEnd()}\u2026`;
|
|
274
|
+
}
|
|
275
|
+
function firstMarkdownSection(text2, heading) {
|
|
276
|
+
const pattern = new RegExp(`\\*\\*${heading}:\\*\\*\\s*([\\s\\S]*?)(?=\\n\\s*\\n\\*\\*[^*]+:\\*\\*|\\n\\s*\\n[A-Z][^\\n]{0,60}:|$)`, "i");
|
|
277
|
+
const match = text2.match(pattern);
|
|
278
|
+
return match?.[1]?.trim();
|
|
279
|
+
}
|
|
280
|
+
function compactSlackSummary(summary) {
|
|
281
|
+
const whatChanged = firstMarkdownSection(summary, "What changed");
|
|
282
|
+
const firstParagraph = summary.split(/\n\s*\n/).map((part) => part.trim()).find(Boolean);
|
|
283
|
+
const selected = whatChanged ?? firstParagraph ?? summary;
|
|
284
|
+
return truncateSlackText(selected.replace(/^\*\*[^*]+:\*\*\s*/i, ""), 360);
|
|
285
|
+
}
|
|
286
|
+
function compactNextAction(nextAction) {
|
|
287
|
+
return truncateSlackText(nextAction, 180);
|
|
288
|
+
}
|
|
289
|
+
function renderSuggestedActionCandidateLines(candidate) {
|
|
290
|
+
const lines = [`${candidate.index}. *${markdownToSlackMrkdwn(candidate.intent.summary)}*`];
|
|
291
|
+
const details = renderSuggestedActionDetails(candidate.intent.params, candidate.intent.action).filter((line) => line.trim().startsWith("Branch:") || line.trim().startsWith("Changed files:")).map((line) => line.replace(/^\s+/, ""));
|
|
292
|
+
lines.push(...details);
|
|
293
|
+
if (candidate.proposalPreconditions?.length) {
|
|
294
|
+
lines.push(`Preconditions: ${candidate.proposalPreconditions.length} check(s) in the audit log.`);
|
|
295
|
+
}
|
|
296
|
+
return lines;
|
|
297
|
+
}
|
|
298
|
+
function renderSuggestedActionsMarkdown(result) {
|
|
299
|
+
const candidates = suggestedActionCandidatesFromResult(result);
|
|
300
|
+
if (candidates.length === 0) return [];
|
|
301
|
+
const lines = ["*Suggested actions*"];
|
|
302
|
+
const visibleCandidates = candidates.slice(0, MAX_SLACK_SUGGESTED_ACTION_CANDIDATES);
|
|
303
|
+
for (const candidate of visibleCandidates) {
|
|
304
|
+
lines.push("", ...renderSuggestedActionCandidateLines(candidate));
|
|
305
|
+
}
|
|
306
|
+
const remainingCount = candidates.length - visibleCandidates.length;
|
|
307
|
+
if (remainingCount > 0) {
|
|
308
|
+
lines.push("", `Showing first ${visibleCandidates.length} of ${candidates.length} actions. Reply with an action number for the rest.`);
|
|
309
|
+
}
|
|
310
|
+
lines.push("", "Use the buttons below, or reply `apply 1`, `approve 1`, or `reject 1`.");
|
|
311
|
+
return lines;
|
|
312
|
+
}
|
|
313
|
+
function createSuggestedActionButtons(candidate) {
|
|
314
|
+
return [
|
|
315
|
+
{
|
|
316
|
+
type: "button",
|
|
317
|
+
text: { type: "plain_text", text: `Apply ${candidate.index}`, emoji: true },
|
|
318
|
+
action_id: `opentag:apply:${candidate.index}`,
|
|
319
|
+
value: buildSlackSuggestedActionButtonValue({
|
|
320
|
+
version: 1,
|
|
321
|
+
command: `apply ${candidate.index}`,
|
|
322
|
+
proposalId: candidate.proposalId,
|
|
323
|
+
intentId: candidate.intent.intentId
|
|
324
|
+
}),
|
|
325
|
+
style: "primary"
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
type: "button",
|
|
329
|
+
text: { type: "plain_text", text: "Approve", emoji: true },
|
|
330
|
+
action_id: `opentag:approve:${candidate.index}`,
|
|
331
|
+
value: buildSlackSuggestedActionButtonValue({
|
|
332
|
+
version: 1,
|
|
333
|
+
command: `approve ${candidate.index}`,
|
|
334
|
+
proposalId: candidate.proposalId,
|
|
335
|
+
intentId: candidate.intent.intentId
|
|
336
|
+
})
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
type: "button",
|
|
340
|
+
text: { type: "plain_text", text: "Reject", emoji: true },
|
|
341
|
+
action_id: `opentag:reject:${candidate.index}`,
|
|
342
|
+
value: buildSlackSuggestedActionButtonValue({
|
|
343
|
+
version: 1,
|
|
344
|
+
command: `reject ${candidate.index}`,
|
|
345
|
+
proposalId: candidate.proposalId,
|
|
346
|
+
intentId: candidate.intent.intentId
|
|
347
|
+
}),
|
|
348
|
+
style: "danger"
|
|
349
|
+
}
|
|
350
|
+
];
|
|
351
|
+
}
|
|
352
|
+
function renderSlackFinalResult(result) {
|
|
353
|
+
const lines = [`*Finished: ${result.conclusion}.*`, markdownToSlackMrkdwn(compactSlackSummary(result.summary))];
|
|
354
|
+
if (result.verification?.length) {
|
|
355
|
+
lines.push(
|
|
356
|
+
`Verified: ${result.verification.slice(0, 3).map((check) => `\`${markdownToSlackMrkdwn(check.command)}\` ${markdownToSlackMrkdwn(check.outcome)}`).join(", ")}`
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
const nextAction = nextActionSummary(result);
|
|
360
|
+
if (nextAction && !result.suggestedChanges?.length) {
|
|
361
|
+
lines.push(`Next: ${markdownToSlackMrkdwn(compactNextAction(nextAction))}`);
|
|
362
|
+
}
|
|
363
|
+
const suggestedActions = renderSuggestedActionsMarkdown(result);
|
|
364
|
+
if (suggestedActions.length > 0) {
|
|
365
|
+
lines.push("", ...suggestedActions);
|
|
366
|
+
}
|
|
367
|
+
return lines.join("\n");
|
|
368
|
+
}
|
|
369
|
+
function createSlackFinalResultBlocks(result) {
|
|
370
|
+
const blocks = [
|
|
371
|
+
{
|
|
372
|
+
type: "section",
|
|
373
|
+
text: {
|
|
374
|
+
type: "mrkdwn",
|
|
375
|
+
text: `*Finished: ${result.conclusion}.*
|
|
376
|
+
${markdownToSlackMrkdwn(compactSlackSummary(result.summary))}`
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
];
|
|
380
|
+
if (result.verification?.length) {
|
|
381
|
+
blocks.push({
|
|
382
|
+
type: "section",
|
|
383
|
+
text: {
|
|
384
|
+
type: "mrkdwn",
|
|
385
|
+
text: `Verified: ${markdownToSlackMrkdwn(
|
|
386
|
+
result.verification.slice(0, 3).map((check) => `\`${check.command}\` ${check.outcome}`).join(", ")
|
|
387
|
+
)}`
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
const nextAction = nextActionSummary(result);
|
|
392
|
+
const suggestedActionCandidates = suggestedActionCandidatesFromResult(result);
|
|
393
|
+
if (nextAction && suggestedActionCandidates.length === 0) {
|
|
394
|
+
blocks.push({
|
|
395
|
+
type: "section",
|
|
396
|
+
text: {
|
|
397
|
+
type: "mrkdwn",
|
|
398
|
+
text: `Next: ${markdownToSlackMrkdwn(compactNextAction(nextAction))}`
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
if (suggestedActionCandidates.length > 0) {
|
|
403
|
+
blocks.push({ type: "divider" });
|
|
404
|
+
blocks.push({
|
|
405
|
+
type: "section",
|
|
406
|
+
text: {
|
|
407
|
+
type: "mrkdwn",
|
|
408
|
+
text: "*Suggested actions*\nChoose an action in this thread. Details stay in the OpenTag audit log."
|
|
409
|
+
}
|
|
410
|
+
});
|
|
411
|
+
const visibleCandidates = suggestedActionCandidates.slice(0, MAX_SLACK_SUGGESTED_ACTION_CANDIDATES);
|
|
412
|
+
for (const candidate of visibleCandidates) {
|
|
413
|
+
blocks.push({
|
|
414
|
+
type: "section",
|
|
415
|
+
text: {
|
|
416
|
+
type: "mrkdwn",
|
|
417
|
+
text: renderSuggestedActionCandidateLines(candidate).join("\n")
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
blocks.push({
|
|
421
|
+
type: "actions",
|
|
422
|
+
block_id: `opentag_actions_${candidate.index}`,
|
|
423
|
+
elements: createSuggestedActionButtons(candidate)
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
const remainingCount = suggestedActionCandidates.length - visibleCandidates.length;
|
|
427
|
+
if (remainingCount > 0) {
|
|
428
|
+
blocks.push({
|
|
429
|
+
type: "section",
|
|
430
|
+
text: {
|
|
431
|
+
type: "mrkdwn",
|
|
432
|
+
text: `Showing first ${visibleCandidates.length} of ${suggestedActionCandidates.length} actions. Reply with an action number for the rest.`
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
return blocks;
|
|
438
|
+
}
|
|
439
|
+
function createSlackPostMessagePayload(input) {
|
|
440
|
+
return {
|
|
441
|
+
channel: input.channelId,
|
|
442
|
+
text: markdownToSlackMrkdwn(input.text),
|
|
443
|
+
thread_ts: input.threadTs,
|
|
444
|
+
...input.blocks?.length ? { blocks: input.blocks } : {}
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
function createSlackUpdateMessagePayload(input) {
|
|
448
|
+
return {
|
|
449
|
+
channel: input.channelId,
|
|
450
|
+
text: markdownToSlackMrkdwn(input.text),
|
|
451
|
+
ts: input.messageTs,
|
|
452
|
+
...input.blocks?.length ? { blocks: input.blocks } : {}
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
|
|
156
456
|
// src/events.ts
|
|
157
457
|
function json(body, status = 200) {
|
|
158
458
|
return { kind: "json", status, body };
|
|
@@ -161,8 +461,77 @@ function text(body, status = 200) {
|
|
|
161
461
|
return { kind: "text", status, body };
|
|
162
462
|
}
|
|
163
463
|
function createSlackEventProcessor(input) {
|
|
464
|
+
async function processBlockActions(payload, slackApp) {
|
|
465
|
+
const action = payload.actions?.find((candidate) => {
|
|
466
|
+
if (candidate.action_id?.startsWith("opentag:")) return true;
|
|
467
|
+
return typeof candidate.value === "string" && parseSlackSuggestedActionButtonValue(candidate.value) !== null;
|
|
468
|
+
});
|
|
469
|
+
if (!action) {
|
|
470
|
+
return json({ ok: true });
|
|
471
|
+
}
|
|
472
|
+
const parsedValue = typeof action.value === "string" ? parseSlackSuggestedActionButtonValue(action.value) : null;
|
|
473
|
+
const rawText = parsedValue?.command ?? (typeof action.value === "string" && parseThreadActionCommand(action.value) ? action.value.trim() : void 0);
|
|
474
|
+
if (!rawText || !parseThreadActionCommand(rawText)) {
|
|
475
|
+
return json({ error: "invalid_interactive_action" }, 400);
|
|
476
|
+
}
|
|
477
|
+
if (!input.submitThreadAction) {
|
|
478
|
+
return json({ ok: true });
|
|
479
|
+
}
|
|
480
|
+
const teamId = payload.team?.id;
|
|
481
|
+
const userId = payload.user?.id;
|
|
482
|
+
const channelId = payload.channel?.id ?? payload.container?.channel_id;
|
|
483
|
+
const messageTs = payload.message?.ts ?? payload.container?.message_ts;
|
|
484
|
+
const threadTs = payload.message?.thread_ts ?? payload.container?.thread_ts ?? messageTs;
|
|
485
|
+
if (!teamId || !userId || !channelId || !messageTs || !threadTs) {
|
|
486
|
+
return json({ error: "invalid_interactive_payload" }, 400);
|
|
487
|
+
}
|
|
488
|
+
const binding = await input.resolveChannelBinding({
|
|
489
|
+
teamId,
|
|
490
|
+
channelId
|
|
491
|
+
});
|
|
492
|
+
if (!binding) {
|
|
493
|
+
return json({ ok: true, ignored: "unbound_channel" });
|
|
494
|
+
}
|
|
495
|
+
await input.submitThreadAction({
|
|
496
|
+
id: `approval_slack_block_${payload.trigger_id ?? `${action.action_id ?? "action"}_${action.action_ts ?? messageTs}`}`,
|
|
497
|
+
rawText,
|
|
498
|
+
actor: {
|
|
499
|
+
provider: "slack",
|
|
500
|
+
providerUserId: userId,
|
|
501
|
+
handle: payload.user?.username ?? payload.user?.name ?? userId,
|
|
502
|
+
organizationId: teamId
|
|
503
|
+
},
|
|
504
|
+
callback: {
|
|
505
|
+
provider: "slack",
|
|
506
|
+
uri: slackApp.callbackUri ?? "https://slack.com/api/chat.postMessage",
|
|
507
|
+
threadKey: encodeSlackThreadKey({
|
|
508
|
+
teamId,
|
|
509
|
+
channelId,
|
|
510
|
+
threadTs
|
|
511
|
+
})
|
|
512
|
+
},
|
|
513
|
+
metadata: {
|
|
514
|
+
source: "slack_button",
|
|
515
|
+
teamId,
|
|
516
|
+
channelId,
|
|
517
|
+
messageTs,
|
|
518
|
+
...payload.api_app_id ? { slackAppId: payload.api_app_id } : {},
|
|
519
|
+
...action.action_id ? { actionId: action.action_id } : {},
|
|
520
|
+
...action.block_id ? { blockId: action.block_id } : {},
|
|
521
|
+
...action.action_ts ? { actionTs: action.action_ts } : {},
|
|
522
|
+
...parsedValue ? { proposalId: parsedValue.proposalId, intentId: parsedValue.intentId } : {},
|
|
523
|
+
repoProvider: binding.repoProvider ?? "github",
|
|
524
|
+
owner: binding.owner,
|
|
525
|
+
repo: binding.repo
|
|
526
|
+
}
|
|
527
|
+
});
|
|
528
|
+
return json({ ok: true });
|
|
529
|
+
}
|
|
164
530
|
return {
|
|
165
531
|
async process(payload, slackApp) {
|
|
532
|
+
if (payload.type === "block_actions") {
|
|
533
|
+
return processBlockActions(payload, slackApp);
|
|
534
|
+
}
|
|
166
535
|
if (payload.type === "url_verification") {
|
|
167
536
|
return text(payload.challenge ?? "");
|
|
168
537
|
}
|
|
@@ -314,8 +683,13 @@ function verifySlackTimestamp(input) {
|
|
|
314
683
|
function createSlackEventsApp(input) {
|
|
315
684
|
const app = new Hono();
|
|
316
685
|
const processor = createSlackEventProcessor(input);
|
|
317
|
-
function parseSlackPayload(rawBody) {
|
|
686
|
+
function parseSlackPayload(rawBody, contentType) {
|
|
318
687
|
try {
|
|
688
|
+
if (contentType?.includes("application/x-www-form-urlencoded") || rawBody.startsWith("payload=")) {
|
|
689
|
+
const interactivePayload = new URLSearchParams(rawBody).get("payload");
|
|
690
|
+
if (!interactivePayload) return null;
|
|
691
|
+
return JSON.parse(interactivePayload);
|
|
692
|
+
}
|
|
319
693
|
return JSON.parse(rawBody);
|
|
320
694
|
} catch {
|
|
321
695
|
return null;
|
|
@@ -346,7 +720,7 @@ function createSlackEventsApp(input) {
|
|
|
346
720
|
return c.json({ error: "stale_signature_timestamp" }, 401);
|
|
347
721
|
}
|
|
348
722
|
const rawBody = await c.req.text();
|
|
349
|
-
const payload = parseSlackPayload(rawBody);
|
|
723
|
+
const payload = parseSlackPayload(rawBody, c.req.header("content-type"));
|
|
350
724
|
if (!payload) {
|
|
351
725
|
return c.json({ error: "invalid_json" }, 400);
|
|
352
726
|
}
|
|
@@ -400,185 +774,25 @@ function startSlackIngress(config) {
|
|
|
400
774
|
};
|
|
401
775
|
}
|
|
402
776
|
|
|
403
|
-
// src/render.ts
|
|
404
|
-
import { suggestedActionCandidatesFromResult } from "@opentag/core";
|
|
405
|
-
function escapeSlackText(text2) {
|
|
406
|
-
return text2.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
407
|
-
}
|
|
408
|
-
function markdownToSlackMrkdwn(text2) {
|
|
409
|
-
const links = [];
|
|
410
|
-
const withoutLinks = text2.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_match, label, url) => {
|
|
411
|
-
const token = `\0SLACK_LINK_${links.length}\0`;
|
|
412
|
-
links.push(`<${url}|${escapeSlackText(label)}>`);
|
|
413
|
-
return token;
|
|
414
|
-
});
|
|
415
|
-
const converted = escapeSlackText(withoutLinks).replace(/\*\*(.+?)\*\*/g, "*$1*").replace(/__(.+?)__/g, "*$1*");
|
|
416
|
-
return links.reduce((output, link, index) => output.replace(`\0SLACK_LINK_${index}\0`, link), converted);
|
|
417
|
-
}
|
|
418
|
-
function renderSlackAcknowledgement(runId) {
|
|
419
|
-
return `I picked this up: \`${runId}\``;
|
|
420
|
-
}
|
|
421
|
-
function nextActionSummary(result) {
|
|
422
|
-
if (!result.nextAction) return void 0;
|
|
423
|
-
if (typeof result.nextAction === "string") return result.nextAction;
|
|
424
|
-
return result.nextAction.summary;
|
|
425
|
-
}
|
|
426
|
-
function stringParam(params, key) {
|
|
427
|
-
const value = params?.[key];
|
|
428
|
-
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
429
|
-
}
|
|
430
|
-
function stringArrayParam(params, key) {
|
|
431
|
-
const value = params?.[key];
|
|
432
|
-
if (!Array.isArray(value)) return [];
|
|
433
|
-
return value.filter((item) => typeof item === "string" && item.length > 0);
|
|
434
|
-
}
|
|
435
|
-
function renderVerificationParams(params) {
|
|
436
|
-
const value = params?.["verification"];
|
|
437
|
-
if (!Array.isArray(value)) return [];
|
|
438
|
-
return value.map((item) => {
|
|
439
|
-
if (!item || typeof item !== "object" || Array.isArray(item)) return void 0;
|
|
440
|
-
const command = item["command"];
|
|
441
|
-
const outcome = item["outcome"];
|
|
442
|
-
return typeof command === "string" && typeof outcome === "string" ? ` - \`${command}\`: ${outcome}` : void 0;
|
|
443
|
-
}).filter((line) => Boolean(line));
|
|
444
|
-
}
|
|
445
|
-
function renderSuggestedActionDetails(params, action) {
|
|
446
|
-
if (action !== "create_pull_request") return [];
|
|
447
|
-
const lines = [];
|
|
448
|
-
const title = stringParam(params, "title");
|
|
449
|
-
const head = stringParam(params, "head") ?? stringParam(params, "branch");
|
|
450
|
-
const base = stringParam(params, "base") ?? stringParam(params, "baseBranch");
|
|
451
|
-
const changedFiles = stringArrayParam(params, "changedFiles");
|
|
452
|
-
const risks = stringArrayParam(params, "risks");
|
|
453
|
-
const verification = renderVerificationParams(params);
|
|
454
|
-
if (title) lines.push(` Title: ${markdownToSlackMrkdwn(title)}`);
|
|
455
|
-
if (head || base) lines.push(` Branch: \`${head ?? "unknown"}\` -> \`${base ?? "main"}\``);
|
|
456
|
-
if (changedFiles.length > 0) lines.push(` Changed files: ${changedFiles.map((file) => `\`${file}\``).join(", ")}`);
|
|
457
|
-
if (risks.length > 0) {
|
|
458
|
-
lines.push(" Risks:");
|
|
459
|
-
for (const risk of risks) {
|
|
460
|
-
lines.push(` - ${markdownToSlackMrkdwn(risk)}`);
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
if (verification.length > 0) {
|
|
464
|
-
lines.push(" Verification:");
|
|
465
|
-
lines.push(...verification);
|
|
466
|
-
}
|
|
467
|
-
return lines;
|
|
468
|
-
}
|
|
469
|
-
function renderSuggestedActionsMarkdown(result) {
|
|
470
|
-
const candidates = suggestedActionCandidatesFromResult(result);
|
|
471
|
-
if (candidates.length === 0) return [];
|
|
472
|
-
const lines = ["*Suggested actions*"];
|
|
473
|
-
for (const candidate of candidates) {
|
|
474
|
-
lines.push(
|
|
475
|
-
"",
|
|
476
|
-
`${candidate.index}. *${markdownToSlackMrkdwn(candidate.intent.summary)}*`,
|
|
477
|
-
` Intent: \`${candidate.intent.action}\` (\`${candidate.intent.domain}\`)`,
|
|
478
|
-
` Proposal: \`${candidate.proposalId}\``,
|
|
479
|
-
` Intent ID: \`${candidate.intent.intentId}\``
|
|
480
|
-
);
|
|
481
|
-
lines.push(...renderSuggestedActionDetails(candidate.intent.params, candidate.intent.action));
|
|
482
|
-
if (candidate.proposalPreconditions?.length) {
|
|
483
|
-
lines.push(" Preconditions:");
|
|
484
|
-
for (const precondition of candidate.proposalPreconditions) {
|
|
485
|
-
lines.push(` - ${markdownToSlackMrkdwn(precondition)}`);
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
lines.push(
|
|
490
|
-
"",
|
|
491
|
-
"Reply with:",
|
|
492
|
-
"- `approve 1` to record approval",
|
|
493
|
-
"- `apply 1` or `apply all` to apply supported actions",
|
|
494
|
-
"- `continue 1` to continue with a follow-up run",
|
|
495
|
-
"- `reject 1` to reject an action"
|
|
496
|
-
);
|
|
497
|
-
return lines;
|
|
498
|
-
}
|
|
499
|
-
function renderSlackFinalResult(result) {
|
|
500
|
-
const lines = [`Finished with *${result.conclusion}*.`, "", markdownToSlackMrkdwn(result.summary)];
|
|
501
|
-
if (result.verification?.length) {
|
|
502
|
-
lines.push("", "*Verification*");
|
|
503
|
-
for (const check of result.verification) {
|
|
504
|
-
lines.push(`- \`${check.command}\`: ${check.outcome}`);
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
const nextAction = nextActionSummary(result);
|
|
508
|
-
if (nextAction) {
|
|
509
|
-
lines.push("", `*Next action*: ${markdownToSlackMrkdwn(nextAction)}`);
|
|
510
|
-
}
|
|
511
|
-
const suggestedActions = renderSuggestedActionsMarkdown(result);
|
|
512
|
-
if (suggestedActions.length > 0) {
|
|
513
|
-
lines.push("", ...suggestedActions);
|
|
514
|
-
}
|
|
515
|
-
return lines.join("\n");
|
|
516
|
-
}
|
|
517
|
-
function createSlackFinalResultBlocks(result) {
|
|
518
|
-
const blocks = [
|
|
519
|
-
{
|
|
520
|
-
type: "section",
|
|
521
|
-
text: {
|
|
522
|
-
type: "mrkdwn",
|
|
523
|
-
text: `*Finished with ${result.conclusion}.*
|
|
524
|
-
${markdownToSlackMrkdwn(result.summary)}`
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
];
|
|
528
|
-
if (result.verification?.length) {
|
|
529
|
-
blocks.push({ type: "divider" });
|
|
530
|
-
blocks.push({
|
|
531
|
-
type: "section",
|
|
532
|
-
text: {
|
|
533
|
-
type: "mrkdwn",
|
|
534
|
-
text: markdownToSlackMrkdwn(["*Verification*", ...result.verification.map((check) => `- \`${check.command}\`: ${check.outcome}`)].join("\n"))
|
|
535
|
-
}
|
|
536
|
-
});
|
|
537
|
-
}
|
|
538
|
-
const nextAction = nextActionSummary(result);
|
|
539
|
-
if (nextAction) {
|
|
540
|
-
blocks.push({
|
|
541
|
-
type: "section",
|
|
542
|
-
text: {
|
|
543
|
-
type: "mrkdwn",
|
|
544
|
-
text: `*Next action*: ${markdownToSlackMrkdwn(nextAction)}`
|
|
545
|
-
}
|
|
546
|
-
});
|
|
547
|
-
}
|
|
548
|
-
const suggestedActions = renderSuggestedActionsMarkdown(result);
|
|
549
|
-
if (suggestedActions.length > 0) {
|
|
550
|
-
blocks.push({ type: "divider" });
|
|
551
|
-
blocks.push({
|
|
552
|
-
type: "section",
|
|
553
|
-
text: {
|
|
554
|
-
type: "mrkdwn",
|
|
555
|
-
text: suggestedActions.join("\n")
|
|
556
|
-
}
|
|
557
|
-
});
|
|
558
|
-
}
|
|
559
|
-
return blocks;
|
|
560
|
-
}
|
|
561
|
-
function createSlackPostMessagePayload(input) {
|
|
562
|
-
return {
|
|
563
|
-
channel: input.channelId,
|
|
564
|
-
text: markdownToSlackMrkdwn(input.text),
|
|
565
|
-
thread_ts: input.threadTs,
|
|
566
|
-
...input.blocks?.length ? { blocks: input.blocks } : {}
|
|
567
|
-
};
|
|
568
|
-
}
|
|
569
|
-
function createSlackUpdateMessagePayload(input) {
|
|
570
|
-
return {
|
|
571
|
-
channel: input.channelId,
|
|
572
|
-
text: markdownToSlackMrkdwn(input.text),
|
|
573
|
-
ts: input.messageTs,
|
|
574
|
-
...input.blocks?.length ? { blocks: input.blocks } : {}
|
|
575
|
-
};
|
|
576
|
-
}
|
|
577
|
-
|
|
578
777
|
// src/socket-mode.ts
|
|
579
778
|
import WebSocket from "ws";
|
|
580
779
|
var SLACK_CONNECTIONS_OPEN_URL = "https://slack.com/api/apps.connections.open";
|
|
581
780
|
var DEFAULT_RECONNECT_DELAY_MS = 1e3;
|
|
781
|
+
var TERMINAL_SLACK_ERROR_CODES = [
|
|
782
|
+
"invalid_auth",
|
|
783
|
+
"not_authed",
|
|
784
|
+
"account_inactive",
|
|
785
|
+
"token_revoked",
|
|
786
|
+
"token_expired",
|
|
787
|
+
"not_allowed_token_type",
|
|
788
|
+
"no_permission",
|
|
789
|
+
"missing_scope",
|
|
790
|
+
"ekm_access_denied"
|
|
791
|
+
];
|
|
792
|
+
function isTerminalSlackAuthError(error) {
|
|
793
|
+
if (!(error instanceof Error)) return false;
|
|
794
|
+
return TERMINAL_SLACK_ERROR_CODES.some((code) => error.message.includes(code));
|
|
795
|
+
}
|
|
582
796
|
function rawDataToString(data) {
|
|
583
797
|
if (typeof data === "string") return data;
|
|
584
798
|
if (Buffer.isBuffer(data)) return data.toString("utf8");
|
|
@@ -614,12 +828,15 @@ async function handleSocketMessage(input) {
|
|
|
614
828
|
return;
|
|
615
829
|
}
|
|
616
830
|
input.socket.send(JSON.stringify({ envelope_id: envelope.envelope_id }));
|
|
617
|
-
if (
|
|
831
|
+
if (!envelope.payload) {
|
|
618
832
|
return;
|
|
619
833
|
}
|
|
620
834
|
if (input.slackApp.appId && envelope.payload.api_app_id && envelope.payload.api_app_id !== input.slackApp.appId) {
|
|
621
835
|
return;
|
|
622
836
|
}
|
|
837
|
+
if (envelope.type !== "events_api" && envelope.payload.type !== "block_actions") {
|
|
838
|
+
return;
|
|
839
|
+
}
|
|
623
840
|
await input.processor.process(envelope.payload, input.slackApp);
|
|
624
841
|
}
|
|
625
842
|
function wait(ms) {
|
|
@@ -671,8 +888,20 @@ function startSlackSocketModeApp(input, dependencies = {}) {
|
|
|
671
888
|
}
|
|
672
889
|
const startPromise = (async () => {
|
|
673
890
|
while (!closed) {
|
|
674
|
-
|
|
675
|
-
|
|
891
|
+
try {
|
|
892
|
+
const socketUrl = await openSlackSocketUrl({ appToken: input.appToken, fetchImpl });
|
|
893
|
+
await runOneConnection(socketUrl);
|
|
894
|
+
} catch (error) {
|
|
895
|
+
if (isTerminalSlackAuthError(error)) {
|
|
896
|
+
if (!closed) {
|
|
897
|
+
logError("[slack] terminal Socket Mode auth/config error, aborting:", error);
|
|
898
|
+
}
|
|
899
|
+
throw error;
|
|
900
|
+
}
|
|
901
|
+
if (!closed) {
|
|
902
|
+
logError("[slack] failed to open Socket Mode connection, retrying:", error);
|
|
903
|
+
}
|
|
904
|
+
}
|
|
676
905
|
if (!closed) {
|
|
677
906
|
await wait(reconnectDelayMs);
|
|
678
907
|
}
|
|
@@ -702,18 +931,22 @@ function startSlackSocketModeIngress(config, dependencies = {}) {
|
|
|
702
931
|
);
|
|
703
932
|
}
|
|
704
933
|
export {
|
|
934
|
+
buildSlackSuggestedActionButtonValue,
|
|
705
935
|
computeSlackSignature,
|
|
706
936
|
createSlackEventProcessor,
|
|
707
937
|
createSlackEventsApp,
|
|
708
938
|
createSlackFinalResultBlocks,
|
|
709
939
|
createSlackPostMessagePayload,
|
|
940
|
+
createSlackReactionPayload,
|
|
710
941
|
createSlackUpdateMessagePayload,
|
|
711
942
|
encodeSlackThreadKey,
|
|
712
943
|
markdownToSlackMrkdwn,
|
|
713
944
|
normalizeSlackAppMention,
|
|
945
|
+
parseSlackSuggestedActionButtonValue,
|
|
714
946
|
parseSlackThreadKey,
|
|
715
947
|
renderSlackAcknowledgement,
|
|
716
948
|
renderSlackFinalResult,
|
|
949
|
+
slackSourceReceiptReactionName,
|
|
717
950
|
startSlackIngress,
|
|
718
951
|
startSlackSocketModeApp,
|
|
719
952
|
startSlackSocketModeIngress,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/events.ts","../src/normalize.ts","../src/ingress.ts","../src/dispatcher-events.ts","../src/render.ts","../src/socket-mode.ts"],"sourcesContent":["import { parseThreadActionCommand, type OpenTagEvent } from \"@opentag/core\";\nimport { encodeSlackThreadKey, normalizeSlackAppMention, stripSlackAppMention, type SlackChannelBinding } from \"./normalize.js\";\n\nexport type SlackThreadActionInput = {\n id: string;\n rawText: string;\n actor: {\n provider: \"slack\";\n providerUserId: string;\n handle: string;\n organizationId: string;\n };\n callback: {\n provider: \"slack\";\n uri: string;\n threadKey: string;\n };\n metadata: Record<string, unknown>;\n};\n\nexport type SlackEventEnvelope = {\n token?: string;\n type: \"url_verification\" | \"event_callback\";\n challenge?: string;\n team_id?: string;\n api_app_id?: string;\n event?: {\n type: string;\n user?: string;\n text?: string;\n ts?: string;\n thread_ts?: string;\n channel?: string;\n subtype?: string;\n bot_id?: string;\n };\n event_id?: string;\n event_time?: number;\n authorizations?: Array<{ user_id?: string }>;\n};\n\nexport type SlackAppRuntimeConfig = {\n agentId: string;\n appId?: string;\n callbackUri?: string;\n};\n\nexport type SlackEventProcessorInput = {\n resolveChannelBinding(input: { teamId: string; channelId: string }): Promise<SlackChannelBinding | null>;\n createRun(event: OpenTagEvent): Promise<{ runId: string }>;\n submitThreadAction?(action: SlackThreadActionInput): Promise<unknown>;\n now(): string;\n};\n\nexport type SlackEventProcessorStatus = 200 | 400;\n\nexport type SlackEventProcessorResult =\n | {\n kind: \"json\";\n status: SlackEventProcessorStatus;\n body: Record<string, unknown>;\n }\n | {\n kind: \"text\";\n status: SlackEventProcessorStatus;\n body: string;\n };\n\nfunction json(body: Record<string, unknown>, status: SlackEventProcessorStatus = 200): SlackEventProcessorResult {\n return { kind: \"json\", status, body };\n}\n\nfunction text(body: string, status: SlackEventProcessorStatus = 200): SlackEventProcessorResult {\n return { kind: \"text\", status, body };\n}\n\nexport function createSlackEventProcessor(input: SlackEventProcessorInput) {\n return {\n async process(payload: SlackEventEnvelope, slackApp: SlackAppRuntimeConfig): Promise<SlackEventProcessorResult> {\n if (payload.type === \"url_verification\") {\n return text(payload.challenge ?? \"\");\n }\n if (payload.type !== \"event_callback\" || !payload.event || ![\"app_mention\", \"message\"].includes(payload.event.type)) {\n return json({ ok: true });\n }\n if (payload.event.type === \"message\" && (payload.event.subtype || payload.event.bot_id)) {\n return json({ ok: true });\n }\n if (!payload.team_id || !payload.event.channel || !payload.event.user || !payload.event.text || !payload.event.ts || !payload.event_id) {\n return json({ error: \"invalid_event_payload\" }, 400);\n }\n\n const rawThreadActionText =\n payload.event.type === \"app_mention\"\n ? stripSlackAppMention(payload.event.text, payload.authorizations?.[0]?.user_id)\n : payload.event.text.trim();\n if (payload.event.type === \"message\" && (!rawThreadActionText || !parseThreadActionCommand(rawThreadActionText))) {\n return json({ ok: true });\n }\n\n const binding = await input.resolveChannelBinding({\n teamId: payload.team_id,\n channelId: payload.event.channel\n });\n if (!binding) {\n return json({ ok: true, ignored: \"unbound_channel\" });\n }\n\n if (rawThreadActionText && parseThreadActionCommand(rawThreadActionText) && input.submitThreadAction) {\n await input.submitThreadAction({\n id: `approval_slack_${payload.event_id}`,\n rawText: rawThreadActionText,\n actor: {\n provider: \"slack\",\n providerUserId: payload.event.user,\n handle: payload.event.user,\n organizationId: payload.team_id\n },\n callback: {\n provider: \"slack\",\n uri: slackApp.callbackUri ?? \"https://slack.com/api/chat.postMessage\",\n threadKey: encodeSlackThreadKey({\n teamId: payload.team_id,\n channelId: payload.event.channel,\n threadTs: payload.event.thread_ts ?? payload.event.ts\n })\n },\n metadata: {\n teamId: payload.team_id,\n channelId: payload.event.channel,\n messageTs: payload.event.ts,\n ...(payload.api_app_id ? { slackAppId: payload.api_app_id } : {}),\n ...(payload.authorizations?.[0]?.user_id ? { slackBotUserId: payload.authorizations[0].user_id } : {}),\n repoProvider: binding.repoProvider ?? \"github\",\n owner: binding.owner,\n repo: binding.repo\n }\n });\n return json({ ok: true });\n }\n\n if (payload.event.type !== \"app_mention\") {\n return json({ ok: true });\n }\n\n const event = normalizeSlackAppMention({\n teamId: payload.team_id,\n channelId: payload.event.channel,\n userId: payload.event.user,\n text: payload.event.text,\n ts: payload.event.ts,\n eventId: payload.event_id,\n eventTime: payload.event_time ?? Math.floor(Date.parse(input.now()) / 1000),\n agentId: slackApp.agentId,\n binding,\n ...(payload.api_app_id ? { appId: payload.api_app_id } : {}),\n ...(payload.event.thread_ts ? { threadTs: payload.event.thread_ts } : {}),\n ...(payload.authorizations?.[0]?.user_id ? { botUserId: payload.authorizations[0].user_id } : {}),\n ...(slackApp.callbackUri ? { callbackUri: slackApp.callbackUri } : {})\n });\n if (!event) {\n return json({ ok: true, ignored: \"empty_command\" });\n }\n\n await input.createRun(event);\n return json({ ok: true });\n }\n };\n}\n","import { commandFromRawText, type ContextPointer, type OpenTagCommand, type OpenTagEvent, type PermissionGrant } from \"@opentag/core\";\n\nexport type SlackChannelBinding = {\n teamId: string;\n channelId: string;\n repoProvider?: string;\n owner: string;\n repo: string;\n};\n\nexport type SlackAppMentionInput = {\n teamId: string;\n channelId: string;\n userId: string;\n text: string;\n ts: string;\n threadTs?: string;\n eventId: string;\n eventTime: number;\n appId?: string;\n agentId?: string;\n botUserId?: string;\n callbackUri?: string;\n binding: SlackChannelBinding;\n};\n\nexport function stripSlackAppMention(text: string, botUserId?: string): string | null {\n const patterns = botUserId\n ? [new RegExp(`^<@${botUserId}>\\\\s*`, \"i\"), /^<@[^>]+>\\s*/]\n : [/^<@[^>]+>\\s*/];\n\n for (const pattern of patterns) {\n const stripped = text.replace(pattern, \"\").trim();\n if (stripped !== text.trim()) {\n return stripped.length > 0 ? stripped : null;\n }\n }\n\n return null;\n}\n\nexport function encodeSlackThreadKey(input: { teamId: string; channelId: string; threadTs: string }): string {\n return `${input.teamId}|${input.channelId}|${input.threadTs}`;\n}\n\nexport function parseSlackThreadKey(threadKey: string): { teamId: string; channelId: string; threadTs: string } {\n const [teamId, channelId, threadTs] = threadKey.split(\"|\");\n if (!teamId || !channelId || !threadTs) {\n throw new Error(`Invalid Slack thread key: ${threadKey}`);\n }\n return { teamId, channelId, threadTs };\n}\n\nfunction permissionsForIntent(intent: OpenTagCommand[\"intent\"]): PermissionGrant[] {\n const permissions: PermissionGrant[] = [\n {\n scope: \"chat:postMessage\",\n reason: \"reply in the originating Slack thread\"\n },\n {\n scope: \"runner:local\",\n reason: \"execute the run on a paired local daemon\"\n }\n ];\n\n if (intent === \"fix\" || intent === \"run\") {\n permissions.push(\n {\n scope: \"repo:read\",\n reason: \"inspect the repository in the paired local checkout\"\n },\n {\n scope: \"repo:write\",\n reason: \"commit code changes on an isolated run branch\"\n },\n {\n scope: \"pr:create\",\n reason: \"open a pull request for completed code changes\"\n }\n );\n }\n\n return permissions;\n}\n\nfunction contextPointersForCommand(command: OpenTagCommand): ContextPointer[] {\n const context: ContextPointer[] = [];\n\n for (const reference of command.parsed?.references ?? []) {\n if (reference.kind === \"url\") {\n context.push({\n kind: \"url\",\n uri: reference.uri,\n visibility: \"organization\",\n title: reference.title ?? \"Command URL reference\"\n });\n continue;\n }\n\n if (reference.kind === \"file\" || reference.kind === \"path\") {\n context.push({\n kind: \"file\",\n uri: reference.uri,\n ...(reference.line ? { line: reference.line } : {}),\n ...(reference.startLine ? { startLine: reference.startLine } : {}),\n ...(reference.endLine ? { endLine: reference.endLine } : {}),\n visibility: \"organization\",\n title: referenceTitle(reference)\n });\n }\n }\n\n return context;\n}\n\nfunction referenceTitle(reference: NonNullable<OpenTagCommand[\"parsed\"]>[\"references\"][number]): string {\n return reference.title ?? \"Command file reference\";\n}\n\nfunction commandMetadata(command: OpenTagCommand): Record<string, unknown> {\n if (!command.parsed) return {};\n return {\n commandParser: command.parsed.version,\n commandDiagnostics: command.parsed.diagnostics,\n ...(command.parsed.approval ? { approval: command.parsed.approval } : {}),\n ...(command.parsed.network ? { network: command.parsed.network } : {})\n };\n}\n\nexport function normalizeSlackAppMention(input: SlackAppMentionInput): OpenTagEvent | null {\n const rawText = stripSlackAppMention(input.text, input.botUserId);\n if (!rawText) return null;\n\n const command = commandFromRawText(rawText);\n const replyThreadTs = input.threadTs ?? input.ts;\n const agentId = input.agentId ?? \"opentag\";\n\n return {\n id: `evt_slack_app_mention_${input.eventId}`,\n source: \"slack\",\n sourceEventId: input.eventId,\n receivedAt: new Date(input.eventTime * 1000).toISOString(),\n actor: {\n provider: \"slack\",\n providerUserId: input.userId,\n handle: input.userId,\n organizationId: input.teamId\n },\n target: {\n mention: input.botUserId ? `<@${input.botUserId}>` : \"<@app>\",\n agentId,\n ...(command.parsed?.executorHint ? { executorHint: command.parsed.executorHint } : {})\n },\n command,\n context: [\n {\n provider: \"slack\",\n kind: \"message\",\n uri: `slack://team/${input.teamId}/channel/${input.channelId}/message/${input.ts}`,\n visibility: \"organization\",\n title: \"Slack message\"\n },\n {\n kind: \"text\",\n uri: input.text,\n visibility: \"organization\",\n title: \"Slack message text\"\n },\n ...contextPointersForCommand(command)\n ],\n permissions: permissionsForIntent(command.intent),\n callback: {\n provider: \"slack\",\n uri: input.callbackUri ?? \"https://slack.com/api/chat.postMessage\",\n threadKey: encodeSlackThreadKey({\n teamId: input.teamId,\n channelId: input.channelId,\n threadTs: replyThreadTs\n })\n },\n metadata: {\n teamId: input.teamId,\n channelId: input.channelId,\n messageTs: input.ts,\n ...(input.appId ? { slackAppId: input.appId } : {}),\n ...(input.botUserId ? { slackBotUserId: input.botUserId } : {}),\n ...commandMetadata(command),\n repoProvider: input.binding.repoProvider ?? \"github\",\n owner: input.binding.owner,\n repo: input.binding.repo\n }\n };\n}\n","import { createHmac, timingSafeEqual } from \"node:crypto\";\nimport { serve } from \"@hono/node-server\";\nimport { Hono } from \"hono\";\nimport { createSlackDispatcherEventProcessorInput } from \"./dispatcher-events.js\";\nimport { createSlackEventProcessor, type SlackAppRuntimeConfig, type SlackEventEnvelope, type SlackEventProcessorInput } from \"./events.js\";\n\nexport type SlackEventsAppInput = {\n slackApps: Array<\n SlackAppRuntimeConfig & {\n signingSecret: string;\n }\n >;\n clock?: () => number;\n} & SlackEventProcessorInput;\n\nexport type SlackEventsApiIngressConfig = {\n signingSecret: string;\n dispatcherUrl: string;\n dispatcherToken?: string;\n port?: number;\n agentId?: string;\n appId?: string;\n callbackUri?: string;\n};\n\nexport type SlackIngressConfig = SlackEventsApiIngressConfig;\n\nexport type SlackIngressHandle = {\n url: string;\n server: ReturnType<typeof serve>;\n close(): Promise<void>;\n};\n\nexport function computeSlackSignature(input: {\n signingSecret: string;\n timestamp: string;\n rawBody: string;\n}): string {\n const base = `v0:${input.timestamp}:${input.rawBody}`;\n const digest = createHmac(\"sha256\", input.signingSecret).update(base).digest(\"hex\");\n return `v0=${digest}`;\n}\n\nexport function verifySlackSignature(input: {\n signingSecret: string;\n timestamp: string;\n rawBody: string;\n signature: string;\n}): boolean {\n const expected = computeSlackSignature(input);\n const expectedBuffer = Buffer.from(expected);\n const actualBuffer = Buffer.from(input.signature);\n return expectedBuffer.length === actualBuffer.length && timingSafeEqual(expectedBuffer, actualBuffer);\n}\n\nexport function verifySlackTimestamp(input: { timestamp: string; nowMs: number; toleranceSeconds?: number }): boolean {\n const timestampSeconds = Number(input.timestamp);\n if (!Number.isFinite(timestampSeconds)) return false;\n const toleranceSeconds = input.toleranceSeconds ?? 300;\n const ageSeconds = Math.abs(Math.floor(input.nowMs / 1000) - timestampSeconds);\n return ageSeconds <= toleranceSeconds;\n}\n\nexport function createSlackEventsApp(input: SlackEventsAppInput) {\n const app = new Hono();\n const processor = createSlackEventProcessor(input);\n\n function parseSlackPayload(rawBody: string): SlackEventEnvelope | null {\n try {\n return JSON.parse(rawBody) as SlackEventEnvelope;\n } catch {\n return null;\n }\n }\n\n function resolveSlackApp(inputValue: {\n apiAppId?: string;\n rawBody: string;\n signature: string;\n timestamp: string;\n }) {\n const candidates = inputValue.apiAppId\n ? input.slackApps.filter((candidate) => !candidate.appId || candidate.appId === inputValue.apiAppId)\n : input.slackApps;\n if (candidates.length === 0) {\n return { error: \"unknown_slack_app\" as const };\n }\n const slackApp = candidates.find((candidate) =>\n verifySlackSignature({\n signingSecret: candidate.signingSecret,\n timestamp: inputValue.timestamp,\n rawBody: inputValue.rawBody,\n signature: inputValue.signature\n })\n );\n return slackApp ? { slackApp } : { error: \"invalid_signature\" as const };\n }\n\n app.post(\"/slack/events\", async (c) => {\n const timestamp = c.req.header(\"x-slack-request-timestamp\");\n const signature = c.req.header(\"x-slack-signature\");\n if (!timestamp || !signature) {\n return c.json({ error: \"missing_signature_headers\" }, 401);\n }\n if (!verifySlackTimestamp({ timestamp, nowMs: input.clock?.() ?? Date.now() })) {\n return c.json({ error: \"stale_signature_timestamp\" }, 401);\n }\n const rawBody = await c.req.text();\n const payload = parseSlackPayload(rawBody);\n if (!payload) {\n return c.json({ error: \"invalid_json\" }, 400);\n }\n const resolvedSlackApp = resolveSlackApp({\n rawBody,\n signature,\n timestamp,\n ...(payload.api_app_id ? { apiAppId: payload.api_app_id } : {})\n });\n if (\"error\" in resolvedSlackApp) {\n return c.json({ error: resolvedSlackApp.error }, 401);\n }\n const result = await processor.process(payload, resolvedSlackApp.slackApp);\n if (result.kind === \"text\") {\n return c.text(result.body, result.status);\n }\n return c.json(result.body, result.status);\n });\n\n return app;\n}\n\nexport function startSlackIngress(config: SlackEventsApiIngressConfig): SlackIngressHandle {\n const port = config.port ?? 3040;\n const server = serve({\n fetch: createSlackEventsApp({\n slackApps: [\n {\n signingSecret: config.signingSecret,\n agentId: config.agentId ?? \"opentag\",\n ...(config.appId ? { appId: config.appId } : {}),\n ...(config.callbackUri ? { callbackUri: config.callbackUri } : {})\n }\n ],\n ...createSlackDispatcherEventProcessorInput(config)\n }).fetch,\n port\n });\n\n return {\n url: `http://localhost:${port}`,\n server,\n close() {\n return new Promise((resolve, reject) => {\n server.close((error?: Error) => {\n if (error) {\n reject(error);\n return;\n }\n resolve();\n });\n });\n }\n };\n}\n","import { randomUUID } from \"node:crypto\";\nimport { createOpenTagClient } from \"@opentag/client\";\nimport type { SlackEventProcessorInput } from \"./events.js\";\n\nexport type SlackDispatcherEventConfig = {\n dispatcherUrl: string;\n dispatcherToken?: string;\n};\n\nexport function createSlackDispatcherEventProcessorInput(config: SlackDispatcherEventConfig): SlackEventProcessorInput {\n const dispatcherClient = createOpenTagClient({\n dispatcherUrl: config.dispatcherUrl,\n ...(config.dispatcherToken ? { pairingToken: config.dispatcherToken } : {})\n });\n\n return {\n async resolveChannelBinding(input) {\n try {\n const { binding } = await dispatcherClient.getChannelBinding({\n provider: \"slack\",\n accountId: input.teamId,\n conversationId: input.channelId\n });\n return {\n teamId: binding.accountId,\n channelId: binding.conversationId,\n repoProvider: binding.repoProvider,\n owner: binding.owner,\n repo: binding.repo\n };\n } catch (error) {\n if (error instanceof Error && error.message.includes(\"channel_binding_not_found\")) {\n return null;\n }\n throw error;\n }\n },\n async createRun(event) {\n const runId = `run_${randomUUID()}`;\n const created = await dispatcherClient.createRun({ runId, event });\n return created.outcome === \"run_created\" ? { runId: created.run.id } : { runId };\n },\n async submitThreadAction(action) {\n await dispatcherClient.submitThreadAction(action);\n },\n now: () => new Date().toISOString()\n };\n}\n","import { suggestedActionCandidatesFromResult, type OpenTagRunResult } from \"@opentag/core\";\n\nexport type SlackTextBlock = {\n type: \"section\";\n text: {\n type: \"mrkdwn\";\n text: string;\n };\n};\n\nexport type SlackDividerBlock = {\n type: \"divider\";\n};\n\nexport type SlackBlock = SlackTextBlock | SlackDividerBlock;\n\nexport type SlackMessagePayload = {\n channel: string;\n text: string;\n thread_ts?: string;\n ts?: string;\n blocks?: SlackBlock[];\n};\n\nfunction escapeSlackText(text: string): string {\n return text.replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\");\n}\n\nexport function markdownToSlackMrkdwn(text: string): string {\n const links: string[] = [];\n const withoutLinks = text.replace(/\\[([^\\]]+)\\]\\(([^)]+)\\)/g, (_match, label: string, url: string) => {\n const token = `\\u0000SLACK_LINK_${links.length}\\u0000`;\n links.push(`<${url}|${escapeSlackText(label)}>`);\n return token;\n });\n const converted = escapeSlackText(withoutLinks)\n .replace(/\\*\\*(.+?)\\*\\*/g, \"*$1*\")\n .replace(/__(.+?)__/g, \"*$1*\");\n return links.reduce((output, link, index) => output.replace(`\\u0000SLACK_LINK_${index}\\u0000`, link), converted);\n}\n\nexport function renderSlackAcknowledgement(runId: string): string {\n return `I picked this up: \\`${runId}\\``;\n}\n\nfunction nextActionSummary(result: OpenTagRunResult): string | undefined {\n if (!result.nextAction) return undefined;\n if (typeof result.nextAction === \"string\") return result.nextAction;\n return result.nextAction.summary;\n}\n\nfunction stringParam(params: Record<string, unknown> | undefined, key: string): string | undefined {\n const value = params?.[key];\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n\nfunction stringArrayParam(params: Record<string, unknown> | undefined, key: string): string[] {\n const value = params?.[key];\n if (!Array.isArray(value)) return [];\n return value.filter((item): item is string => typeof item === \"string\" && item.length > 0);\n}\n\nfunction renderVerificationParams(params: Record<string, unknown> | undefined): string[] {\n const value = params?.[\"verification\"];\n if (!Array.isArray(value)) return [];\n return value\n .map((item) => {\n if (!item || typeof item !== \"object\" || Array.isArray(item)) return undefined;\n const command = (item as Record<string, unknown>)[\"command\"];\n const outcome = (item as Record<string, unknown>)[\"outcome\"];\n return typeof command === \"string\" && typeof outcome === \"string\" ? ` - \\`${command}\\`: ${outcome}` : undefined;\n })\n .filter((line): line is string => Boolean(line));\n}\n\nfunction renderSuggestedActionDetails(params: Record<string, unknown> | undefined, action: string): string[] {\n if (action !== \"create_pull_request\") return [];\n const lines: string[] = [];\n const title = stringParam(params, \"title\");\n const head = stringParam(params, \"head\") ?? stringParam(params, \"branch\");\n const base = stringParam(params, \"base\") ?? stringParam(params, \"baseBranch\");\n const changedFiles = stringArrayParam(params, \"changedFiles\");\n const risks = stringArrayParam(params, \"risks\");\n const verification = renderVerificationParams(params);\n if (title) lines.push(` Title: ${markdownToSlackMrkdwn(title)}`);\n if (head || base) lines.push(` Branch: \\`${head ?? \"unknown\"}\\` -> \\`${base ?? \"main\"}\\``);\n if (changedFiles.length > 0) lines.push(` Changed files: ${changedFiles.map((file) => `\\`${file}\\``).join(\", \")}`);\n if (risks.length > 0) {\n lines.push(\" Risks:\");\n for (const risk of risks) {\n lines.push(` - ${markdownToSlackMrkdwn(risk)}`);\n }\n }\n if (verification.length > 0) {\n lines.push(\" Verification:\");\n lines.push(...verification);\n }\n return lines;\n}\n\nfunction renderSuggestedActionsMarkdown(result: OpenTagRunResult): string[] {\n const candidates = suggestedActionCandidatesFromResult(result);\n if (candidates.length === 0) return [];\n\n const lines = [\"*Suggested actions*\"];\n for (const candidate of candidates) {\n lines.push(\n \"\",\n `${candidate.index}. *${markdownToSlackMrkdwn(candidate.intent.summary)}*`,\n ` Intent: \\`${candidate.intent.action}\\` (\\`${candidate.intent.domain}\\`)`,\n ` Proposal: \\`${candidate.proposalId}\\``,\n ` Intent ID: \\`${candidate.intent.intentId}\\``\n );\n lines.push(...renderSuggestedActionDetails(candidate.intent.params, candidate.intent.action));\n if (candidate.proposalPreconditions?.length) {\n lines.push(\" Preconditions:\");\n for (const precondition of candidate.proposalPreconditions) {\n lines.push(` - ${markdownToSlackMrkdwn(precondition)}`);\n }\n }\n }\n\n lines.push(\n \"\",\n \"Reply with:\",\n \"- `approve 1` to record approval\",\n \"- `apply 1` or `apply all` to apply supported actions\",\n \"- `continue 1` to continue with a follow-up run\",\n \"- `reject 1` to reject an action\"\n );\n return lines;\n}\n\nexport function renderSlackFinalResult(result: OpenTagRunResult): string {\n const lines = [`Finished with *${result.conclusion}*.`, \"\", markdownToSlackMrkdwn(result.summary)];\n\n if (result.verification?.length) {\n lines.push(\"\", \"*Verification*\");\n for (const check of result.verification) {\n lines.push(`- \\`${check.command}\\`: ${check.outcome}`);\n }\n }\n\n const nextAction = nextActionSummary(result);\n if (nextAction) {\n lines.push(\"\", `*Next action*: ${markdownToSlackMrkdwn(nextAction)}`);\n }\n\n const suggestedActions = renderSuggestedActionsMarkdown(result);\n if (suggestedActions.length > 0) {\n lines.push(\"\", ...suggestedActions);\n }\n\n return lines.join(\"\\n\");\n}\n\nexport function createSlackFinalResultBlocks(result: OpenTagRunResult): SlackBlock[] {\n const blocks: SlackBlock[] = [\n {\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text: `*Finished with ${result.conclusion}.*\\n${markdownToSlackMrkdwn(result.summary)}`\n }\n }\n ];\n\n if (result.verification?.length) {\n blocks.push({ type: \"divider\" });\n blocks.push({\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text: markdownToSlackMrkdwn([\"*Verification*\", ...result.verification.map((check) => `- \\`${check.command}\\`: ${check.outcome}`)].join(\"\\n\"))\n }\n });\n }\n\n const nextAction = nextActionSummary(result);\n if (nextAction) {\n blocks.push({\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text: `*Next action*: ${markdownToSlackMrkdwn(nextAction)}`\n }\n });\n }\n\n const suggestedActions = renderSuggestedActionsMarkdown(result);\n if (suggestedActions.length > 0) {\n blocks.push({ type: \"divider\" });\n blocks.push({\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text: suggestedActions.join(\"\\n\")\n }\n });\n }\n\n return blocks;\n}\n\nexport function createSlackPostMessagePayload(input: { channelId: string; text: string; threadTs: string; blocks?: SlackBlock[] }): SlackMessagePayload {\n return {\n channel: input.channelId,\n text: markdownToSlackMrkdwn(input.text),\n thread_ts: input.threadTs,\n ...(input.blocks?.length ? { blocks: input.blocks } : {})\n };\n}\n\nexport function createSlackUpdateMessagePayload(input: { channelId: string; text: string; messageTs: string; blocks?: SlackBlock[] }): SlackMessagePayload {\n return {\n channel: input.channelId,\n text: markdownToSlackMrkdwn(input.text),\n ts: input.messageTs,\n ...(input.blocks?.length ? { blocks: input.blocks } : {})\n };\n}\n","import WebSocket, { type RawData } from \"ws\";\nimport { createSlackDispatcherEventProcessorInput, type SlackDispatcherEventConfig } from \"./dispatcher-events.js\";\nimport { createSlackEventProcessor, type SlackAppRuntimeConfig, type SlackEventEnvelope, type SlackEventProcessorInput } from \"./events.js\";\n\nconst SLACK_CONNECTIONS_OPEN_URL = \"https://slack.com/api/apps.connections.open\";\nconst DEFAULT_RECONNECT_DELAY_MS = 1_000;\n\nexport type SlackSocketModeEnvelope = {\n type?: string;\n envelope_id?: string;\n payload?: SlackEventEnvelope;\n accepts_response_payload?: boolean;\n};\n\nexport type SlackSocketModeAppInput = {\n appToken: string;\n slackApp: SlackAppRuntimeConfig;\n} & SlackEventProcessorInput;\n\nexport type SlackSocketModeIngressConfig = SlackDispatcherEventConfig & {\n appToken: string;\n agentId?: string;\n appId?: string;\n callbackUri?: string;\n};\n\nexport type SlackSocketModeIngressHandle = {\n startPromise: Promise<void>;\n close(): Promise<void>;\n};\n\nexport type SlackSocketModeDependencies = {\n fetchImpl?: typeof fetch;\n createWebSocket?(url: string): WebSocket;\n reconnectDelayMs?: number;\n log?(message: string): void;\n logError?(message: string, error?: unknown): void;\n};\n\ntype SlackConnectionsOpenResponse = {\n ok?: boolean;\n url?: string;\n error?: string;\n needed?: string;\n provided?: string;\n};\n\nfunction rawDataToString(data: RawData): string {\n if (typeof data === \"string\") return data;\n if (Buffer.isBuffer(data)) return data.toString(\"utf8\");\n if (Array.isArray(data)) return Buffer.concat(data).toString(\"utf8\");\n if (data instanceof ArrayBuffer) return Buffer.from(data).toString(\"utf8\");\n return Buffer.from(data).toString(\"utf8\");\n}\n\nasync function openSlackSocketUrl(input: { appToken: string; fetchImpl: typeof fetch }): Promise<string> {\n const response = await input.fetchImpl(SLACK_CONNECTIONS_OPEN_URL, {\n method: \"POST\",\n headers: {\n authorization: `Bearer ${input.appToken}`\n }\n });\n const body = (await response.json().catch(() => ({}))) as SlackConnectionsOpenResponse;\n if (!response.ok || !body.ok || !body.url) {\n const reason = body.error ?? `http_${response.status}`;\n throw new Error(`Slack Socket Mode connection failed: ${reason}`);\n }\n return body.url;\n}\n\nfunction parseSocketEnvelope(data: RawData): SlackSocketModeEnvelope | null {\n try {\n return JSON.parse(rawDataToString(data)) as SlackSocketModeEnvelope;\n } catch {\n return null;\n }\n}\n\nasync function handleSocketMessage(input: {\n data: RawData;\n socket: WebSocket;\n processor: ReturnType<typeof createSlackEventProcessor>;\n slackApp: SlackAppRuntimeConfig;\n logError(message: string, error?: unknown): void;\n}): Promise<void> {\n const envelope = parseSocketEnvelope(input.data);\n if (!envelope?.envelope_id) {\n input.logError(\"[slack] ignored Socket Mode envelope without envelope_id\");\n return;\n }\n\n input.socket.send(JSON.stringify({ envelope_id: envelope.envelope_id }));\n\n if (envelope.type !== \"events_api\" || !envelope.payload) {\n return;\n }\n if (input.slackApp.appId && envelope.payload.api_app_id && envelope.payload.api_app_id !== input.slackApp.appId) {\n return;\n }\n\n await input.processor.process(envelope.payload, input.slackApp);\n}\n\nfunction wait(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport function startSlackSocketModeApp(\n input: SlackSocketModeAppInput,\n dependencies: SlackSocketModeDependencies = {}\n): SlackSocketModeIngressHandle {\n const fetchImpl = dependencies.fetchImpl ?? fetch;\n const createWebSocket = dependencies.createWebSocket ?? ((url: string) => new WebSocket(url));\n const reconnectDelayMs = dependencies.reconnectDelayMs ?? DEFAULT_RECONNECT_DELAY_MS;\n const log = dependencies.log ?? ((message: string) => console.log(message));\n const logError = dependencies.logError ?? ((message: string, error?: unknown) => (error ? console.error(message, error) : console.error(message)));\n const processor = createSlackEventProcessor(input);\n let closed = false;\n let activeSocket: WebSocket | undefined;\n\n async function runOneConnection(socketUrl: string): Promise<void> {\n await new Promise<void>((resolve) => {\n const socket = createWebSocket(socketUrl);\n activeSocket = socket;\n let settled = false;\n const finish = () => {\n if (settled) return;\n settled = true;\n if (activeSocket === socket) activeSocket = undefined;\n resolve();\n };\n\n socket.once(\"open\", () => {\n log(\"[slack] Socket Mode connected\");\n });\n socket.on(\"message\", (data) => {\n void handleSocketMessage({\n data,\n socket,\n processor,\n slackApp: input.slackApp,\n logError\n }).catch((error: unknown) => {\n logError(\"[slack] failed to handle Socket Mode event:\", error);\n });\n });\n socket.once(\"close\", finish);\n socket.once(\"error\", (error) => {\n if (!closed) {\n logError(\"[slack] Socket Mode connection error:\", error);\n }\n socket.close();\n finish();\n });\n });\n }\n\n const startPromise = (async () => {\n while (!closed) {\n const socketUrl = await openSlackSocketUrl({ appToken: input.appToken, fetchImpl });\n await runOneConnection(socketUrl);\n if (!closed) {\n await wait(reconnectDelayMs);\n }\n }\n })();\n\n return {\n startPromise,\n async close() {\n closed = true;\n activeSocket?.close();\n await startPromise.catch(() => undefined);\n }\n };\n}\n\nexport function startSlackSocketModeIngress(\n config: SlackSocketModeIngressConfig,\n dependencies: SlackSocketModeDependencies = {}\n): SlackSocketModeIngressHandle {\n return startSlackSocketModeApp(\n {\n appToken: config.appToken,\n slackApp: {\n agentId: config.agentId ?? \"opentag\",\n ...(config.appId ? { appId: config.appId } : {}),\n ...(config.callbackUri ? { callbackUri: config.callbackUri } : {})\n },\n ...createSlackDispatcherEventProcessorInput(config)\n },\n dependencies\n );\n}\n"],"mappings":";AAAA,SAAS,gCAAmD;;;ACA5D,SAAS,0BAA6G;AA0B/G,SAAS,qBAAqBA,OAAc,WAAmC;AACpF,QAAM,WAAW,YACb,CAAC,IAAI,OAAO,MAAM,SAAS,SAAS,GAAG,GAAG,cAAc,IACxD,CAAC,cAAc;AAEnB,aAAW,WAAW,UAAU;AAC9B,UAAM,WAAWA,MAAK,QAAQ,SAAS,EAAE,EAAE,KAAK;AAChD,QAAI,aAAaA,MAAK,KAAK,GAAG;AAC5B,aAAO,SAAS,SAAS,IAAI,WAAW;AAAA,IAC1C;AAAA,EACF;AAEA,SAAO;AACT;AAEO,SAAS,qBAAqB,OAAwE;AAC3G,SAAO,GAAG,MAAM,MAAM,IAAI,MAAM,SAAS,IAAI,MAAM,QAAQ;AAC7D;AAEO,SAAS,oBAAoB,WAA4E;AAC9G,QAAM,CAAC,QAAQ,WAAW,QAAQ,IAAI,UAAU,MAAM,GAAG;AACzD,MAAI,CAAC,UAAU,CAAC,aAAa,CAAC,UAAU;AACtC,UAAM,IAAI,MAAM,6BAA6B,SAAS,EAAE;AAAA,EAC1D;AACA,SAAO,EAAE,QAAQ,WAAW,SAAS;AACvC;AAEA,SAAS,qBAAqB,QAAqD;AACjF,QAAM,cAAiC;AAAA,IACrC;AAAA,MACE,OAAO;AAAA,MACP,QAAQ;AAAA,IACV;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,QAAQ;AAAA,IACV;AAAA,EACF;AAEA,MAAI,WAAW,SAAS,WAAW,OAAO;AACxC,gBAAY;AAAA,MACV;AAAA,QACE,OAAO;AAAA,QACP,QAAQ;AAAA,MACV;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,QAAQ;AAAA,MACV;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,QAAQ;AAAA,MACV;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,0BAA0B,SAA2C;AAC5E,QAAM,UAA4B,CAAC;AAEnC,aAAW,aAAa,QAAQ,QAAQ,cAAc,CAAC,GAAG;AACxD,QAAI,UAAU,SAAS,OAAO;AAC5B,cAAQ,KAAK;AAAA,QACX,MAAM;AAAA,QACN,KAAK,UAAU;AAAA,QACf,YAAY;AAAA,QACZ,OAAO,UAAU,SAAS;AAAA,MAC5B,CAAC;AACD;AAAA,IACF;AAEA,QAAI,UAAU,SAAS,UAAU,UAAU,SAAS,QAAQ;AAC1D,cAAQ,KAAK;AAAA,QACX,MAAM;AAAA,QACN,KAAK,UAAU;AAAA,QACf,GAAI,UAAU,OAAO,EAAE,MAAM,UAAU,KAAK,IAAI,CAAC;AAAA,QACjD,GAAI,UAAU,YAAY,EAAE,WAAW,UAAU,UAAU,IAAI,CAAC;AAAA,QAChE,GAAI,UAAU,UAAU,EAAE,SAAS,UAAU,QAAQ,IAAI,CAAC;AAAA,QAC1D,YAAY;AAAA,QACZ,OAAO,eAAe,SAAS;AAAA,MACjC,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,eAAe,WAAgF;AACtG,SAAO,UAAU,SAAS;AAC5B;AAEA,SAAS,gBAAgB,SAAkD;AACzE,MAAI,CAAC,QAAQ,OAAQ,QAAO,CAAC;AAC7B,SAAO;AAAA,IACL,eAAe,QAAQ,OAAO;AAAA,IAC9B,oBAAoB,QAAQ,OAAO;AAAA,IACnC,GAAI,QAAQ,OAAO,WAAW,EAAE,UAAU,QAAQ,OAAO,SAAS,IAAI,CAAC;AAAA,IACvE,GAAI,QAAQ,OAAO,UAAU,EAAE,SAAS,QAAQ,OAAO,QAAQ,IAAI,CAAC;AAAA,EACtE;AACF;AAEO,SAAS,yBAAyB,OAAkD;AACzF,QAAM,UAAU,qBAAqB,MAAM,MAAM,MAAM,SAAS;AAChE,MAAI,CAAC,QAAS,QAAO;AAErB,QAAM,UAAU,mBAAmB,OAAO;AAC1C,QAAM,gBAAgB,MAAM,YAAY,MAAM;AAC9C,QAAM,UAAU,MAAM,WAAW;AAEjC,SAAO;AAAA,IACL,IAAI,yBAAyB,MAAM,OAAO;AAAA,IAC1C,QAAQ;AAAA,IACR,eAAe,MAAM;AAAA,IACrB,YAAY,IAAI,KAAK,MAAM,YAAY,GAAI,EAAE,YAAY;AAAA,IACzD,OAAO;AAAA,MACL,UAAU;AAAA,MACV,gBAAgB,MAAM;AAAA,MACtB,QAAQ,MAAM;AAAA,MACd,gBAAgB,MAAM;AAAA,IACxB;AAAA,IACA,QAAQ;AAAA,MACN,SAAS,MAAM,YAAY,KAAK,MAAM,SAAS,MAAM;AAAA,MACrD;AAAA,MACA,GAAI,QAAQ,QAAQ,eAAe,EAAE,cAAc,QAAQ,OAAO,aAAa,IAAI,CAAC;AAAA,IACtF;AAAA,IACA;AAAA,IACA,SAAS;AAAA,MACP;AAAA,QACE,UAAU;AAAA,QACV,MAAM;AAAA,QACN,KAAK,gBAAgB,MAAM,MAAM,YAAY,MAAM,SAAS,YAAY,MAAM,EAAE;AAAA,QAChF,YAAY;AAAA,QACZ,OAAO;AAAA,MACT;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,KAAK,MAAM;AAAA,QACX,YAAY;AAAA,QACZ,OAAO;AAAA,MACT;AAAA,MACA,GAAG,0BAA0B,OAAO;AAAA,IACtC;AAAA,IACA,aAAa,qBAAqB,QAAQ,MAAM;AAAA,IAChD,UAAU;AAAA,MACR,UAAU;AAAA,MACV,KAAK,MAAM,eAAe;AAAA,MAC1B,WAAW,qBAAqB;AAAA,QAC9B,QAAQ,MAAM;AAAA,QACd,WAAW,MAAM;AAAA,QACjB,UAAU;AAAA,MACZ,CAAC;AAAA,IACH;AAAA,IACA,UAAU;AAAA,MACR,QAAQ,MAAM;AAAA,MACd,WAAW,MAAM;AAAA,MACjB,WAAW,MAAM;AAAA,MACjB,GAAI,MAAM,QAAQ,EAAE,YAAY,MAAM,MAAM,IAAI,CAAC;AAAA,MACjD,GAAI,MAAM,YAAY,EAAE,gBAAgB,MAAM,UAAU,IAAI,CAAC;AAAA,MAC7D,GAAG,gBAAgB,OAAO;AAAA,MAC1B,cAAc,MAAM,QAAQ,gBAAgB;AAAA,MAC5C,OAAO,MAAM,QAAQ;AAAA,MACrB,MAAM,MAAM,QAAQ;AAAA,IACtB;AAAA,EACF;AACF;;;AD5HA,SAAS,KAAK,MAA+B,SAAoC,KAAgC;AAC/G,SAAO,EAAE,MAAM,QAAQ,QAAQ,KAAK;AACtC;AAEA,SAAS,KAAK,MAAc,SAAoC,KAAgC;AAC9F,SAAO,EAAE,MAAM,QAAQ,QAAQ,KAAK;AACtC;AAEO,SAAS,0BAA0B,OAAiC;AACzE,SAAO;AAAA,IACL,MAAM,QAAQ,SAA6B,UAAqE;AAC9G,UAAI,QAAQ,SAAS,oBAAoB;AACvC,eAAO,KAAK,QAAQ,aAAa,EAAE;AAAA,MACrC;AACA,UAAI,QAAQ,SAAS,oBAAoB,CAAC,QAAQ,SAAS,CAAC,CAAC,eAAe,SAAS,EAAE,SAAS,QAAQ,MAAM,IAAI,GAAG;AACnH,eAAO,KAAK,EAAE,IAAI,KAAK,CAAC;AAAA,MAC1B;AACA,UAAI,QAAQ,MAAM,SAAS,cAAc,QAAQ,MAAM,WAAW,QAAQ,MAAM,SAAS;AACvF,eAAO,KAAK,EAAE,IAAI,KAAK,CAAC;AAAA,MAC1B;AACA,UAAI,CAAC,QAAQ,WAAW,CAAC,QAAQ,MAAM,WAAW,CAAC,QAAQ,MAAM,QAAQ,CAAC,QAAQ,MAAM,QAAQ,CAAC,QAAQ,MAAM,MAAM,CAAC,QAAQ,UAAU;AACtI,eAAO,KAAK,EAAE,OAAO,wBAAwB,GAAG,GAAG;AAAA,MACrD;AAEA,YAAM,sBACJ,QAAQ,MAAM,SAAS,gBACnB,qBAAqB,QAAQ,MAAM,MAAM,QAAQ,iBAAiB,CAAC,GAAG,OAAO,IAC7E,QAAQ,MAAM,KAAK,KAAK;AAC9B,UAAI,QAAQ,MAAM,SAAS,cAAc,CAAC,uBAAuB,CAAC,yBAAyB,mBAAmB,IAAI;AAChH,eAAO,KAAK,EAAE,IAAI,KAAK,CAAC;AAAA,MAC1B;AAEA,YAAM,UAAU,MAAM,MAAM,sBAAsB;AAAA,QAChD,QAAQ,QAAQ;AAAA,QAChB,WAAW,QAAQ,MAAM;AAAA,MAC3B,CAAC;AACD,UAAI,CAAC,SAAS;AACZ,eAAO,KAAK,EAAE,IAAI,MAAM,SAAS,kBAAkB,CAAC;AAAA,MACtD;AAEA,UAAI,uBAAuB,yBAAyB,mBAAmB,KAAK,MAAM,oBAAoB;AACpG,cAAM,MAAM,mBAAmB;AAAA,UAC7B,IAAI,kBAAkB,QAAQ,QAAQ;AAAA,UACtC,SAAS;AAAA,UACT,OAAO;AAAA,YACL,UAAU;AAAA,YACV,gBAAgB,QAAQ,MAAM;AAAA,YAC9B,QAAQ,QAAQ,MAAM;AAAA,YACtB,gBAAgB,QAAQ;AAAA,UAC1B;AAAA,UACA,UAAU;AAAA,YACR,UAAU;AAAA,YACV,KAAK,SAAS,eAAe;AAAA,YAC7B,WAAW,qBAAqB;AAAA,cAC9B,QAAQ,QAAQ;AAAA,cAChB,WAAW,QAAQ,MAAM;AAAA,cACzB,UAAU,QAAQ,MAAM,aAAa,QAAQ,MAAM;AAAA,YACrD,CAAC;AAAA,UACH;AAAA,UACA,UAAU;AAAA,YACR,QAAQ,QAAQ;AAAA,YAChB,WAAW,QAAQ,MAAM;AAAA,YACzB,WAAW,QAAQ,MAAM;AAAA,YACzB,GAAI,QAAQ,aAAa,EAAE,YAAY,QAAQ,WAAW,IAAI,CAAC;AAAA,YAC/D,GAAI,QAAQ,iBAAiB,CAAC,GAAG,UAAU,EAAE,gBAAgB,QAAQ,eAAe,CAAC,EAAE,QAAQ,IAAI,CAAC;AAAA,YACpG,cAAc,QAAQ,gBAAgB;AAAA,YACtC,OAAO,QAAQ;AAAA,YACf,MAAM,QAAQ;AAAA,UAChB;AAAA,QACF,CAAC;AACD,eAAO,KAAK,EAAE,IAAI,KAAK,CAAC;AAAA,MAC1B;AAEA,UAAI,QAAQ,MAAM,SAAS,eAAe;AACxC,eAAO,KAAK,EAAE,IAAI,KAAK,CAAC;AAAA,MAC1B;AAEA,YAAM,QAAQ,yBAAyB;AAAA,QACrC,QAAQ,QAAQ;AAAA,QAChB,WAAW,QAAQ,MAAM;AAAA,QACzB,QAAQ,QAAQ,MAAM;AAAA,QACtB,MAAM,QAAQ,MAAM;AAAA,QACpB,IAAI,QAAQ,MAAM;AAAA,QAClB,SAAS,QAAQ;AAAA,QACjB,WAAW,QAAQ,cAAc,KAAK,MAAM,KAAK,MAAM,MAAM,IAAI,CAAC,IAAI,GAAI;AAAA,QAC1E,SAAS,SAAS;AAAA,QAClB;AAAA,QACA,GAAI,QAAQ,aAAa,EAAE,OAAO,QAAQ,WAAW,IAAI,CAAC;AAAA,QAC1D,GAAI,QAAQ,MAAM,YAAY,EAAE,UAAU,QAAQ,MAAM,UAAU,IAAI,CAAC;AAAA,QACvE,GAAI,QAAQ,iBAAiB,CAAC,GAAG,UAAU,EAAE,WAAW,QAAQ,eAAe,CAAC,EAAE,QAAQ,IAAI,CAAC;AAAA,QAC/F,GAAI,SAAS,cAAc,EAAE,aAAa,SAAS,YAAY,IAAI,CAAC;AAAA,MACtE,CAAC;AACD,UAAI,CAAC,OAAO;AACV,eAAO,KAAK,EAAE,IAAI,MAAM,SAAS,gBAAgB,CAAC;AAAA,MACpD;AAEA,YAAM,MAAM,UAAU,KAAK;AAC3B,aAAO,KAAK,EAAE,IAAI,KAAK,CAAC;AAAA,IAC1B;AAAA,EACF;AACF;;;AExKA,SAAS,YAAY,uBAAuB;AAC5C,SAAS,aAAa;AACtB,SAAS,YAAY;;;ACFrB,SAAS,kBAAkB;AAC3B,SAAS,2BAA2B;AAQ7B,SAAS,yCAAyC,QAA8D;AACrH,QAAM,mBAAmB,oBAAoB;AAAA,IAC3C,eAAe,OAAO;AAAA,IACtB,GAAI,OAAO,kBAAkB,EAAE,cAAc,OAAO,gBAAgB,IAAI,CAAC;AAAA,EAC3E,CAAC;AAED,SAAO;AAAA,IACL,MAAM,sBAAsB,OAAO;AACjC,UAAI;AACF,cAAM,EAAE,QAAQ,IAAI,MAAM,iBAAiB,kBAAkB;AAAA,UAC3D,UAAU;AAAA,UACV,WAAW,MAAM;AAAA,UACjB,gBAAgB,MAAM;AAAA,QACxB,CAAC;AACD,eAAO;AAAA,UACL,QAAQ,QAAQ;AAAA,UAChB,WAAW,QAAQ;AAAA,UACnB,cAAc,QAAQ;AAAA,UACtB,OAAO,QAAQ;AAAA,UACf,MAAM,QAAQ;AAAA,QAChB;AAAA,MACF,SAAS,OAAO;AACd,YAAI,iBAAiB,SAAS,MAAM,QAAQ,SAAS,2BAA2B,GAAG;AACjF,iBAAO;AAAA,QACT;AACA,cAAM;AAAA,MACR;AAAA,IACF;AAAA,IACA,MAAM,UAAU,OAAO;AACrB,YAAM,QAAQ,OAAO,WAAW,CAAC;AACjC,YAAM,UAAU,MAAM,iBAAiB,UAAU,EAAE,OAAO,MAAM,CAAC;AACjE,aAAO,QAAQ,YAAY,gBAAgB,EAAE,OAAO,QAAQ,IAAI,GAAG,IAAI,EAAE,MAAM;AAAA,IACjF;AAAA,IACA,MAAM,mBAAmB,QAAQ;AAC/B,YAAM,iBAAiB,mBAAmB,MAAM;AAAA,IAClD;AAAA,IACA,KAAK,OAAM,oBAAI,KAAK,GAAE,YAAY;AAAA,EACpC;AACF;;;ADdO,SAAS,sBAAsB,OAI3B;AACT,QAAM,OAAO,MAAM,MAAM,SAAS,IAAI,MAAM,OAAO;AACnD,QAAM,SAAS,WAAW,UAAU,MAAM,aAAa,EAAE,OAAO,IAAI,EAAE,OAAO,KAAK;AAClF,SAAO,MAAM,MAAM;AACrB;AAEO,SAAS,qBAAqB,OAKzB;AACV,QAAM,WAAW,sBAAsB,KAAK;AAC5C,QAAM,iBAAiB,OAAO,KAAK,QAAQ;AAC3C,QAAM,eAAe,OAAO,KAAK,MAAM,SAAS;AAChD,SAAO,eAAe,WAAW,aAAa,UAAU,gBAAgB,gBAAgB,YAAY;AACtG;AAEO,SAAS,qBAAqB,OAAiF;AACpH,QAAM,mBAAmB,OAAO,MAAM,SAAS;AAC/C,MAAI,CAAC,OAAO,SAAS,gBAAgB,EAAG,QAAO;AAC/C,QAAM,mBAAmB,MAAM,oBAAoB;AACnD,QAAM,aAAa,KAAK,IAAI,KAAK,MAAM,MAAM,QAAQ,GAAI,IAAI,gBAAgB;AAC7E,SAAO,cAAc;AACvB;AAEO,SAAS,qBAAqB,OAA4B;AAC/D,QAAM,MAAM,IAAI,KAAK;AACrB,QAAM,YAAY,0BAA0B,KAAK;AAEjD,WAAS,kBAAkB,SAA4C;AACrE,QAAI;AACF,aAAO,KAAK,MAAM,OAAO;AAAA,IAC3B,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAEA,WAAS,gBAAgB,YAKtB;AACD,UAAM,aAAa,WAAW,WAC1B,MAAM,UAAU,OAAO,CAAC,cAAc,CAAC,UAAU,SAAS,UAAU,UAAU,WAAW,QAAQ,IACjG,MAAM;AACV,QAAI,WAAW,WAAW,GAAG;AAC3B,aAAO,EAAE,OAAO,oBAA6B;AAAA,IAC/C;AACA,UAAM,WAAW,WAAW;AAAA,MAAK,CAAC,cAChC,qBAAqB;AAAA,QACnB,eAAe,UAAU;AAAA,QACzB,WAAW,WAAW;AAAA,QACtB,SAAS,WAAW;AAAA,QACpB,WAAW,WAAW;AAAA,MACxB,CAAC;AAAA,IACH;AACA,WAAO,WAAW,EAAE,SAAS,IAAI,EAAE,OAAO,oBAA6B;AAAA,EACzE;AAEA,MAAI,KAAK,iBAAiB,OAAO,MAAM;AACrC,UAAM,YAAY,EAAE,IAAI,OAAO,2BAA2B;AAC1D,UAAM,YAAY,EAAE,IAAI,OAAO,mBAAmB;AAClD,QAAI,CAAC,aAAa,CAAC,WAAW;AAC5B,aAAO,EAAE,KAAK,EAAE,OAAO,4BAA4B,GAAG,GAAG;AAAA,IAC3D;AACA,QAAI,CAAC,qBAAqB,EAAE,WAAW,OAAO,MAAM,QAAQ,KAAK,KAAK,IAAI,EAAE,CAAC,GAAG;AAC9E,aAAO,EAAE,KAAK,EAAE,OAAO,4BAA4B,GAAG,GAAG;AAAA,IAC3D;AACA,UAAM,UAAU,MAAM,EAAE,IAAI,KAAK;AACjC,UAAM,UAAU,kBAAkB,OAAO;AACzC,QAAI,CAAC,SAAS;AACZ,aAAO,EAAE,KAAK,EAAE,OAAO,eAAe,GAAG,GAAG;AAAA,IAC9C;AACA,UAAM,mBAAmB,gBAAgB;AAAA,MACvC;AAAA,MACA;AAAA,MACA;AAAA,MACA,GAAI,QAAQ,aAAa,EAAE,UAAU,QAAQ,WAAW,IAAI,CAAC;AAAA,IAC/D,CAAC;AACD,QAAI,WAAW,kBAAkB;AAC/B,aAAO,EAAE,KAAK,EAAE,OAAO,iBAAiB,MAAM,GAAG,GAAG;AAAA,IACtD;AACA,UAAM,SAAS,MAAM,UAAU,QAAQ,SAAS,iBAAiB,QAAQ;AACzE,QAAI,OAAO,SAAS,QAAQ;AAC1B,aAAO,EAAE,KAAK,OAAO,MAAM,OAAO,MAAM;AAAA,IAC1C;AACA,WAAO,EAAE,KAAK,OAAO,MAAM,OAAO,MAAM;AAAA,EAC1C,CAAC;AAED,SAAO;AACT;AAEO,SAAS,kBAAkB,QAAyD;AACzF,QAAM,OAAO,OAAO,QAAQ;AAC5B,QAAM,SAAS,MAAM;AAAA,IACnB,OAAO,qBAAqB;AAAA,MAC1B,WAAW;AAAA,QACT;AAAA,UACE,eAAe,OAAO;AAAA,UACtB,SAAS,OAAO,WAAW;AAAA,UAC3B,GAAI,OAAO,QAAQ,EAAE,OAAO,OAAO,MAAM,IAAI,CAAC;AAAA,UAC9C,GAAI,OAAO,cAAc,EAAE,aAAa,OAAO,YAAY,IAAI,CAAC;AAAA,QAClE;AAAA,MACF;AAAA,MACA,GAAG,yCAAyC,MAAM;AAAA,IACpD,CAAC,EAAE;AAAA,IACH;AAAA,EACF,CAAC;AAED,SAAO;AAAA,IACL,KAAK,oBAAoB,IAAI;AAAA,IAC7B;AAAA,IACA,QAAQ;AACN,aAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,eAAO,MAAM,CAAC,UAAkB;AAC9B,cAAI,OAAO;AACT,mBAAO,KAAK;AACZ;AAAA,UACF;AACA,kBAAQ;AAAA,QACV,CAAC;AAAA,MACH,CAAC;AAAA,IACH;AAAA,EACF;AACF;;;AEnKA,SAAS,2CAAkE;AAwB3E,SAAS,gBAAgBC,OAAsB;AAC7C,SAAOA,MAAK,QAAQ,MAAM,OAAO,EAAE,QAAQ,MAAM,MAAM,EAAE,QAAQ,MAAM,MAAM;AAC/E;AAEO,SAAS,sBAAsBA,OAAsB;AAC1D,QAAM,QAAkB,CAAC;AACzB,QAAM,eAAeA,MAAK,QAAQ,4BAA4B,CAAC,QAAQ,OAAe,QAAgB;AACpG,UAAM,QAAQ,gBAAoB,MAAM,MAAM;AAC9C,UAAM,KAAK,IAAI,GAAG,IAAI,gBAAgB,KAAK,CAAC,GAAG;AAC/C,WAAO;AAAA,EACT,CAAC;AACD,QAAM,YAAY,gBAAgB,YAAY,EAC3C,QAAQ,kBAAkB,MAAM,EAChC,QAAQ,cAAc,MAAM;AAC/B,SAAO,MAAM,OAAO,CAAC,QAAQ,MAAM,UAAU,OAAO,QAAQ,gBAAoB,KAAK,MAAU,IAAI,GAAG,SAAS;AACjH;AAEO,SAAS,2BAA2B,OAAuB;AAChE,SAAO,uBAAuB,KAAK;AACrC;AAEA,SAAS,kBAAkB,QAA8C;AACvE,MAAI,CAAC,OAAO,WAAY,QAAO;AAC/B,MAAI,OAAO,OAAO,eAAe,SAAU,QAAO,OAAO;AACzD,SAAO,OAAO,WAAW;AAC3B;AAEA,SAAS,YAAY,QAA6C,KAAiC;AACjG,QAAM,QAAQ,SAAS,GAAG;AAC1B,SAAO,OAAO,UAAU,YAAY,MAAM,SAAS,IAAI,QAAQ;AACjE;AAEA,SAAS,iBAAiB,QAA6C,KAAuB;AAC5F,QAAM,QAAQ,SAAS,GAAG;AAC1B,MAAI,CAAC,MAAM,QAAQ,KAAK,EAAG,QAAO,CAAC;AACnC,SAAO,MAAM,OAAO,CAAC,SAAyB,OAAO,SAAS,YAAY,KAAK,SAAS,CAAC;AAC3F;AAEA,SAAS,yBAAyB,QAAuD;AACvF,QAAM,QAAQ,SAAS,cAAc;AACrC,MAAI,CAAC,MAAM,QAAQ,KAAK,EAAG,QAAO,CAAC;AACnC,SAAO,MACJ,IAAI,CAAC,SAAS;AACb,QAAI,CAAC,QAAQ,OAAO,SAAS,YAAY,MAAM,QAAQ,IAAI,EAAG,QAAO;AACrE,UAAM,UAAW,KAAiC,SAAS;AAC3D,UAAM,UAAW,KAAiC,SAAS;AAC3D,WAAO,OAAO,YAAY,YAAY,OAAO,YAAY,WAAW,UAAU,OAAO,OAAO,OAAO,KAAK;AAAA,EAC1G,CAAC,EACA,OAAO,CAAC,SAAyB,QAAQ,IAAI,CAAC;AACnD;AAEA,SAAS,6BAA6B,QAA6C,QAA0B;AAC3G,MAAI,WAAW,sBAAuB,QAAO,CAAC;AAC9C,QAAM,QAAkB,CAAC;AACzB,QAAM,QAAQ,YAAY,QAAQ,OAAO;AACzC,QAAM,OAAO,YAAY,QAAQ,MAAM,KAAK,YAAY,QAAQ,QAAQ;AACxE,QAAM,OAAO,YAAY,QAAQ,MAAM,KAAK,YAAY,QAAQ,YAAY;AAC5E,QAAM,eAAe,iBAAiB,QAAQ,cAAc;AAC5D,QAAM,QAAQ,iBAAiB,QAAQ,OAAO;AAC9C,QAAM,eAAe,yBAAyB,MAAM;AACpD,MAAI,MAAO,OAAM,KAAK,aAAa,sBAAsB,KAAK,CAAC,EAAE;AACjE,MAAI,QAAQ,KAAM,OAAM,KAAK,gBAAgB,QAAQ,SAAS,WAAW,QAAQ,MAAM,IAAI;AAC3F,MAAI,aAAa,SAAS,EAAG,OAAM,KAAK,qBAAqB,aAAa,IAAI,CAAC,SAAS,KAAK,IAAI,IAAI,EAAE,KAAK,IAAI,CAAC,EAAE;AACnH,MAAI,MAAM,SAAS,GAAG;AACpB,UAAM,KAAK,WAAW;AACtB,eAAW,QAAQ,OAAO;AACxB,YAAM,KAAK,QAAQ,sBAAsB,IAAI,CAAC,EAAE;AAAA,IAClD;AAAA,EACF;AACA,MAAI,aAAa,SAAS,GAAG;AAC3B,UAAM,KAAK,kBAAkB;AAC7B,UAAM,KAAK,GAAG,YAAY;AAAA,EAC5B;AACA,SAAO;AACT;AAEA,SAAS,+BAA+B,QAAoC;AAC1E,QAAM,aAAa,oCAAoC,MAAM;AAC7D,MAAI,WAAW,WAAW,EAAG,QAAO,CAAC;AAErC,QAAM,QAAQ,CAAC,qBAAqB;AACpC,aAAW,aAAa,YAAY;AAClC,UAAM;AAAA,MACJ;AAAA,MACA,GAAG,UAAU,KAAK,MAAM,sBAAsB,UAAU,OAAO,OAAO,CAAC;AAAA,MACvE,gBAAgB,UAAU,OAAO,MAAM,SAAS,UAAU,OAAO,MAAM;AAAA,MACvE,kBAAkB,UAAU,UAAU;AAAA,MACtC,mBAAmB,UAAU,OAAO,QAAQ;AAAA,IAC9C;AACA,UAAM,KAAK,GAAG,6BAA6B,UAAU,OAAO,QAAQ,UAAU,OAAO,MAAM,CAAC;AAC5F,QAAI,UAAU,uBAAuB,QAAQ;AAC3C,YAAM,KAAK,mBAAmB;AAC9B,iBAAW,gBAAgB,UAAU,uBAAuB;AAC1D,cAAM,KAAK,QAAQ,sBAAsB,YAAY,CAAC,EAAE;AAAA,MAC1D;AAAA,IACF;AAAA,EACF;AAEA,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,uBAAuB,QAAkC;AACvE,QAAM,QAAQ,CAAC,kBAAkB,OAAO,UAAU,MAAM,IAAI,sBAAsB,OAAO,OAAO,CAAC;AAEjG,MAAI,OAAO,cAAc,QAAQ;AAC/B,UAAM,KAAK,IAAI,gBAAgB;AAC/B,eAAW,SAAS,OAAO,cAAc;AACvC,YAAM,KAAK,OAAO,MAAM,OAAO,OAAO,MAAM,OAAO,EAAE;AAAA,IACvD;AAAA,EACF;AAEA,QAAM,aAAa,kBAAkB,MAAM;AAC3C,MAAI,YAAY;AACd,UAAM,KAAK,IAAI,kBAAkB,sBAAsB,UAAU,CAAC,EAAE;AAAA,EACtE;AAEA,QAAM,mBAAmB,+BAA+B,MAAM;AAC9D,MAAI,iBAAiB,SAAS,GAAG;AAC/B,UAAM,KAAK,IAAI,GAAG,gBAAgB;AAAA,EACpC;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;AAEO,SAAS,6BAA6B,QAAwC;AACnF,QAAM,SAAuB;AAAA,IAC3B;AAAA,MACE,MAAM;AAAA,MACN,MAAM;AAAA,QACJ,MAAM;AAAA,QACN,MAAM,kBAAkB,OAAO,UAAU;AAAA,EAAO,sBAAsB,OAAO,OAAO,CAAC;AAAA,MACvF;AAAA,IACF;AAAA,EACF;AAEA,MAAI,OAAO,cAAc,QAAQ;AAC/B,WAAO,KAAK,EAAE,MAAM,UAAU,CAAC;AAC/B,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,MAAM;AAAA,QACJ,MAAM;AAAA,QACN,MAAM,sBAAsB,CAAC,kBAAkB,GAAG,OAAO,aAAa,IAAI,CAAC,UAAU,OAAO,MAAM,OAAO,OAAO,MAAM,OAAO,EAAE,CAAC,EAAE,KAAK,IAAI,CAAC;AAAA,MAC9I;AAAA,IACF,CAAC;AAAA,EACH;AAEA,QAAM,aAAa,kBAAkB,MAAM;AAC3C,MAAI,YAAY;AACd,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,MAAM;AAAA,QACJ,MAAM;AAAA,QACN,MAAM,kBAAkB,sBAAsB,UAAU,CAAC;AAAA,MAC3D;AAAA,IACF,CAAC;AAAA,EACH;AAEA,QAAM,mBAAmB,+BAA+B,MAAM;AAC9D,MAAI,iBAAiB,SAAS,GAAG;AAC/B,WAAO,KAAK,EAAE,MAAM,UAAU,CAAC;AAC/B,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,MAAM;AAAA,QACJ,MAAM;AAAA,QACN,MAAM,iBAAiB,KAAK,IAAI;AAAA,MAClC;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAEO,SAAS,8BAA8B,OAA0G;AACtJ,SAAO;AAAA,IACL,SAAS,MAAM;AAAA,IACf,MAAM,sBAAsB,MAAM,IAAI;AAAA,IACtC,WAAW,MAAM;AAAA,IACjB,GAAI,MAAM,QAAQ,SAAS,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;AAAA,EACzD;AACF;AAEO,SAAS,gCAAgC,OAA2G;AACzJ,SAAO;AAAA,IACL,SAAS,MAAM;AAAA,IACf,MAAM,sBAAsB,MAAM,IAAI;AAAA,IACtC,IAAI,MAAM;AAAA,IACV,GAAI,MAAM,QAAQ,SAAS,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;AAAA,EACzD;AACF;;;AC5NA,OAAO,eAAiC;AAIxC,IAAM,6BAA6B;AACnC,IAAM,6BAA6B;AA0CnC,SAAS,gBAAgB,MAAuB;AAC9C,MAAI,OAAO,SAAS,SAAU,QAAO;AACrC,MAAI,OAAO,SAAS,IAAI,EAAG,QAAO,KAAK,SAAS,MAAM;AACtD,MAAI,MAAM,QAAQ,IAAI,EAAG,QAAO,OAAO,OAAO,IAAI,EAAE,SAAS,MAAM;AACnE,MAAI,gBAAgB,YAAa,QAAO,OAAO,KAAK,IAAI,EAAE,SAAS,MAAM;AACzE,SAAO,OAAO,KAAK,IAAI,EAAE,SAAS,MAAM;AAC1C;AAEA,eAAe,mBAAmB,OAAuE;AACvG,QAAM,WAAW,MAAM,MAAM,UAAU,4BAA4B;AAAA,IACjE,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,eAAe,UAAU,MAAM,QAAQ;AAAA,IACzC;AAAA,EACF,CAAC;AACD,QAAM,OAAQ,MAAM,SAAS,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AACpD,MAAI,CAAC,SAAS,MAAM,CAAC,KAAK,MAAM,CAAC,KAAK,KAAK;AACzC,UAAM,SAAS,KAAK,SAAS,QAAQ,SAAS,MAAM;AACpD,UAAM,IAAI,MAAM,wCAAwC,MAAM,EAAE;AAAA,EAClE;AACA,SAAO,KAAK;AACd;AAEA,SAAS,oBAAoB,MAA+C;AAC1E,MAAI;AACF,WAAO,KAAK,MAAM,gBAAgB,IAAI,CAAC;AAAA,EACzC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAe,oBAAoB,OAMjB;AAChB,QAAM,WAAW,oBAAoB,MAAM,IAAI;AAC/C,MAAI,CAAC,UAAU,aAAa;AAC1B,UAAM,SAAS,0DAA0D;AACzE;AAAA,EACF;AAEA,QAAM,OAAO,KAAK,KAAK,UAAU,EAAE,aAAa,SAAS,YAAY,CAAC,CAAC;AAEvE,MAAI,SAAS,SAAS,gBAAgB,CAAC,SAAS,SAAS;AACvD;AAAA,EACF;AACA,MAAI,MAAM,SAAS,SAAS,SAAS,QAAQ,cAAc,SAAS,QAAQ,eAAe,MAAM,SAAS,OAAO;AAC/G;AAAA,EACF;AAEA,QAAM,MAAM,UAAU,QAAQ,SAAS,SAAS,MAAM,QAAQ;AAChE;AAEA,SAAS,KAAK,IAA2B;AACvC,SAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AACzD;AAEO,SAAS,wBACd,OACA,eAA4C,CAAC,GACf;AAC9B,QAAM,YAAY,aAAa,aAAa;AAC5C,QAAM,kBAAkB,aAAa,oBAAoB,CAAC,QAAgB,IAAI,UAAU,GAAG;AAC3F,QAAM,mBAAmB,aAAa,oBAAoB;AAC1D,QAAM,MAAM,aAAa,QAAQ,CAAC,YAAoB,QAAQ,IAAI,OAAO;AACzE,QAAM,WAAW,aAAa,aAAa,CAAC,SAAiB,UAAqB,QAAQ,QAAQ,MAAM,SAAS,KAAK,IAAI,QAAQ,MAAM,OAAO;AAC/I,QAAM,YAAY,0BAA0B,KAAK;AACjD,MAAI,SAAS;AACb,MAAI;AAEJ,iBAAe,iBAAiB,WAAkC;AAChE,UAAM,IAAI,QAAc,CAAC,YAAY;AACnC,YAAM,SAAS,gBAAgB,SAAS;AACxC,qBAAe;AACf,UAAI,UAAU;AACd,YAAM,SAAS,MAAM;AACnB,YAAI,QAAS;AACb,kBAAU;AACV,YAAI,iBAAiB,OAAQ,gBAAe;AAC5C,gBAAQ;AAAA,MACV;AAEA,aAAO,KAAK,QAAQ,MAAM;AACxB,YAAI,+BAA+B;AAAA,MACrC,CAAC;AACD,aAAO,GAAG,WAAW,CAAC,SAAS;AAC7B,aAAK,oBAAoB;AAAA,UACvB;AAAA,UACA;AAAA,UACA;AAAA,UACA,UAAU,MAAM;AAAA,UAChB;AAAA,QACF,CAAC,EAAE,MAAM,CAAC,UAAmB;AAC3B,mBAAS,+CAA+C,KAAK;AAAA,QAC/D,CAAC;AAAA,MACH,CAAC;AACD,aAAO,KAAK,SAAS,MAAM;AAC3B,aAAO,KAAK,SAAS,CAAC,UAAU;AAC9B,YAAI,CAAC,QAAQ;AACX,mBAAS,yCAAyC,KAAK;AAAA,QACzD;AACA,eAAO,MAAM;AACb,eAAO;AAAA,MACT,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAEA,QAAM,gBAAgB,YAAY;AAChC,WAAO,CAAC,QAAQ;AACd,YAAM,YAAY,MAAM,mBAAmB,EAAE,UAAU,MAAM,UAAU,UAAU,CAAC;AAClF,YAAM,iBAAiB,SAAS;AAChC,UAAI,CAAC,QAAQ;AACX,cAAM,KAAK,gBAAgB;AAAA,MAC7B;AAAA,IACF;AAAA,EACF,GAAG;AAEH,SAAO;AAAA,IACL;AAAA,IACA,MAAM,QAAQ;AACZ,eAAS;AACT,oBAAc,MAAM;AACpB,YAAM,aAAa,MAAM,MAAM,MAAS;AAAA,IAC1C;AAAA,EACF;AACF;AAEO,SAAS,4BACd,QACA,eAA4C,CAAC,GACf;AAC9B,SAAO;AAAA,IACL;AAAA,MACE,UAAU,OAAO;AAAA,MACjB,UAAU;AAAA,QACR,SAAS,OAAO,WAAW;AAAA,QAC3B,GAAI,OAAO,QAAQ,EAAE,OAAO,OAAO,MAAM,IAAI,CAAC;AAAA,QAC9C,GAAI,OAAO,cAAc,EAAE,aAAa,OAAO,YAAY,IAAI,CAAC;AAAA,MAClE;AAAA,MACA,GAAG,yCAAyC,MAAM;AAAA,IACpD;AAAA,IACA;AAAA,EACF;AACF;","names":["text","text"]}
|
|
1
|
+
{"version":3,"sources":["../src/events.ts","../src/normalize.ts","../src/render.ts","../src/ingress.ts","../src/dispatcher-events.ts","../src/socket-mode.ts"],"sourcesContent":["import { parseThreadActionCommand, type OpenTagEvent } from \"@opentag/core\";\nimport { encodeSlackThreadKey, normalizeSlackAppMention, stripSlackAppMention, type SlackChannelBinding } from \"./normalize.js\";\nimport { parseSlackSuggestedActionButtonValue } from \"./render.js\";\n\nexport type SlackThreadActionInput = {\n id: string;\n rawText: string;\n actor: {\n provider: \"slack\";\n providerUserId: string;\n handle: string;\n organizationId: string;\n };\n callback: {\n provider: \"slack\";\n uri: string;\n threadKey: string;\n };\n metadata: Record<string, unknown>;\n};\n\nexport type SlackEventEnvelope = {\n token?: string;\n type: \"url_verification\" | \"event_callback\";\n challenge?: string;\n team_id?: string;\n api_app_id?: string;\n event?: {\n type: string;\n user?: string;\n text?: string;\n ts?: string;\n thread_ts?: string;\n channel?: string;\n subtype?: string;\n bot_id?: string;\n };\n event_id?: string;\n event_time?: number;\n authorizations?: Array<{ user_id?: string }>;\n};\n\nexport type SlackInteractiveBlockAction = {\n type?: string;\n action_id?: string;\n block_id?: string;\n value?: string;\n action_ts?: string;\n};\n\nexport type SlackInteractivePayload = {\n type: \"block_actions\";\n api_app_id?: string;\n team?: { id?: string; domain?: string };\n user?: { id?: string; username?: string; name?: string };\n channel?: { id?: string; name?: string };\n message?: { ts?: string; thread_ts?: string };\n container?: { type?: string; channel_id?: string; message_ts?: string; thread_ts?: string };\n trigger_id?: string;\n actions?: SlackInteractiveBlockAction[];\n};\n\nexport type SlackIngressPayload = SlackEventEnvelope | SlackInteractivePayload;\n\nexport type SlackAppRuntimeConfig = {\n agentId: string;\n appId?: string;\n callbackUri?: string;\n};\n\nexport type SlackEventProcessorInput = {\n resolveChannelBinding(input: { teamId: string; channelId: string }): Promise<SlackChannelBinding | null>;\n createRun(event: OpenTagEvent): Promise<{ runId: string }>;\n submitThreadAction?(action: SlackThreadActionInput): Promise<unknown>;\n now(): string;\n};\n\nexport type SlackEventProcessorStatus = 200 | 400;\n\nexport type SlackEventProcessorResult =\n | {\n kind: \"json\";\n status: SlackEventProcessorStatus;\n body: Record<string, unknown>;\n }\n | {\n kind: \"text\";\n status: SlackEventProcessorStatus;\n body: string;\n };\n\nfunction json(body: Record<string, unknown>, status: SlackEventProcessorStatus = 200): SlackEventProcessorResult {\n return { kind: \"json\", status, body };\n}\n\nfunction text(body: string, status: SlackEventProcessorStatus = 200): SlackEventProcessorResult {\n return { kind: \"text\", status, body };\n}\n\nexport function createSlackEventProcessor(input: SlackEventProcessorInput) {\n async function processBlockActions(payload: SlackInteractivePayload, slackApp: SlackAppRuntimeConfig): Promise<SlackEventProcessorResult> {\n const action = payload.actions?.find((candidate) => {\n if (candidate.action_id?.startsWith(\"opentag:\")) return true;\n return typeof candidate.value === \"string\" && parseSlackSuggestedActionButtonValue(candidate.value) !== null;\n });\n if (!action) {\n return json({ ok: true });\n }\n\n const parsedValue = typeof action.value === \"string\" ? parseSlackSuggestedActionButtonValue(action.value) : null;\n const rawText =\n parsedValue?.command ??\n (typeof action.value === \"string\" && parseThreadActionCommand(action.value) ? action.value.trim() : undefined);\n if (!rawText || !parseThreadActionCommand(rawText)) {\n return json({ error: \"invalid_interactive_action\" }, 400);\n }\n if (!input.submitThreadAction) {\n return json({ ok: true });\n }\n\n const teamId = payload.team?.id;\n const userId = payload.user?.id;\n const channelId = payload.channel?.id ?? payload.container?.channel_id;\n const messageTs = payload.message?.ts ?? payload.container?.message_ts;\n const threadTs = payload.message?.thread_ts ?? payload.container?.thread_ts ?? messageTs;\n if (!teamId || !userId || !channelId || !messageTs || !threadTs) {\n return json({ error: \"invalid_interactive_payload\" }, 400);\n }\n\n const binding = await input.resolveChannelBinding({\n teamId,\n channelId\n });\n if (!binding) {\n return json({ ok: true, ignored: \"unbound_channel\" });\n }\n\n await input.submitThreadAction({\n id: `approval_slack_block_${payload.trigger_id ?? `${action.action_id ?? \"action\"}_${action.action_ts ?? messageTs}`}`,\n rawText,\n actor: {\n provider: \"slack\",\n providerUserId: userId,\n handle: payload.user?.username ?? payload.user?.name ?? userId,\n organizationId: teamId\n },\n callback: {\n provider: \"slack\",\n uri: slackApp.callbackUri ?? \"https://slack.com/api/chat.postMessage\",\n threadKey: encodeSlackThreadKey({\n teamId,\n channelId,\n threadTs\n })\n },\n metadata: {\n source: \"slack_button\",\n teamId,\n channelId,\n messageTs,\n ...(payload.api_app_id ? { slackAppId: payload.api_app_id } : {}),\n ...(action.action_id ? { actionId: action.action_id } : {}),\n ...(action.block_id ? { blockId: action.block_id } : {}),\n ...(action.action_ts ? { actionTs: action.action_ts } : {}),\n ...(parsedValue ? { proposalId: parsedValue.proposalId, intentId: parsedValue.intentId } : {}),\n repoProvider: binding.repoProvider ?? \"github\",\n owner: binding.owner,\n repo: binding.repo\n }\n });\n return json({ ok: true });\n }\n\n return {\n async process(payload: SlackIngressPayload, slackApp: SlackAppRuntimeConfig): Promise<SlackEventProcessorResult> {\n if (payload.type === \"block_actions\") {\n return processBlockActions(payload, slackApp);\n }\n if (payload.type === \"url_verification\") {\n return text(payload.challenge ?? \"\");\n }\n if (payload.type !== \"event_callback\" || !payload.event || ![\"app_mention\", \"message\"].includes(payload.event.type)) {\n return json({ ok: true });\n }\n if (payload.event.type === \"message\" && (payload.event.subtype || payload.event.bot_id)) {\n return json({ ok: true });\n }\n if (!payload.team_id || !payload.event.channel || !payload.event.user || !payload.event.text || !payload.event.ts || !payload.event_id) {\n return json({ error: \"invalid_event_payload\" }, 400);\n }\n\n const rawThreadActionText =\n payload.event.type === \"app_mention\"\n ? stripSlackAppMention(payload.event.text, payload.authorizations?.[0]?.user_id)\n : payload.event.text.trim();\n if (payload.event.type === \"message\" && (!rawThreadActionText || !parseThreadActionCommand(rawThreadActionText))) {\n return json({ ok: true });\n }\n\n const binding = await input.resolveChannelBinding({\n teamId: payload.team_id,\n channelId: payload.event.channel\n });\n if (!binding) {\n return json({ ok: true, ignored: \"unbound_channel\" });\n }\n\n if (rawThreadActionText && parseThreadActionCommand(rawThreadActionText) && input.submitThreadAction) {\n await input.submitThreadAction({\n id: `approval_slack_${payload.event_id}`,\n rawText: rawThreadActionText,\n actor: {\n provider: \"slack\",\n providerUserId: payload.event.user,\n handle: payload.event.user,\n organizationId: payload.team_id\n },\n callback: {\n provider: \"slack\",\n uri: slackApp.callbackUri ?? \"https://slack.com/api/chat.postMessage\",\n threadKey: encodeSlackThreadKey({\n teamId: payload.team_id,\n channelId: payload.event.channel,\n threadTs: payload.event.thread_ts ?? payload.event.ts\n })\n },\n metadata: {\n teamId: payload.team_id,\n channelId: payload.event.channel,\n messageTs: payload.event.ts,\n ...(payload.api_app_id ? { slackAppId: payload.api_app_id } : {}),\n ...(payload.authorizations?.[0]?.user_id ? { slackBotUserId: payload.authorizations[0].user_id } : {}),\n repoProvider: binding.repoProvider ?? \"github\",\n owner: binding.owner,\n repo: binding.repo\n }\n });\n return json({ ok: true });\n }\n\n if (payload.event.type !== \"app_mention\") {\n return json({ ok: true });\n }\n\n const event = normalizeSlackAppMention({\n teamId: payload.team_id,\n channelId: payload.event.channel,\n userId: payload.event.user,\n text: payload.event.text,\n ts: payload.event.ts,\n eventId: payload.event_id,\n eventTime: payload.event_time ?? Math.floor(Date.parse(input.now()) / 1000),\n agentId: slackApp.agentId,\n binding,\n ...(payload.api_app_id ? { appId: payload.api_app_id } : {}),\n ...(payload.event.thread_ts ? { threadTs: payload.event.thread_ts } : {}),\n ...(payload.authorizations?.[0]?.user_id ? { botUserId: payload.authorizations[0].user_id } : {}),\n ...(slackApp.callbackUri ? { callbackUri: slackApp.callbackUri } : {})\n });\n if (!event) {\n return json({ ok: true, ignored: \"empty_command\" });\n }\n\n await input.createRun(event);\n return json({ ok: true });\n }\n };\n}\n","import { commandFromRawText, type ContextPointer, type OpenTagCommand, type OpenTagEvent, type PermissionGrant } from \"@opentag/core\";\n\nexport type SlackChannelBinding = {\n teamId: string;\n channelId: string;\n repoProvider?: string;\n owner: string;\n repo: string;\n};\n\nexport type SlackAppMentionInput = {\n teamId: string;\n channelId: string;\n userId: string;\n text: string;\n ts: string;\n threadTs?: string;\n eventId: string;\n eventTime: number;\n appId?: string;\n agentId?: string;\n botUserId?: string;\n callbackUri?: string;\n binding: SlackChannelBinding;\n};\n\n// Matches a run of one or more leading user mentions (e.g. a teammate mention\n// placed before the bot mention) along with the whitespace separating them.\nconst LEADING_MENTION_RUN = /^(?:<@[^>]+>\\s*)+/;\n// Matches a single mention token, used to confirm the bot itself appears in the\n// leading run so we only treat the message as an at-mention of the bot.\nconst MENTION_TOKEN = /<@([^>]+)>/g;\n\nexport function stripSlackAppMention(text: string, botUserId?: string): string | null {\n const run = text.match(LEADING_MENTION_RUN);\n if (!run) return null;\n\n // Only strip the *leading* run of mentions; mentions later in the message\n // (e.g. \"<@bot> ping <@teammate> now\") must be preserved as command text.\n const leadingRun = run[0];\n\n if (botUserId) {\n // Require that the bot is mentioned somewhere in the leading run before we\n // accept this as a command directed at the bot. This lets a teammate\n // mention precede the bot mention without breaking routing.\n const mentionedIds = leadingRun.match(MENTION_TOKEN) ?? [];\n const botIsMentioned = mentionedIds.some((token) => {\n // Slack mentions may carry a display-name/label suffix, e.g.\n // \"<@U12345|alice>\". Strip the label so we compare against the bare\n // user ID rather than \"U12345|alice\".\n const id = token.slice(2, -1).split(\"|\")[0] ?? \"\";\n return id.toLowerCase() === botUserId.toLowerCase();\n });\n if (!botIsMentioned) return null;\n }\n\n const stripped = text.slice(leadingRun.length).trim();\n return stripped.length > 0 ? stripped : null;\n}\n\nexport function encodeSlackThreadKey(input: { teamId: string; channelId: string; threadTs: string }): string {\n return `${input.teamId}|${input.channelId}|${input.threadTs}`;\n}\n\nexport function parseSlackThreadKey(threadKey: string): { teamId: string; channelId: string; threadTs: string } {\n const [teamId, channelId, threadTs] = threadKey.split(\"|\");\n if (!teamId || !channelId || !threadTs) {\n throw new Error(`Invalid Slack thread key: ${threadKey}`);\n }\n return { teamId, channelId, threadTs };\n}\n\nconst UNKNOWN_WRITE_VERB_PATTERN = /\\b(add|append|apply|change|commit|create|delete|edit|fix|modify|open\\s+a?\\s*pr|pull\\s+request|remove|update|write)\\b/i;\nconst REPO_WRITE_TARGET_PATTERN =\n /\\b(repo|repository|code|file|files|branch|commit|diff|patch|readme|pr|pull\\s+request|package\\.json|pnpm|npm|test|build)\\b|(?:^|\\s)[./\\w-]+\\.(?:cjs|css|gitignore|go|html|js|json|jsx|lock|md|mjs|py|rb|rs|sh|toml|ts|tsx|txt|yaml|yml)\\b|(?:^|[\\s`'\"(])(?:[./\\w-]+\\/)?(?:Dockerfile|Makefile|Procfile|Rakefile|Gemfile|Brewfile|Justfile|Taskfile|\\.dockerignore|\\.env(?:\\.[\\w-]+)?|\\.gitignore|\\.npmrc)(?=$|[\\s`'\",.):])/i;\n\nfunction commandLooksRepoWriteCapable(command: OpenTagCommand): boolean {\n return UNKNOWN_WRITE_VERB_PATTERN.test(command.rawText) && REPO_WRITE_TARGET_PATTERN.test(command.rawText);\n}\n\nfunction permissionsForCommand(command: OpenTagCommand): PermissionGrant[] {\n const permissions: PermissionGrant[] = [\n {\n scope: \"chat:postMessage\",\n reason: \"reply in the originating Slack thread\"\n },\n {\n scope: \"reactions:write\",\n reason: \"mark the originating Slack message as received without posting a thread reply\"\n },\n {\n scope: \"runner:local\",\n reason: \"execute the run on a paired local daemon\"\n }\n ];\n\n if (command.intent === \"fix\" || command.intent === \"run\" || (command.intent === \"unknown\" && commandLooksRepoWriteCapable(command))) {\n permissions.push(\n {\n scope: \"repo:read\",\n reason: \"inspect the repository in the paired local checkout\"\n },\n {\n scope: \"repo:write\",\n reason: \"commit code changes on an isolated run branch\"\n },\n {\n scope: \"pr:create\",\n reason: \"open a pull request for completed code changes\"\n }\n );\n }\n\n return permissions;\n}\n\nfunction contextPointersForCommand(command: OpenTagCommand): ContextPointer[] {\n const context: ContextPointer[] = [];\n\n for (const reference of command.parsed?.references ?? []) {\n if (reference.kind === \"url\") {\n context.push({\n kind: \"url\",\n uri: reference.uri,\n visibility: \"organization\",\n title: reference.title ?? \"Command URL reference\"\n });\n continue;\n }\n\n if (reference.kind === \"file\" || reference.kind === \"path\") {\n context.push({\n kind: \"file\",\n uri: reference.uri,\n ...(reference.line ? { line: reference.line } : {}),\n ...(reference.startLine ? { startLine: reference.startLine } : {}),\n ...(reference.endLine ? { endLine: reference.endLine } : {}),\n visibility: \"organization\",\n title: referenceTitle(reference)\n });\n }\n }\n\n return context;\n}\n\nfunction referenceTitle(reference: NonNullable<OpenTagCommand[\"parsed\"]>[\"references\"][number]): string {\n return reference.title ?? \"Command file reference\";\n}\n\nfunction commandMetadata(command: OpenTagCommand): Record<string, unknown> {\n if (!command.parsed) return {};\n return {\n commandParser: command.parsed.version,\n commandDiagnostics: command.parsed.diagnostics,\n ...(command.parsed.approval ? { approval: command.parsed.approval } : {}),\n ...(command.parsed.network ? { network: command.parsed.network } : {})\n };\n}\n\nexport function normalizeSlackAppMention(input: SlackAppMentionInput): OpenTagEvent | null {\n const rawText = stripSlackAppMention(input.text, input.botUserId);\n if (!rawText) return null;\n\n const command = commandFromRawText(rawText);\n const replyThreadTs = input.threadTs ?? input.ts;\n const agentId = input.agentId ?? \"opentag\";\n\n return {\n id: `evt_slack_app_mention_${input.eventId}`,\n source: \"slack\",\n sourceEventId: input.eventId,\n receivedAt: new Date(input.eventTime * 1000).toISOString(),\n actor: {\n provider: \"slack\",\n providerUserId: input.userId,\n handle: input.userId,\n organizationId: input.teamId\n },\n target: {\n mention: input.botUserId ? `<@${input.botUserId}>` : \"<@app>\",\n agentId,\n ...(command.parsed?.executorHint ? { executorHint: command.parsed.executorHint } : {})\n },\n command,\n context: [\n {\n provider: \"slack\",\n kind: \"message\",\n uri: `slack://team/${input.teamId}/channel/${input.channelId}/message/${input.ts}`,\n visibility: \"organization\",\n title: \"Slack message\"\n },\n {\n kind: \"text\",\n uri: input.text,\n visibility: \"organization\",\n title: \"Slack message text\"\n },\n ...contextPointersForCommand(command)\n ],\n permissions: permissionsForCommand(command),\n callback: {\n provider: \"slack\",\n uri: input.callbackUri ?? \"https://slack.com/api/chat.postMessage\",\n threadKey: encodeSlackThreadKey({\n teamId: input.teamId,\n channelId: input.channelId,\n threadTs: replyThreadTs\n })\n },\n metadata: {\n teamId: input.teamId,\n channelId: input.channelId,\n messageTs: input.ts,\n ...(input.appId ? { slackAppId: input.appId } : {}),\n ...(input.botUserId ? { slackBotUserId: input.botUserId } : {}),\n ...commandMetadata(command),\n repoProvider: input.binding.repoProvider ?? \"github\",\n owner: input.binding.owner,\n repo: input.binding.repo\n }\n };\n}\n","import { suggestedActionCandidatesFromResult, type OpenTagRunResult, type SuggestedActionCandidate } from \"@opentag/core\";\n\nexport type SlackTextBlock = {\n type: \"section\";\n text: {\n type: \"mrkdwn\";\n text: string;\n };\n};\n\nexport type SlackDividerBlock = {\n type: \"divider\";\n};\n\nexport type SlackButtonElement = {\n type: \"button\";\n text: {\n type: \"plain_text\";\n text: string;\n emoji?: boolean;\n };\n action_id: string;\n value: string;\n style?: \"primary\" | \"danger\";\n};\n\nexport type SlackActionsBlock = {\n type: \"actions\";\n block_id?: string;\n elements: SlackButtonElement[];\n};\n\nexport type SlackBlock = SlackTextBlock | SlackDividerBlock | SlackActionsBlock;\n\nexport type SlackSuggestedActionButtonValue = {\n version: 1;\n command: string;\n proposalId: string;\n intentId: string;\n};\n\nexport type SlackMessagePayload = {\n channel: string;\n text: string;\n thread_ts?: string;\n ts?: string;\n blocks?: SlackBlock[];\n};\n\nexport type SlackReactionPayload = {\n channel: string;\n timestamp: string;\n name: string;\n};\n\nexport type SlackSourceReceiptState = \"received\";\n\nconst MAX_SLACK_SUGGESTED_ACTION_CANDIDATES = 20;\n\nexport function buildSlackSuggestedActionButtonValue(input: SlackSuggestedActionButtonValue): string {\n return JSON.stringify(input);\n}\n\nexport function parseSlackSuggestedActionButtonValue(value: string): SlackSuggestedActionButtonValue | null {\n try {\n const parsed = JSON.parse(value) as Partial<SlackSuggestedActionButtonValue>;\n if (\n parsed.version !== 1 ||\n typeof parsed.command !== \"string\" ||\n parsed.command.trim().length === 0 ||\n typeof parsed.proposalId !== \"string\" ||\n parsed.proposalId.length === 0 ||\n typeof parsed.intentId !== \"string\" ||\n parsed.intentId.length === 0\n ) {\n return null;\n }\n return {\n version: 1,\n command: parsed.command.trim(),\n proposalId: parsed.proposalId,\n intentId: parsed.intentId\n };\n } catch {\n return null;\n }\n}\n\nfunction escapeSlackText(text: string): string {\n return text.replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\");\n}\n\nexport function markdownToSlackMrkdwn(text: string): string {\n const links: string[] = [];\n const withoutLinks = text.replace(/\\[([^\\]]+)\\]\\(([^)]+)\\)/g, (_match, label: string, url: string) => {\n const token = `\\u0000SLACK_LINK_${links.length}\\u0000`;\n links.push(`<${url}|${escapeSlackText(label)}>`);\n return token;\n });\n const converted = escapeSlackText(withoutLinks)\n .replace(/\\*\\*(.+?)\\*\\*/g, \"*$1*\")\n .replace(/__(.+?)__/g, \"*$1*\");\n return links.reduce((output, link, index) => output.replace(`\\u0000SLACK_LINK_${index}\\u0000`, link), converted);\n}\n\nexport function renderSlackAcknowledgement(runId: string): string {\n void runId;\n return \"Working on it.\";\n}\n\nexport function slackSourceReceiptReactionName(state: SlackSourceReceiptState): string {\n if (state === \"received\") return \"eyes\";\n return \"eyes\";\n}\n\nexport function createSlackReactionPayload(input: { channelId: string; messageTs: string; name: string }): SlackReactionPayload {\n return {\n channel: input.channelId,\n timestamp: input.messageTs,\n name: input.name\n };\n}\n\nfunction nextActionSummary(result: OpenTagRunResult): string | undefined {\n if (!result.nextAction) return undefined;\n if (typeof result.nextAction === \"string\") return result.nextAction;\n return result.nextAction.summary;\n}\n\nfunction stringParam(params: Record<string, unknown> | undefined, key: string): string | undefined {\n const value = params?.[key];\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n\nfunction stringArrayParam(params: Record<string, unknown> | undefined, key: string): string[] {\n const value = params?.[key];\n if (!Array.isArray(value)) return [];\n return value.filter((item): item is string => typeof item === \"string\" && item.length > 0);\n}\n\nfunction renderVerificationParams(params: Record<string, unknown> | undefined): string[] {\n const value = params?.[\"verification\"];\n if (!Array.isArray(value)) return [];\n return value\n .map((item) => {\n if (!item || typeof item !== \"object\" || Array.isArray(item)) return undefined;\n const command = (item as Record<string, unknown>)[\"command\"];\n const outcome = (item as Record<string, unknown>)[\"outcome\"];\n return typeof command === \"string\" && typeof outcome === \"string\" ? ` - \\`${command}\\`: ${outcome}` : undefined;\n })\n .filter((line): line is string => Boolean(line));\n}\n\nfunction renderSuggestedActionDetails(params: Record<string, unknown> | undefined, action: string): string[] {\n if (action !== \"create_pull_request\") return [];\n const lines: string[] = [];\n const title = stringParam(params, \"title\");\n const head = stringParam(params, \"head\") ?? stringParam(params, \"branch\");\n const base = stringParam(params, \"base\") ?? stringParam(params, \"baseBranch\");\n const changedFiles = stringArrayParam(params, \"changedFiles\");\n const risks = stringArrayParam(params, \"risks\");\n const verification = renderVerificationParams(params);\n if (title) lines.push(` Title: ${markdownToSlackMrkdwn(title)}`);\n if (head || base) lines.push(` Branch: \\`${head ?? \"unknown\"}\\` -> \\`${base ?? \"main\"}\\``);\n if (changedFiles.length > 0) lines.push(` Changed files: ${changedFiles.map((file) => `\\`${file}\\``).join(\", \")}`);\n if (risks.length > 0) {\n lines.push(\" Risks:\");\n for (const risk of risks) {\n lines.push(` - ${markdownToSlackMrkdwn(risk)}`);\n }\n }\n if (verification.length > 0) {\n lines.push(\" Verification:\");\n lines.push(...verification);\n }\n return lines;\n}\n\nfunction truncateSlackText(text: string, maxLength: number): string {\n const normalized = text.replace(/\\s+/g, \" \").trim();\n if (normalized.length <= maxLength) return normalized;\n return `${normalized.slice(0, Math.max(0, maxLength - 1)).trimEnd()}…`;\n}\n\nfunction firstMarkdownSection(text: string, heading: string): string | undefined {\n const pattern = new RegExp(`\\\\*\\\\*${heading}:\\\\*\\\\*\\\\s*([\\\\s\\\\S]*?)(?=\\\\n\\\\s*\\\\n\\\\*\\\\*[^*]+:\\\\*\\\\*|\\\\n\\\\s*\\\\n[A-Z][^\\\\n]{0,60}:|$)`, \"i\");\n const match = text.match(pattern);\n return match?.[1]?.trim();\n}\n\nfunction compactSlackSummary(summary: string): string {\n const whatChanged = firstMarkdownSection(summary, \"What changed\");\n const firstParagraph = summary\n .split(/\\n\\s*\\n/)\n .map((part) => part.trim())\n .find(Boolean);\n const selected = whatChanged ?? firstParagraph ?? summary;\n return truncateSlackText(selected.replace(/^\\*\\*[^*]+:\\*\\*\\s*/i, \"\"), 360);\n}\n\nfunction compactNextAction(nextAction: string): string {\n return truncateSlackText(nextAction, 180);\n}\n\nfunction renderSuggestedActionCandidateLines(candidate: SuggestedActionCandidate): string[] {\n const lines = [`${candidate.index}. *${markdownToSlackMrkdwn(candidate.intent.summary)}*`];\n const details = renderSuggestedActionDetails(candidate.intent.params, candidate.intent.action)\n .filter((line) => line.trim().startsWith(\"Branch:\") || line.trim().startsWith(\"Changed files:\"))\n .map((line) => line.replace(/^\\s+/, \"\"));\n lines.push(...details);\n if (candidate.proposalPreconditions?.length) {\n lines.push(`Preconditions: ${candidate.proposalPreconditions.length} check(s) in the audit log.`);\n }\n return lines;\n}\n\nfunction renderSuggestedActionsMarkdown(result: OpenTagRunResult): string[] {\n const candidates = suggestedActionCandidatesFromResult(result);\n if (candidates.length === 0) return [];\n\n const lines = [\"*Suggested actions*\"];\n const visibleCandidates = candidates.slice(0, MAX_SLACK_SUGGESTED_ACTION_CANDIDATES);\n for (const candidate of visibleCandidates) {\n lines.push(\"\", ...renderSuggestedActionCandidateLines(candidate));\n }\n\n const remainingCount = candidates.length - visibleCandidates.length;\n if (remainingCount > 0) {\n lines.push(\"\", `Showing first ${visibleCandidates.length} of ${candidates.length} actions. Reply with an action number for the rest.`);\n }\n lines.push(\"\", \"Use the buttons below, or reply `apply 1`, `approve 1`, or `reject 1`.\");\n return lines;\n}\n\nfunction createSuggestedActionButtons(candidate: SuggestedActionCandidate): SlackButtonElement[] {\n return [\n {\n type: \"button\",\n text: { type: \"plain_text\", text: `Apply ${candidate.index}`, emoji: true },\n action_id: `opentag:apply:${candidate.index}`,\n value: buildSlackSuggestedActionButtonValue({\n version: 1,\n command: `apply ${candidate.index}`,\n proposalId: candidate.proposalId,\n intentId: candidate.intent.intentId\n }),\n style: \"primary\"\n },\n {\n type: \"button\",\n text: { type: \"plain_text\", text: \"Approve\", emoji: true },\n action_id: `opentag:approve:${candidate.index}`,\n value: buildSlackSuggestedActionButtonValue({\n version: 1,\n command: `approve ${candidate.index}`,\n proposalId: candidate.proposalId,\n intentId: candidate.intent.intentId\n })\n },\n {\n type: \"button\",\n text: { type: \"plain_text\", text: \"Reject\", emoji: true },\n action_id: `opentag:reject:${candidate.index}`,\n value: buildSlackSuggestedActionButtonValue({\n version: 1,\n command: `reject ${candidate.index}`,\n proposalId: candidate.proposalId,\n intentId: candidate.intent.intentId\n }),\n style: \"danger\"\n }\n ];\n}\n\nexport function renderSlackFinalResult(result: OpenTagRunResult): string {\n const lines = [`*Finished: ${result.conclusion}.*`, markdownToSlackMrkdwn(compactSlackSummary(result.summary))];\n\n if (result.verification?.length) {\n lines.push(\n `Verified: ${result.verification\n .slice(0, 3)\n .map((check) => `\\`${markdownToSlackMrkdwn(check.command)}\\` ${markdownToSlackMrkdwn(check.outcome)}`)\n .join(\", \")}`\n );\n }\n\n const nextAction = nextActionSummary(result);\n if (nextAction && !result.suggestedChanges?.length) {\n lines.push(`Next: ${markdownToSlackMrkdwn(compactNextAction(nextAction))}`);\n }\n\n const suggestedActions = renderSuggestedActionsMarkdown(result);\n if (suggestedActions.length > 0) {\n lines.push(\"\", ...suggestedActions);\n }\n\n return lines.join(\"\\n\");\n}\n\nexport function createSlackFinalResultBlocks(result: OpenTagRunResult): SlackBlock[] {\n const blocks: SlackBlock[] = [\n {\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text: `*Finished: ${result.conclusion}.*\\n${markdownToSlackMrkdwn(compactSlackSummary(result.summary))}`\n }\n }\n ];\n\n if (result.verification?.length) {\n blocks.push({\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text: `Verified: ${markdownToSlackMrkdwn(\n result.verification\n .slice(0, 3)\n .map((check) => `\\`${check.command}\\` ${check.outcome}`)\n .join(\", \")\n )}`\n }\n });\n }\n\n const nextAction = nextActionSummary(result);\n const suggestedActionCandidates = suggestedActionCandidatesFromResult(result);\n if (nextAction && suggestedActionCandidates.length === 0) {\n blocks.push({\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text: `Next: ${markdownToSlackMrkdwn(compactNextAction(nextAction))}`\n }\n });\n }\n\n if (suggestedActionCandidates.length > 0) {\n blocks.push({ type: \"divider\" });\n blocks.push({\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text: \"*Suggested actions*\\nChoose an action in this thread. Details stay in the OpenTag audit log.\"\n }\n });\n const visibleCandidates = suggestedActionCandidates.slice(0, MAX_SLACK_SUGGESTED_ACTION_CANDIDATES);\n for (const candidate of visibleCandidates) {\n blocks.push({\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text: renderSuggestedActionCandidateLines(candidate).join(\"\\n\")\n }\n });\n blocks.push({\n type: \"actions\",\n block_id: `opentag_actions_${candidate.index}`,\n elements: createSuggestedActionButtons(candidate)\n });\n }\n const remainingCount = suggestedActionCandidates.length - visibleCandidates.length;\n if (remainingCount > 0) {\n blocks.push({\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text: `Showing first ${visibleCandidates.length} of ${suggestedActionCandidates.length} actions. Reply with an action number for the rest.`\n }\n });\n }\n }\n\n return blocks;\n}\n\nexport function createSlackPostMessagePayload(input: { channelId: string; text: string; threadTs: string; blocks?: SlackBlock[] }): SlackMessagePayload {\n return {\n channel: input.channelId,\n text: markdownToSlackMrkdwn(input.text),\n thread_ts: input.threadTs,\n ...(input.blocks?.length ? { blocks: input.blocks } : {})\n };\n}\n\nexport function createSlackUpdateMessagePayload(input: { channelId: string; text: string; messageTs: string; blocks?: SlackBlock[] }): SlackMessagePayload {\n return {\n channel: input.channelId,\n text: markdownToSlackMrkdwn(input.text),\n ts: input.messageTs,\n ...(input.blocks?.length ? { blocks: input.blocks } : {})\n };\n}\n","import { createHmac, timingSafeEqual } from \"node:crypto\";\nimport { serve } from \"@hono/node-server\";\nimport { Hono } from \"hono\";\nimport { createSlackDispatcherEventProcessorInput } from \"./dispatcher-events.js\";\nimport { createSlackEventProcessor, type SlackAppRuntimeConfig, type SlackEventProcessorInput, type SlackIngressPayload } from \"./events.js\";\n\nexport type SlackEventsAppInput = {\n slackApps: Array<\n SlackAppRuntimeConfig & {\n signingSecret: string;\n }\n >;\n clock?: () => number;\n} & SlackEventProcessorInput;\n\nexport type SlackEventsApiIngressConfig = {\n signingSecret: string;\n dispatcherUrl: string;\n dispatcherToken?: string;\n port?: number;\n agentId?: string;\n appId?: string;\n callbackUri?: string;\n};\n\nexport type SlackIngressConfig = SlackEventsApiIngressConfig;\n\nexport type SlackIngressHandle = {\n url: string;\n server: ReturnType<typeof serve>;\n close(): Promise<void>;\n};\n\nexport function computeSlackSignature(input: {\n signingSecret: string;\n timestamp: string;\n rawBody: string;\n}): string {\n const base = `v0:${input.timestamp}:${input.rawBody}`;\n const digest = createHmac(\"sha256\", input.signingSecret).update(base).digest(\"hex\");\n return `v0=${digest}`;\n}\n\nexport function verifySlackSignature(input: {\n signingSecret: string;\n timestamp: string;\n rawBody: string;\n signature: string;\n}): boolean {\n const expected = computeSlackSignature(input);\n const expectedBuffer = Buffer.from(expected);\n const actualBuffer = Buffer.from(input.signature);\n return expectedBuffer.length === actualBuffer.length && timingSafeEqual(expectedBuffer, actualBuffer);\n}\n\nexport function verifySlackTimestamp(input: { timestamp: string; nowMs: number; toleranceSeconds?: number }): boolean {\n const timestampSeconds = Number(input.timestamp);\n if (!Number.isFinite(timestampSeconds)) return false;\n const toleranceSeconds = input.toleranceSeconds ?? 300;\n const ageSeconds = Math.abs(Math.floor(input.nowMs / 1000) - timestampSeconds);\n return ageSeconds <= toleranceSeconds;\n}\n\nexport function createSlackEventsApp(input: SlackEventsAppInput) {\n const app = new Hono();\n const processor = createSlackEventProcessor(input);\n\n function parseSlackPayload(rawBody: string, contentType?: string): SlackIngressPayload | null {\n try {\n if (contentType?.includes(\"application/x-www-form-urlencoded\") || rawBody.startsWith(\"payload=\")) {\n const interactivePayload = new URLSearchParams(rawBody).get(\"payload\");\n if (!interactivePayload) return null;\n return JSON.parse(interactivePayload) as SlackIngressPayload;\n }\n return JSON.parse(rawBody) as SlackIngressPayload;\n } catch {\n return null;\n }\n }\n\n function resolveSlackApp(inputValue: {\n apiAppId?: string;\n rawBody: string;\n signature: string;\n timestamp: string;\n }) {\n const candidates = inputValue.apiAppId\n ? input.slackApps.filter((candidate) => !candidate.appId || candidate.appId === inputValue.apiAppId)\n : input.slackApps;\n if (candidates.length === 0) {\n return { error: \"unknown_slack_app\" as const };\n }\n const slackApp = candidates.find((candidate) =>\n verifySlackSignature({\n signingSecret: candidate.signingSecret,\n timestamp: inputValue.timestamp,\n rawBody: inputValue.rawBody,\n signature: inputValue.signature\n })\n );\n return slackApp ? { slackApp } : { error: \"invalid_signature\" as const };\n }\n\n app.post(\"/slack/events\", async (c) => {\n const timestamp = c.req.header(\"x-slack-request-timestamp\");\n const signature = c.req.header(\"x-slack-signature\");\n if (!timestamp || !signature) {\n return c.json({ error: \"missing_signature_headers\" }, 401);\n }\n if (!verifySlackTimestamp({ timestamp, nowMs: input.clock?.() ?? Date.now() })) {\n return c.json({ error: \"stale_signature_timestamp\" }, 401);\n }\n const rawBody = await c.req.text();\n const payload = parseSlackPayload(rawBody, c.req.header(\"content-type\"));\n if (!payload) {\n return c.json({ error: \"invalid_json\" }, 400);\n }\n const resolvedSlackApp = resolveSlackApp({\n rawBody,\n signature,\n timestamp,\n ...(payload.api_app_id ? { apiAppId: payload.api_app_id } : {})\n });\n if (\"error\" in resolvedSlackApp) {\n return c.json({ error: resolvedSlackApp.error }, 401);\n }\n const result = await processor.process(payload, resolvedSlackApp.slackApp);\n if (result.kind === \"text\") {\n return c.text(result.body, result.status);\n }\n return c.json(result.body, result.status);\n });\n\n return app;\n}\n\nexport function startSlackIngress(config: SlackEventsApiIngressConfig): SlackIngressHandle {\n const port = config.port ?? 3040;\n const server = serve({\n fetch: createSlackEventsApp({\n slackApps: [\n {\n signingSecret: config.signingSecret,\n agentId: config.agentId ?? \"opentag\",\n ...(config.appId ? { appId: config.appId } : {}),\n ...(config.callbackUri ? { callbackUri: config.callbackUri } : {})\n }\n ],\n ...createSlackDispatcherEventProcessorInput(config)\n }).fetch,\n port\n });\n\n return {\n url: `http://localhost:${port}`,\n server,\n close() {\n return new Promise((resolve, reject) => {\n server.close((error?: Error) => {\n if (error) {\n reject(error);\n return;\n }\n resolve();\n });\n });\n }\n };\n}\n","import { randomUUID } from \"node:crypto\";\nimport { createOpenTagClient } from \"@opentag/client\";\nimport type { SlackEventProcessorInput } from \"./events.js\";\n\nexport type SlackDispatcherEventConfig = {\n dispatcherUrl: string;\n dispatcherToken?: string;\n};\n\nexport function createSlackDispatcherEventProcessorInput(config: SlackDispatcherEventConfig): SlackEventProcessorInput {\n const dispatcherClient = createOpenTagClient({\n dispatcherUrl: config.dispatcherUrl,\n ...(config.dispatcherToken ? { pairingToken: config.dispatcherToken } : {})\n });\n\n return {\n async resolveChannelBinding(input) {\n try {\n const { binding } = await dispatcherClient.getChannelBinding({\n provider: \"slack\",\n accountId: input.teamId,\n conversationId: input.channelId\n });\n return {\n teamId: binding.accountId,\n channelId: binding.conversationId,\n repoProvider: binding.repoProvider,\n owner: binding.owner,\n repo: binding.repo\n };\n } catch (error) {\n if (error instanceof Error && error.message.includes(\"channel_binding_not_found\")) {\n return null;\n }\n throw error;\n }\n },\n async createRun(event) {\n const runId = `run_${randomUUID()}`;\n const created = await dispatcherClient.createRun({ runId, event });\n return created.outcome === \"run_created\" ? { runId: created.run.id } : { runId };\n },\n async submitThreadAction(action) {\n await dispatcherClient.submitThreadAction(action);\n },\n now: () => new Date().toISOString()\n };\n}\n","import WebSocket, { type RawData } from \"ws\";\nimport { createSlackDispatcherEventProcessorInput, type SlackDispatcherEventConfig } from \"./dispatcher-events.js\";\nimport { createSlackEventProcessor, type SlackAppRuntimeConfig, type SlackEventProcessorInput, type SlackIngressPayload } from \"./events.js\";\n\nconst SLACK_CONNECTIONS_OPEN_URL = \"https://slack.com/api/apps.connections.open\";\nconst DEFAULT_RECONNECT_DELAY_MS = 1_000;\n\n// Slack Web API `error` codes that represent a terminal authentication or\n// configuration problem with the app token. Retrying these is pointless: the\n// request will fail identically forever, spamming logs and risking rate limits\n// or an API ban. Startup must fail loudly so the operator fixes the config,\n// rather than the daemon silently looping. Transient/network errors (e.g.\n// `ratelimited`, HTTP 5xx, fetch failures) are NOT in this set and are retried.\nconst TERMINAL_SLACK_ERROR_CODES = [\n \"invalid_auth\",\n \"not_authed\",\n \"account_inactive\",\n \"token_revoked\",\n \"token_expired\",\n \"not_allowed_token_type\",\n \"no_permission\",\n \"missing_scope\",\n \"ekm_access_denied\"\n] as const;\n\n/**\n * Returns true when the error from opening the Socket Mode connection is a\n * terminal auth/config failure that must propagate (reject startup) instead of\n * being retried. The Slack error code is embedded in the thrown Error message by\n * {@link openSlackSocketUrl} (e.g. \"...connection failed: invalid_auth\").\n */\nfunction isTerminalSlackAuthError(error: unknown): boolean {\n if (!(error instanceof Error)) return false;\n return TERMINAL_SLACK_ERROR_CODES.some((code) => error.message.includes(code));\n}\n\nexport type SlackSocketModeEnvelope = {\n type?: string;\n envelope_id?: string;\n payload?: SlackIngressPayload;\n accepts_response_payload?: boolean;\n};\n\nexport type SlackSocketModeAppInput = {\n appToken: string;\n slackApp: SlackAppRuntimeConfig;\n} & SlackEventProcessorInput;\n\nexport type SlackSocketModeIngressConfig = SlackDispatcherEventConfig & {\n appToken: string;\n agentId?: string;\n appId?: string;\n callbackUri?: string;\n};\n\nexport type SlackSocketModeIngressHandle = {\n startPromise: Promise<void>;\n close(): Promise<void>;\n};\n\nexport type SlackSocketModeDependencies = {\n fetchImpl?: typeof fetch;\n createWebSocket?(url: string): WebSocket;\n reconnectDelayMs?: number;\n log?(message: string): void;\n logError?(message: string, error?: unknown): void;\n};\n\ntype SlackConnectionsOpenResponse = {\n ok?: boolean;\n url?: string;\n error?: string;\n needed?: string;\n provided?: string;\n};\n\nfunction rawDataToString(data: RawData): string {\n if (typeof data === \"string\") return data;\n if (Buffer.isBuffer(data)) return data.toString(\"utf8\");\n if (Array.isArray(data)) return Buffer.concat(data).toString(\"utf8\");\n if (data instanceof ArrayBuffer) return Buffer.from(data).toString(\"utf8\");\n return Buffer.from(data).toString(\"utf8\");\n}\n\nasync function openSlackSocketUrl(input: { appToken: string; fetchImpl: typeof fetch }): Promise<string> {\n const response = await input.fetchImpl(SLACK_CONNECTIONS_OPEN_URL, {\n method: \"POST\",\n headers: {\n authorization: `Bearer ${input.appToken}`\n }\n });\n const body = (await response.json().catch(() => ({}))) as SlackConnectionsOpenResponse;\n if (!response.ok || !body.ok || !body.url) {\n const reason = body.error ?? `http_${response.status}`;\n throw new Error(`Slack Socket Mode connection failed: ${reason}`);\n }\n return body.url;\n}\n\nfunction parseSocketEnvelope(data: RawData): SlackSocketModeEnvelope | null {\n try {\n return JSON.parse(rawDataToString(data)) as SlackSocketModeEnvelope;\n } catch {\n return null;\n }\n}\n\nasync function handleSocketMessage(input: {\n data: RawData;\n socket: WebSocket;\n processor: ReturnType<typeof createSlackEventProcessor>;\n slackApp: SlackAppRuntimeConfig;\n logError(message: string, error?: unknown): void;\n}): Promise<void> {\n const envelope = parseSocketEnvelope(input.data);\n if (!envelope?.envelope_id) {\n input.logError(\"[slack] ignored Socket Mode envelope without envelope_id\");\n return;\n }\n\n input.socket.send(JSON.stringify({ envelope_id: envelope.envelope_id }));\n\n if (!envelope.payload) {\n return;\n }\n if (input.slackApp.appId && envelope.payload.api_app_id && envelope.payload.api_app_id !== input.slackApp.appId) {\n return;\n }\n if (envelope.type !== \"events_api\" && envelope.payload.type !== \"block_actions\") {\n return;\n }\n\n await input.processor.process(envelope.payload, input.slackApp);\n}\n\nfunction wait(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport function startSlackSocketModeApp(\n input: SlackSocketModeAppInput,\n dependencies: SlackSocketModeDependencies = {}\n): SlackSocketModeIngressHandle {\n const fetchImpl = dependencies.fetchImpl ?? fetch;\n const createWebSocket = dependencies.createWebSocket ?? ((url: string) => new WebSocket(url));\n const reconnectDelayMs = dependencies.reconnectDelayMs ?? DEFAULT_RECONNECT_DELAY_MS;\n const log = dependencies.log ?? ((message: string) => console.log(message));\n const logError = dependencies.logError ?? ((message: string, error?: unknown) => (error ? console.error(message, error) : console.error(message)));\n const processor = createSlackEventProcessor(input);\n let closed = false;\n let activeSocket: WebSocket | undefined;\n\n async function runOneConnection(socketUrl: string): Promise<void> {\n await new Promise<void>((resolve) => {\n const socket = createWebSocket(socketUrl);\n activeSocket = socket;\n let settled = false;\n const finish = () => {\n if (settled) return;\n settled = true;\n if (activeSocket === socket) activeSocket = undefined;\n resolve();\n };\n\n socket.once(\"open\", () => {\n log(\"[slack] Socket Mode connected\");\n });\n socket.on(\"message\", (data) => {\n void handleSocketMessage({\n data,\n socket,\n processor,\n slackApp: input.slackApp,\n logError\n }).catch((error: unknown) => {\n logError(\"[slack] failed to handle Socket Mode event:\", error);\n });\n });\n socket.once(\"close\", finish);\n socket.once(\"error\", (error) => {\n if (!closed) {\n logError(\"[slack] Socket Mode connection error:\", error);\n }\n socket.close();\n finish();\n });\n });\n }\n\n const startPromise = (async () => {\n while (!closed) {\n try {\n const socketUrl = await openSlackSocketUrl({ appToken: input.appToken, fetchImpl });\n await runOneConnection(socketUrl);\n } catch (error) {\n // A terminal auth/config error (invalid app token, missing scope, etc.)\n // will never succeed on retry, so we must NOT swallow it: rethrow so\n // startPromise rejects and startup fails loudly instead of looping\n // forever against Slack's API.\n if (isTerminalSlackAuthError(error)) {\n if (!closed) {\n logError(\"[slack] terminal Socket Mode auth/config error, aborting:\", error);\n }\n throw error;\n }\n // A transient apps.connections.open failure (or any error opening the\n // connection) must NOT reject startPromise: the CLI aborts the entire\n // OpenTag daemon when this promise rejects, so one blip in Slack's API\n // would otherwise take down every other ingress and the dispatcher.\n // Log it and fall through to the shared backoff/retry below.\n if (!closed) {\n logError(\"[slack] failed to open Socket Mode connection, retrying:\", error);\n }\n }\n if (!closed) {\n await wait(reconnectDelayMs);\n }\n }\n })();\n\n return {\n startPromise,\n async close() {\n closed = true;\n activeSocket?.close();\n await startPromise.catch(() => undefined);\n }\n };\n}\n\nexport function startSlackSocketModeIngress(\n config: SlackSocketModeIngressConfig,\n dependencies: SlackSocketModeDependencies = {}\n): SlackSocketModeIngressHandle {\n return startSlackSocketModeApp(\n {\n appToken: config.appToken,\n slackApp: {\n agentId: config.agentId ?? \"opentag\",\n ...(config.appId ? { appId: config.appId } : {}),\n ...(config.callbackUri ? { callbackUri: config.callbackUri } : {})\n },\n ...createSlackDispatcherEventProcessorInput(config)\n },\n dependencies\n );\n}\n"],"mappings":"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","names":["text","text"]}
|
package/dist/ingress.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ingress.d.ts","sourceRoot":"","sources":["../src/ingress.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,EAA6B,KAAK,qBAAqB,
|
|
1
|
+
{"version":3,"file":"ingress.d.ts","sourceRoot":"","sources":["../src/ingress.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,EAA6B,KAAK,qBAAqB,EAAE,KAAK,wBAAwB,EAA4B,MAAM,aAAa,CAAC;AAE7I,MAAM,MAAM,mBAAmB,GAAG;IAChC,SAAS,EAAE,KAAK,CACd,qBAAqB,GAAG;QACtB,aAAa,EAAE,MAAM,CAAC;KACvB,CACF,CAAC;IACF,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC;CACtB,GAAG,wBAAwB,CAAC;AAE7B,MAAM,MAAM,2BAA2B,GAAG;IACxC,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG,2BAA2B,CAAC;AAE7D,MAAM,MAAM,kBAAkB,GAAG;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,UAAU,CAAC,OAAO,KAAK,CAAC,CAAC;IACjC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,KAAK,EAAE;IAC3C,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB,GAAG,MAAM,CAIT;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE;IAC1C,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAKV;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAMpH;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,mBAAmB,8EAuE9D;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,2BAA2B,GAAG,kBAAkB,CAgCzF"}
|
package/dist/normalize.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../src/normalize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgE,KAAK,YAAY,EAAwB,MAAM,eAAe,CAAC;AAEtI,MAAM,MAAM,mBAAmB,GAAG;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,mBAAmB,CAAC;CAC9B,CAAC;
|
|
1
|
+
{"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../src/normalize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgE,KAAK,YAAY,EAAwB,MAAM,eAAe,CAAC;AAEtI,MAAM,MAAM,mBAAmB,GAAG;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,mBAAmB,CAAC;CAC9B,CAAC;AASF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAyBpF;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAE3G;AAED,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAM9G;AA0FD,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,oBAAoB,GAAG,YAAY,GAAG,IAAI,CA+DzF"}
|
package/dist/render.d.ts
CHANGED
|
@@ -9,7 +9,29 @@ export type SlackTextBlock = {
|
|
|
9
9
|
export type SlackDividerBlock = {
|
|
10
10
|
type: "divider";
|
|
11
11
|
};
|
|
12
|
-
export type
|
|
12
|
+
export type SlackButtonElement = {
|
|
13
|
+
type: "button";
|
|
14
|
+
text: {
|
|
15
|
+
type: "plain_text";
|
|
16
|
+
text: string;
|
|
17
|
+
emoji?: boolean;
|
|
18
|
+
};
|
|
19
|
+
action_id: string;
|
|
20
|
+
value: string;
|
|
21
|
+
style?: "primary" | "danger";
|
|
22
|
+
};
|
|
23
|
+
export type SlackActionsBlock = {
|
|
24
|
+
type: "actions";
|
|
25
|
+
block_id?: string;
|
|
26
|
+
elements: SlackButtonElement[];
|
|
27
|
+
};
|
|
28
|
+
export type SlackBlock = SlackTextBlock | SlackDividerBlock | SlackActionsBlock;
|
|
29
|
+
export type SlackSuggestedActionButtonValue = {
|
|
30
|
+
version: 1;
|
|
31
|
+
command: string;
|
|
32
|
+
proposalId: string;
|
|
33
|
+
intentId: string;
|
|
34
|
+
};
|
|
13
35
|
export type SlackMessagePayload = {
|
|
14
36
|
channel: string;
|
|
15
37
|
text: string;
|
|
@@ -17,8 +39,22 @@ export type SlackMessagePayload = {
|
|
|
17
39
|
ts?: string;
|
|
18
40
|
blocks?: SlackBlock[];
|
|
19
41
|
};
|
|
42
|
+
export type SlackReactionPayload = {
|
|
43
|
+
channel: string;
|
|
44
|
+
timestamp: string;
|
|
45
|
+
name: string;
|
|
46
|
+
};
|
|
47
|
+
export type SlackSourceReceiptState = "received";
|
|
48
|
+
export declare function buildSlackSuggestedActionButtonValue(input: SlackSuggestedActionButtonValue): string;
|
|
49
|
+
export declare function parseSlackSuggestedActionButtonValue(value: string): SlackSuggestedActionButtonValue | null;
|
|
20
50
|
export declare function markdownToSlackMrkdwn(text: string): string;
|
|
21
51
|
export declare function renderSlackAcknowledgement(runId: string): string;
|
|
52
|
+
export declare function slackSourceReceiptReactionName(state: SlackSourceReceiptState): string;
|
|
53
|
+
export declare function createSlackReactionPayload(input: {
|
|
54
|
+
channelId: string;
|
|
55
|
+
messageTs: string;
|
|
56
|
+
name: string;
|
|
57
|
+
}): SlackReactionPayload;
|
|
22
58
|
export declare function renderSlackFinalResult(result: OpenTagRunResult): string;
|
|
23
59
|
export declare function createSlackFinalResultBlocks(result: OpenTagRunResult): SlackBlock[];
|
|
24
60
|
export declare function createSlackPostMessagePayload(input: {
|
package/dist/render.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuC,KAAK,gBAAgB,
|
|
1
|
+
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"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"}
|
package/dist/socket-mode.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import WebSocket from "ws";
|
|
2
2
|
import { type SlackDispatcherEventConfig } from "./dispatcher-events.js";
|
|
3
|
-
import { type SlackAppRuntimeConfig, type
|
|
3
|
+
import { type SlackAppRuntimeConfig, type SlackEventProcessorInput, type SlackIngressPayload } from "./events.js";
|
|
4
4
|
export type SlackSocketModeEnvelope = {
|
|
5
5
|
type?: string;
|
|
6
6
|
envelope_id?: string;
|
|
7
|
-
payload?:
|
|
7
|
+
payload?: SlackIngressPayload;
|
|
8
8
|
accepts_response_payload?: boolean;
|
|
9
9
|
};
|
|
10
10
|
export type SlackSocketModeAppInput = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"socket-mode.d.ts","sourceRoot":"","sources":["../src/socket-mode.ts"],"names":[],"mappings":"AAAA,OAAO,SAA2B,MAAM,IAAI,CAAC;AAC7C,OAAO,EAA4C,KAAK,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AACnH,OAAO,EAA6B,KAAK,qBAAqB,EAAE,KAAK,
|
|
1
|
+
{"version":3,"file":"socket-mode.d.ts","sourceRoot":"","sources":["../src/socket-mode.ts"],"names":[],"mappings":"AAAA,OAAO,SAA2B,MAAM,IAAI,CAAC;AAC7C,OAAO,EAA4C,KAAK,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AACnH,OAAO,EAA6B,KAAK,qBAAqB,EAAE,KAAK,wBAAwB,EAAE,KAAK,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAkC7I,MAAM,MAAM,uBAAuB,GAAG;IACpC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,wBAAwB,CAAC,EAAE,OAAO,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,qBAAqB,CAAC;CACjC,GAAG,wBAAwB,CAAC;AAE7B,MAAM,MAAM,4BAA4B,GAAG,0BAA0B,GAAG;IACtE,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IACzC,YAAY,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACxC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,eAAe,CAAC,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,GAAG,CAAC,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;CACnD,CAAC;AAyEF,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,uBAAuB,EAC9B,YAAY,GAAE,2BAAgC,GAC7C,4BAA4B,CAsF9B;AAED,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,4BAA4B,EACpC,YAAY,GAAE,2BAAgC,GAC7C,4BAA4B,CAa9B"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opentag/slack",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Slack app mention normalization and callback helpers for OpenTag.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
"@hono/node-server": "^1.13.7",
|
|
35
35
|
"hono": "^4.6.15",
|
|
36
36
|
"ws": "^8.21.0",
|
|
37
|
-
"@opentag/client": "0.
|
|
38
|
-
"@opentag/core": "0.
|
|
37
|
+
"@opentag/client": "0.3.0",
|
|
38
|
+
"@opentag/core": "0.3.0"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@types/ws": "^8.18.1",
|