@probelabs/visor 0.1.155-ee → 0.1.156-ee

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/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
- process.env.VISOR_VERSION = '0.1.155';
2
+ process.env.VISOR_VERSION = '0.1.156';
3
3
  process.env.PROBE_VERSION = '0.6.0-rc271';
4
- process.env.VISOR_COMMIT_SHA = '37220799c91e64df29a72348ad88a26bd94f6214';
5
- process.env.VISOR_COMMIT_SHORT = '3722079';
4
+ process.env.VISOR_COMMIT_SHA = '73be999d91c7100356dd411f4d37327963e9953c';
5
+ process.env.VISOR_COMMIT_SHORT = '73be999';
6
6
  /******/ (() => { // webpackBootstrap
7
7
  /******/ var __webpack_modules__ = ({
8
8
 
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  CheckProviderRegistry,
3
3
  init_check_provider_registry
4
- } from "./chunk-WPF7PJ64.mjs";
4
+ } from "./chunk-PLMM6W3K.mjs";
5
5
  import "./chunk-KFKHU6CM.mjs";
6
6
  import "./chunk-M3BYMES6.mjs";
7
7
  import "./chunk-LG4AUKHB.mjs";
@@ -26,4 +26,4 @@ init_check_provider_registry();
26
26
  export {
27
27
  CheckProviderRegistry
28
28
  };
29
- //# sourceMappingURL=check-provider-registry-PK3OTEX6.mjs.map
29
+ //# sourceMappingURL=check-provider-registry-3O2PAJPF.mjs.map
@@ -3959,7 +3959,7 @@ async function executeWorkflowAsTool(workflowId, args, context2, argsOverrides)
3959
3959
  ...args,
3960
3960
  ...argsOverrides
3961
3961
  };
3962
- const { WorkflowCheckProvider: WorkflowCheckProvider2 } = await import("./workflow-check-provider-HGFTX64I.mjs");
3962
+ const { WorkflowCheckProvider: WorkflowCheckProvider2 } = await import("./workflow-check-provider-4DC4KOHI.mjs");
3963
3963
  const provider = new WorkflowCheckProvider2();
3964
3964
  const checkConfig = {
3965
3965
  type: "workflow",
@@ -13027,7 +13027,7 @@ var init_state_machine_execution_engine = __esm({
13027
13027
  try {
13028
13028
  const map = options?.webhookContext?.webhookData;
13029
13029
  if (map) {
13030
- const { CheckProviderRegistry: CheckProviderRegistry2 } = await import("./check-provider-registry-PK3OTEX6.mjs");
13030
+ const { CheckProviderRegistry: CheckProviderRegistry2 } = await import("./check-provider-registry-3O2PAJPF.mjs");
13031
13031
  const reg = CheckProviderRegistry2.getInstance();
13032
13032
  const p = reg.getProvider("http_input");
13033
13033
  if (p && typeof p.setWebhookContext === "function") p.setWebhookContext(map);
@@ -13150,7 +13150,7 @@ var init_state_machine_execution_engine = __esm({
13150
13150
  tag_filter: tagFilter
13151
13151
  } : config;
13152
13152
  try {
13153
- const { CheckProviderRegistry: CheckProviderRegistry2 } = await import("./check-provider-registry-PK3OTEX6.mjs");
13153
+ const { CheckProviderRegistry: CheckProviderRegistry2 } = await import("./check-provider-registry-3O2PAJPF.mjs");
13154
13154
  const registry = CheckProviderRegistry2.getInstance();
13155
13155
  registry.setCustomTools(configWithTagFilter.tools || {});
13156
13156
  } catch (error) {
@@ -13214,7 +13214,7 @@ var init_state_machine_execution_engine = __esm({
13214
13214
  try {
13215
13215
  const webhookData = this.executionContext?.webhookContext?.webhookData;
13216
13216
  if (webhookData instanceof Map) {
13217
- const { extractSlackContext: extractSlackContext2 } = await import("./schedule-tool-handler-KXZC4ZOR.mjs");
13217
+ const { extractSlackContext: extractSlackContext2 } = await import("./schedule-tool-handler-O7T66M3I.mjs");
13218
13218
  const slackCtx = extractSlackContext2(webhookData);
13219
13219
  if (slackCtx) {
13220
13220
  const payload = Array.from(webhookData.values())[0];
@@ -13243,7 +13243,7 @@ var init_state_machine_execution_engine = __esm({
13243
13243
  if (Array.isArray(configWithTagFilter.frontends) && configWithTagFilter.frontends.length > 0) {
13244
13244
  try {
13245
13245
  const { EventBus } = await import("./event-bus-5K3Y2FCS.mjs");
13246
- const { FrontendsHost } = await import("./host-6DJCOUJE.mjs");
13246
+ const { FrontendsHost } = await import("./host-4ONWAJ6Q.mjs");
13247
13247
  const bus = new EventBus();
13248
13248
  context2.eventBus = bus;
13249
13249
  frontendsHost = new FrontendsHost(bus, logger);
@@ -40722,8 +40722,8 @@ function buildBuiltinGlobals(opts) {
40722
40722
  const asyncFunctionNames = /* @__PURE__ */ new Set();
40723
40723
  const scheduleFn = async (args = {}) => {
40724
40724
  try {
40725
- const { handleScheduleAction: handleScheduleAction2, buildScheduleToolContext: buildScheduleToolContext2 } = await import("./schedule-tool-HRFONU5J.mjs");
40726
- const { extractSlackContext: extractSlackContext2 } = await import("./schedule-tool-handler-KXZC4ZOR.mjs");
40725
+ const { handleScheduleAction: handleScheduleAction2, buildScheduleToolContext: buildScheduleToolContext2 } = await import("./schedule-tool-EZVQOF55.mjs");
40726
+ const { extractSlackContext: extractSlackContext2 } = await import("./schedule-tool-handler-O7T66M3I.mjs");
40727
40727
  const parentCtx = opts.sessionInfo?._parentContext;
40728
40728
  const webhookData = parentCtx?.prInfo?.eventContext?.webhookData;
40729
40729
  const visorCfg = parentCtx?.config;
@@ -43771,4 +43771,4 @@ undici/lib/fetch/body.js:
43771
43771
  undici/lib/websocket/frame.js:
43772
43772
  (*! ws. MIT License. Einar Otto Stangvik <einaros@gmail.com> *)
43773
43773
  */
43774
- //# sourceMappingURL=chunk-WPF7PJ64.mjs.map
43774
+ //# sourceMappingURL=chunk-PLMM6W3K.mjs.map
@@ -24,7 +24,7 @@ var init_host = __esm({
24
24
  const { GitHubFrontend } = await import("./github-frontend-XKPAYXOT.mjs");
25
25
  this.frontends.push(new GitHubFrontend());
26
26
  } else if (spec.name === "slack") {
27
- const { SlackFrontend } = await import("./slack-frontend-TZU2HIK7.mjs");
27
+ const { SlackFrontend } = await import("./slack-frontend-6KDUMDDI.mjs");
28
28
  this.frontends.push(new SlackFrontend(spec.config));
29
29
  } else if (spec.name === "tui") {
30
30
  const { TuiFrontend } = await import("./tui-frontend-T56PZB67.mjs");
@@ -60,4 +60,4 @@ init_host();
60
60
  export {
61
61
  FrontendsHost
62
62
  };
63
- //# sourceMappingURL=host-6DJCOUJE.mjs.map
63
+ //# sourceMappingURL=host-4ONWAJ6Q.mjs.map
@@ -4,7 +4,7 @@ import {
4
4
  handleScheduleAction,
5
5
  init_schedule_tool,
6
6
  isScheduleTool
7
- } from "./chunk-WPF7PJ64.mjs";
7
+ } from "./chunk-PLMM6W3K.mjs";
8
8
  import "./chunk-KFKHU6CM.mjs";
9
9
  import "./chunk-M3BYMES6.mjs";
10
10
  import "./chunk-LG4AUKHB.mjs";
@@ -32,4 +32,4 @@ export {
32
32
  handleScheduleAction,
33
33
  isScheduleTool
34
34
  };
35
- //# sourceMappingURL=schedule-tool-HRFONU5J.mjs.map
35
+ //# sourceMappingURL=schedule-tool-EZVQOF55.mjs.map
@@ -6,7 +6,7 @@ import {
6
6
  extractSlackContext,
7
7
  init_schedule_tool_handler,
8
8
  isScheduleToolCall
9
- } from "./chunk-WPF7PJ64.mjs";
9
+ } from "./chunk-PLMM6W3K.mjs";
10
10
  import "./chunk-KFKHU6CM.mjs";
11
11
  import "./chunk-M3BYMES6.mjs";
12
12
  import "./chunk-LG4AUKHB.mjs";
@@ -36,4 +36,4 @@ export {
36
36
  extractSlackContext,
37
37
  isScheduleToolCall
38
38
  };
39
- //# sourceMappingURL=schedule-tool-handler-KXZC4ZOR.mjs.map
39
+ //# sourceMappingURL=schedule-tool-handler-O7T66M3I.mjs.map
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  WorkflowCheckProvider,
3
3
  init_workflow_check_provider
4
- } from "./chunk-WPF7PJ64.mjs";
4
+ } from "./chunk-PLMM6W3K.mjs";
5
5
  import "./chunk-KFKHU6CM.mjs";
6
6
  import "./chunk-M3BYMES6.mjs";
7
7
  import "./chunk-LG4AUKHB.mjs";
@@ -26,4 +26,4 @@ init_workflow_check_provider();
26
26
  export {
27
27
  WorkflowCheckProvider
28
28
  };
29
- //# sourceMappingURL=workflow-check-provider-HGFTX64I.mjs.map
29
+ //# sourceMappingURL=workflow-check-provider-4DC4KOHI.mjs.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@probelabs/visor",
3
- "version": "0.1.155-ee",
3
+ "version": "0.1.156-ee",
4
4
  "main": "dist/index.js",
5
5
  "bin": {
6
6
  "visor": "./dist/index.js"
@@ -1,875 +0,0 @@
1
- import {
2
- extractFileSections,
3
- extractMermaidDiagrams,
4
- formatSlackText,
5
- init_markdown,
6
- renderMermaidToPng,
7
- replaceFileSections,
8
- replaceMermaidBlocks
9
- } from "./chunk-M3BYMES6.mjs";
10
- import {
11
- context,
12
- init_lazy_otel,
13
- trace
14
- } from "./chunk-UCMJJ3IM.mjs";
15
- import {
16
- __esm
17
- } from "./chunk-J7LXIPZS.mjs";
18
-
19
- // src/slack/client.ts
20
- var SlackClient;
21
- var init_client = __esm({
22
- "src/slack/client.ts"() {
23
- "use strict";
24
- SlackClient = class {
25
- token;
26
- constructor(botToken) {
27
- if (!botToken || typeof botToken !== "string") {
28
- throw new Error("SlackClient: botToken is required");
29
- }
30
- this.token = botToken;
31
- }
32
- reactions = {
33
- add: async ({
34
- channel,
35
- timestamp,
36
- name
37
- }) => {
38
- const resp = await this.api("reactions.add", { channel, timestamp, name });
39
- if (!resp || resp.ok !== true) {
40
- const err = resp && resp.error || "unknown_error";
41
- console.warn(`Slack reactions.add failed (non-fatal): ${err}`);
42
- return { ok: false };
43
- }
44
- return { ok: true };
45
- },
46
- remove: async ({
47
- channel,
48
- timestamp,
49
- name
50
- }) => {
51
- const resp = await this.api("reactions.remove", { channel, timestamp, name });
52
- if (!resp || resp.ok !== true) {
53
- const err = resp && resp.error || "unknown_error";
54
- console.warn(`Slack reactions.remove failed (non-fatal): ${err}`);
55
- return { ok: false };
56
- }
57
- return { ok: true };
58
- }
59
- };
60
- chat = {
61
- postMessage: async ({
62
- channel,
63
- text,
64
- thread_ts
65
- }) => {
66
- try {
67
- const resp = await this.api("chat.postMessage", { channel, text, thread_ts });
68
- if (!resp || resp.ok !== true) {
69
- const err = resp && resp.error || "unknown_error";
70
- const warnings = Array.isArray(resp?.response_metadata?.warnings) ? resp.response_metadata.warnings.join(",") : "";
71
- console.warn(
72
- `Slack chat.postMessage failed (non-fatal): error=${err} channel=${channel} thread_ts=${thread_ts || "-"} text_len=${text.length}${warnings ? ` warnings=${warnings}` : ""}`
73
- );
74
- return {
75
- ok: false,
76
- ts: void 0,
77
- message: void 0,
78
- data: resp,
79
- error: err
80
- };
81
- }
82
- return {
83
- ok: true,
84
- ts: resp.ts || resp.message && resp.message.ts || void 0,
85
- message: resp.message,
86
- data: resp,
87
- error: void 0
88
- };
89
- } catch (e) {
90
- console.warn(
91
- `Slack chat.postMessage threw (non-fatal): channel=${channel} thread_ts=${thread_ts || "-"} text_len=${text.length} error=${e instanceof Error ? e.message : String(e)}`
92
- );
93
- return {
94
- ok: false,
95
- ts: void 0,
96
- message: void 0,
97
- data: void 0,
98
- error: e instanceof Error ? e.message : String(e)
99
- };
100
- }
101
- },
102
- update: async ({ channel, ts, text }) => {
103
- try {
104
- const resp = await this.api("chat.update", { channel, ts, text });
105
- if (!resp || resp.ok !== true) {
106
- const err = resp && resp.error || "unknown_error";
107
- const warnings = Array.isArray(resp?.response_metadata?.warnings) ? resp.response_metadata.warnings.join(",") : "";
108
- console.warn(
109
- `Slack chat.update failed (non-fatal): error=${err} channel=${channel} ts=${ts} text_len=${text.length}${warnings ? ` warnings=${warnings}` : ""}`
110
- );
111
- return { ok: false, ts, error: err, data: resp };
112
- }
113
- return { ok: true, ts: resp.ts || ts, error: void 0, data: resp };
114
- } catch (e) {
115
- console.warn(
116
- `Slack chat.update threw (non-fatal): channel=${channel} ts=${ts} text_len=${text.length} error=${e instanceof Error ? e.message : String(e)}`
117
- );
118
- return {
119
- ok: false,
120
- ts,
121
- error: e instanceof Error ? e.message : String(e),
122
- data: void 0
123
- };
124
- }
125
- }
126
- };
127
- async getBotUserId() {
128
- const resp = await this.api("auth.test", {});
129
- if (!resp || resp.ok !== true || !resp.user_id) {
130
- console.warn("Slack auth.test failed (non-fatal); bot user id unavailable");
131
- return "UNKNOWN_BOT";
132
- }
133
- return String(resp.user_id);
134
- }
135
- /**
136
- * Fetch user info from Slack API.
137
- * Returns user profile including guest status flags, email, display name, and timezone.
138
- */
139
- async getUserInfo(userId) {
140
- try {
141
- const resp = await this.api("users.info", { user: userId });
142
- if (!resp || resp.ok !== true || !resp.user) {
143
- return { ok: false };
144
- }
145
- return {
146
- ok: true,
147
- user: {
148
- id: resp.user.id,
149
- name: resp.user.name,
150
- real_name: resp.user.real_name || resp.user.profile?.real_name,
151
- email: resp.user.profile?.email,
152
- is_restricted: resp.user.is_restricted,
153
- is_ultra_restricted: resp.user.is_ultra_restricted,
154
- is_bot: resp.user.is_bot,
155
- is_app_user: resp.user.is_app_user,
156
- deleted: resp.user.deleted,
157
- tz: resp.user.tz,
158
- tz_offset: resp.user.tz_offset
159
- }
160
- };
161
- } catch (e) {
162
- console.warn(`Slack users.info failed: ${e instanceof Error ? e.message : String(e)}`);
163
- return { ok: false };
164
- }
165
- }
166
- /**
167
- * Open a DM channel with a user.
168
- * Returns the DM channel ID.
169
- */
170
- async openDM(userId) {
171
- try {
172
- const resp = await this.api("conversations.open", { users: userId });
173
- if (!resp || resp.ok !== true || !resp.channel?.id) {
174
- console.warn(`Slack conversations.open failed: ${resp?.error || "unknown_error"}`);
175
- return { ok: false };
176
- }
177
- return { ok: true, channel: resp.channel.id };
178
- } catch (e) {
179
- console.warn(
180
- `Slack conversations.open failed: ${e instanceof Error ? e.message : String(e)}`
181
- );
182
- return { ok: false };
183
- }
184
- }
185
- async fetchThreadReplies(channel, thread_ts, limit = 40) {
186
- try {
187
- const params = new URLSearchParams({
188
- channel,
189
- ts: thread_ts,
190
- limit: String(limit)
191
- });
192
- const res = await fetch(`https://slack.com/api/conversations.replies?${params.toString()}`, {
193
- method: "GET",
194
- headers: {
195
- Authorization: `Bearer ${this.token}`
196
- }
197
- });
198
- const resp = await res.json();
199
- if (!resp || resp.ok !== true || !Array.isArray(resp.messages)) {
200
- const err = resp && resp.error || "unknown_error";
201
- console.warn(
202
- `Slack conversations.replies failed (non-fatal): ${err} (channel=${channel}, ts=${thread_ts}, limit=${limit})`
203
- );
204
- return [];
205
- }
206
- return resp.messages.map((m) => ({
207
- ts: String(m.ts || ""),
208
- user: m.user,
209
- text: m.text,
210
- bot_id: m.bot_id,
211
- thread_ts: m.thread_ts,
212
- files: Array.isArray(m.files) ? m.files : void 0
213
- }));
214
- } catch (e) {
215
- console.warn(
216
- `Slack conversations.replies failed (non-fatal): ${e instanceof Error ? e.message : String(e)} (channel=${channel}, ts=${thread_ts}, limit=${limit})`
217
- );
218
- return [];
219
- }
220
- }
221
- files = {
222
- /**
223
- * Upload a file to Slack using files.uploadV2 API
224
- * @param options Upload options including file content, filename, channel, and thread_ts
225
- */
226
- uploadV2: async ({
227
- content,
228
- filename,
229
- channel,
230
- thread_ts,
231
- title,
232
- initial_comment
233
- }) => {
234
- try {
235
- const getUrlResp = await this.apiForm("files.getUploadURLExternal", {
236
- filename,
237
- length: String(content.length)
238
- });
239
- if (!getUrlResp || getUrlResp.ok !== true || !getUrlResp.upload_url) {
240
- console.warn(
241
- `Slack files.getUploadURLExternal failed: ${getUrlResp?.error || "unknown"}`
242
- );
243
- return { ok: false };
244
- }
245
- const uploadResp = await fetch(getUrlResp.upload_url, {
246
- method: "POST",
247
- body: content
248
- });
249
- if (!uploadResp.ok) {
250
- console.warn(`Slack file upload to URL failed: ${uploadResp.status}`);
251
- return { ok: false };
252
- }
253
- const completeResp = await this.api("files.completeUploadExternal", {
254
- files: [{ id: getUrlResp.file_id, title: title || filename }],
255
- channel_id: channel,
256
- thread_ts,
257
- initial_comment
258
- });
259
- if (!completeResp || completeResp.ok !== true) {
260
- console.warn(
261
- `Slack files.completeUploadExternal failed: ${completeResp?.error || "unknown"}`
262
- );
263
- return { ok: false };
264
- }
265
- return {
266
- ok: true,
267
- file: completeResp.files?.[0] || { id: getUrlResp.file_id }
268
- };
269
- } catch (e) {
270
- console.warn(`Slack file upload failed: ${e instanceof Error ? e.message : String(e)}`);
271
- return { ok: false };
272
- }
273
- }
274
- };
275
- getWebClient() {
276
- return {
277
- conversations: {
278
- history: async ({ channel, limit }) => await this.api("conversations.history", { channel, limit }),
279
- open: async ({ users }) => await this.api("conversations.open", { users }),
280
- replies: async ({ channel, ts, limit }) => await this.api("conversations.replies", { channel, ts, limit })
281
- }
282
- };
283
- }
284
- async api(method, body) {
285
- const res = await fetch(`https://slack.com/api/${method}`, {
286
- method: "POST",
287
- headers: {
288
- "Content-Type": "application/json; charset=utf-8",
289
- Authorization: `Bearer ${this.token}`
290
- },
291
- body: JSON.stringify(body)
292
- });
293
- return await res.json();
294
- }
295
- /** Send a Slack API request as application/x-www-form-urlencoded (required by some file methods). */
296
- async apiForm(method, params) {
297
- const body = new URLSearchParams(params);
298
- const res = await fetch(`https://slack.com/api/${method}`, {
299
- method: "POST",
300
- headers: {
301
- "Content-Type": "application/x-www-form-urlencoded",
302
- Authorization: `Bearer ${this.token}`
303
- },
304
- body: body.toString()
305
- });
306
- return await res.json();
307
- }
308
- };
309
- }
310
- });
311
-
312
- // src/frontends/slack-frontend.ts
313
- var SlackFrontend;
314
- var init_slack_frontend = __esm({
315
- "src/frontends/slack-frontend.ts"() {
316
- init_client();
317
- init_markdown();
318
- init_lazy_otel();
319
- SlackFrontend = class {
320
- name = "slack";
321
- subs = [];
322
- cfg;
323
- // Reactions ack/done per run (inbound Slack events only)
324
- acked = false;
325
- ackRef = null;
326
- ackName = "eyes";
327
- doneName = "thumbsup";
328
- errorNotified = false;
329
- cachedTraceInfo = null;
330
- constructor(config) {
331
- this.cfg = config || {};
332
- }
333
- start(ctx) {
334
- const bus = ctx.eventBus;
335
- if (!this.cachedTraceInfo) {
336
- this.cachedTraceInfo = this.getTraceInfo();
337
- }
338
- if (!this.cachedTraceInfo) {
339
- const runTraceId = ctx?.run?.traceId;
340
- if (typeof runTraceId === "string" && runTraceId) {
341
- this.cachedTraceInfo = { traceId: runTraceId, spanId: "" };
342
- }
343
- }
344
- try {
345
- const hasClient = !!(ctx.slack || ctx.slackClient || this.cfg?.botToken || process.env.SLACK_BOT_TOKEN);
346
- ctx.logger.info(`[slack-frontend] started; hasClient=${hasClient} defaultChannel=unset`);
347
- } catch {
348
- }
349
- try {
350
- const payload = this.getInboundSlackPayload(ctx);
351
- if (payload) {
352
- const ev = payload.event || {};
353
- const ch = String(ev.channel || "-");
354
- const ts = String(ev.ts || ev.event_ts || "-");
355
- const user = String(ev.user || ev.bot_id || "-");
356
- const type = String(ev.type || "-");
357
- const thread = String(ev.thread_ts || "");
358
- ctx.logger.info(
359
- `[slack-frontend] inbound event received: type=${type} channel=${ch} ts=${ts}` + (thread ? ` thread_ts=${thread}` : "") + ` user=${user}`
360
- );
361
- }
362
- } catch {
363
- }
364
- this.subs.push(
365
- bus.on("CheckCompleted", async (env) => {
366
- const ev = env && env.payload || env;
367
- await this.maybePostDirectReply(ctx, ev.checkId, ev.result).catch(() => {
368
- });
369
- await this.maybePostExecutionFailure(ctx, ev.checkId, ev.result).catch(() => {
370
- });
371
- })
372
- );
373
- this.subs.push(
374
- bus.on("CheckErrored", async (env) => {
375
- const ev = env && env.payload || env;
376
- const message = ev?.error?.message || "Execution error";
377
- await this.maybePostError(ctx, "Check failed", message, ev?.checkId).catch(() => {
378
- });
379
- })
380
- );
381
- this.subs.push(
382
- bus.on("StateTransition", async (env) => {
383
- const ev = env && env.payload || env;
384
- if (ev && (ev.to === "Completed" || ev.to === "Error")) {
385
- await this.finalizeReactions(ctx).catch(() => {
386
- });
387
- }
388
- })
389
- );
390
- this.subs.push(
391
- bus.on("Shutdown", async (env) => {
392
- const ev = env && env.payload || env;
393
- const message = ev?.error?.message || "Fatal error";
394
- await this.maybePostError(ctx, "Run failed", message).catch(() => {
395
- });
396
- })
397
- );
398
- this.subs.push(
399
- bus.on("CheckScheduled", async () => {
400
- await this.ensureAcknowledgement(ctx).catch(() => {
401
- });
402
- })
403
- );
404
- this.subs.push(
405
- bus.on("HumanInputRequested", async (env) => {
406
- try {
407
- const ev = env && env.payload || env;
408
- if (!ev || typeof ev.prompt !== "string" || !ev.checkId) return;
409
- let channel = ev.channel;
410
- let threadTs = ev.threadTs;
411
- if (!channel || !threadTs) {
412
- const payload = this.getInboundSlackPayload(ctx);
413
- const e = payload?.event;
414
- const derivedTs = String(e?.thread_ts || e?.ts || e?.event_ts || "");
415
- const derivedCh = String(e?.channel || "");
416
- if (derivedCh && derivedTs) {
417
- channel = channel || derivedCh;
418
- threadTs = threadTs || derivedTs;
419
- }
420
- }
421
- if (!channel || !threadTs) return;
422
- const { getPromptStateManager } = await import("./prompt-state-VAKKC773.mjs");
423
- const mgr = getPromptStateManager();
424
- const prev = mgr.getWaiting(channel, threadTs);
425
- const text = String(ev.prompt);
426
- mgr.setWaiting(channel, threadTs, {
427
- checkName: String(ev.checkId),
428
- prompt: text,
429
- promptMessageTs: prev?.promptMessageTs,
430
- promptsPosted: (prev?.promptsPosted || 0) + 1
431
- });
432
- try {
433
- ctx.logger.info(
434
- `[slack-frontend] registered human-input waiting state for ${channel} thread=${threadTs}`
435
- );
436
- } catch {
437
- }
438
- } catch (e) {
439
- try {
440
- ctx.logger.warn(
441
- `[slack-frontend] HumanInputRequested handling failed: ${e instanceof Error ? e.message : String(e)}`
442
- );
443
- } catch {
444
- }
445
- }
446
- })
447
- );
448
- this.subs.push(
449
- bus.on("SnapshotSaved", async (env) => {
450
- try {
451
- const ev = env && env.payload || env;
452
- const channel = String(ev?.channel || "");
453
- const threadTs = String(ev?.threadTs || "");
454
- const filePath = String(ev?.filePath || "");
455
- if (!channel || !threadTs || !filePath) return;
456
- const { getPromptStateManager } = await import("./prompt-state-VAKKC773.mjs");
457
- const mgr = getPromptStateManager();
458
- mgr.update(channel, threadTs, { snapshotPath: filePath });
459
- try {
460
- ctx.logger.info(
461
- `[slack-frontend] snapshot path attached to waiting prompt: ${filePath}`
462
- );
463
- } catch {
464
- }
465
- } catch {
466
- }
467
- })
468
- );
469
- }
470
- stop() {
471
- for (const s of this.subs) s.unsubscribe();
472
- this.subs = [];
473
- }
474
- getSlack(ctx) {
475
- const injected = ctx.slack || ctx.slackClient;
476
- if (injected) return injected;
477
- try {
478
- const token = this.cfg?.botToken || process.env.SLACK_BOT_TOKEN;
479
- if (typeof token === "string" && token.trim()) {
480
- return new SlackClient(token.trim());
481
- }
482
- } catch {
483
- }
484
- return void 0;
485
- }
486
- getInboundSlackPayload(ctx) {
487
- try {
488
- const anyCfg = ctx.config || {};
489
- const slackCfg = anyCfg.slack || {};
490
- const endpoint = slackCfg.endpoint || "/bots/slack/support";
491
- const payload = ctx.webhookContext?.webhookData?.get(endpoint);
492
- return payload || null;
493
- } catch {
494
- return null;
495
- }
496
- }
497
- getInboundSlackEvent(ctx) {
498
- try {
499
- const payload = this.getInboundSlackPayload(ctx);
500
- const ev = payload?.event;
501
- const channel = String(ev?.channel || "");
502
- const ts = String(ev?.ts || ev?.event_ts || "");
503
- if (channel && ts) return { channel, ts };
504
- } catch {
505
- }
506
- return null;
507
- }
508
- isTelemetryEnabled(ctx) {
509
- try {
510
- const anyCfg = ctx.config || {};
511
- const slackCfg = anyCfg.slack || {};
512
- const telemetryCfg = slackCfg.telemetry ?? this.cfg?.telemetry;
513
- return telemetryCfg === true || telemetryCfg && typeof telemetryCfg === "object" && telemetryCfg.enabled === true;
514
- } catch {
515
- return false;
516
- }
517
- }
518
- async maybePostError(ctx, title, message, checkId) {
519
- if (this.errorNotified) return;
520
- const slack = this.getSlack(ctx);
521
- if (!slack) return;
522
- const payload = this.getInboundSlackPayload(ctx);
523
- const ev = payload?.event;
524
- const channel = String(ev?.channel || "");
525
- const threadTs = String(ev?.thread_ts || ev?.ts || ev?.event_ts || "");
526
- if (!channel || !threadTs) {
527
- try {
528
- ctx.logger.warn(
529
- `[slack-frontend] skip posting error notice: missing channel/thread (channel=${channel || "-"} thread=${threadTs || "-"})`
530
- );
531
- } catch {
532
- }
533
- return;
534
- }
535
- let text = `\u274C ${title}`;
536
- if (checkId) text += `
537
- Check: ${checkId}`;
538
- if (message) text += `
539
- ${message}`;
540
- if (this.isTelemetryEnabled(ctx)) {
541
- const traceInfo = this.getTraceInfo() || this.cachedTraceInfo;
542
- if (traceInfo?.traceId) {
543
- text += `
544
-
545
- \`trace_id: ${traceInfo.traceId}\``;
546
- }
547
- }
548
- const formattedText = formatSlackText(text);
549
- const postResult = await slack.chat.postMessage({
550
- channel,
551
- text: formattedText,
552
- thread_ts: threadTs
553
- });
554
- if (!postResult?.ok) {
555
- try {
556
- ctx.logger.warn(
557
- `[slack-frontend] failed to post error notice to ${channel} thread=${threadTs} check=${checkId || "run"} error=${postResult?.error || "unknown_error"}`
558
- );
559
- } catch {
560
- }
561
- return;
562
- }
563
- try {
564
- ctx.logger.info(
565
- `[slack-frontend] posted error notice to ${channel} thread=${threadTs} check=${checkId || "run"}`
566
- );
567
- } catch {
568
- }
569
- this.errorNotified = true;
570
- }
571
- isExecutionFailureIssue(issue) {
572
- const ruleId = String(issue?.ruleId || "");
573
- const msg = String(issue?.message || "");
574
- const msgLower = msg.toLowerCase();
575
- return ruleId.endsWith("/error") || ruleId.includes("/execution_error") || ruleId.includes("timeout") || ruleId.includes("sandbox_runner_error") || msgLower.includes("timed out") || msg.includes("Command execution failed");
576
- }
577
- async maybePostExecutionFailure(ctx, checkId, result) {
578
- try {
579
- if (this.errorNotified) return;
580
- const cfg = ctx.config || {};
581
- const checkCfg = cfg.checks?.[checkId];
582
- if (!checkCfg) return;
583
- if (checkCfg.type === "human-input") return;
584
- if (checkCfg.criticality === "internal") return;
585
- const issues = result?.issues;
586
- if (!Array.isArray(issues) || issues.length === 0) return;
587
- const failureIssue = issues.find((issue) => this.isExecutionFailureIssue(issue));
588
- if (!failureIssue) return;
589
- if (typeof failureIssue.message === "string" && failureIssue.message.toLowerCase().includes("awaiting human input")) {
590
- return;
591
- }
592
- const msg = typeof failureIssue.message === "string" && failureIssue.message.trim().length > 0 ? failureIssue.message.trim() : `Execution failed (${String(failureIssue.ruleId || "unknown")})`;
593
- await this.maybePostError(ctx, "Check failed", msg, checkId);
594
- } catch {
595
- }
596
- }
597
- async ensureAcknowledgement(ctx) {
598
- if (this.acked) return;
599
- const ref = this.getInboundSlackEvent(ctx);
600
- if (!ref) return;
601
- const slack = this.getSlack(ctx);
602
- if (!slack) return;
603
- try {
604
- const payload = this.getInboundSlackPayload(ctx);
605
- const ev = payload?.event;
606
- try {
607
- const botId = await slack.getBotUserId?.();
608
- if (botId && ev?.user && String(ev.user) === String(botId)) return;
609
- } catch {
610
- }
611
- } catch {
612
- }
613
- try {
614
- const anyCfg = ctx.config || {};
615
- const slackCfg = anyCfg.slack || {};
616
- if (slackCfg?.reactions?.enabled === false) return;
617
- this.ackName = slackCfg?.reactions?.ack || this.ackName;
618
- this.doneName = slackCfg?.reactions?.done || this.doneName;
619
- } catch {
620
- }
621
- await slack.reactions.add({ channel: ref.channel, timestamp: ref.ts, name: this.ackName });
622
- try {
623
- ctx.logger.info(
624
- `[slack-frontend] added acknowledgement reaction :${this.ackName}: channel=${ref.channel} ts=${ref.ts}`
625
- );
626
- } catch {
627
- }
628
- this.acked = true;
629
- this.ackRef = ref;
630
- if (!this.cachedTraceInfo) {
631
- this.cachedTraceInfo = this.getTraceInfo();
632
- }
633
- }
634
- async finalizeReactions(ctx) {
635
- if (!this.acked || !this.ackRef) return;
636
- const slack = this.getSlack(ctx);
637
- if (!slack) return;
638
- try {
639
- try {
640
- await slack.reactions.remove({
641
- channel: this.ackRef.channel,
642
- timestamp: this.ackRef.ts,
643
- name: this.ackName
644
- });
645
- } catch {
646
- }
647
- await slack.reactions.add({
648
- channel: this.ackRef.channel,
649
- timestamp: this.ackRef.ts,
650
- name: this.doneName
651
- });
652
- try {
653
- ctx.logger.info(
654
- `[slack-frontend] replaced acknowledgement with completion reaction :${this.doneName}: channel=${this.ackRef.channel} ts=${this.ackRef.ts}`
655
- );
656
- } catch {
657
- }
658
- } finally {
659
- this.acked = false;
660
- this.ackRef = null;
661
- }
662
- }
663
- /**
664
- * Post direct replies into the originating Slack thread when appropriate.
665
- * This is independent of summary messages and is intended for chat-style flows
666
- * (e.g., AI answers and explicit chat/notify steps).
667
- */
668
- async maybePostDirectReply(ctx, checkId, result) {
669
- try {
670
- const cfg = ctx.config || {};
671
- const checkCfg = cfg.checks?.[checkId];
672
- if (!checkCfg) return;
673
- const slackRoot = cfg.slack || {};
674
- const showRawOutput = slackRoot.show_raw_output === true || this.cfg?.showRawOutput === true;
675
- const telemetryCfg = slackRoot.telemetry ?? this.cfg?.telemetry;
676
- const providerType = checkCfg.type || "";
677
- const isAi = providerType === "ai";
678
- const isLogChat = providerType === "log" && checkCfg.group === "chat";
679
- const isWorkflow = providerType === "workflow";
680
- if (!isAi && !isLogChat && !isWorkflow) return;
681
- if (checkCfg.criticality === "internal") return;
682
- if (isAi) {
683
- const schema = checkCfg.schema;
684
- if (typeof schema === "string") {
685
- const simpleSchemas = ["code-review", "markdown", "text", "plain"];
686
- if (!simpleSchemas.includes(schema)) return;
687
- }
688
- }
689
- const slack = this.getSlack(ctx);
690
- if (!slack) return;
691
- const payload = this.getInboundSlackPayload(ctx);
692
- const ev = payload?.event;
693
- const channel = String(ev?.channel || "");
694
- const threadTs = String(ev?.thread_ts || ev?.ts || ev?.event_ts || "");
695
- if (!channel || !threadTs) {
696
- ctx.logger.warn(
697
- `[slack-frontend] skip posting AI reply for ${checkId}: missing channel/thread (channel=${channel || "-"} thread=${threadTs || "-"})`
698
- );
699
- return;
700
- }
701
- const out = result?.output;
702
- let text;
703
- if (out && typeof out.text === "string" && out.text.trim().length > 0) {
704
- text = out.text.trim();
705
- } else if (isAi && typeof checkCfg.schema === "string") {
706
- if (typeof result?.content === "string" && result.content.trim().length > 0) {
707
- text = result.content.trim();
708
- }
709
- } else if (isLogChat && typeof result?.logOutput === "string") {
710
- const raw = result.logOutput;
711
- if (raw.trim().length > 0) {
712
- text = raw.trim();
713
- }
714
- } else if (isAi && showRawOutput && out !== void 0) {
715
- try {
716
- text = JSON.stringify(out, null, 2);
717
- } catch {
718
- text = String(out);
719
- }
720
- }
721
- if (out && typeof out._rawOutput === "string" && out._rawOutput.trim().length > 0) {
722
- text = (text || "") + "\n\n" + out._rawOutput.trim();
723
- }
724
- if (!text) {
725
- ctx.logger.info(
726
- `[slack-frontend] skip posting AI reply for ${checkId}: no renderable text in check output`
727
- );
728
- return;
729
- }
730
- const diagrams = extractMermaidDiagrams(text);
731
- let processedText = text;
732
- if (diagrams.length > 0) {
733
- try {
734
- ctx.logger.info(
735
- `[slack-frontend] found ${diagrams.length} mermaid diagram(s) to render for ${checkId}`
736
- );
737
- } catch {
738
- }
739
- const uploadedCount = [];
740
- for (let i = 0; i < diagrams.length; i++) {
741
- const diagram = diagrams[i];
742
- try {
743
- ctx.logger.info(`[slack-frontend] rendering mermaid diagram ${i + 1}...`);
744
- const pngBuffer = await renderMermaidToPng(diagram.code);
745
- if (pngBuffer) {
746
- ctx.logger.info(
747
- `[slack-frontend] rendered diagram ${i + 1}, size=${pngBuffer.length} bytes, uploading...`
748
- );
749
- const filename = `diagram-${i + 1}.png`;
750
- const uploadResult = await slack.files.uploadV2({
751
- content: pngBuffer,
752
- filename,
753
- channel,
754
- thread_ts: threadTs,
755
- title: `Diagram ${i + 1}`
756
- });
757
- if (uploadResult.ok) {
758
- uploadedCount.push(i);
759
- ctx.logger.info(`[slack-frontend] uploaded mermaid diagram ${i + 1} to ${channel}`);
760
- } else {
761
- ctx.logger.warn(`[slack-frontend] upload failed for diagram ${i + 1}`);
762
- }
763
- } else {
764
- ctx.logger.warn(
765
- `[slack-frontend] mermaid rendering returned null for diagram ${i + 1} (mmdc failed or not installed)`
766
- );
767
- }
768
- } catch (e) {
769
- ctx.logger.warn(
770
- `[slack-frontend] failed to render/upload mermaid diagram ${i + 1}: ${e instanceof Error ? e.message : String(e)}`
771
- );
772
- }
773
- }
774
- if (uploadedCount.length > 0) {
775
- processedText = replaceMermaidBlocks(
776
- text,
777
- diagrams,
778
- (idx) => uploadedCount.includes(idx) ? "_(See diagram above)_" : "_(Diagram rendering failed)_"
779
- );
780
- }
781
- }
782
- processedText = processedText.replace(/\\n/g, "\n");
783
- const fileSections = extractFileSections(processedText);
784
- if (fileSections.length > 0) {
785
- const uploadedFileIndices = [];
786
- for (let i = 0; i < fileSections.length; i++) {
787
- const section = fileSections[i];
788
- try {
789
- const buffer = Buffer.from(section.content, "utf-8");
790
- const uploadResult = await slack.files.uploadV2({
791
- content: buffer,
792
- filename: section.filename,
793
- channel,
794
- thread_ts: threadTs,
795
- title: section.filename
796
- });
797
- if (uploadResult.ok) {
798
- uploadedFileIndices.push(i);
799
- ctx.logger.info(`[slack-frontend] uploaded file ${section.filename} to ${channel}`);
800
- } else {
801
- ctx.logger.warn(`[slack-frontend] upload failed for file ${section.filename}`);
802
- }
803
- } catch (e) {
804
- ctx.logger.warn(
805
- `[slack-frontend] failed to upload file ${section.filename}: ${e instanceof Error ? e.message : String(e)}`
806
- );
807
- }
808
- }
809
- processedText = replaceFileSections(
810
- processedText,
811
- fileSections,
812
- (idx) => uploadedFileIndices.includes(idx) ? `_(See file: ${fileSections[idx].filename} above)_` : `_(File upload failed: ${fileSections[idx].filename})_`
813
- );
814
- }
815
- let decoratedText = processedText;
816
- const telemetryEnabled = telemetryCfg === true || telemetryCfg && typeof telemetryCfg === "object" && telemetryCfg.enabled === true;
817
- if (telemetryEnabled) {
818
- const traceInfo = this.getTraceInfo() || this.cachedTraceInfo;
819
- if (traceInfo?.traceId) {
820
- const suffix = `\`trace_id: ${traceInfo.traceId}\``;
821
- decoratedText = `${decoratedText}
822
-
823
- ${suffix}`;
824
- }
825
- }
826
- const formattedText = formatSlackText(decoratedText);
827
- const postResult = await slack.chat.postMessage({
828
- channel,
829
- text: formattedText,
830
- thread_ts: threadTs
831
- });
832
- if (!postResult?.ok) {
833
- ctx.logger.warn(
834
- `[slack-frontend] failed to post AI reply for ${checkId} to ${channel} thread=${threadTs} error=${postResult?.error || "unknown_error"}`
835
- );
836
- return;
837
- }
838
- ctx.logger.info(
839
- `[slack-frontend] posted AI reply for ${checkId} to ${channel} thread=${threadTs} ts=${postResult.ts || "-"}`
840
- );
841
- const responseCapture = ctx.responseCapture;
842
- if (responseCapture && typeof responseCapture === "function") {
843
- try {
844
- responseCapture(processedText);
845
- } catch {
846
- }
847
- }
848
- } catch (outerErr) {
849
- try {
850
- ctx.logger.warn(
851
- `[slack-frontend] maybePostDirectReply failed for ${checkId}: ${outerErr instanceof Error ? outerErr.message : String(outerErr)}`
852
- );
853
- } catch {
854
- }
855
- }
856
- }
857
- getTraceInfo() {
858
- try {
859
- const span = trace.getSpan(context.active());
860
- if (!span) return null;
861
- const ctx = span.spanContext();
862
- if (!ctx || !ctx.traceId) return null;
863
- return { traceId: ctx.traceId, spanId: ctx.spanId };
864
- } catch {
865
- return null;
866
- }
867
- }
868
- };
869
- }
870
- });
871
- init_slack_frontend();
872
- export {
873
- SlackFrontend
874
- };
875
- //# sourceMappingURL=slack-frontend-TZU2HIK7.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/slack/client.ts","../../src/frontends/slack-frontend.ts"],"sourcesContent":["// Lightweight Slack Web API wrapper implemented with fetch (no external deps).\n// Only methods needed by SlackFrontend are implemented.\n\nexport class SlackClient {\n private token: string;\n\n constructor(botToken: string) {\n if (!botToken || typeof botToken !== 'string') {\n throw new Error('SlackClient: botToken is required');\n }\n this.token = botToken;\n }\n\n public readonly reactions = {\n add: async ({\n channel,\n timestamp,\n name,\n }: {\n channel: string;\n timestamp: string;\n name: string;\n }) => {\n const resp: any = await this.api('reactions.add', { channel, timestamp, name });\n if (!resp || resp.ok !== true) {\n // Non-fatal in CLI/test runs – log and continue\n const err = (resp && resp.error) || 'unknown_error';\n console.warn(`Slack reactions.add failed (non-fatal): ${err}`);\n return { ok: false as const };\n }\n return { ok: true } as const;\n },\n remove: async ({\n channel,\n timestamp,\n name,\n }: {\n channel: string;\n timestamp: string;\n name: string;\n }) => {\n const resp: any = await this.api('reactions.remove', { channel, timestamp, name });\n if (!resp || resp.ok !== true) {\n const err = (resp && resp.error) || 'unknown_error';\n console.warn(`Slack reactions.remove failed (non-fatal): ${err}`);\n return { ok: false as const };\n }\n return { ok: true } as const;\n },\n };\n\n public readonly chat = {\n postMessage: async ({\n channel,\n text,\n thread_ts,\n }: {\n channel: string;\n text: string;\n thread_ts?: string;\n }) => {\n try {\n const resp: any = await this.api('chat.postMessage', { channel, text, thread_ts });\n if (!resp || resp.ok !== true) {\n const err = (resp && resp.error) || 'unknown_error';\n const warnings = Array.isArray(resp?.response_metadata?.warnings)\n ? resp.response_metadata.warnings.join(',')\n : '';\n console.warn(\n `Slack chat.postMessage failed (non-fatal): error=${err} channel=${channel} thread_ts=${\n thread_ts || '-'\n } text_len=${text.length}${warnings ? ` warnings=${warnings}` : ''}`\n );\n return {\n ok: false as const,\n ts: undefined,\n message: undefined,\n data: resp,\n error: err,\n };\n }\n // Normalize common fields for tests/frontend\n return {\n ok: true as const,\n ts: resp.ts || (resp.message && resp.message.ts) || undefined,\n message: resp.message,\n data: resp,\n error: undefined,\n };\n } catch (e) {\n console.warn(\n `Slack chat.postMessage threw (non-fatal): channel=${channel} thread_ts=${thread_ts || '-'} text_len=${\n text.length\n } error=${e instanceof Error ? e.message : String(e)}`\n );\n return {\n ok: false as const,\n ts: undefined,\n message: undefined,\n data: undefined,\n error: e instanceof Error ? e.message : String(e),\n };\n }\n },\n update: async ({ channel, ts, text }: { channel: string; ts: string; text: string }) => {\n try {\n const resp: any = await this.api('chat.update', { channel, ts, text });\n if (!resp || resp.ok !== true) {\n const err = (resp && resp.error) || 'unknown_error';\n const warnings = Array.isArray(resp?.response_metadata?.warnings)\n ? resp.response_metadata.warnings.join(',')\n : '';\n console.warn(\n `Slack chat.update failed (non-fatal): error=${err} channel=${channel} ts=${ts} text_len=${\n text.length\n }${warnings ? ` warnings=${warnings}` : ''}`\n );\n return { ok: false as const, ts, error: err, data: resp };\n }\n return { ok: true as const, ts: resp.ts || ts, error: undefined, data: resp };\n } catch (e) {\n console.warn(\n `Slack chat.update threw (non-fatal): channel=${channel} ts=${ts} text_len=${text.length} error=${\n e instanceof Error ? e.message : String(e)\n }`\n );\n return {\n ok: false as const,\n ts,\n error: e instanceof Error ? e.message : String(e),\n data: undefined,\n };\n }\n },\n };\n\n async getBotUserId(): Promise<string> {\n const resp: any = await this.api('auth.test', {});\n if (!resp || resp.ok !== true || !resp.user_id) {\n console.warn('Slack auth.test failed (non-fatal); bot user id unavailable');\n return 'UNKNOWN_BOT';\n }\n return String(resp.user_id);\n }\n\n /**\n * Fetch user info from Slack API.\n * Returns user profile including guest status flags, email, display name, and timezone.\n */\n async getUserInfo(userId: string): Promise<{\n ok: boolean;\n user?: {\n id: string;\n name?: string; // username\n real_name?: string; // full name\n email?: string; // requires users:read.email scope\n is_restricted?: boolean; // Multi-channel guest\n is_ultra_restricted?: boolean; // Single-channel guest\n is_bot?: boolean;\n is_app_user?: boolean;\n deleted?: boolean;\n tz?: string; // IANA timezone (e.g., \"America/New_York\")\n tz_offset?: number; // Timezone offset in seconds from UTC\n };\n }> {\n try {\n const resp: any = await this.api('users.info', { user: userId });\n if (!resp || resp.ok !== true || !resp.user) {\n return { ok: false };\n }\n return {\n ok: true,\n user: {\n id: resp.user.id,\n name: resp.user.name,\n real_name: resp.user.real_name || resp.user.profile?.real_name,\n email: resp.user.profile?.email,\n is_restricted: resp.user.is_restricted,\n is_ultra_restricted: resp.user.is_ultra_restricted,\n is_bot: resp.user.is_bot,\n is_app_user: resp.user.is_app_user,\n deleted: resp.user.deleted,\n tz: resp.user.tz,\n tz_offset: resp.user.tz_offset,\n },\n };\n } catch (e) {\n console.warn(`Slack users.info failed: ${e instanceof Error ? e.message : String(e)}`);\n return { ok: false };\n }\n }\n\n /**\n * Open a DM channel with a user.\n * Returns the DM channel ID.\n */\n async openDM(userId: string): Promise<{ ok: boolean; channel?: string }> {\n try {\n const resp: any = await this.api('conversations.open', { users: userId });\n if (!resp || resp.ok !== true || !resp.channel?.id) {\n console.warn(`Slack conversations.open failed: ${resp?.error || 'unknown_error'}`);\n return { ok: false };\n }\n return { ok: true, channel: resp.channel.id };\n } catch (e) {\n console.warn(\n `Slack conversations.open failed: ${e instanceof Error ? e.message : String(e)}`\n );\n return { ok: false };\n }\n }\n\n async fetchThreadReplies(\n channel: string,\n thread_ts: string,\n limit: number = 40\n ): Promise<\n Array<{\n ts: string;\n user?: string;\n text?: string;\n bot_id?: string;\n thread_ts?: string;\n files?: any[];\n }>\n > {\n try {\n // Use query-string GET semantics similar to Slack WebClient to avoid\n // subtle JSON/form encoding issues that can cause invalid_arguments\n const params = new URLSearchParams({\n channel,\n ts: thread_ts,\n limit: String(limit),\n });\n const res = await fetch(`https://slack.com/api/conversations.replies?${params.toString()}`, {\n method: 'GET',\n headers: {\n Authorization: `Bearer ${this.token}`,\n },\n });\n const resp: any = await res.json();\n if (!resp || resp.ok !== true || !Array.isArray(resp.messages)) {\n const err = (resp && resp.error) || 'unknown_error';\n console.warn(\n `Slack conversations.replies failed (non-fatal): ${err} (channel=${channel}, ts=${thread_ts}, limit=${limit})`\n );\n return [];\n }\n return resp.messages.map((m: any) => ({\n ts: String(m.ts || ''),\n user: m.user,\n text: m.text,\n bot_id: m.bot_id,\n thread_ts: m.thread_ts,\n files: Array.isArray(m.files) ? m.files : undefined,\n }));\n } catch (e) {\n console.warn(\n `Slack conversations.replies failed (non-fatal): ${\n e instanceof Error ? e.message : String(e)\n } (channel=${channel}, ts=${thread_ts}, limit=${limit})`\n );\n return [];\n }\n }\n\n public readonly files = {\n /**\n * Upload a file to Slack using files.uploadV2 API\n * @param options Upload options including file content, filename, channel, and thread_ts\n */\n uploadV2: async ({\n content,\n filename,\n channel,\n thread_ts,\n title,\n initial_comment,\n }: {\n content: Buffer;\n filename: string;\n channel: string;\n thread_ts?: string;\n title?: string;\n initial_comment?: string;\n }): Promise<{ ok: boolean; file?: { id: string; permalink?: string } }> => {\n try {\n // Step 1: Get upload URL (uses form-urlencoded — this endpoint rejects JSON bodies)\n const getUrlResp: any = await this.apiForm('files.getUploadURLExternal', {\n filename,\n length: String(content.length),\n });\n if (!getUrlResp || getUrlResp.ok !== true || !getUrlResp.upload_url) {\n console.warn(\n `Slack files.getUploadURLExternal failed: ${getUrlResp?.error || 'unknown'}`\n );\n return { ok: false };\n }\n\n // Step 2: Upload file content to the URL\n const uploadResp = await fetch(getUrlResp.upload_url, {\n method: 'POST',\n body: content,\n });\n if (!uploadResp.ok) {\n console.warn(`Slack file upload to URL failed: ${uploadResp.status}`);\n return { ok: false };\n }\n\n // Step 3: Complete the upload and share to channel\n const completeResp: any = await this.api('files.completeUploadExternal', {\n files: [{ id: getUrlResp.file_id, title: title || filename }],\n channel_id: channel,\n thread_ts,\n initial_comment,\n });\n if (!completeResp || completeResp.ok !== true) {\n console.warn(\n `Slack files.completeUploadExternal failed: ${completeResp?.error || 'unknown'}`\n );\n return { ok: false };\n }\n\n return {\n ok: true,\n file: completeResp.files?.[0] || { id: getUrlResp.file_id },\n };\n } catch (e) {\n console.warn(`Slack file upload failed: ${e instanceof Error ? e.message : String(e)}`);\n return { ok: false };\n }\n },\n };\n\n getWebClient(): any {\n return {\n conversations: {\n history: async ({ channel, limit }: { channel: string; limit?: number }) =>\n (await this.api('conversations.history', { channel, limit })) as any,\n open: async ({ users }: { users: string }) =>\n (await this.api('conversations.open', { users })) as any,\n replies: async ({ channel, ts, limit }: { channel: string; ts: string; limit?: number }) =>\n (await this.api('conversations.replies', { channel, ts, limit })) as any,\n },\n };\n }\n\n private async api(method: string, body: Record<string, unknown>): Promise<unknown> {\n // Node 18+ global fetch\n const res = await fetch(`https://slack.com/api/${method}`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json; charset=utf-8',\n Authorization: `Bearer ${this.token}`,\n },\n body: JSON.stringify(body),\n });\n return (await res.json()) as unknown;\n }\n\n /** Send a Slack API request as application/x-www-form-urlencoded (required by some file methods). */\n private async apiForm(method: string, params: Record<string, string>): Promise<unknown> {\n const body = new URLSearchParams(params);\n const res = await fetch(`https://slack.com/api/${method}`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n Authorization: `Bearer ${this.token}`,\n },\n body: body.toString(),\n });\n return (await res.json()) as unknown;\n }\n}\n","/**\n * Slack Frontend for Visor workflows.\n *\n * Features:\n * - Posts AI replies to Slack threads\n * - Converts Markdown to Slack mrkdwn format\n * - Renders mermaid diagrams to PNG and uploads as images\n * - Manages 👀/👍 reactions for acknowledgement\n * - Handles human input prompts via prompt-state\n *\n * Mermaid Diagram Rendering:\n * - Detects ```mermaid code blocks in AI responses\n * - Renders to PNG using @mermaid-js/mermaid-cli (mmdc)\n * - Uploads rendered images to Slack thread\n * - Replaces mermaid blocks with \"_(See diagram above)_\" placeholder\n *\n * Requirements for mermaid rendering:\n * - Node.js and npx in PATH\n * - Puppeteer/Chromium dependencies (mermaid-cli uses headless browser)\n * - On Linux: apt-get install chromium-browser libatk-bridge2.0-0 libgtk-3-0\n */\nimport type { Frontend, FrontendContext } from './host';\nimport { SlackClient } from '../slack/client';\nimport {\n formatSlackText,\n extractMermaidDiagrams,\n renderMermaidToPng,\n replaceMermaidBlocks,\n extractFileSections,\n replaceFileSections,\n} from '../slack/markdown';\nimport { context as otContext, trace } from '../telemetry/lazy-otel';\n\ntype SlackFrontendConfig = {\n defaultChannel?: string;\n groupChannels?: Record<string, string>;\n debounceMs?: number;\n maxWaitMs?: number;\n showRawOutput?: boolean;\n telemetry?: {\n enabled?: boolean;\n };\n};\n\nexport class SlackFrontend implements Frontend {\n public readonly name = 'slack';\n private subs: Array<{ unsubscribe(): void }> = [];\n private cfg: SlackFrontendConfig;\n // Reactions ack/done per run (inbound Slack events only)\n private acked: boolean = false;\n private ackRef: { channel: string; ts: string } | null = null;\n private ackName: string = 'eyes';\n private doneName: string = 'thumbsup';\n private errorNotified: boolean = false;\n private cachedTraceInfo: { traceId: string; spanId: string } | null = null;\n\n constructor(config?: SlackFrontendConfig) {\n this.cfg = config || {};\n }\n\n start(ctx: FrontendContext): void {\n const bus = ctx.eventBus;\n\n // Capture trace info now while the OTel span may still be active.\n // EventBus handlers run in a different async context where the span is lost.\n if (!this.cachedTraceInfo) {\n this.cachedTraceInfo = this.getTraceInfo();\n }\n // Also check ctx.run.traceId which is injected by the execution engine\n if (!this.cachedTraceInfo) {\n const runTraceId = (ctx as any)?.run?.traceId;\n if (typeof runTraceId === 'string' && runTraceId) {\n this.cachedTraceInfo = { traceId: runTraceId, spanId: '' };\n }\n }\n\n // Info-level boot log\n try {\n const hasClient = !!(\n (ctx as any).slack ||\n (ctx as any).slackClient ||\n (this.cfg as any)?.botToken ||\n process.env.SLACK_BOT_TOKEN\n );\n ctx.logger.info(`[slack-frontend] started; hasClient=${hasClient} defaultChannel=unset`);\n } catch {}\n\n // If this run was triggered by a Slack event, log key attributes\n try {\n const payload = this.getInboundSlackPayload(ctx);\n if (payload) {\n const ev: any = payload.event || {};\n const ch = String(ev.channel || '-');\n const ts = String(ev.ts || ev.event_ts || '-');\n const user = String(ev.user || ev.bot_id || '-');\n const type = String(ev.type || '-');\n const thread = String(ev.thread_ts || '');\n ctx.logger.info(\n `[slack-frontend] inbound event received: type=${type} channel=${ch} ts=${ts}` +\n (thread ? ` thread_ts=${thread}` : '') +\n ` user=${user}`\n );\n }\n } catch {}\n\n // Listen to check lifecycle; we only post on completion/error (no queued placeholders)\n this.subs.push(\n bus.on('CheckCompleted', async (env: any) => {\n const ev = (env && env.payload) || env;\n // For chat-style AI checks, post direct replies into the Slack thread\n await this.maybePostDirectReply(ctx, ev.checkId, ev.result).catch(() => {});\n // Post execution failure notices when a check completes with fatal issues\n await this.maybePostExecutionFailure(ctx, ev.checkId, ev.result).catch(() => {});\n })\n );\n this.subs.push(\n bus.on('CheckErrored', async (env: any) => {\n const ev = (env && env.payload) || env;\n const message = ev?.error?.message || 'Execution error';\n await this.maybePostError(ctx, 'Check failed', message, ev?.checkId).catch(() => {});\n })\n );\n\n // On terminal state, replace 👀 with 👍 if we acked an inbound Slack message\n this.subs.push(\n bus.on('StateTransition', async (env: any) => {\n const ev = (env && env.payload) || env;\n if (ev && (ev.to === 'Completed' || ev.to === 'Error')) {\n await this.finalizeReactions(ctx).catch(() => {});\n }\n })\n );\n this.subs.push(\n bus.on('Shutdown', async (env: any) => {\n const ev = (env && env.payload) || env;\n const message = ev?.error?.message || 'Fatal error';\n await this.maybePostError(ctx, 'Run failed', message).catch(() => {});\n })\n );\n // Add 👀 acknowledgement as soon as first check is scheduled for Slack-driven runs\n this.subs.push(\n bus.on('CheckScheduled', async () => {\n await this.ensureAcknowledgement(ctx).catch(() => {});\n })\n );\n\n // Human-input requests: post prompt to Slack and mark waiting using prompt-state\n this.subs.push(\n bus.on('HumanInputRequested', async (env: any) => {\n try {\n const ev = (env && env.payload) || env;\n if (!ev || typeof ev.prompt !== 'string' || !ev.checkId) return;\n // Determine channel/thread (Slack SocketMode); if we can't, just ignore.\n let channel = ev.channel as string | undefined;\n let threadTs = ev.threadTs as string | undefined;\n if (!channel || !threadTs) {\n const payload = this.getInboundSlackPayload(ctx);\n const e: any = payload?.event;\n const derivedTs = String(e?.thread_ts || e?.ts || e?.event_ts || '');\n const derivedCh = String(e?.channel || '');\n if (derivedCh && derivedTs) {\n channel = channel || derivedCh;\n threadTs = threadTs || derivedTs;\n }\n }\n if (!channel || !threadTs) return;\n\n // Mark waiting in prompt-state without posting the prompt text to Slack.\n const { getPromptStateManager } = await import('../slack/prompt-state');\n const mgr = getPromptStateManager();\n const prev = mgr.getWaiting(channel, threadTs);\n const text = String(ev.prompt);\n mgr.setWaiting(channel, threadTs, {\n checkName: String(ev.checkId),\n prompt: text,\n promptMessageTs: prev?.promptMessageTs,\n promptsPosted: ((prev?.promptsPosted || 0) + 1) as any,\n });\n try {\n ctx.logger.info(\n `[slack-frontend] registered human-input waiting state for ${channel} thread=${threadTs}`\n );\n } catch {}\n } catch (e) {\n try {\n ctx.logger.warn(\n `[slack-frontend] HumanInputRequested handling failed: ${\n e instanceof Error ? e.message : String(e)\n }`\n );\n } catch {}\n }\n })\n );\n\n // SnapshotSaved: attach snapshot path to waiting entry for this thread\n this.subs.push(\n bus.on('SnapshotSaved', async (env: any) => {\n try {\n const ev = (env && env.payload) || env;\n const channel = String(ev?.channel || '');\n const threadTs = String(ev?.threadTs || '');\n const filePath = String(ev?.filePath || '');\n if (!channel || !threadTs || !filePath) return;\n const { getPromptStateManager } = await import('../slack/prompt-state');\n const mgr = getPromptStateManager();\n mgr.update(channel, threadTs, { snapshotPath: filePath });\n try {\n ctx.logger.info(\n `[slack-frontend] snapshot path attached to waiting prompt: ${filePath}`\n );\n } catch {}\n } catch {}\n })\n );\n }\n\n stop(): void {\n for (const s of this.subs) s.unsubscribe();\n this.subs = [];\n }\n\n private getSlack(ctx: FrontendContext): any | undefined {\n // Prefer injected fake client in tests: ctx.slack or ctx.slackClient\n const injected = (ctx as any).slack || (ctx as any).slackClient;\n if (injected) return injected;\n // Else try to lazy-create from env or frontend config\n try {\n const token = (this.cfg as any)?.botToken || process.env.SLACK_BOT_TOKEN;\n if (typeof token === 'string' && token.trim()) {\n return new SlackClient(token.trim());\n }\n } catch {}\n return undefined;\n }\n\n private getInboundSlackPayload(ctx: FrontendContext): any | null {\n try {\n const anyCfg: any = ctx.config || {};\n const slackCfg: any = anyCfg.slack || {};\n const endpoint: string = slackCfg.endpoint || '/bots/slack/support';\n const payload: any = (ctx as any).webhookContext?.webhookData?.get(endpoint);\n return payload || null;\n } catch {\n return null;\n }\n }\n\n private getInboundSlackEvent(ctx: FrontendContext): { channel: string; ts: string } | null {\n try {\n const payload = this.getInboundSlackPayload(ctx);\n const ev: any = payload?.event;\n const channel = String(ev?.channel || '');\n const ts = String(ev?.ts || ev?.event_ts || '');\n if (channel && ts) return { channel, ts };\n } catch {}\n return null;\n }\n\n private isTelemetryEnabled(ctx: FrontendContext): boolean {\n try {\n const anyCfg: any = ctx.config || {};\n const slackCfg: any = anyCfg.slack || {};\n const telemetryCfg = slackCfg.telemetry ?? (this.cfg as any)?.telemetry;\n return (\n telemetryCfg === true ||\n (telemetryCfg && typeof telemetryCfg === 'object' && telemetryCfg.enabled === true)\n );\n } catch {\n return false;\n }\n }\n\n private async maybePostError(\n ctx: FrontendContext,\n title: string,\n message: string,\n checkId?: string\n ): Promise<void> {\n if (this.errorNotified) return;\n const slack = this.getSlack(ctx);\n if (!slack) return;\n const payload = this.getInboundSlackPayload(ctx);\n const ev: any = payload?.event;\n const channel = String(ev?.channel || '');\n const threadTs = String(ev?.thread_ts || ev?.ts || ev?.event_ts || '');\n if (!channel || !threadTs) {\n try {\n ctx.logger.warn(\n `[slack-frontend] skip posting error notice: missing channel/thread (channel=${\n channel || '-'\n } thread=${threadTs || '-'})`\n );\n } catch {}\n return;\n }\n\n let text = `❌ ${title}`;\n if (checkId) text += `\\nCheck: ${checkId}`;\n if (message) text += `\\n${message}`;\n\n if (this.isTelemetryEnabled(ctx)) {\n const traceInfo = this.getTraceInfo() || this.cachedTraceInfo;\n if (traceInfo?.traceId) {\n text += `\\n\\n\\`trace_id: ${traceInfo.traceId}\\``;\n }\n }\n\n const formattedText = formatSlackText(text);\n const postResult = await slack.chat.postMessage({\n channel,\n text: formattedText,\n thread_ts: threadTs,\n });\n if (!postResult?.ok) {\n try {\n ctx.logger.warn(\n `[slack-frontend] failed to post error notice to ${channel} thread=${threadTs} check=${\n checkId || 'run'\n } error=${postResult?.error || 'unknown_error'}`\n );\n } catch {}\n return;\n }\n try {\n ctx.logger.info(\n `[slack-frontend] posted error notice to ${channel} thread=${threadTs} check=${checkId || 'run'}`\n );\n } catch {}\n this.errorNotified = true;\n }\n\n private isExecutionFailureIssue(issue: any): boolean {\n const ruleId = String(issue?.ruleId || '');\n const msg = String(issue?.message || '');\n const msgLower = msg.toLowerCase();\n return (\n ruleId.endsWith('/error') ||\n ruleId.includes('/execution_error') ||\n ruleId.includes('timeout') ||\n ruleId.includes('sandbox_runner_error') ||\n msgLower.includes('timed out') ||\n msg.includes('Command execution failed')\n );\n }\n\n private async maybePostExecutionFailure(\n ctx: FrontendContext,\n checkId: string,\n result: { issues?: any[] }\n ): Promise<void> {\n try {\n if (this.errorNotified) return;\n const cfg: any = ctx.config || {};\n const checkCfg: any = cfg.checks?.[checkId];\n if (!checkCfg) return;\n if (checkCfg.type === 'human-input') return;\n if (checkCfg.criticality === 'internal') return;\n const issues = (result as any)?.issues;\n if (!Array.isArray(issues) || issues.length === 0) return;\n\n const failureIssue = issues.find(issue => this.isExecutionFailureIssue(issue));\n if (!failureIssue) return;\n if (\n typeof failureIssue.message === 'string' &&\n failureIssue.message.toLowerCase().includes('awaiting human input')\n ) {\n return;\n }\n\n const msg =\n typeof failureIssue.message === 'string' && failureIssue.message.trim().length > 0\n ? failureIssue.message.trim()\n : `Execution failed (${String(failureIssue.ruleId || 'unknown')})`;\n await this.maybePostError(ctx, 'Check failed', msg, checkId);\n } catch {}\n }\n\n private async ensureAcknowledgement(ctx: FrontendContext): Promise<void> {\n if (this.acked) return;\n const ref = this.getInboundSlackEvent(ctx);\n if (!ref) return;\n const slack = this.getSlack(ctx);\n if (!slack) return;\n // Skip ack for our own bot messages to avoid loops (allow other bots)\n try {\n const payload = this.getInboundSlackPayload(ctx);\n const ev: any = payload?.event;\n // If we can resolve bot user id, skip if the sender is the bot\n try {\n const botId = await slack.getBotUserId?.();\n if (botId && ev?.user && String(ev.user) === String(botId)) return;\n } catch {}\n } catch {}\n // Allow overrides via config\n try {\n const anyCfg: any = ctx.config || {};\n const slackCfg: any = anyCfg.slack || {};\n if (slackCfg?.reactions?.enabled === false) return;\n this.ackName = slackCfg?.reactions?.ack || this.ackName;\n this.doneName = slackCfg?.reactions?.done || this.doneName;\n } catch {}\n await slack.reactions.add({ channel: ref.channel, timestamp: ref.ts, name: this.ackName });\n try {\n ctx.logger.info(\n `[slack-frontend] added acknowledgement reaction :${this.ackName}: channel=${ref.channel} ts=${ref.ts}`\n );\n } catch {}\n this.acked = true;\n this.ackRef = ref;\n // Capture trace info while span is active (event handlers lose OTel context)\n if (!this.cachedTraceInfo) {\n this.cachedTraceInfo = this.getTraceInfo();\n }\n }\n\n private async finalizeReactions(ctx: FrontendContext): Promise<void> {\n if (!this.acked || !this.ackRef) return;\n const slack = this.getSlack(ctx);\n if (!slack) return;\n try {\n try {\n await slack.reactions.remove({\n channel: this.ackRef.channel,\n timestamp: this.ackRef.ts,\n name: this.ackName,\n });\n } catch {}\n await slack.reactions.add({\n channel: this.ackRef.channel,\n timestamp: this.ackRef.ts,\n name: this.doneName,\n });\n try {\n ctx.logger.info(\n `[slack-frontend] replaced acknowledgement with completion reaction :${this.doneName}: channel=${this.ackRef.channel} ts=${this.ackRef.ts}`\n );\n } catch {}\n } finally {\n // Reset for safety\n this.acked = false;\n this.ackRef = null;\n }\n }\n\n /**\n * Post direct replies into the originating Slack thread when appropriate.\n * This is independent of summary messages and is intended for chat-style flows\n * (e.g., AI answers and explicit chat/notify steps).\n */\n private async maybePostDirectReply(\n ctx: FrontendContext,\n checkId: string,\n result: { output?: any; content?: string }\n ): Promise<void> {\n try {\n const cfg: any = ctx.config || {};\n const checkCfg: any = cfg.checks?.[checkId];\n if (!checkCfg) return;\n\n // Per-workflow / per-frontend flag to allow posting raw JSON\n // outputs for AI steps (useful for debugging router outputs).\n const slackRoot: any = (cfg as any).slack || {};\n const showRawOutput =\n slackRoot.show_raw_output === true || (this.cfg as any)?.showRawOutput === true;\n const telemetryCfg = slackRoot.telemetry ?? (this.cfg as any)?.telemetry;\n\n const providerType = (checkCfg.type as string) || '';\n const isAi = providerType === 'ai';\n const isLogChat = providerType === 'log' && checkCfg.group === 'chat';\n const isWorkflow = providerType === 'workflow';\n\n // Allow ai, log-chat, and workflow types; skip everything else\n if (!isAi && !isLogChat && !isWorkflow) return;\n\n // Skip internal steps - they're intermediate processing and shouldn't post to Slack\n if (checkCfg.criticality === 'internal') return;\n\n // For AI checks, only post when using simple/unstructured schemas (or none).\n if (isAi) {\n const schema = checkCfg.schema;\n // String schemas: allow only simple/plain ones\n if (typeof schema === 'string') {\n const simpleSchemas = ['code-review', 'markdown', 'text', 'plain'];\n if (!simpleSchemas.includes(schema)) return;\n }\n // Object schemas (custom JSON): treat as structured; require output.text\n }\n\n const slack = this.getSlack(ctx);\n if (!slack) return;\n\n const payload = this.getInboundSlackPayload(ctx);\n const ev: any = payload?.event;\n const channel = String(ev?.channel || '');\n const threadTs = String(ev?.thread_ts || ev?.ts || ev?.event_ts || '');\n if (!channel || !threadTs) {\n ctx.logger.warn(\n `[slack-frontend] skip posting AI reply for ${checkId}: missing channel/thread (channel=${\n channel || '-'\n } thread=${threadTs || '-'})`\n );\n return;\n }\n\n // Prefer output.text; fall back to content ONLY for string/simple schemas.\n const out: any = (result as any)?.output;\n let text: string | undefined;\n if (out && typeof out.text === 'string' && out.text.trim().length > 0) {\n text = out.text.trim();\n } else if (isAi && typeof checkCfg.schema === 'string') {\n if (\n typeof (result as any)?.content === 'string' &&\n (result as any).content.trim().length > 0\n ) {\n text = (result as any).content.trim();\n }\n } else if (isLogChat && typeof (result as any)?.logOutput === 'string') {\n // For log-based chat checks, render the formatted log output as the\n // Slack message when no structured text field is present.\n const raw = (result as any).logOutput;\n if (raw.trim().length > 0) {\n text = raw.trim();\n }\n } else if (isAi && showRawOutput && out !== undefined) {\n try {\n text = JSON.stringify(out, null, 2);\n } catch {\n text = String(out);\n }\n }\n // Append raw output from DSL execute_plan (bypasses LLM rewriting chain)\n if (out && typeof out._rawOutput === 'string' && out._rawOutput.trim().length > 0) {\n text = (text || '') + '\\n\\n' + out._rawOutput.trim();\n }\n\n if (!text) {\n ctx.logger.info(\n `[slack-frontend] skip posting AI reply for ${checkId}: no renderable text in check output`\n );\n return;\n }\n\n // Extract and render mermaid diagrams before posting\n const diagrams = extractMermaidDiagrams(text);\n let processedText = text;\n\n if (diagrams.length > 0) {\n try {\n ctx.logger.info(\n `[slack-frontend] found ${diagrams.length} mermaid diagram(s) to render for ${checkId}`\n );\n } catch {}\n\n // Render and upload each diagram\n const uploadedCount: number[] = [];\n for (let i = 0; i < diagrams.length; i++) {\n const diagram = diagrams[i];\n try {\n ctx.logger.info(`[slack-frontend] rendering mermaid diagram ${i + 1}...`);\n const pngBuffer = await renderMermaidToPng(diagram.code);\n if (pngBuffer) {\n ctx.logger.info(\n `[slack-frontend] rendered diagram ${i + 1}, size=${pngBuffer.length} bytes, uploading...`\n );\n const filename = `diagram-${i + 1}.png`;\n const uploadResult = await slack.files.uploadV2({\n content: pngBuffer,\n filename,\n channel,\n thread_ts: threadTs,\n title: `Diagram ${i + 1}`,\n });\n if (uploadResult.ok) {\n uploadedCount.push(i);\n ctx.logger.info(`[slack-frontend] uploaded mermaid diagram ${i + 1} to ${channel}`);\n } else {\n ctx.logger.warn(`[slack-frontend] upload failed for diagram ${i + 1}`);\n }\n } else {\n ctx.logger.warn(\n `[slack-frontend] mermaid rendering returned null for diagram ${i + 1} (mmdc failed or not installed)`\n );\n }\n } catch (e) {\n ctx.logger.warn(\n `[slack-frontend] failed to render/upload mermaid diagram ${i + 1}: ${\n e instanceof Error ? e.message : String(e)\n }`\n );\n }\n }\n\n // Replace mermaid blocks with placeholder text\n if (uploadedCount.length > 0) {\n processedText = replaceMermaidBlocks(text, diagrams, idx =>\n uploadedCount.includes(idx) ? '_(See diagram above)_' : '_(Diagram rendering failed)_'\n );\n }\n }\n\n // Normalize literal \\n escape sequences from DSL output buffer before extraction.\n // AI-generated DSL code often writes \"\\\\n\" producing literal backslash-n instead of\n // actual newlines, which prevents the --- delimiter --- from matching on its own line.\n processedText = processedText.replace(/\\\\n/g, '\\n');\n\n // Extract and upload file sections (--- filename.ext --- delimiters)\n const fileSections = extractFileSections(processedText);\n if (fileSections.length > 0) {\n const uploadedFileIndices: number[] = [];\n for (let i = 0; i < fileSections.length; i++) {\n const section = fileSections[i];\n try {\n const buffer = Buffer.from(section.content, 'utf-8');\n const uploadResult = await slack.files.uploadV2({\n content: buffer,\n filename: section.filename,\n channel,\n thread_ts: threadTs,\n title: section.filename,\n });\n if (uploadResult.ok) {\n uploadedFileIndices.push(i);\n ctx.logger.info(`[slack-frontend] uploaded file ${section.filename} to ${channel}`);\n } else {\n ctx.logger.warn(`[slack-frontend] upload failed for file ${section.filename}`);\n }\n } catch (e) {\n ctx.logger.warn(\n `[slack-frontend] failed to upload file ${section.filename}: ${\n e instanceof Error ? e.message : String(e)\n }`\n );\n }\n }\n processedText = replaceFileSections(processedText, fileSections, idx =>\n uploadedFileIndices.includes(idx)\n ? `_(See file: ${fileSections[idx].filename} above)_`\n : `_(File upload failed: ${fileSections[idx].filename})_`\n );\n }\n\n let decoratedText = processedText;\n const telemetryEnabled =\n telemetryCfg === true ||\n (telemetryCfg && typeof telemetryCfg === 'object' && telemetryCfg.enabled === true);\n if (telemetryEnabled) {\n const traceInfo = this.getTraceInfo() || this.cachedTraceInfo;\n if (traceInfo?.traceId) {\n const suffix = `\\`trace_id: ${traceInfo.traceId}\\``;\n decoratedText = `${decoratedText}\\n\\n${suffix}`;\n }\n }\n\n const formattedText = formatSlackText(decoratedText);\n const postResult = await slack.chat.postMessage({\n channel,\n text: formattedText,\n thread_ts: threadTs,\n });\n if (!postResult?.ok) {\n ctx.logger.warn(\n `[slack-frontend] failed to post AI reply for ${checkId} to ${channel} thread=${threadTs} error=${\n postResult?.error || 'unknown_error'\n }`\n );\n return;\n }\n ctx.logger.info(\n `[slack-frontend] posted AI reply for ${checkId} to ${channel} thread=${threadTs} ts=${\n postResult.ts || '-'\n }`\n );\n\n // Capture response for scheduled reminders (allows storing previousResponse)\n const responseCapture = (ctx as any).responseCapture as ((text: string) => void) | undefined;\n if (responseCapture && typeof responseCapture === 'function') {\n try {\n // Store the original text (before mrkdwn formatting) for continuity\n responseCapture(processedText);\n } catch {}\n }\n } catch (outerErr) {\n // Log errors instead of silently swallowing them\n try {\n ctx.logger.warn(\n `[slack-frontend] maybePostDirectReply failed for ${checkId}: ${\n outerErr instanceof Error ? outerErr.message : String(outerErr)\n }`\n );\n } catch {}\n }\n }\n\n private getTraceInfo(): { traceId: string; spanId: string } | null {\n try {\n const span = trace.getSpan(otContext.active());\n if (!span) return null;\n const ctx = span.spanContext();\n if (!ctx || !ctx.traceId) return null;\n return { traceId: ctx.traceId, spanId: ctx.spanId };\n } catch {\n return null;\n }\n }\n}\n"],"mappings":"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","names":[]}