@probelabs/visor 0.1.107 → 0.1.112

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.
Files changed (235) hide show
  1. package/README.md +6 -0
  2. package/defaults/task-refinement.yaml +7 -3
  3. package/defaults/visor.tests.yaml +13 -2
  4. package/defaults/visor.yaml +1 -0
  5. package/dist/663.index.js +3 -2
  6. package/dist/80.index.js +3 -2
  7. package/dist/ai-review-service.d.ts +13 -9
  8. package/dist/ai-review-service.d.ts.map +1 -1
  9. package/dist/cli-main.d.ts.map +1 -1
  10. package/dist/cli.d.ts.map +1 -1
  11. package/dist/config.d.ts.map +1 -1
  12. package/dist/debug-visualizer/ws-server.d.ts +7 -1
  13. package/dist/debug-visualizer/ws-server.d.ts.map +1 -1
  14. package/dist/defaults/task-refinement.yaml +7 -3
  15. package/dist/defaults/visor.tests.yaml +13 -2
  16. package/dist/defaults/visor.yaml +1 -0
  17. package/dist/docs/advanced-ai.md +60 -1
  18. package/dist/docs/ai-configuration.md +67 -0
  19. package/dist/docs/ai-custom-tools-usage.md +261 -0
  20. package/dist/docs/ai-custom-tools.md +392 -0
  21. package/dist/docs/bot-transports-rfc.md +23 -0
  22. package/dist/docs/configuration.md +21 -0
  23. package/dist/docs/engine-pause-resume-rfc.md +192 -0
  24. package/dist/docs/lifecycle-hooks.md +253 -0
  25. package/dist/docs/liquid-templates.md +143 -0
  26. package/dist/docs/providers/git-checkout.md +589 -0
  27. package/dist/docs/recipes.md +458 -5
  28. package/dist/docs/rfc/git-checkout-step.md +601 -0
  29. package/dist/docs/rfc/on_init-hook.md +1294 -0
  30. package/dist/docs/rfc/workspace-isolation.md +216 -0
  31. package/dist/docs/router-patterns.md +339 -0
  32. package/dist/event-bus/types.d.ts +14 -0
  33. package/dist/event-bus/types.d.ts.map +1 -1
  34. package/dist/examples/ai-custom-tools-example.yaml +206 -0
  35. package/dist/examples/ai-custom-tools-simple.yaml +76 -0
  36. package/dist/examples/git-checkout-basic.yaml +32 -0
  37. package/dist/examples/git-checkout-compare.yaml +59 -0
  38. package/dist/examples/git-checkout-cross-repo.yaml +76 -0
  39. package/dist/examples/on-init-import-demo.yaml +179 -0
  40. package/dist/examples/reusable-tools.yaml +92 -0
  41. package/dist/examples/reusable-workflows.yaml +88 -0
  42. package/dist/examples/session-reuse-self.yaml +81 -0
  43. package/dist/examples/slack-simple-chat.yaml +775 -0
  44. package/dist/failure-condition-evaluator.d.ts +2 -0
  45. package/dist/failure-condition-evaluator.d.ts.map +1 -1
  46. package/dist/frontends/github-frontend.d.ts +20 -0
  47. package/dist/frontends/github-frontend.d.ts.map +1 -1
  48. package/dist/frontends/host.d.ts +4 -0
  49. package/dist/frontends/host.d.ts.map +1 -1
  50. package/dist/frontends/slack-frontend.d.ts +58 -0
  51. package/dist/frontends/slack-frontend.d.ts.map +1 -0
  52. package/dist/generated/config-schema.d.ts +409 -41
  53. package/dist/generated/config-schema.d.ts.map +1 -1
  54. package/dist/generated/config-schema.json +436 -47
  55. package/dist/github-comments.d.ts +2 -0
  56. package/dist/github-comments.d.ts.map +1 -1
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js +83587 -56085
  59. package/dist/liquid-extensions.d.ts.map +1 -1
  60. package/dist/logger.d.ts +1 -0
  61. package/dist/logger.d.ts.map +1 -1
  62. package/dist/output/traces/{run-2025-11-21T11-50-46-505Z.ndjson → run-2026-01-21T05-37-24-446Z.ndjson} +91 -91
  63. package/dist/output/traces/run-2026-01-21T05-38-18-580Z.ndjson +1067 -0
  64. package/dist/output-formatters.d.ts.map +1 -1
  65. package/dist/providers/ai-check-provider.d.ts +12 -0
  66. package/dist/providers/ai-check-provider.d.ts.map +1 -1
  67. package/dist/providers/check-provider-registry.d.ts.map +1 -1
  68. package/dist/providers/check-provider.interface.d.ts +9 -0
  69. package/dist/providers/check-provider.interface.d.ts.map +1 -1
  70. package/dist/providers/command-check-provider.d.ts.map +1 -1
  71. package/dist/providers/custom-tool-executor.d.ts.map +1 -1
  72. package/dist/providers/git-checkout-provider.d.ts +25 -0
  73. package/dist/providers/git-checkout-provider.d.ts.map +1 -0
  74. package/dist/providers/http-client-provider.d.ts +3 -0
  75. package/dist/providers/http-client-provider.d.ts.map +1 -1
  76. package/dist/providers/human-input-check-provider.d.ts +2 -0
  77. package/dist/providers/human-input-check-provider.d.ts.map +1 -1
  78. package/dist/providers/log-check-provider.d.ts.map +1 -1
  79. package/dist/providers/mcp-check-provider.d.ts +1 -1
  80. package/dist/providers/mcp-check-provider.d.ts.map +1 -1
  81. package/dist/providers/mcp-custom-sse-server.d.ts +66 -0
  82. package/dist/providers/mcp-custom-sse-server.d.ts.map +1 -0
  83. package/dist/providers/memory-check-provider.d.ts.map +1 -1
  84. package/dist/providers/script-check-provider.d.ts.map +1 -1
  85. package/dist/providers/workflow-check-provider.d.ts.map +1 -1
  86. package/dist/reviewer.d.ts.map +1 -1
  87. package/dist/sdk/check-provider-registry-534KL5HT.mjs +27 -0
  88. package/dist/sdk/chunk-23L3QRYX.mjs +16872 -0
  89. package/dist/sdk/chunk-23L3QRYX.mjs.map +1 -0
  90. package/dist/sdk/{chunk-OOZITMRU.mjs → chunk-3OMWVM6J.mjs} +11 -1
  91. package/dist/sdk/{chunk-OOZITMRU.mjs.map → chunk-3OMWVM6J.mjs.map} +1 -1
  92. package/dist/sdk/{chunk-37ZSCMFC.mjs → chunk-7UK3NIIT.mjs} +2 -2
  93. package/dist/sdk/{chunk-VMPLF6FT.mjs → chunk-AGIZJ4UZ.mjs} +50 -4
  94. package/dist/sdk/chunk-AGIZJ4UZ.mjs.map +1 -0
  95. package/dist/sdk/{chunk-IEO6CFLG.mjs → chunk-AIVFBIS4.mjs} +161 -5
  96. package/dist/sdk/chunk-AIVFBIS4.mjs.map +1 -0
  97. package/dist/sdk/chunk-AK6BVWIT.mjs +426 -0
  98. package/dist/sdk/chunk-AK6BVWIT.mjs.map +1 -0
  99. package/dist/sdk/chunk-AUT26LHW.mjs +139 -0
  100. package/dist/sdk/chunk-AUT26LHW.mjs.map +1 -0
  101. package/dist/sdk/chunk-BOVFH3LI.mjs +232 -0
  102. package/dist/sdk/chunk-BOVFH3LI.mjs.map +1 -0
  103. package/dist/sdk/chunk-HTOKWMPO.mjs +157 -0
  104. package/dist/sdk/chunk-HTOKWMPO.mjs.map +1 -0
  105. package/dist/sdk/{chunk-6Y4YTKCF.mjs → chunk-NAW3DB3I.mjs} +2 -2
  106. package/dist/sdk/{chunk-OWUVOILT.mjs → chunk-QR7MOMJH.mjs} +4 -3
  107. package/dist/sdk/{chunk-OWUVOILT.mjs.map → chunk-QR7MOMJH.mjs.map} +1 -1
  108. package/dist/sdk/{chunk-PTL3K3PN.mjs → chunk-QY2XYPEV.mjs} +488 -60
  109. package/dist/sdk/chunk-QY2XYPEV.mjs.map +1 -0
  110. package/dist/sdk/{chunk-OZJ263FM.mjs → chunk-SIWNBRTK.mjs} +29 -215
  111. package/dist/sdk/chunk-SIWNBRTK.mjs.map +1 -0
  112. package/dist/sdk/command-executor-TYUV6HUS.mjs +14 -0
  113. package/dist/sdk/{config-M4ZNO6NU.mjs → config-YNC2EOOT.mjs} +5 -3
  114. package/dist/sdk/{failure-condition-evaluator-NBO5YRXW.mjs → failure-condition-evaluator-YGTF2GHG.mjs} +6 -5
  115. package/dist/sdk/{github-frontend-4AWRJT7D.mjs → github-frontend-SIAEOCON.mjs} +190 -12
  116. package/dist/sdk/github-frontend-SIAEOCON.mjs.map +1 -0
  117. package/dist/sdk/{host-7GBC3S7L.mjs → host-DXUYTNMU.mjs} +5 -2
  118. package/dist/sdk/host-DXUYTNMU.mjs.map +1 -0
  119. package/dist/sdk/{liquid-extensions-C7EG3YKH.mjs → liquid-extensions-PKWCKK7E.mjs} +5 -4
  120. package/dist/sdk/memory-store-XGBB7LX7.mjs +12 -0
  121. package/dist/sdk/prompt-state-YRJY6QAL.mjs +16 -0
  122. package/dist/sdk/{renderer-schema-6RF26VUS.mjs → renderer-schema-LPKN5UJS.mjs} +3 -2
  123. package/dist/sdk/{renderer-schema-6RF26VUS.mjs.map → renderer-schema-LPKN5UJS.mjs.map} +1 -1
  124. package/dist/sdk/{routing-RP56JTV2.mjs → routing-6N45MJ4F.mjs} +7 -6
  125. package/dist/sdk/sdk.d.mts +219 -5
  126. package/dist/sdk/sdk.d.ts +219 -5
  127. package/dist/sdk/sdk.js +21329 -14908
  128. package/dist/sdk/sdk.js.map +1 -1
  129. package/dist/sdk/sdk.mjs +407 -12874
  130. package/dist/sdk/sdk.mjs.map +1 -1
  131. package/dist/sdk/{session-registry-N5FFYFTM.mjs → session-registry-4E6YRQ77.mjs} +2 -2
  132. package/dist/sdk/session-registry-4E6YRQ77.mjs.map +1 -0
  133. package/dist/sdk/slack-frontend-BVKW3GD5.mjs +735 -0
  134. package/dist/sdk/slack-frontend-BVKW3GD5.mjs.map +1 -0
  135. package/dist/sdk/{tracer-init-WP4X46IF.mjs → tracer-init-GSLPPLCD.mjs} +2 -2
  136. package/dist/sdk/tracer-init-GSLPPLCD.mjs.map +1 -0
  137. package/dist/sdk/workflow-registry-R6KSACFR.mjs +12 -0
  138. package/dist/sdk/workflow-registry-R6KSACFR.mjs.map +1 -0
  139. package/dist/slack/adapter.d.ts +36 -0
  140. package/dist/slack/adapter.d.ts.map +1 -0
  141. package/dist/slack/cache-prewarmer.d.ts +31 -0
  142. package/dist/slack/cache-prewarmer.d.ts.map +1 -0
  143. package/dist/slack/client.d.ts +77 -0
  144. package/dist/slack/client.d.ts.map +1 -0
  145. package/dist/slack/markdown.d.ts +45 -0
  146. package/dist/slack/markdown.d.ts.map +1 -0
  147. package/dist/slack/prompt-state.d.ts +33 -0
  148. package/dist/slack/prompt-state.d.ts.map +1 -0
  149. package/dist/slack/rate-limiter.d.ts +56 -0
  150. package/dist/slack/rate-limiter.d.ts.map +1 -0
  151. package/dist/slack/signature.d.ts +2 -0
  152. package/dist/slack/signature.d.ts.map +1 -0
  153. package/dist/slack/socket-runner.d.ts +42 -0
  154. package/dist/slack/socket-runner.d.ts.map +1 -0
  155. package/dist/slack/thread-cache.d.ts +51 -0
  156. package/dist/slack/thread-cache.d.ts.map +1 -0
  157. package/dist/state-machine/context/build-engine-context.d.ts +8 -0
  158. package/dist/state-machine/context/build-engine-context.d.ts.map +1 -1
  159. package/dist/state-machine/dispatch/execution-invoker.d.ts.map +1 -1
  160. package/dist/state-machine/dispatch/foreach-processor.d.ts.map +1 -1
  161. package/dist/state-machine/dispatch/on-init-handlers.d.ts +43 -0
  162. package/dist/state-machine/dispatch/on-init-handlers.d.ts.map +1 -0
  163. package/dist/state-machine/dispatch/stats-manager.d.ts.map +1 -1
  164. package/dist/state-machine/dispatch/template-renderer.d.ts.map +1 -1
  165. package/dist/state-machine/runner.d.ts +6 -0
  166. package/dist/state-machine/runner.d.ts.map +1 -1
  167. package/dist/state-machine/states/level-dispatch.d.ts.map +1 -1
  168. package/dist/state-machine/states/plan-ready.d.ts.map +1 -1
  169. package/dist/state-machine/states/routing.d.ts.map +1 -1
  170. package/dist/state-machine/states/wave-planning.d.ts.map +1 -1
  171. package/dist/state-machine/workflow-projection.d.ts.map +1 -1
  172. package/dist/state-machine-execution-engine.d.ts +21 -9
  173. package/dist/state-machine-execution-engine.d.ts.map +1 -1
  174. package/dist/telemetry/state-capture.d.ts +5 -0
  175. package/dist/telemetry/state-capture.d.ts.map +1 -1
  176. package/dist/test-runner/core/flow-stage.d.ts.map +1 -1
  177. package/dist/test-runner/core/test-execution-wrapper.d.ts.map +1 -1
  178. package/dist/test-runner/evaluators.d.ts +37 -4
  179. package/dist/test-runner/evaluators.d.ts.map +1 -1
  180. package/dist/test-runner/index.d.ts +7 -0
  181. package/dist/test-runner/index.d.ts.map +1 -1
  182. package/dist/test-runner/recorders/slack-recorder.d.ts +17 -0
  183. package/dist/test-runner/recorders/slack-recorder.d.ts.map +1 -0
  184. package/dist/test-runner/validator.d.ts.map +1 -1
  185. package/dist/traces/{run-2025-11-21T11-50-46-505Z.ndjson → run-2026-01-21T05-37-24-446Z.ndjson} +91 -91
  186. package/dist/traces/run-2026-01-21T05-38-18-580Z.ndjson +1067 -0
  187. package/dist/types/bot.d.ts +109 -0
  188. package/dist/types/bot.d.ts.map +1 -0
  189. package/dist/types/cli.d.ts +4 -0
  190. package/dist/types/cli.d.ts.map +1 -1
  191. package/dist/types/config.d.ts +182 -5
  192. package/dist/types/config.d.ts.map +1 -1
  193. package/dist/types/engine.d.ts +5 -0
  194. package/dist/types/engine.d.ts.map +1 -1
  195. package/dist/types/git-checkout.d.ts +76 -0
  196. package/dist/types/git-checkout.d.ts.map +1 -0
  197. package/dist/utils/json-text-extractor.d.ts +17 -0
  198. package/dist/utils/json-text-extractor.d.ts.map +1 -0
  199. package/dist/utils/sandbox.d.ts +10 -0
  200. package/dist/utils/sandbox.d.ts.map +1 -1
  201. package/dist/utils/template-context.d.ts +1 -0
  202. package/dist/utils/template-context.d.ts.map +1 -1
  203. package/dist/utils/tracer-init.d.ts.map +1 -1
  204. package/dist/utils/workspace-manager.d.ts +118 -0
  205. package/dist/utils/workspace-manager.d.ts.map +1 -0
  206. package/dist/utils/worktree-cleanup.d.ts +33 -0
  207. package/dist/utils/worktree-cleanup.d.ts.map +1 -0
  208. package/dist/utils/worktree-manager.d.ts +153 -0
  209. package/dist/utils/worktree-manager.d.ts.map +1 -0
  210. package/dist/webhook-server.d.ts.map +1 -1
  211. package/dist/workflow-executor.d.ts.map +1 -1
  212. package/dist/workflow-registry.d.ts.map +1 -1
  213. package/package.json +5 -4
  214. package/dist/output/traces/run-2025-11-21T11-51-33-674Z.ndjson +0 -839
  215. package/dist/sdk/chunk-IEO6CFLG.mjs.map +0 -1
  216. package/dist/sdk/chunk-JEHPDJIF.mjs +0 -223
  217. package/dist/sdk/chunk-JEHPDJIF.mjs.map +0 -1
  218. package/dist/sdk/chunk-OZJ263FM.mjs.map +0 -1
  219. package/dist/sdk/chunk-PTL3K3PN.mjs.map +0 -1
  220. package/dist/sdk/chunk-VMPLF6FT.mjs.map +0 -1
  221. package/dist/sdk/github-frontend-4AWRJT7D.mjs.map +0 -1
  222. package/dist/sdk/host-7GBC3S7L.mjs.map +0 -1
  223. package/dist/sdk/memory-store-GJACZC2A.mjs +0 -11
  224. package/dist/sdk/workflow-registry-2YIIXQCK.mjs +0 -11
  225. package/dist/traces/run-2025-11-21T11-51-33-674Z.ndjson +0 -839
  226. /package/dist/sdk/{config-M4ZNO6NU.mjs.map → check-provider-registry-534KL5HT.mjs.map} +0 -0
  227. /package/dist/sdk/{chunk-37ZSCMFC.mjs.map → chunk-7UK3NIIT.mjs.map} +0 -0
  228. /package/dist/sdk/{chunk-6Y4YTKCF.mjs.map → chunk-NAW3DB3I.mjs.map} +0 -0
  229. /package/dist/sdk/{failure-condition-evaluator-NBO5YRXW.mjs.map → command-executor-TYUV6HUS.mjs.map} +0 -0
  230. /package/dist/sdk/{liquid-extensions-C7EG3YKH.mjs.map → config-YNC2EOOT.mjs.map} +0 -0
  231. /package/dist/sdk/{memory-store-GJACZC2A.mjs.map → failure-condition-evaluator-YGTF2GHG.mjs.map} +0 -0
  232. /package/dist/sdk/{routing-RP56JTV2.mjs.map → liquid-extensions-PKWCKK7E.mjs.map} +0 -0
  233. /package/dist/sdk/{session-registry-N5FFYFTM.mjs.map → memory-store-XGBB7LX7.mjs.map} +0 -0
  234. /package/dist/sdk/{tracer-init-WP4X46IF.mjs.map → prompt-state-YRJY6QAL.mjs.map} +0 -0
  235. /package/dist/sdk/{workflow-registry-2YIIXQCK.mjs.map → routing-6N45MJ4F.mjs.map} +0 -0
@@ -0,0 +1,735 @@
1
+ import {
2
+ context,
3
+ init_lazy_otel,
4
+ trace
5
+ } from "./chunk-YSN4G6CI.mjs";
6
+ import "./chunk-WMJKH4XE.mjs";
7
+
8
+ // src/slack/client.ts
9
+ var SlackClient = class {
10
+ token;
11
+ constructor(botToken) {
12
+ if (!botToken || typeof botToken !== "string") {
13
+ throw new Error("SlackClient: botToken is required");
14
+ }
15
+ this.token = botToken;
16
+ }
17
+ reactions = {
18
+ add: async ({
19
+ channel,
20
+ timestamp,
21
+ name
22
+ }) => {
23
+ const resp = await this.api("reactions.add", { channel, timestamp, name });
24
+ if (!resp || resp.ok !== true) {
25
+ const err = resp && resp.error || "unknown_error";
26
+ console.warn(`Slack reactions.add failed (non-fatal): ${err}`);
27
+ return { ok: false };
28
+ }
29
+ return { ok: true };
30
+ },
31
+ remove: async ({
32
+ channel,
33
+ timestamp,
34
+ name
35
+ }) => {
36
+ const resp = await this.api("reactions.remove", { channel, timestamp, name });
37
+ if (!resp || resp.ok !== true) {
38
+ const err = resp && resp.error || "unknown_error";
39
+ console.warn(`Slack reactions.remove failed (non-fatal): ${err}`);
40
+ return { ok: false };
41
+ }
42
+ return { ok: true };
43
+ }
44
+ };
45
+ chat = {
46
+ postMessage: async ({
47
+ channel,
48
+ text,
49
+ thread_ts
50
+ }) => {
51
+ const resp = await this.api("chat.postMessage", { channel, text, thread_ts });
52
+ if (!resp || resp.ok !== true) {
53
+ const err = resp && resp.error || "unknown_error";
54
+ console.warn(`Slack chat.postMessage failed (non-fatal): ${err}`);
55
+ return {
56
+ ts: void 0,
57
+ message: void 0,
58
+ data: resp
59
+ };
60
+ }
61
+ return {
62
+ ts: resp.ts || resp.message && resp.message.ts || void 0,
63
+ message: resp.message,
64
+ data: resp
65
+ };
66
+ },
67
+ update: async ({ channel, ts, text }) => {
68
+ const resp = await this.api("chat.update", { channel, ts, text });
69
+ if (!resp || resp.ok !== true) {
70
+ const err = resp && resp.error || "unknown_error";
71
+ console.warn(`Slack chat.update failed (non-fatal): ${err}`);
72
+ return { ok: false, ts };
73
+ }
74
+ return { ok: true, ts: resp.ts || ts };
75
+ }
76
+ };
77
+ async getBotUserId() {
78
+ const resp = await this.api("auth.test", {});
79
+ if (!resp || resp.ok !== true || !resp.user_id) {
80
+ console.warn("Slack auth.test failed (non-fatal); bot user id unavailable");
81
+ return "UNKNOWN_BOT";
82
+ }
83
+ return String(resp.user_id);
84
+ }
85
+ async fetchThreadReplies(channel, thread_ts, limit = 40) {
86
+ try {
87
+ const params = new URLSearchParams({
88
+ channel,
89
+ ts: thread_ts,
90
+ limit: String(limit)
91
+ });
92
+ const res = await fetch(`https://slack.com/api/conversations.replies?${params.toString()}`, {
93
+ method: "GET",
94
+ headers: {
95
+ Authorization: `Bearer ${this.token}`
96
+ }
97
+ });
98
+ const resp = await res.json();
99
+ if (!resp || resp.ok !== true || !Array.isArray(resp.messages)) {
100
+ const err = resp && resp.error || "unknown_error";
101
+ console.warn(
102
+ `Slack conversations.replies failed (non-fatal): ${err} (channel=${channel}, ts=${thread_ts}, limit=${limit})`
103
+ );
104
+ return [];
105
+ }
106
+ return resp.messages.map((m) => ({
107
+ ts: String(m.ts || ""),
108
+ user: m.user,
109
+ text: m.text,
110
+ bot_id: m.bot_id,
111
+ thread_ts: m.thread_ts
112
+ }));
113
+ } catch (e) {
114
+ console.warn(
115
+ `Slack conversations.replies failed (non-fatal): ${e instanceof Error ? e.message : String(e)} (channel=${channel}, ts=${thread_ts}, limit=${limit})`
116
+ );
117
+ return [];
118
+ }
119
+ }
120
+ files = {
121
+ /**
122
+ * Upload a file to Slack using files.uploadV2 API
123
+ * @param options Upload options including file content, filename, channel, and thread_ts
124
+ */
125
+ uploadV2: async ({
126
+ content,
127
+ filename,
128
+ channel,
129
+ thread_ts,
130
+ title,
131
+ initial_comment
132
+ }) => {
133
+ try {
134
+ const getUrlResp = await this.api("files.getUploadURLExternal", {
135
+ filename,
136
+ length: content.length
137
+ });
138
+ if (!getUrlResp || getUrlResp.ok !== true || !getUrlResp.upload_url) {
139
+ console.warn(
140
+ `Slack files.getUploadURLExternal failed: ${getUrlResp?.error || "unknown"}`
141
+ );
142
+ return { ok: false };
143
+ }
144
+ const uploadResp = await fetch(getUrlResp.upload_url, {
145
+ method: "POST",
146
+ body: content
147
+ });
148
+ if (!uploadResp.ok) {
149
+ console.warn(`Slack file upload to URL failed: ${uploadResp.status}`);
150
+ return { ok: false };
151
+ }
152
+ const completeResp = await this.api("files.completeUploadExternal", {
153
+ files: [{ id: getUrlResp.file_id, title: title || filename }],
154
+ channel_id: channel,
155
+ thread_ts,
156
+ initial_comment
157
+ });
158
+ if (!completeResp || completeResp.ok !== true) {
159
+ console.warn(
160
+ `Slack files.completeUploadExternal failed: ${completeResp?.error || "unknown"}`
161
+ );
162
+ return { ok: false };
163
+ }
164
+ return {
165
+ ok: true,
166
+ file: completeResp.files?.[0] || { id: getUrlResp.file_id }
167
+ };
168
+ } catch (e) {
169
+ console.warn(`Slack file upload failed: ${e instanceof Error ? e.message : String(e)}`);
170
+ return { ok: false };
171
+ }
172
+ }
173
+ };
174
+ getWebClient() {
175
+ return {
176
+ conversations: {
177
+ history: async ({ channel, limit }) => await this.api("conversations.history", { channel, limit }),
178
+ open: async ({ users }) => await this.api("conversations.open", { users }),
179
+ replies: async ({ channel, ts, limit }) => await this.api("conversations.replies", { channel, ts, limit })
180
+ }
181
+ };
182
+ }
183
+ async api(method, body) {
184
+ const res = await fetch(`https://slack.com/api/${method}`, {
185
+ method: "POST",
186
+ headers: {
187
+ "Content-Type": "application/json; charset=utf-8",
188
+ Authorization: `Bearer ${this.token}`
189
+ },
190
+ body: JSON.stringify(body)
191
+ });
192
+ return await res.json();
193
+ }
194
+ };
195
+
196
+ // src/slack/markdown.ts
197
+ import { spawn } from "child_process";
198
+ import * as fs from "fs";
199
+ import * as path from "path";
200
+ import * as os from "os";
201
+ function extractMermaidDiagrams(text) {
202
+ const diagrams = [];
203
+ const regex = /```mermaid\s*\n([\s\S]*?)```/g;
204
+ let match;
205
+ while ((match = regex.exec(text)) !== null) {
206
+ diagrams.push({
207
+ fullMatch: match[0],
208
+ code: match[1].trim(),
209
+ startIndex: match.index,
210
+ endIndex: match.index + match[0].length
211
+ });
212
+ }
213
+ return diagrams;
214
+ }
215
+ async function renderMermaidToPng(mermaidCode) {
216
+ const tmpDir = os.tmpdir();
217
+ const inputFile = path.join(
218
+ tmpDir,
219
+ `mermaid-${Date.now()}-${Math.random().toString(36).slice(2)}.mmd`
220
+ );
221
+ const outputFile = path.join(
222
+ tmpDir,
223
+ `mermaid-${Date.now()}-${Math.random().toString(36).slice(2)}.png`
224
+ );
225
+ try {
226
+ fs.writeFileSync(inputFile, mermaidCode, "utf-8");
227
+ const chromiumPaths = [
228
+ "/usr/bin/chromium",
229
+ "/usr/bin/chromium-browser",
230
+ "/usr/bin/google-chrome",
231
+ "/usr/bin/chrome"
232
+ ];
233
+ let chromiumPath;
234
+ for (const p of chromiumPaths) {
235
+ if (fs.existsSync(p)) {
236
+ chromiumPath = p;
237
+ break;
238
+ }
239
+ }
240
+ const env = { ...process.env };
241
+ if (chromiumPath) {
242
+ env.PUPPETEER_EXECUTABLE_PATH = chromiumPath;
243
+ }
244
+ const result = await new Promise((resolve) => {
245
+ const proc = spawn(
246
+ "npx",
247
+ [
248
+ "--yes",
249
+ "@mermaid-js/mermaid-cli",
250
+ "-i",
251
+ inputFile,
252
+ "-o",
253
+ outputFile,
254
+ "-e",
255
+ "png",
256
+ "-b",
257
+ "white",
258
+ "-w",
259
+ "1200"
260
+ ],
261
+ {
262
+ timeout: 6e4,
263
+ // 60 second timeout (first run may download packages)
264
+ stdio: ["pipe", "pipe", "pipe"],
265
+ env
266
+ }
267
+ );
268
+ let stderr = "";
269
+ proc.stderr?.on("data", (data) => {
270
+ stderr += data.toString();
271
+ });
272
+ proc.on("close", (code) => {
273
+ if (code === 0) {
274
+ resolve({ success: true });
275
+ } else {
276
+ resolve({ success: false, error: stderr || `Exit code ${code}` });
277
+ }
278
+ });
279
+ proc.on("error", (err) => {
280
+ resolve({ success: false, error: err.message });
281
+ });
282
+ });
283
+ if (!result.success) {
284
+ console.warn(`Mermaid rendering failed: ${result.error}`);
285
+ return null;
286
+ }
287
+ if (!fs.existsSync(outputFile)) {
288
+ console.warn("Mermaid output file not created");
289
+ return null;
290
+ }
291
+ const pngBuffer = fs.readFileSync(outputFile);
292
+ return pngBuffer;
293
+ } catch (e) {
294
+ console.warn(`Mermaid rendering error: ${e instanceof Error ? e.message : String(e)}`);
295
+ return null;
296
+ } finally {
297
+ try {
298
+ if (fs.existsSync(inputFile)) fs.unlinkSync(inputFile);
299
+ if (fs.existsSync(outputFile)) fs.unlinkSync(outputFile);
300
+ } catch {
301
+ }
302
+ }
303
+ }
304
+ function replaceMermaidBlocks(text, diagrams, replacement = "_(See diagram above)_") {
305
+ if (diagrams.length === 0) return text;
306
+ const sorted = [...diagrams].sort((a, b) => b.startIndex - a.startIndex);
307
+ let result = text;
308
+ sorted.forEach((diagram, sortedIndex) => {
309
+ const originalIndex = diagrams.length - 1 - sortedIndex;
310
+ const rep = typeof replacement === "function" ? replacement(originalIndex) : replacement;
311
+ result = result.slice(0, diagram.startIndex) + rep + result.slice(diagram.endIndex);
312
+ });
313
+ return result;
314
+ }
315
+ function markdownToSlack(text) {
316
+ if (!text || typeof text !== "string") return "";
317
+ let out = text;
318
+ out = out.replace(
319
+ /!\[([^\]]*)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g,
320
+ (_m, alt, url) => `<${url}|${alt || "image"}>`
321
+ );
322
+ out = out.replace(
323
+ /\[([^\]]+)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g,
324
+ (_m, label, url) => `<${url}|${label}>`
325
+ );
326
+ out = out.replace(/\*\*([^*]+)\*\*/g, (_m, inner) => `*${inner}*`);
327
+ out = out.replace(/__([^_]+)__/g, (_m, inner) => `*${inner}*`);
328
+ const lines = out.split(/\r?\n/);
329
+ let inCodeBlock = false;
330
+ for (let i = 0; i < lines.length; i++) {
331
+ const line = lines[i];
332
+ const trimmed = line.trimStart();
333
+ if (/^```/.test(trimmed)) {
334
+ inCodeBlock = !inCodeBlock;
335
+ continue;
336
+ }
337
+ if (inCodeBlock) continue;
338
+ const headerMatch = /^(#{1,6})\s+(.+)$/.exec(trimmed);
339
+ if (headerMatch) {
340
+ const [, hashes, headerText] = headerMatch;
341
+ const prevLine = i > 0 ? lines[i - 1].trim() : "";
342
+ const prevIsHeaderOrFence = /^#{1,6}\s+/.test(prevLine) || /^\*[^*]+\*$/.test(prevLine) || /^```/.test(prevLine);
343
+ if (hashes.length <= 2 && i > 0 && prevLine !== "" && !prevIsHeaderOrFence) {
344
+ lines[i] = `
345
+ *${headerText.trim()}*`;
346
+ } else {
347
+ lines[i] = `*${headerText.trim()}*`;
348
+ }
349
+ continue;
350
+ }
351
+ const bulletMatch = /^(\s*)([-*])\s+(.+)$/.exec(line);
352
+ if (bulletMatch) {
353
+ const [, indent, , rest] = bulletMatch;
354
+ lines[i] = `${indent}\u2022 ${rest}`;
355
+ }
356
+ }
357
+ out = lines.join("\n");
358
+ return out;
359
+ }
360
+ function formatSlackText(text) {
361
+ return markdownToSlack(text);
362
+ }
363
+
364
+ // src/frontends/slack-frontend.ts
365
+ init_lazy_otel();
366
+ var SlackFrontend = class {
367
+ name = "slack";
368
+ subs = [];
369
+ cfg;
370
+ // Reactions ack/done per run (inbound Slack events only)
371
+ acked = false;
372
+ ackRef = null;
373
+ ackName = "eyes";
374
+ doneName = "thumbsup";
375
+ constructor(config) {
376
+ this.cfg = config || {};
377
+ }
378
+ start(ctx) {
379
+ const bus = ctx.eventBus;
380
+ try {
381
+ const hasClient = !!(ctx.slack || ctx.slackClient || this.cfg?.botToken || process.env.SLACK_BOT_TOKEN);
382
+ ctx.logger.info(`[slack-frontend] started; hasClient=${hasClient} defaultChannel=unset`);
383
+ } catch {
384
+ }
385
+ try {
386
+ const payload = this.getInboundSlackPayload(ctx);
387
+ if (payload) {
388
+ const ev = payload.event || {};
389
+ const ch = String(ev.channel || "-");
390
+ const ts = String(ev.ts || ev.event_ts || "-");
391
+ const user = String(ev.user || ev.bot_id || "-");
392
+ const type = String(ev.type || "-");
393
+ const thread = String(ev.thread_ts || "");
394
+ ctx.logger.info(
395
+ `[slack-frontend] inbound event received: type=${type} channel=${ch} ts=${ts}` + (thread ? ` thread_ts=${thread}` : "") + ` user=${user}`
396
+ );
397
+ }
398
+ } catch {
399
+ }
400
+ this.subs.push(
401
+ bus.on("CheckCompleted", async (env) => {
402
+ const ev = env && env.payload || env;
403
+ await this.maybePostDirectReply(ctx, ev.checkId, ev.result).catch(() => {
404
+ });
405
+ })
406
+ );
407
+ this.subs.push(
408
+ bus.on("StateTransition", async (env) => {
409
+ const ev = env && env.payload || env;
410
+ if (ev && (ev.to === "Completed" || ev.to === "Error")) {
411
+ await this.finalizeReactions(ctx).catch(() => {
412
+ });
413
+ }
414
+ })
415
+ );
416
+ this.subs.push(
417
+ bus.on("CheckScheduled", async () => {
418
+ await this.ensureAcknowledgement(ctx).catch(() => {
419
+ });
420
+ })
421
+ );
422
+ this.subs.push(
423
+ bus.on("HumanInputRequested", async (env) => {
424
+ try {
425
+ const ev = env && env.payload || env;
426
+ if (!ev || typeof ev.prompt !== "string" || !ev.checkId) return;
427
+ let channel = ev.channel;
428
+ let threadTs = ev.threadTs;
429
+ if (!channel || !threadTs) {
430
+ const payload = this.getInboundSlackPayload(ctx);
431
+ const e = payload?.event;
432
+ const derivedTs = String(e?.thread_ts || e?.ts || e?.event_ts || "");
433
+ const derivedCh = String(e?.channel || "");
434
+ if (derivedCh && derivedTs) {
435
+ channel = channel || derivedCh;
436
+ threadTs = threadTs || derivedTs;
437
+ }
438
+ }
439
+ if (!channel || !threadTs) return;
440
+ const { getPromptStateManager } = await import("./prompt-state-YRJY6QAL.mjs");
441
+ const mgr = getPromptStateManager();
442
+ const prev = mgr.getWaiting(channel, threadTs);
443
+ const text = String(ev.prompt);
444
+ mgr.setWaiting(channel, threadTs, {
445
+ checkName: String(ev.checkId),
446
+ prompt: text,
447
+ promptMessageTs: prev?.promptMessageTs,
448
+ promptsPosted: (prev?.promptsPosted || 0) + 1
449
+ });
450
+ try {
451
+ ctx.logger.info(
452
+ `[slack-frontend] registered human-input waiting state for ${channel} thread=${threadTs}`
453
+ );
454
+ } catch {
455
+ }
456
+ } catch (e) {
457
+ try {
458
+ ctx.logger.warn(
459
+ `[slack-frontend] HumanInputRequested handling failed: ${e instanceof Error ? e.message : String(e)}`
460
+ );
461
+ } catch {
462
+ }
463
+ }
464
+ })
465
+ );
466
+ this.subs.push(
467
+ bus.on("SnapshotSaved", async (env) => {
468
+ try {
469
+ const ev = env && env.payload || env;
470
+ const channel = String(ev?.channel || "");
471
+ const threadTs = String(ev?.threadTs || "");
472
+ const filePath = String(ev?.filePath || "");
473
+ if (!channel || !threadTs || !filePath) return;
474
+ const { getPromptStateManager } = await import("./prompt-state-YRJY6QAL.mjs");
475
+ const mgr = getPromptStateManager();
476
+ mgr.update(channel, threadTs, { snapshotPath: filePath });
477
+ try {
478
+ ctx.logger.info(
479
+ `[slack-frontend] snapshot path attached to waiting prompt: ${filePath}`
480
+ );
481
+ } catch {
482
+ }
483
+ } catch {
484
+ }
485
+ })
486
+ );
487
+ }
488
+ stop() {
489
+ for (const s of this.subs) s.unsubscribe();
490
+ this.subs = [];
491
+ }
492
+ getSlack(ctx) {
493
+ const injected = ctx.slack || ctx.slackClient;
494
+ if (injected) return injected;
495
+ try {
496
+ const token = this.cfg?.botToken || process.env.SLACK_BOT_TOKEN;
497
+ if (typeof token === "string" && token.trim()) {
498
+ return new SlackClient(token.trim());
499
+ }
500
+ } catch {
501
+ }
502
+ return void 0;
503
+ }
504
+ getInboundSlackPayload(ctx) {
505
+ try {
506
+ const anyCfg = ctx.config || {};
507
+ const slackCfg = anyCfg.slack || {};
508
+ const endpoint = slackCfg.endpoint || "/bots/slack/support";
509
+ const payload = ctx.webhookContext?.webhookData?.get(endpoint);
510
+ return payload || null;
511
+ } catch {
512
+ return null;
513
+ }
514
+ }
515
+ getInboundSlackEvent(ctx) {
516
+ try {
517
+ const payload = this.getInboundSlackPayload(ctx);
518
+ const ev = payload?.event;
519
+ const channel = String(ev?.channel || "");
520
+ const ts = String(ev?.ts || ev?.event_ts || "");
521
+ if (channel && ts) return { channel, ts };
522
+ } catch {
523
+ }
524
+ return null;
525
+ }
526
+ async ensureAcknowledgement(ctx) {
527
+ if (this.acked) return;
528
+ const ref = this.getInboundSlackEvent(ctx);
529
+ if (!ref) return;
530
+ const slack = this.getSlack(ctx);
531
+ if (!slack) return;
532
+ try {
533
+ const payload = this.getInboundSlackPayload(ctx);
534
+ const ev = payload?.event;
535
+ if (ev?.subtype === "bot_message") return;
536
+ try {
537
+ const botId = await slack.getBotUserId?.();
538
+ if (botId && ev?.user && String(ev.user) === String(botId)) return;
539
+ } catch {
540
+ }
541
+ } catch {
542
+ }
543
+ try {
544
+ const anyCfg = ctx.config || {};
545
+ const slackCfg = anyCfg.slack || {};
546
+ if (slackCfg?.reactions?.enabled === false) return;
547
+ this.ackName = slackCfg?.reactions?.ack || this.ackName;
548
+ this.doneName = slackCfg?.reactions?.done || this.doneName;
549
+ } catch {
550
+ }
551
+ await slack.reactions.add({ channel: ref.channel, timestamp: ref.ts, name: this.ackName });
552
+ try {
553
+ ctx.logger.info(
554
+ `[slack-frontend] added acknowledgement reaction :${this.ackName}: channel=${ref.channel} ts=${ref.ts}`
555
+ );
556
+ } catch {
557
+ }
558
+ this.acked = true;
559
+ this.ackRef = ref;
560
+ }
561
+ async finalizeReactions(ctx) {
562
+ if (!this.acked || !this.ackRef) return;
563
+ const slack = this.getSlack(ctx);
564
+ if (!slack) return;
565
+ try {
566
+ try {
567
+ await slack.reactions.remove({
568
+ channel: this.ackRef.channel,
569
+ timestamp: this.ackRef.ts,
570
+ name: this.ackName
571
+ });
572
+ } catch {
573
+ }
574
+ await slack.reactions.add({
575
+ channel: this.ackRef.channel,
576
+ timestamp: this.ackRef.ts,
577
+ name: this.doneName
578
+ });
579
+ try {
580
+ ctx.logger.info(
581
+ `[slack-frontend] replaced acknowledgement with completion reaction :${this.doneName}: channel=${this.ackRef.channel} ts=${this.ackRef.ts}`
582
+ );
583
+ } catch {
584
+ }
585
+ } finally {
586
+ this.acked = false;
587
+ this.ackRef = null;
588
+ }
589
+ }
590
+ /**
591
+ * Post direct replies into the originating Slack thread when appropriate.
592
+ * This is independent of summary messages and is intended for chat-style flows
593
+ * (e.g., AI answers and explicit chat/notify steps).
594
+ */
595
+ async maybePostDirectReply(ctx, checkId, result) {
596
+ try {
597
+ const cfg = ctx.config || {};
598
+ const checkCfg = cfg.checks?.[checkId];
599
+ if (!checkCfg) return;
600
+ const slackRoot = cfg.slack || {};
601
+ const showRawOutput = slackRoot.show_raw_output === true || this.cfg?.showRawOutput === true;
602
+ const telemetryCfg = slackRoot.telemetry ?? this.cfg?.telemetry;
603
+ const providerType = checkCfg.type || "";
604
+ const isAi = providerType === "ai";
605
+ const isLogChat = providerType === "log" && checkCfg.group === "chat";
606
+ if (!isAi && !isLogChat) return;
607
+ if (checkCfg.criticality === "internal") return;
608
+ if (isAi) {
609
+ const schema = checkCfg.schema;
610
+ if (typeof schema === "string") {
611
+ const simpleSchemas = ["code-review", "markdown", "text", "plain"];
612
+ if (!simpleSchemas.includes(schema)) return;
613
+ }
614
+ }
615
+ const slack = this.getSlack(ctx);
616
+ if (!slack) return;
617
+ const payload = this.getInboundSlackPayload(ctx);
618
+ const ev = payload?.event;
619
+ const channel = String(ev?.channel || "");
620
+ const threadTs = String(ev?.thread_ts || ev?.ts || ev?.event_ts || "");
621
+ if (!channel || !threadTs) return;
622
+ const out = result?.output;
623
+ let text;
624
+ if (out && typeof out.text === "string" && out.text.trim().length > 0) {
625
+ text = out.text.trim();
626
+ } else if (isAi && typeof checkCfg.schema === "string") {
627
+ if (typeof result?.content === "string" && result.content.trim().length > 0) {
628
+ text = result.content.trim();
629
+ }
630
+ } else if (isLogChat && typeof result?.logOutput === "string") {
631
+ const raw = result.logOutput;
632
+ if (raw.trim().length > 0) {
633
+ text = raw.trim();
634
+ }
635
+ } else if (isAi && showRawOutput && out !== void 0) {
636
+ try {
637
+ text = JSON.stringify(out, null, 2);
638
+ } catch {
639
+ text = String(out);
640
+ }
641
+ }
642
+ if (!text) return;
643
+ const diagrams = extractMermaidDiagrams(text);
644
+ let processedText = text;
645
+ if (diagrams.length > 0) {
646
+ try {
647
+ ctx.logger.info(
648
+ `[slack-frontend] found ${diagrams.length} mermaid diagram(s) to render for ${checkId}`
649
+ );
650
+ } catch {
651
+ }
652
+ const uploadedCount = [];
653
+ for (let i = 0; i < diagrams.length; i++) {
654
+ const diagram = diagrams[i];
655
+ try {
656
+ ctx.logger.info(`[slack-frontend] rendering mermaid diagram ${i + 1}...`);
657
+ const pngBuffer = await renderMermaidToPng(diagram.code);
658
+ if (pngBuffer) {
659
+ ctx.logger.info(
660
+ `[slack-frontend] rendered diagram ${i + 1}, size=${pngBuffer.length} bytes, uploading...`
661
+ );
662
+ const filename = `diagram-${i + 1}.png`;
663
+ const uploadResult = await slack.files.uploadV2({
664
+ content: pngBuffer,
665
+ filename,
666
+ channel,
667
+ thread_ts: threadTs,
668
+ title: `Diagram ${i + 1}`
669
+ });
670
+ if (uploadResult.ok) {
671
+ uploadedCount.push(i);
672
+ ctx.logger.info(`[slack-frontend] uploaded mermaid diagram ${i + 1} to ${channel}`);
673
+ } else {
674
+ ctx.logger.warn(`[slack-frontend] upload failed for diagram ${i + 1}`);
675
+ }
676
+ } else {
677
+ ctx.logger.warn(
678
+ `[slack-frontend] mermaid rendering returned null for diagram ${i + 1} (mmdc failed or not installed)`
679
+ );
680
+ }
681
+ } catch (e) {
682
+ ctx.logger.warn(
683
+ `[slack-frontend] failed to render/upload mermaid diagram ${i + 1}: ${e instanceof Error ? e.message : String(e)}`
684
+ );
685
+ }
686
+ }
687
+ if (uploadedCount.length > 0) {
688
+ processedText = replaceMermaidBlocks(
689
+ text,
690
+ diagrams,
691
+ (idx) => uploadedCount.includes(idx) ? "_(See diagram above)_" : "_(Diagram rendering failed)_"
692
+ );
693
+ }
694
+ }
695
+ let decoratedText = processedText;
696
+ const telemetryEnabled = telemetryCfg === true || telemetryCfg && typeof telemetryCfg === "object" && telemetryCfg.enabled === true;
697
+ if (telemetryEnabled) {
698
+ const traceInfo = this.getTraceInfo();
699
+ if (traceInfo?.traceId) {
700
+ const suffix = `\`trace_id: ${traceInfo.traceId}\``;
701
+ decoratedText = `${decoratedText}
702
+
703
+ ${suffix}`;
704
+ }
705
+ }
706
+ const formattedText = formatSlackText(decoratedText);
707
+ await slack.chat.postMessage({ channel, text: formattedText, thread_ts: threadTs });
708
+ ctx.logger.info(
709
+ `[slack-frontend] posted AI reply for ${checkId} to ${channel} thread=${threadTs}`
710
+ );
711
+ } catch (outerErr) {
712
+ try {
713
+ ctx.logger.warn(
714
+ `[slack-frontend] maybePostDirectReply failed for ${checkId}: ${outerErr instanceof Error ? outerErr.message : String(outerErr)}`
715
+ );
716
+ } catch {
717
+ }
718
+ }
719
+ }
720
+ getTraceInfo() {
721
+ try {
722
+ const span = trace.getSpan(context.active());
723
+ if (!span) return null;
724
+ const ctx = span.spanContext();
725
+ if (!ctx || !ctx.traceId) return null;
726
+ return { traceId: ctx.traceId, spanId: ctx.spanId };
727
+ } catch {
728
+ return null;
729
+ }
730
+ }
731
+ };
732
+ export {
733
+ SlackFrontend
734
+ };
735
+ //# sourceMappingURL=slack-frontend-BVKW3GD5.mjs.map