@probelabs/visor 0.1.106 → 0.1.111

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 (530) hide show
  1. package/README.md +71 -2
  2. package/action.yml +1 -1
  3. package/defaults/code-refiner.yaml +114 -0
  4. package/defaults/{.visor.yaml → code-review.yaml} +35 -226
  5. package/defaults/override.yaml +52 -0
  6. package/defaults/task-refinement.yaml +624 -0
  7. package/defaults/visor.tests.yaml +685 -0
  8. package/defaults/visor.yaml +483 -0
  9. package/dist/action-cli-bridge.d.ts +11 -82
  10. package/dist/action-cli-bridge.d.ts.map +1 -1
  11. package/dist/ai-review-service.d.ts +28 -9
  12. package/dist/ai-review-service.d.ts.map +1 -1
  13. package/dist/check-execution-engine.d.ts +19 -331
  14. package/dist/check-execution-engine.d.ts.map +1 -1
  15. package/dist/cli-main.d.ts.map +1 -1
  16. package/dist/cli.d.ts +0 -1
  17. package/dist/cli.d.ts.map +1 -1
  18. package/dist/config.d.ts +16 -0
  19. package/dist/config.d.ts.map +1 -1
  20. package/dist/cron-scheduler.d.ts +3 -3
  21. package/dist/cron-scheduler.d.ts.map +1 -1
  22. package/dist/debug-visualizer/ws-server.d.ts +7 -1
  23. package/dist/debug-visualizer/ws-server.d.ts.map +1 -1
  24. package/dist/defaults/code-refiner.yaml +114 -0
  25. package/dist/defaults/{.visor.yaml → code-review.yaml} +35 -226
  26. package/dist/defaults/override.yaml +52 -0
  27. package/dist/defaults/task-refinement.yaml +624 -0
  28. package/dist/defaults/visor.tests.yaml +685 -0
  29. package/dist/defaults/visor.yaml +483 -0
  30. package/dist/docs/DEPLOYMENT.md +118 -0
  31. package/dist/docs/GITHUB_CHECKS.md +280 -0
  32. package/dist/docs/NPM_USAGE.md +208 -0
  33. package/dist/docs/action-reference.md +19 -0
  34. package/dist/docs/advanced-ai.md +237 -0
  35. package/dist/docs/ai-configuration.md +535 -0
  36. package/dist/docs/ai-custom-tools-usage.md +261 -0
  37. package/dist/docs/ai-custom-tools.md +392 -0
  38. package/dist/docs/author-permissions.md +610 -0
  39. package/dist/docs/bot-transports-rfc.md +23 -0
  40. package/dist/docs/ci-cli-mode.md +34 -0
  41. package/dist/docs/claude-code.md +74 -0
  42. package/dist/docs/command-provider.md +559 -0
  43. package/dist/docs/commands.md +8 -0
  44. package/dist/docs/configuration.md +324 -0
  45. package/dist/docs/custom-tools.md +424 -0
  46. package/dist/docs/dashboards/README.md +23 -0
  47. package/dist/docs/dashboards/grafana-visor-diagrams.json +20 -0
  48. package/dist/docs/dashboards/grafana-visor-overview.json +33 -0
  49. package/dist/docs/debug-visualizer-progress.md +572 -0
  50. package/dist/docs/debug-visualizer-rfc.md +691 -0
  51. package/dist/docs/debug-visualizer.md +114 -0
  52. package/dist/docs/debugging.md +636 -0
  53. package/dist/docs/default-output-schema.md +28 -0
  54. package/dist/docs/dependencies.md +369 -0
  55. package/dist/docs/dev-playbook.md +9 -0
  56. package/dist/docs/engine-pause-resume-rfc.md +192 -0
  57. package/dist/docs/engine-state-machine-plan.md +333 -0
  58. package/dist/docs/event-driven-github-integration-rfc.md +743 -0
  59. package/dist/docs/event-triggers.md +292 -0
  60. package/dist/docs/execution-statistics-rfc.md +290 -0
  61. package/dist/docs/fact-validator-gap-analysis.md +178 -0
  62. package/dist/docs/fact-validator-implementation-plan.md +1235 -0
  63. package/dist/docs/fail-if.md +95 -0
  64. package/dist/docs/failure-conditions-implementation.md +271 -0
  65. package/dist/docs/failure-conditions-schema.md +173 -0
  66. package/dist/docs/failure-routing-rfc.md +193 -0
  67. package/dist/docs/failure-routing.md +507 -0
  68. package/dist/docs/foreach-dependency-propagation.md +473 -0
  69. package/dist/docs/github-ops.md +89 -0
  70. package/dist/docs/goto-forward-run-plan.md +113 -0
  71. package/dist/docs/guides/criticality-modes.md +332 -0
  72. package/dist/docs/guides/fault-management-and-contracts.md +738 -0
  73. package/dist/docs/guides/workflow-style-guide.md +224 -0
  74. package/dist/docs/http.md +299 -0
  75. package/dist/docs/human-input-provider.md +372 -0
  76. package/dist/docs/lifecycle-hooks.md +253 -0
  77. package/dist/docs/limits.md +64 -0
  78. package/dist/docs/liquid-templates.md +490 -0
  79. package/dist/docs/loop-routing-refactor.md +89 -0
  80. package/dist/docs/mcp-provider.md +557 -0
  81. package/dist/docs/mcp.md +124 -0
  82. package/dist/docs/memory.md +903 -0
  83. package/dist/docs/observability.md +12 -0
  84. package/dist/docs/output-formats.md +20 -0
  85. package/dist/docs/output-formatting.md +29 -0
  86. package/dist/docs/output-history.md +383 -0
  87. package/dist/docs/performance.md +6 -0
  88. package/dist/docs/pluggable.md +124 -0
  89. package/dist/docs/proposals/snapshot-scope-execution.md +236 -0
  90. package/dist/docs/providers/git-checkout.md +589 -0
  91. package/dist/docs/recipes.md +474 -0
  92. package/dist/docs/rfc/git-checkout-step.md +601 -0
  93. package/dist/docs/rfc/on_init-hook.md +1294 -0
  94. package/dist/docs/rfc/workspace-isolation.md +216 -0
  95. package/dist/docs/roadmap/criticality-implementation-tasks.md +92 -0
  96. package/dist/docs/router-patterns.md +339 -0
  97. package/dist/docs/schema-next-pr.md +10 -0
  98. package/dist/docs/schema-templates.md +68 -0
  99. package/dist/docs/script.md +34 -0
  100. package/dist/docs/sdk.md +222 -0
  101. package/dist/docs/security.md +7 -0
  102. package/dist/docs/suppressions.md +89 -0
  103. package/dist/docs/tag-filtering.md +258 -0
  104. package/dist/docs/telemetry-setup.md +119 -0
  105. package/dist/docs/telemetry-tracing-rfc.md +275 -0
  106. package/dist/docs/test-framework-rfc.md +680 -0
  107. package/dist/docs/testing/assertions.md +85 -0
  108. package/dist/docs/testing/ci.md +44 -0
  109. package/dist/docs/testing/cli.md +41 -0
  110. package/dist/docs/testing/cookbook.md +172 -0
  111. package/dist/docs/testing/dsl-reference.md +199 -0
  112. package/dist/docs/testing/fixtures-and-mocks.md +91 -0
  113. package/dist/docs/testing/flows.md +92 -0
  114. package/dist/docs/testing/getting-started.md +93 -0
  115. package/dist/docs/testing/troubleshooting.md +55 -0
  116. package/dist/docs/timeouts.md +50 -0
  117. package/dist/docs/troubleshooting.md +7 -0
  118. package/dist/docs/visor-sdk-rfc.md +186 -0
  119. package/dist/docs/workflows.md +569 -0
  120. package/dist/engine/on-finish/orchestrator.d.ts +19 -0
  121. package/dist/engine/on-finish/orchestrator.d.ts.map +1 -0
  122. package/dist/engine/on-finish/utils.d.ts +44 -0
  123. package/dist/engine/on-finish/utils.d.ts.map +1 -0
  124. package/dist/event-bus/event-bus.d.ts +13 -0
  125. package/dist/event-bus/event-bus.d.ts.map +1 -0
  126. package/dist/event-bus/types.d.ts +71 -0
  127. package/dist/event-bus/types.d.ts.map +1 -0
  128. package/dist/examples/.claude/agents/code-reviewer.md +69 -0
  129. package/dist/examples/.mcp.json +34 -0
  130. package/dist/examples/CALCULATOR-SDK.md +364 -0
  131. package/dist/examples/README.md +384 -0
  132. package/dist/examples/ai-custom-tools-example.yaml +206 -0
  133. package/dist/examples/ai-custom-tools-simple.yaml +76 -0
  134. package/dist/examples/ai-retry-fallback-config.yaml +180 -0
  135. package/dist/examples/ai-with-bash.yaml +126 -0
  136. package/dist/examples/ai-with-mcp.yaml +82 -0
  137. package/dist/examples/basic-human-input.yaml +15 -0
  138. package/dist/examples/bedrock-config.yaml +77 -0
  139. package/dist/examples/calculator-config.yaml +133 -0
  140. package/dist/examples/calculator-json-output-guide.md +311 -0
  141. package/dist/examples/calculator-sdk-automated.ts +340 -0
  142. package/dist/examples/calculator-sdk-example.ts +275 -0
  143. package/dist/examples/calculator-sdk-json.ts +331 -0
  144. package/dist/examples/calculator-sdk-real.ts +374 -0
  145. package/dist/examples/calculator-sdk-test.ts +148 -0
  146. package/dist/examples/claude-code-config.yaml +191 -0
  147. package/dist/examples/cron-webhook-config.yaml +215 -0
  148. package/dist/examples/custom-template.liquid +57 -0
  149. package/dist/examples/custom-tools-example.yaml +281 -0
  150. package/dist/examples/enhanced-config.yaml +165 -0
  151. package/dist/examples/environments/visor.base.yaml +92 -0
  152. package/dist/examples/environments/visor.dev.yaml +33 -0
  153. package/dist/examples/environments/visor.prod.yaml +95 -0
  154. package/dist/examples/environments/visor.staging.yaml +46 -0
  155. package/dist/examples/fact-validator.yaml +361 -0
  156. package/dist/examples/fail-if-simple.yaml +90 -0
  157. package/dist/examples/failure-conditions-advanced.yaml +136 -0
  158. package/dist/examples/failure-conditions-basic.yaml +48 -0
  159. package/dist/examples/failure-conditions-github-style.yaml +119 -0
  160. package/dist/examples/failure-conditions-migration.yaml +74 -0
  161. package/dist/examples/for-loop-example.yaml +176 -0
  162. package/dist/examples/forEach-example.yaml +120 -0
  163. package/dist/examples/git-checkout-basic.yaml +32 -0
  164. package/dist/examples/git-checkout-compare.yaml +59 -0
  165. package/dist/examples/git-checkout-cross-repo.yaml +76 -0
  166. package/dist/examples/github-workflow-with-tags.yml +163 -0
  167. package/dist/examples/http-integration-config.yaml +240 -0
  168. package/dist/examples/https-server-config.yaml +209 -0
  169. package/dist/examples/human-input-example.yaml +63 -0
  170. package/dist/examples/if-conditions.yaml +173 -0
  171. package/dist/examples/jira-simple-example.yaml +56 -0
  172. package/dist/examples/jira-single-issue-workflow.yaml +166 -0
  173. package/dist/examples/jira-workflow-mcp.yaml +182 -0
  174. package/dist/examples/mcp/analyzer.py +119 -0
  175. package/dist/examples/mcp-provider-example.yaml +301 -0
  176. package/dist/examples/memory-counter.yaml +99 -0
  177. package/dist/examples/memory-error-collection.yaml +104 -0
  178. package/dist/examples/memory-exec-js.yaml +247 -0
  179. package/dist/examples/memory-namespace-isolation.yaml +184 -0
  180. package/dist/examples/memory-retry-counter.yaml +65 -0
  181. package/dist/examples/memory-state-machine.yaml +170 -0
  182. package/dist/examples/on-init-import-demo.yaml +179 -0
  183. package/dist/examples/outputs-raw-basic.yaml +26 -0
  184. package/dist/examples/project-with-tools.yaml +174 -0
  185. package/dist/examples/prompts/architecture-analysis.liquid +116 -0
  186. package/dist/examples/prompts/security-comprehensive.liquid +107 -0
  187. package/dist/examples/quick-start-tags.yaml +53 -0
  188. package/dist/examples/reusable-tools.yaml +92 -0
  189. package/dist/examples/reusable-workflows.yaml +88 -0
  190. package/dist/examples/routing-basic.yaml +35 -0
  191. package/dist/examples/routing-dynamic-js.yaml +46 -0
  192. package/dist/examples/routing-foreach.yaml +34 -0
  193. package/dist/examples/routing-goto-event.yaml +34 -0
  194. package/dist/examples/routing-on-success.yaml +25 -0
  195. package/dist/examples/run-calculator-demo.sh +71 -0
  196. package/dist/examples/sdk-basic.mjs +10 -0
  197. package/dist/examples/sdk-cjs.cjs +10 -0
  198. package/dist/examples/sdk-comprehensive.mjs +175 -0
  199. package/dist/examples/sdk-manual-config.mjs +65 -0
  200. package/dist/examples/sdk-typescript.js +81 -0
  201. package/dist/examples/sdk-typescript.ts +92 -0
  202. package/dist/examples/session-reuse-config.yaml +151 -0
  203. package/dist/examples/session-reuse-self.yaml +81 -0
  204. package/dist/examples/slack-simple-chat.yaml +775 -0
  205. package/dist/examples/templates/security-report.liquid +137 -0
  206. package/dist/examples/tools-library.yaml +281 -0
  207. package/dist/examples/transform-example.yaml +199 -0
  208. package/dist/examples/visor-with-tags.yaml +198 -0
  209. package/dist/examples/webhook-pipeline-config.yaml +218 -0
  210. package/dist/examples/workflows/calculator-workflow.yaml +163 -0
  211. package/dist/examples/workflows/code-quality.yaml +222 -0
  212. package/dist/examples/workflows/quick-pr-check.yaml +90 -0
  213. package/dist/examples/workflows/workflow-composition-example.yaml +130 -0
  214. package/dist/failure-condition-evaluator.d.ts +3 -0
  215. package/dist/failure-condition-evaluator.d.ts.map +1 -1
  216. package/dist/frontends/github-frontend.d.ts +58 -0
  217. package/dist/frontends/github-frontend.d.ts.map +1 -0
  218. package/dist/frontends/host.d.ts +47 -0
  219. package/dist/frontends/host.d.ts.map +1 -0
  220. package/dist/frontends/ndjson-sink.d.ts +12 -0
  221. package/dist/frontends/ndjson-sink.d.ts.map +1 -0
  222. package/dist/frontends/slack-frontend.d.ts +58 -0
  223. package/dist/frontends/slack-frontend.d.ts.map +1 -0
  224. package/dist/generated/config-schema.d.ts +967 -57
  225. package/dist/generated/config-schema.d.ts.map +1 -1
  226. package/dist/generated/config-schema.json +1033 -56
  227. package/dist/github-check-service.d.ts +4 -6
  228. package/dist/github-check-service.d.ts.map +1 -1
  229. package/dist/github-comments.d.ts +2 -4
  230. package/dist/github-comments.d.ts.map +1 -1
  231. package/dist/index.d.ts.map +1 -1
  232. package/dist/index.js +134327 -99004
  233. package/dist/liquid-extensions.d.ts.map +1 -1
  234. package/dist/logger.d.ts +2 -0
  235. package/dist/logger.d.ts.map +1 -1
  236. package/dist/memory-store.d.ts +6 -0
  237. package/dist/memory-store.d.ts.map +1 -1
  238. package/dist/output/assistant-json/template.liquid +0 -0
  239. package/dist/output/traces/run-2026-01-20T19-22-58-043Z.ndjson +138 -0
  240. package/dist/output/traces/run-2026-01-20T19-23-52-175Z.ndjson +1067 -0
  241. package/dist/output-formatters.d.ts +1 -1
  242. package/dist/output-formatters.d.ts.map +1 -1
  243. package/dist/providers/ai-check-provider.d.ts +12 -0
  244. package/dist/providers/ai-check-provider.d.ts.map +1 -1
  245. package/dist/providers/check-provider-registry.d.ts +6 -0
  246. package/dist/providers/check-provider-registry.d.ts.map +1 -1
  247. package/dist/providers/check-provider.interface.d.ts +43 -1
  248. package/dist/providers/check-provider.interface.d.ts.map +1 -1
  249. package/dist/providers/claude-code-check-provider.d.ts.map +1 -1
  250. package/dist/providers/command-check-provider.d.ts +1 -1
  251. package/dist/providers/command-check-provider.d.ts.map +1 -1
  252. package/dist/providers/custom-tool-executor.d.ts +61 -0
  253. package/dist/providers/custom-tool-executor.d.ts.map +1 -0
  254. package/dist/providers/git-checkout-provider.d.ts +25 -0
  255. package/dist/providers/git-checkout-provider.d.ts.map +1 -0
  256. package/dist/providers/github-ops-provider.d.ts.map +1 -1
  257. package/dist/providers/http-client-provider.d.ts +4 -4
  258. package/dist/providers/http-client-provider.d.ts.map +1 -1
  259. package/dist/providers/human-input-check-provider.d.ts +5 -0
  260. package/dist/providers/human-input-check-provider.d.ts.map +1 -1
  261. package/dist/providers/index.d.ts +1 -0
  262. package/dist/providers/index.d.ts.map +1 -1
  263. package/dist/providers/log-check-provider.d.ts +2 -5
  264. package/dist/providers/log-check-provider.d.ts.map +1 -1
  265. package/dist/providers/mcp-check-provider.d.ts +10 -4
  266. package/dist/providers/mcp-check-provider.d.ts.map +1 -1
  267. package/dist/providers/mcp-custom-sse-server.d.ts +66 -0
  268. package/dist/providers/mcp-custom-sse-server.d.ts.map +1 -0
  269. package/dist/providers/memory-check-provider.d.ts +2 -8
  270. package/dist/providers/memory-check-provider.d.ts.map +1 -1
  271. package/dist/providers/script-check-provider.d.ts +25 -0
  272. package/dist/providers/script-check-provider.d.ts.map +1 -0
  273. package/dist/providers/workflow-check-provider.d.ts +56 -0
  274. package/dist/providers/workflow-check-provider.d.ts.map +1 -0
  275. package/dist/reviewer.d.ts +2 -1
  276. package/dist/reviewer.d.ts.map +1 -1
  277. package/dist/sdk/check-provider-registry-534KL5HT.mjs +27 -0
  278. package/dist/sdk/chunk-23L3QRYX.mjs +16872 -0
  279. package/dist/sdk/chunk-23L3QRYX.mjs.map +1 -0
  280. package/dist/sdk/{chunk-TUTOLSFV.mjs → chunk-3OMWVM6J.mjs} +11 -1
  281. package/dist/sdk/chunk-3OMWVM6J.mjs.map +1 -0
  282. package/dist/sdk/chunk-7UK3NIIT.mjs +482 -0
  283. package/dist/sdk/chunk-7UK3NIIT.mjs.map +1 -0
  284. package/dist/sdk/chunk-AGIZJ4UZ.mjs +173 -0
  285. package/dist/sdk/chunk-AGIZJ4UZ.mjs.map +1 -0
  286. package/dist/sdk/chunk-AIVFBIS4.mjs +1371 -0
  287. package/dist/sdk/chunk-AIVFBIS4.mjs.map +1 -0
  288. package/dist/sdk/chunk-AK6BVWIT.mjs +426 -0
  289. package/dist/sdk/chunk-AK6BVWIT.mjs.map +1 -0
  290. package/dist/sdk/chunk-AUT26LHW.mjs +139 -0
  291. package/dist/sdk/chunk-AUT26LHW.mjs.map +1 -0
  292. package/dist/sdk/chunk-BOVFH3LI.mjs +232 -0
  293. package/dist/sdk/chunk-BOVFH3LI.mjs.map +1 -0
  294. package/dist/sdk/chunk-CNX7V5JK.mjs +89 -0
  295. package/dist/sdk/chunk-CNX7V5JK.mjs.map +1 -0
  296. package/dist/sdk/chunk-HTOKWMPO.mjs +157 -0
  297. package/dist/sdk/chunk-HTOKWMPO.mjs.map +1 -0
  298. package/dist/sdk/chunk-NAW3DB3I.mjs +197 -0
  299. package/dist/sdk/chunk-NAW3DB3I.mjs.map +1 -0
  300. package/dist/sdk/chunk-O5EZDNYL.mjs +274 -0
  301. package/dist/sdk/chunk-O5EZDNYL.mjs.map +1 -0
  302. package/dist/sdk/chunk-QR7MOMJH.mjs +558 -0
  303. package/dist/sdk/chunk-QR7MOMJH.mjs.map +1 -0
  304. package/dist/sdk/chunk-QY2XYPEV.mjs +3556 -0
  305. package/dist/sdk/chunk-QY2XYPEV.mjs.map +1 -0
  306. package/dist/sdk/chunk-S2RUE2RG.mjs +145 -0
  307. package/dist/sdk/chunk-S2RUE2RG.mjs.map +1 -0
  308. package/dist/sdk/chunk-SIWNBRTK.mjs +800 -0
  309. package/dist/sdk/chunk-SIWNBRTK.mjs.map +1 -0
  310. package/dist/sdk/chunk-YSN4G6CI.mjs +146 -0
  311. package/dist/sdk/chunk-YSN4G6CI.mjs.map +1 -0
  312. package/dist/sdk/chunk-ZYAUYXSW.mjs +206 -0
  313. package/dist/sdk/chunk-ZYAUYXSW.mjs.map +1 -0
  314. package/dist/sdk/command-executor-TYUV6HUS.mjs +14 -0
  315. package/dist/sdk/config-YNC2EOOT.mjs +16 -0
  316. package/dist/sdk/config-merger-PX3WIT57.mjs +10 -0
  317. package/dist/sdk/event-bus-5BEVPQ6T.mjs +35 -0
  318. package/dist/sdk/event-bus-5BEVPQ6T.mjs.map +1 -0
  319. package/dist/sdk/failure-condition-evaluator-YGTF2GHG.mjs +17 -0
  320. package/dist/sdk/git-repository-analyzer-HJC4MYW4.mjs +458 -0
  321. package/dist/sdk/git-repository-analyzer-HJC4MYW4.mjs.map +1 -0
  322. package/dist/sdk/github-frontend-SIAEOCON.mjs +1420 -0
  323. package/dist/sdk/github-frontend-SIAEOCON.mjs.map +1 -0
  324. package/dist/sdk/host-DXUYTNMU.mjs +52 -0
  325. package/dist/sdk/host-DXUYTNMU.mjs.map +1 -0
  326. package/dist/sdk/{liquid-extensions-KVL4MKRH.mjs → liquid-extensions-PKWCKK7E.mjs} +8 -2
  327. package/dist/sdk/memory-store-XGBB7LX7.mjs +12 -0
  328. package/dist/sdk/memory-store-XGBB7LX7.mjs.map +1 -0
  329. package/dist/sdk/metrics-7PP3EJUH.mjs +29 -0
  330. package/dist/sdk/metrics-7PP3EJUH.mjs.map +1 -0
  331. package/dist/sdk/ndjson-sink-B4V4NTAQ.mjs +44 -0
  332. package/dist/sdk/ndjson-sink-B4V4NTAQ.mjs.map +1 -0
  333. package/dist/sdk/prompt-state-YRJY6QAL.mjs +16 -0
  334. package/dist/sdk/prompt-state-YRJY6QAL.mjs.map +1 -0
  335. package/dist/sdk/renderer-schema-LPKN5UJS.mjs +51 -0
  336. package/dist/sdk/renderer-schema-LPKN5UJS.mjs.map +1 -0
  337. package/dist/sdk/routing-6N45MJ4F.mjs +24 -0
  338. package/dist/sdk/routing-6N45MJ4F.mjs.map +1 -0
  339. package/dist/sdk/sdk.d.mts +541 -22
  340. package/dist/sdk/sdk.d.ts +541 -22
  341. package/dist/sdk/sdk.js +27963 -16505
  342. package/dist/sdk/sdk.js.map +1 -1
  343. package/dist/sdk/sdk.mjs +1116 -2169
  344. package/dist/sdk/sdk.mjs.map +1 -1
  345. package/dist/sdk/session-registry-4E6YRQ77.mjs +10 -0
  346. package/dist/sdk/session-registry-4E6YRQ77.mjs.map +1 -0
  347. package/dist/sdk/slack-frontend-BVKW3GD5.mjs +735 -0
  348. package/dist/sdk/slack-frontend-BVKW3GD5.mjs.map +1 -0
  349. package/dist/sdk/trace-helpers-VP6QYVBX.mjs +23 -0
  350. package/dist/sdk/trace-helpers-VP6QYVBX.mjs.map +1 -0
  351. package/dist/sdk/{tracer-init-WC75N5NW.mjs → tracer-init-GSLPPLCD.mjs} +2 -2
  352. package/dist/sdk/tracer-init-GSLPPLCD.mjs.map +1 -0
  353. package/dist/sdk/workflow-registry-R6KSACFR.mjs +12 -0
  354. package/dist/sdk/workflow-registry-R6KSACFR.mjs.map +1 -0
  355. package/dist/sdk.d.ts.map +1 -1
  356. package/dist/slack/adapter.d.ts +36 -0
  357. package/dist/slack/adapter.d.ts.map +1 -0
  358. package/dist/slack/cache-prewarmer.d.ts +31 -0
  359. package/dist/slack/cache-prewarmer.d.ts.map +1 -0
  360. package/dist/slack/client.d.ts +77 -0
  361. package/dist/slack/client.d.ts.map +1 -0
  362. package/dist/slack/markdown.d.ts +45 -0
  363. package/dist/slack/markdown.d.ts.map +1 -0
  364. package/dist/slack/prompt-state.d.ts +33 -0
  365. package/dist/slack/prompt-state.d.ts.map +1 -0
  366. package/dist/slack/rate-limiter.d.ts +56 -0
  367. package/dist/slack/rate-limiter.d.ts.map +1 -0
  368. package/dist/slack/signature.d.ts +2 -0
  369. package/dist/slack/signature.d.ts.map +1 -0
  370. package/dist/slack/socket-runner.d.ts +42 -0
  371. package/dist/slack/socket-runner.d.ts.map +1 -0
  372. package/dist/slack/thread-cache.d.ts +51 -0
  373. package/dist/slack/thread-cache.d.ts.map +1 -0
  374. package/dist/snapshot-store.d.ts +59 -0
  375. package/dist/snapshot-store.d.ts.map +1 -0
  376. package/dist/state-machine/context/build-engine-context.d.ts +17 -0
  377. package/dist/state-machine/context/build-engine-context.d.ts.map +1 -0
  378. package/dist/state-machine/dispatch/dependency-gating.d.ts +12 -0
  379. package/dist/state-machine/dispatch/dependency-gating.d.ts.map +1 -0
  380. package/dist/state-machine/dispatch/execution-invoker.d.ts +14 -0
  381. package/dist/state-machine/dispatch/execution-invoker.d.ts.map +1 -0
  382. package/dist/state-machine/dispatch/foreach-processor.d.ts +8 -0
  383. package/dist/state-machine/dispatch/foreach-processor.d.ts.map +1 -0
  384. package/dist/state-machine/dispatch/history-snapshot.d.ts +8 -0
  385. package/dist/state-machine/dispatch/history-snapshot.d.ts.map +1 -0
  386. package/dist/state-machine/dispatch/on-init-handlers.d.ts +43 -0
  387. package/dist/state-machine/dispatch/on-init-handlers.d.ts.map +1 -0
  388. package/dist/state-machine/dispatch/renderer-schema.d.ts +8 -0
  389. package/dist/state-machine/dispatch/renderer-schema.d.ts.map +1 -0
  390. package/dist/state-machine/dispatch/stats-manager.d.ts +15 -0
  391. package/dist/state-machine/dispatch/stats-manager.d.ts.map +1 -0
  392. package/dist/state-machine/dispatch/template-renderer.d.ts +7 -0
  393. package/dist/state-machine/dispatch/template-renderer.d.ts.map +1 -0
  394. package/dist/state-machine/execution/summary.d.ts +8 -0
  395. package/dist/state-machine/execution/summary.d.ts.map +1 -0
  396. package/dist/state-machine/runner.d.ts +79 -0
  397. package/dist/state-machine/runner.d.ts.map +1 -0
  398. package/dist/state-machine/states/check-running.d.ts +14 -0
  399. package/dist/state-machine/states/check-running.d.ts.map +1 -0
  400. package/dist/state-machine/states/completed.d.ts +12 -0
  401. package/dist/state-machine/states/completed.d.ts.map +1 -0
  402. package/dist/state-machine/states/error.d.ts +11 -0
  403. package/dist/state-machine/states/error.d.ts.map +1 -0
  404. package/dist/state-machine/states/init.d.ts +11 -0
  405. package/dist/state-machine/states/init.d.ts.map +1 -0
  406. package/dist/state-machine/states/level-dispatch.d.ts +17 -0
  407. package/dist/state-machine/states/level-dispatch.d.ts.map +1 -0
  408. package/dist/state-machine/states/plan-ready.d.ts +12 -0
  409. package/dist/state-machine/states/plan-ready.d.ts.map +1 -0
  410. package/dist/state-machine/states/routing.d.ts +52 -0
  411. package/dist/state-machine/states/routing.d.ts.map +1 -0
  412. package/dist/state-machine/states/wave-planning.d.ts +14 -0
  413. package/dist/state-machine/states/wave-planning.d.ts.map +1 -0
  414. package/dist/state-machine/workflow-projection.d.ts +47 -0
  415. package/dist/state-machine/workflow-projection.d.ts.map +1 -0
  416. package/dist/state-machine-execution-engine.d.ts +159 -0
  417. package/dist/state-machine-execution-engine.d.ts.map +1 -0
  418. package/dist/telemetry/opentelemetry.d.ts.map +1 -1
  419. package/dist/telemetry/state-capture.d.ts +5 -0
  420. package/dist/telemetry/state-capture.d.ts.map +1 -1
  421. package/dist/test-runner/assertions.d.ts +59 -0
  422. package/dist/test-runner/assertions.d.ts.map +1 -0
  423. package/dist/test-runner/core/environment.d.ts +8 -0
  424. package/dist/test-runner/core/environment.d.ts.map +1 -0
  425. package/dist/test-runner/core/fixture.d.ts +3 -0
  426. package/dist/test-runner/core/fixture.d.ts.map +1 -0
  427. package/dist/test-runner/core/flow-stage.d.ts +32 -0
  428. package/dist/test-runner/core/flow-stage.d.ts.map +1 -0
  429. package/dist/test-runner/core/mocks.d.ts +8 -0
  430. package/dist/test-runner/core/mocks.d.ts.map +1 -0
  431. package/dist/test-runner/core/test-execution-wrapper.d.ts +18 -0
  432. package/dist/test-runner/core/test-execution-wrapper.d.ts.map +1 -0
  433. package/dist/test-runner/evaluators.d.ts +45 -0
  434. package/dist/test-runner/evaluators.d.ts.map +1 -0
  435. package/dist/test-runner/fixture-loader.d.ts +30 -0
  436. package/dist/test-runner/fixture-loader.d.ts.map +1 -0
  437. package/dist/test-runner/index.d.ts +127 -0
  438. package/dist/test-runner/index.d.ts.map +1 -0
  439. package/dist/test-runner/recorders/github-recorder.d.ts +23 -0
  440. package/dist/test-runner/recorders/github-recorder.d.ts.map +1 -0
  441. package/dist/test-runner/recorders/global-recorder.d.ts +4 -0
  442. package/dist/test-runner/recorders/global-recorder.d.ts.map +1 -0
  443. package/dist/test-runner/recorders/slack-recorder.d.ts +17 -0
  444. package/dist/test-runner/recorders/slack-recorder.d.ts.map +1 -0
  445. package/dist/test-runner/utils/selectors.d.ts +2 -0
  446. package/dist/test-runner/utils/selectors.d.ts.map +1 -0
  447. package/dist/test-runner/validator.d.ts +8 -0
  448. package/dist/test-runner/validator.d.ts.map +1 -0
  449. package/dist/traces/run-2026-01-20T19-22-58-043Z.ndjson +138 -0
  450. package/dist/traces/run-2026-01-20T19-23-52-175Z.ndjson +1067 -0
  451. package/dist/types/bot.d.ts +109 -0
  452. package/dist/types/bot.d.ts.map +1 -0
  453. package/dist/types/cli.d.ts +8 -1
  454. package/dist/types/cli.d.ts.map +1 -1
  455. package/dist/types/config.d.ts +459 -9
  456. package/dist/types/config.d.ts.map +1 -1
  457. package/dist/types/engine.d.ts +177 -0
  458. package/dist/types/engine.d.ts.map +1 -0
  459. package/dist/types/execution.d.ts +73 -0
  460. package/dist/types/execution.d.ts.map +1 -0
  461. package/dist/types/git-checkout.d.ts +76 -0
  462. package/dist/types/git-checkout.d.ts.map +1 -0
  463. package/dist/types/github.d.ts +51 -0
  464. package/dist/types/github.d.ts.map +1 -0
  465. package/dist/types/workflow.d.ts +237 -0
  466. package/dist/types/workflow.d.ts.map +1 -0
  467. package/dist/utils/command-executor.d.ts +43 -0
  468. package/dist/utils/command-executor.d.ts.map +1 -0
  469. package/dist/utils/comment-metadata.d.ts +21 -0
  470. package/dist/utils/comment-metadata.d.ts.map +1 -0
  471. package/dist/utils/config-loader.d.ts.map +1 -1
  472. package/dist/utils/config-merger.d.ts.map +1 -1
  473. package/dist/utils/env-exposure.d.ts +3 -0
  474. package/dist/utils/env-exposure.d.ts.map +1 -0
  475. package/dist/utils/file-exclusion.d.ts.map +1 -1
  476. package/dist/utils/interactive-prompt.d.ts +1 -1
  477. package/dist/utils/interactive-prompt.d.ts.map +1 -1
  478. package/dist/utils/json-text-extractor.d.ts +17 -0
  479. package/dist/utils/json-text-extractor.d.ts.map +1 -0
  480. package/dist/utils/sandbox.d.ts +10 -0
  481. package/dist/utils/sandbox.d.ts.map +1 -1
  482. package/dist/utils/script-memory-ops.d.ts +21 -0
  483. package/dist/utils/script-memory-ops.d.ts.map +1 -0
  484. package/dist/utils/template-context.d.ts +8 -0
  485. package/dist/utils/template-context.d.ts.map +1 -0
  486. package/dist/utils/tracer-init.d.ts.map +1 -1
  487. package/dist/utils/workspace-manager.d.ts +118 -0
  488. package/dist/utils/workspace-manager.d.ts.map +1 -0
  489. package/dist/utils/worktree-cleanup.d.ts +33 -0
  490. package/dist/utils/worktree-cleanup.d.ts.map +1 -0
  491. package/dist/utils/worktree-manager.d.ts +153 -0
  492. package/dist/utils/worktree-manager.d.ts.map +1 -0
  493. package/dist/webhook-server.d.ts +3 -3
  494. package/dist/webhook-server.d.ts.map +1 -1
  495. package/dist/workflow-executor.d.ts +81 -0
  496. package/dist/workflow-executor.d.ts.map +1 -0
  497. package/dist/workflow-registry.d.ts +79 -0
  498. package/dist/workflow-registry.d.ts.map +1 -0
  499. package/package.json +12 -5
  500. package/dist/output/traces/run-2025-10-22T18-22-56-873Z.ndjson +0 -218
  501. package/dist/sdk/check-execution-engine-2YYKUUSH.mjs +0 -11
  502. package/dist/sdk/check-execution-engine-6QJXYYON.mjs +0 -11
  503. package/dist/sdk/check-execution-engine-PJZ4ZOKG.mjs +0 -11
  504. package/dist/sdk/chunk-33QVZ2D4.mjs +0 -316
  505. package/dist/sdk/chunk-33QVZ2D4.mjs.map +0 -1
  506. package/dist/sdk/chunk-B5QBV2QJ.mjs +0 -752
  507. package/dist/sdk/chunk-B5QBV2QJ.mjs.map +0 -1
  508. package/dist/sdk/chunk-BVFNRCHT.mjs +0 -14129
  509. package/dist/sdk/chunk-BVFNRCHT.mjs.map +0 -1
  510. package/dist/sdk/chunk-KWZW23FG.mjs +0 -14129
  511. package/dist/sdk/chunk-KWZW23FG.mjs.map +0 -1
  512. package/dist/sdk/chunk-O4RP4BRH.mjs +0 -14092
  513. package/dist/sdk/chunk-O4RP4BRH.mjs.map +0 -1
  514. package/dist/sdk/chunk-TUTOLSFV.mjs.map +0 -1
  515. package/dist/sdk/chunk-U5D2LY66.mjs +0 -245
  516. package/dist/sdk/chunk-U5D2LY66.mjs.map +0 -1
  517. package/dist/sdk/chunk-U7X54EMV.mjs +0 -331
  518. package/dist/sdk/chunk-U7X54EMV.mjs.map +0 -1
  519. package/dist/sdk/config-merger-TWUBWFC2.mjs +0 -8
  520. package/dist/sdk/mermaid-telemetry-SN6A2TKW.mjs +0 -61
  521. package/dist/sdk/mermaid-telemetry-SN6A2TKW.mjs.map +0 -1
  522. package/dist/sdk/mermaid-telemetry-YCTIG76M.mjs +0 -61
  523. package/dist/sdk/mermaid-telemetry-YCTIG76M.mjs.map +0 -1
  524. package/dist/traces/run-2025-10-22T18-22-56-873Z.ndjson +0 -218
  525. /package/dist/sdk/{check-execution-engine-2YYKUUSH.mjs.map → check-provider-registry-534KL5HT.mjs.map} +0 -0
  526. /package/dist/sdk/{check-execution-engine-6QJXYYON.mjs.map → command-executor-TYUV6HUS.mjs.map} +0 -0
  527. /package/dist/sdk/{check-execution-engine-PJZ4ZOKG.mjs.map → config-YNC2EOOT.mjs.map} +0 -0
  528. /package/dist/sdk/{config-merger-TWUBWFC2.mjs.map → config-merger-PX3WIT57.mjs.map} +0 -0
  529. /package/dist/sdk/{liquid-extensions-KVL4MKRH.mjs.map → failure-condition-evaluator-YGTF2GHG.mjs.map} +0 -0
  530. /package/dist/sdk/{tracer-init-WC75N5NW.mjs.map → liquid-extensions-PKWCKK7E.mjs.map} +0 -0
@@ -0,0 +1,1420 @@
1
+ import {
2
+ failure_condition_evaluator_exports,
3
+ init_failure_condition_evaluator
4
+ } from "./chunk-SIWNBRTK.mjs";
5
+ import "./chunk-BOVFH3LI.mjs";
6
+ import "./chunk-ZYAUYXSW.mjs";
7
+ import "./chunk-S2RUE2RG.mjs";
8
+ import "./chunk-CNX7V5JK.mjs";
9
+ import "./chunk-7UK3NIIT.mjs";
10
+ import {
11
+ init_logger,
12
+ logger
13
+ } from "./chunk-AGIZJ4UZ.mjs";
14
+ import "./chunk-YSN4G6CI.mjs";
15
+ import {
16
+ __esm,
17
+ __export,
18
+ __toCommonJS
19
+ } from "./chunk-WMJKH4XE.mjs";
20
+
21
+ // src/footer.ts
22
+ function generateFooter(options = {}) {
23
+ const { includeMetadata, includeSeparator = true } = options;
24
+ const parts = [];
25
+ if (includeSeparator) {
26
+ parts.push("---");
27
+ parts.push("");
28
+ }
29
+ parts.push(
30
+ "*Powered by [Visor](https://probelabs.com/visor) from [Probelabs](https://probelabs.com)*"
31
+ );
32
+ if (includeMetadata) {
33
+ const { lastUpdated, triggeredBy, commitSha } = includeMetadata;
34
+ const commitInfo = commitSha ? ` | Commit: ${commitSha.substring(0, 7)}` : "";
35
+ parts.push("");
36
+ parts.push(`*Last updated: ${lastUpdated} | Triggered by: ${triggeredBy}${commitInfo}*`);
37
+ }
38
+ parts.push("");
39
+ parts.push("\u{1F4A1} **TIP:** You can chat with Visor using `/visor ask <your question>`");
40
+ return parts.join("\n");
41
+ }
42
+ var init_footer = __esm({
43
+ "src/footer.ts"() {
44
+ "use strict";
45
+ }
46
+ });
47
+
48
+ // src/github-check-service.ts
49
+ var github_check_service_exports = {};
50
+ __export(github_check_service_exports, {
51
+ GitHubCheckService: () => GitHubCheckService
52
+ });
53
+ var GitHubCheckService;
54
+ var init_github_check_service = __esm({
55
+ "src/github-check-service.ts"() {
56
+ "use strict";
57
+ init_footer();
58
+ GitHubCheckService = class {
59
+ octokit;
60
+ maxAnnotations = 50;
61
+ // GitHub API limit
62
+ constructor(octokit) {
63
+ this.octokit = octokit;
64
+ }
65
+ /**
66
+ * Create a new check run in queued status
67
+ * M4: Includes engine_mode metadata in summary
68
+ */
69
+ async createCheckRun(options, summary) {
70
+ try {
71
+ const enhancedSummary = summary && options.engine_mode ? {
72
+ ...summary,
73
+ summary: `${summary.summary}
74
+
75
+ _Engine: ${options.engine_mode}_`
76
+ } : summary;
77
+ const response = await this.octokit.rest.checks.create({
78
+ owner: options.owner,
79
+ repo: options.repo,
80
+ name: options.name,
81
+ head_sha: options.head_sha,
82
+ status: "queued",
83
+ details_url: options.details_url,
84
+ external_id: options.external_id,
85
+ output: enhancedSummary ? {
86
+ title: enhancedSummary.title,
87
+ summary: enhancedSummary.summary,
88
+ text: enhancedSummary.text
89
+ } : void 0
90
+ });
91
+ return {
92
+ id: response.data.id,
93
+ url: response.data.html_url || ""
94
+ };
95
+ } catch (error) {
96
+ throw new Error(
97
+ `Failed to create check run: ${error instanceof Error ? error.message : String(error)}`
98
+ );
99
+ }
100
+ }
101
+ /**
102
+ * Update check run to in_progress status
103
+ */
104
+ async updateCheckRunInProgress(owner, repo, check_run_id, summary) {
105
+ try {
106
+ await this.octokit.rest.checks.update({
107
+ owner,
108
+ repo,
109
+ check_run_id,
110
+ status: "in_progress",
111
+ output: summary ? {
112
+ title: summary.title,
113
+ summary: summary.summary,
114
+ text: summary.text
115
+ } : void 0
116
+ });
117
+ } catch (error) {
118
+ throw new Error(
119
+ `Failed to update check run to in_progress: ${error instanceof Error ? error.message : String(error)}`
120
+ );
121
+ }
122
+ }
123
+ /**
124
+ * Complete a check run with results based on failure conditions
125
+ */
126
+ async completeCheckRun(owner, repo, check_run_id, checkName, failureResults, reviewIssues = [], executionError, filesChangedInCommit, prNumber, currentCommitSha) {
127
+ try {
128
+ if (prNumber && currentCommitSha) {
129
+ await this.clearOldAnnotations(
130
+ owner,
131
+ repo,
132
+ prNumber,
133
+ checkName,
134
+ currentCommitSha,
135
+ check_run_id
136
+ );
137
+ }
138
+ const { conclusion, summary } = this.determineCheckRunConclusion(
139
+ checkName,
140
+ failureResults,
141
+ reviewIssues,
142
+ executionError
143
+ );
144
+ let filteredIssues = reviewIssues.filter(
145
+ (issue) => !(issue.file === "system" && issue.line === 0)
146
+ );
147
+ if (filesChangedInCommit && filesChangedInCommit.length > 0) {
148
+ filteredIssues = filteredIssues.filter(
149
+ (issue) => filesChangedInCommit.some((changedFile) => issue.file === changedFile)
150
+ );
151
+ }
152
+ const annotations = this.convertIssuesToAnnotations(filteredIssues);
153
+ await this.octokit.rest.checks.update({
154
+ owner,
155
+ repo,
156
+ check_run_id,
157
+ status: "completed",
158
+ conclusion,
159
+ completed_at: (/* @__PURE__ */ new Date()).toISOString(),
160
+ output: {
161
+ title: summary.title,
162
+ summary: summary.summary,
163
+ text: summary.text,
164
+ annotations: annotations.slice(0, this.maxAnnotations)
165
+ // GitHub limit
166
+ }
167
+ });
168
+ } catch (error) {
169
+ throw new Error(
170
+ `Failed to complete check run: ${error instanceof Error ? error.message : String(error)}`
171
+ );
172
+ }
173
+ }
174
+ /**
175
+ * Determine check run conclusion based on failure conditions and issues
176
+ */
177
+ determineCheckRunConclusion(checkName, failureResults, reviewIssues, executionError) {
178
+ if (executionError) {
179
+ return {
180
+ conclusion: "failure",
181
+ summary: {
182
+ title: "\u274C Check Execution Failed",
183
+ summary: `The ${checkName} check failed to execute properly.`,
184
+ text: `**Error:** ${executionError}
185
+
186
+ Please check your configuration and try again.`
187
+ }
188
+ };
189
+ }
190
+ const failedConditions = failureResults.filter((result) => result.failed);
191
+ const criticalIssues = reviewIssues.filter((issue) => issue.severity === "critical").length;
192
+ const errorIssues = reviewIssues.filter((issue) => issue.severity === "error").length;
193
+ const warningIssues = reviewIssues.filter((issue) => issue.severity === "warning").length;
194
+ const totalIssues = reviewIssues.length;
195
+ let conclusion;
196
+ let title;
197
+ let summaryText;
198
+ let details;
199
+ if (failedConditions.length > 0) {
200
+ conclusion = "failure";
201
+ title = "\u{1F6A8} Check Failed";
202
+ summaryText = `${checkName} check failed because fail_if condition was met.`;
203
+ details = this.formatCheckDetails(failureResults, reviewIssues, {
204
+ failedConditions: failedConditions.length,
205
+ warningConditions: 0,
206
+ criticalIssues,
207
+ errorIssues,
208
+ warningIssues,
209
+ totalIssues
210
+ });
211
+ } else {
212
+ conclusion = "success";
213
+ if (criticalIssues > 0 || errorIssues > 0) {
214
+ title = "\u2705 Check Passed (Issues Found)";
215
+ summaryText = `${checkName} check passed. Found ${criticalIssues} critical and ${errorIssues} error issues, but fail_if condition was not met.`;
216
+ } else if (warningIssues > 0) {
217
+ title = "\u2705 Check Passed (Warnings Found)";
218
+ summaryText = `${checkName} check passed. Found ${warningIssues} warning${warningIssues === 1 ? "" : "s"}, but fail_if condition was not met.`;
219
+ } else {
220
+ title = "\u2705 Check Passed";
221
+ summaryText = `${checkName} check completed successfully with no issues found.`;
222
+ }
223
+ details = this.formatCheckDetails(failureResults, reviewIssues, {
224
+ failedConditions: 0,
225
+ warningConditions: 0,
226
+ criticalIssues,
227
+ errorIssues,
228
+ warningIssues,
229
+ totalIssues
230
+ });
231
+ }
232
+ return {
233
+ conclusion,
234
+ summary: {
235
+ title,
236
+ summary: summaryText,
237
+ text: details
238
+ }
239
+ };
240
+ }
241
+ /**
242
+ * Format detailed check results for the check run summary
243
+ */
244
+ formatCheckDetails(failureResults, reviewIssues, counts) {
245
+ const sections = [];
246
+ sections.push("## \u{1F4CA} Summary");
247
+ sections.push(`- **Total Issues:** ${counts.totalIssues}`);
248
+ if (counts.criticalIssues > 0) {
249
+ sections.push(`- **Critical Issues:** ${counts.criticalIssues}`);
250
+ }
251
+ if (counts.errorIssues > 0) {
252
+ sections.push(`- **Error Issues:** ${counts.errorIssues}`);
253
+ }
254
+ if (counts.warningIssues > 0) {
255
+ sections.push(`- **Warning Issues:** ${counts.warningIssues}`);
256
+ }
257
+ sections.push("");
258
+ if (failureResults.length > 0) {
259
+ sections.push("## \u{1F50D} Failure Condition Results");
260
+ const failedConditions = failureResults.filter((result) => result.failed);
261
+ const passedConditions = failureResults.filter((result) => !result.failed);
262
+ if (failedConditions.length > 0) {
263
+ sections.push("### Failed Conditions");
264
+ failedConditions.forEach((condition) => {
265
+ sections.push(
266
+ `- **${condition.conditionName}**: ${condition.message || condition.expression}`
267
+ );
268
+ if (condition.severity) {
269
+ const icon = this.getSeverityEmoji(condition.severity);
270
+ sections.push(` - Severity: ${icon} ${condition.severity}`);
271
+ }
272
+ });
273
+ sections.push("");
274
+ }
275
+ if (passedConditions.length > 0) {
276
+ sections.push("### Passed Conditions");
277
+ passedConditions.forEach((condition) => {
278
+ sections.push(
279
+ `- **${condition.conditionName}**: ${condition.message || "Condition passed"}`
280
+ );
281
+ });
282
+ sections.push("");
283
+ }
284
+ }
285
+ if (reviewIssues.length > 0) {
286
+ const issuesByCategory = this.groupIssuesByCategory(reviewIssues);
287
+ sections.push("## Issues by Category");
288
+ Object.entries(issuesByCategory).forEach(([category, issues]) => {
289
+ if (issues.length > 0) {
290
+ sections.push(
291
+ `### ${category.charAt(0).toUpperCase() + category.slice(1)} (${issues.length})`
292
+ );
293
+ const displayIssues = issues.slice(0, 5);
294
+ displayIssues.forEach((issue) => {
295
+ const severityIcon = this.getSeverityEmoji(issue.severity);
296
+ sections.push(`- ${severityIcon} **${issue.file}:${issue.line}** - ${issue.message}`);
297
+ });
298
+ if (issues.length > 5) {
299
+ sections.push(`- *...and ${issues.length - 5} more ${category} issues*`);
300
+ }
301
+ sections.push("");
302
+ }
303
+ });
304
+ }
305
+ sections.push("");
306
+ sections.push(generateFooter());
307
+ return sections.join("\n");
308
+ }
309
+ /**
310
+ * Convert review issues to GitHub check run annotations
311
+ */
312
+ convertIssuesToAnnotations(reviewIssues) {
313
+ return reviewIssues.slice(0, this.maxAnnotations).map((issue) => ({
314
+ path: issue.file,
315
+ start_line: issue.line,
316
+ end_line: issue.endLine || issue.line,
317
+ annotation_level: this.mapSeverityToAnnotationLevel(issue.severity),
318
+ message: issue.message,
319
+ title: `${issue.category} Issue`,
320
+ raw_details: issue.suggestion || void 0
321
+ }));
322
+ }
323
+ /**
324
+ * Map Visor issue severity to GitHub annotation level
325
+ */
326
+ mapSeverityToAnnotationLevel(severity) {
327
+ switch (severity) {
328
+ case "critical":
329
+ case "error":
330
+ return "failure";
331
+ case "warning":
332
+ return "warning";
333
+ case "info":
334
+ default:
335
+ return "notice";
336
+ }
337
+ }
338
+ /**
339
+ * Group issues by category
340
+ */
341
+ groupIssuesByCategory(issues) {
342
+ const grouped = {};
343
+ issues.forEach((issue) => {
344
+ const category = issue.category || "general";
345
+ if (!grouped[category]) {
346
+ grouped[category] = [];
347
+ }
348
+ grouped[category].push(issue);
349
+ });
350
+ return grouped;
351
+ }
352
+ /**
353
+ * Get emoji for issue severity (allowed; step/category emojis are removed)
354
+ */
355
+ getSeverityEmoji(severity) {
356
+ const iconMap = {
357
+ critical: "\u{1F6A8}",
358
+ error: "\u274C",
359
+ warning: "\u26A0\uFE0F",
360
+ info: "\u2139\uFE0F"
361
+ };
362
+ return iconMap[String(severity || "").toLowerCase()] || "";
363
+ }
364
+ /**
365
+ * Create multiple check runs for different checks with failure condition support
366
+ */
367
+ async createMultipleCheckRuns(options, checkResults) {
368
+ const results = [];
369
+ for (const checkResult of checkResults) {
370
+ try {
371
+ const checkRun = await this.createCheckRun({
372
+ ...options,
373
+ name: `Visor: ${checkResult.checkName}`,
374
+ external_id: `visor-${checkResult.checkName}-${options.head_sha.substring(0, 7)}`
375
+ });
376
+ await this.updateCheckRunInProgress(options.owner, options.repo, checkRun.id, {
377
+ title: `Running ${checkResult.checkName} check...`,
378
+ summary: `Analyzing code with ${checkResult.checkName} check using AI.`
379
+ });
380
+ await this.completeCheckRun(
381
+ options.owner,
382
+ options.repo,
383
+ checkRun.id,
384
+ checkResult.checkName,
385
+ checkResult.failureResults,
386
+ checkResult.reviewIssues,
387
+ checkResult.executionError
388
+ );
389
+ results.push({
390
+ checkName: checkResult.checkName,
391
+ id: checkRun.id,
392
+ url: checkRun.url
393
+ });
394
+ } catch (error) {
395
+ console.error(`Failed to create check run for ${checkResult.checkName}:`, error);
396
+ }
397
+ }
398
+ return results;
399
+ }
400
+ /**
401
+ * Get check runs for a specific commit
402
+ */
403
+ async getCheckRuns(owner, repo, ref) {
404
+ try {
405
+ const response = await this.octokit.rest.checks.listForRef({
406
+ owner,
407
+ repo,
408
+ ref,
409
+ filter: "all"
410
+ });
411
+ return response.data.check_runs.filter((check) => check.name.startsWith("Visor:")).map((check) => ({
412
+ id: check.id,
413
+ name: check.name,
414
+ status: check.status,
415
+ conclusion: check.conclusion
416
+ }));
417
+ } catch (error) {
418
+ throw new Error(
419
+ `Failed to get check runs: ${error instanceof Error ? error.message : String(error)}`
420
+ );
421
+ }
422
+ }
423
+ /**
424
+ * Get check runs for a specific commit SHA
425
+ * Returns all check runs with the given name on this commit
426
+ */
427
+ async getCheckRunsForCommit(owner, repo, commitSha, checkName) {
428
+ try {
429
+ const checksResponse = await this.octokit.rest.checks.listForRef({
430
+ owner,
431
+ repo,
432
+ ref: commitSha,
433
+ check_name: `Visor: ${checkName}`
434
+ });
435
+ return checksResponse.data.check_runs.map((check) => ({
436
+ id: check.id,
437
+ head_sha: commitSha
438
+ }));
439
+ } catch (error) {
440
+ throw new Error(
441
+ `Failed to get check runs for commit ${commitSha}: ${error instanceof Error ? error.message : String(error)}`
442
+ );
443
+ }
444
+ }
445
+ /**
446
+ * Clear annotations from old check runs on the current commit
447
+ * This prevents annotation accumulation when a check runs multiple times on the same commit
448
+ * (e.g., force push, re-running checks)
449
+ */
450
+ async clearOldAnnotations(owner, repo, prNumber, checkName, currentCommitSha, currentCheckRunId) {
451
+ try {
452
+ const allCheckRuns = await this.getCheckRunsForCommit(
453
+ owner,
454
+ repo,
455
+ currentCommitSha,
456
+ checkName
457
+ );
458
+ const oldRuns = allCheckRuns.filter((run) => run.id !== currentCheckRunId);
459
+ if (oldRuns.length === 0) {
460
+ console.debug(`No old check runs to clear for ${checkName} on commit ${currentCommitSha}`);
461
+ return;
462
+ }
463
+ console.debug(
464
+ `Clearing ${oldRuns.length} old check run(s) for ${checkName} on commit ${currentCommitSha.substring(0, 7)} (keeping current run ${currentCheckRunId})`
465
+ );
466
+ for (const run of oldRuns) {
467
+ try {
468
+ await this.octokit.rest.checks.update({
469
+ owner,
470
+ repo,
471
+ check_run_id: run.id,
472
+ output: {
473
+ title: "Outdated",
474
+ summary: "This check has been superseded by a newer run.",
475
+ annotations: []
476
+ // Clear annotations
477
+ }
478
+ });
479
+ console.debug(`\u2713 Cleared annotations from check run ${run.id}`);
480
+ } catch (error) {
481
+ console.debug(`Could not clear annotations for check run ${run.id}:`, error);
482
+ }
483
+ }
484
+ } catch (error) {
485
+ console.warn("Failed to clear old annotations:", error);
486
+ }
487
+ }
488
+ };
489
+ }
490
+ });
491
+
492
+ // src/github-comments.ts
493
+ var github_comments_exports = {};
494
+ __export(github_comments_exports, {
495
+ CommentManager: () => CommentManager
496
+ });
497
+ import { v4 as uuidv4 } from "uuid";
498
+ var CommentManager;
499
+ var init_github_comments = __esm({
500
+ "src/github-comments.ts"() {
501
+ "use strict";
502
+ init_logger();
503
+ init_footer();
504
+ CommentManager = class {
505
+ octokit;
506
+ retryConfig;
507
+ constructor(octokit, retryConfig) {
508
+ this.octokit = octokit;
509
+ this.retryConfig = {
510
+ maxRetries: 3,
511
+ baseDelay: 1e3,
512
+ maxDelay: 1e4,
513
+ backoffFactor: 2,
514
+ ...retryConfig
515
+ };
516
+ }
517
+ /**
518
+ * Find existing Visor comment by comment ID marker
519
+ */
520
+ async findVisorComment(owner, repo, prNumber, commentId) {
521
+ try {
522
+ const comments = await this.octokit.rest.issues.listComments({
523
+ owner,
524
+ repo,
525
+ issue_number: prNumber,
526
+ per_page: 100
527
+ // GitHub default max
528
+ });
529
+ for (const comment of comments.data) {
530
+ if (comment.body && this.isVisorComment(comment.body, commentId)) {
531
+ return comment;
532
+ }
533
+ }
534
+ return null;
535
+ } catch (error) {
536
+ if (this.isRateLimitError(
537
+ error
538
+ )) {
539
+ await this.handleRateLimit(error);
540
+ return this.findVisorComment(owner, repo, prNumber, commentId);
541
+ }
542
+ throw error;
543
+ }
544
+ }
545
+ /**
546
+ * Update existing comment or create new one with collision detection
547
+ */
548
+ async updateOrCreateComment(owner, repo, prNumber, content, options = {}) {
549
+ const {
550
+ commentId = this.generateCommentId(),
551
+ triggeredBy = "unknown",
552
+ allowConcurrentUpdates = false,
553
+ commitSha,
554
+ cachedGithubCommentId
555
+ } = options;
556
+ return this.withRetry(async () => {
557
+ let existingComment = await this.findVisorComment(owner, repo, prNumber, commentId);
558
+ if (!existingComment && cachedGithubCommentId) {
559
+ try {
560
+ const cachedComment = await this.octokit.rest.issues.getComment({
561
+ owner,
562
+ repo,
563
+ comment_id: cachedGithubCommentId
564
+ });
565
+ if (cachedComment.data && this.isVisorComment(cachedComment.data.body || "", commentId)) {
566
+ existingComment = cachedComment.data;
567
+ logger.debug(
568
+ `[github-comments] Found comment via cached ID ${cachedGithubCommentId} (not visible in listComments yet)`
569
+ );
570
+ }
571
+ } catch (_e) {
572
+ logger.debug(
573
+ `[github-comments] Cached comment ${cachedGithubCommentId} not found, will create new`
574
+ );
575
+ }
576
+ }
577
+ const formattedContent = this.formatCommentWithMetadata(content, {
578
+ commentId,
579
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
580
+ triggeredBy,
581
+ commitSha
582
+ });
583
+ if (existingComment) {
584
+ if (!allowConcurrentUpdates) {
585
+ const currentComment = await this.octokit.rest.issues.getComment({
586
+ owner,
587
+ repo,
588
+ comment_id: existingComment.id
589
+ });
590
+ if (currentComment.data.updated_at !== existingComment.updated_at) {
591
+ throw new Error(
592
+ `Comment collision detected for comment ${commentId}. Another process may have updated it.`
593
+ );
594
+ }
595
+ }
596
+ const updatedComment = await this.octokit.rest.issues.updateComment({
597
+ owner,
598
+ repo,
599
+ comment_id: existingComment.id,
600
+ body: formattedContent
601
+ });
602
+ logger.info(
603
+ `\u2705 Successfully updated comment (ID: ${commentId}, GitHub ID: ${existingComment.id}) on PR #${prNumber} in ${owner}/${repo}`
604
+ );
605
+ return updatedComment.data;
606
+ } else {
607
+ const newComment = await this.octokit.rest.issues.createComment({
608
+ owner,
609
+ repo,
610
+ issue_number: prNumber,
611
+ body: formattedContent
612
+ });
613
+ logger.info(
614
+ `\u2705 Successfully created comment (ID: ${commentId}, GitHub ID: ${newComment.data.id}) on PR #${prNumber} in ${owner}/${repo}`
615
+ );
616
+ return newComment.data;
617
+ }
618
+ });
619
+ }
620
+ /**
621
+ * Format comment content with metadata markers
622
+ */
623
+ formatCommentWithMetadata(content, metadata) {
624
+ const { commentId, lastUpdated, triggeredBy, commitSha } = metadata;
625
+ const footer = generateFooter({
626
+ includeMetadata: {
627
+ lastUpdated,
628
+ triggeredBy,
629
+ commitSha
630
+ }
631
+ });
632
+ return `<!-- visor-comment-id:${commentId} -->
633
+ ${content}
634
+
635
+ ${footer}
636
+ <!-- /visor-comment-id:${commentId} -->`;
637
+ }
638
+ /**
639
+ * Create collapsible sections for comment content
640
+ */
641
+ createCollapsibleSection(title, content, isExpanded = false) {
642
+ const openAttribute = isExpanded ? " open" : "";
643
+ return `<details${openAttribute}>
644
+ <summary>${title}</summary>
645
+
646
+ ${content}
647
+
648
+ </details>`;
649
+ }
650
+ /**
651
+ * Group review results by check type with collapsible sections
652
+ */
653
+ formatGroupedResults(results, groupBy = "check") {
654
+ const grouped = this.groupResults(results, groupBy);
655
+ const sections = [];
656
+ for (const [groupKey, items] of Object.entries(grouped)) {
657
+ const totalScore = items.reduce((sum, item) => sum + (item.score || 0), 0) / items.length;
658
+ const totalIssues = items.reduce((sum, item) => sum + (item.issuesFound || 0), 0);
659
+ const title = this.formatGroupTitle(groupKey, totalScore, totalIssues);
660
+ const sectionContent = items.map((item) => item.content).join("\n\n");
661
+ sections.push(this.createCollapsibleSection(title, sectionContent, totalIssues > 0));
662
+ }
663
+ return sections.join("\n\n");
664
+ }
665
+ /**
666
+ * Generate unique comment ID
667
+ */
668
+ generateCommentId() {
669
+ return uuidv4().substring(0, 8);
670
+ }
671
+ /**
672
+ * Check if comment is a Visor comment
673
+ */
674
+ isVisorComment(body, commentId) {
675
+ if (commentId) {
676
+ if (body.includes(`visor-comment-id:${commentId} `) || body.includes(`visor-comment-id:${commentId} -->`)) {
677
+ return true;
678
+ }
679
+ if (commentId.startsWith("pr-review-") && body.includes("visor-review-")) {
680
+ return true;
681
+ }
682
+ return false;
683
+ }
684
+ return body.includes("visor-comment-id:") && body.includes("<!-- /visor-comment-id:") || body.includes("visor-review-");
685
+ }
686
+ /**
687
+ * Extract comment ID from comment body
688
+ */
689
+ extractCommentId(body) {
690
+ const match = body.match(/visor-comment-id:([a-f0-9-]+)/);
691
+ return match ? match[1] : null;
692
+ }
693
+ /**
694
+ * Handle rate limiting with exponential backoff
695
+ */
696
+ async handleRateLimit(error) {
697
+ const resetTime = error.response?.headers?.["x-ratelimit-reset"];
698
+ if (resetTime) {
699
+ const resetDate = new Date(parseInt(resetTime) * 1e3);
700
+ const waitTime = Math.max(resetDate.getTime() - Date.now(), this.retryConfig.baseDelay);
701
+ console.log(`Rate limit exceeded. Waiting ${Math.round(waitTime / 1e3)}s until reset...`);
702
+ await this.sleep(Math.min(waitTime, this.retryConfig.maxDelay));
703
+ } else {
704
+ await this.sleep(this.retryConfig.baseDelay);
705
+ }
706
+ }
707
+ /**
708
+ * Check if error is a rate limit error
709
+ */
710
+ isRateLimitError(error) {
711
+ return error.status === 403 && (error.response?.data?.message?.includes("rate limit") ?? false);
712
+ }
713
+ /**
714
+ * Check if error should not be retried (auth errors, not found, etc.)
715
+ */
716
+ isNonRetryableError(error) {
717
+ const nonRetryableStatuses = [401, 404, 422];
718
+ const status = error.status || error.response?.status;
719
+ if (status === 403) {
720
+ return !this.isRateLimitError(error);
721
+ }
722
+ return status !== void 0 && nonRetryableStatuses.includes(status);
723
+ }
724
+ /**
725
+ * Retry wrapper with exponential backoff
726
+ */
727
+ async withRetry(operation) {
728
+ let lastError = new Error("Unknown error");
729
+ for (let attempt = 0; attempt <= this.retryConfig.maxRetries; attempt++) {
730
+ try {
731
+ return await operation();
732
+ } catch (error) {
733
+ lastError = error instanceof Error ? error : new Error(String(error));
734
+ if (attempt === this.retryConfig.maxRetries) {
735
+ break;
736
+ }
737
+ if (this.isRateLimitError(
738
+ error
739
+ )) {
740
+ await this.handleRateLimit(error);
741
+ } else if (this.isNonRetryableError(error)) {
742
+ throw error;
743
+ } else {
744
+ const computed = this.retryConfig.baseDelay * Math.pow(this.retryConfig.backoffFactor, attempt);
745
+ const delay = computed > this.retryConfig.maxDelay ? Math.max(0, this.retryConfig.maxDelay - 1) : computed;
746
+ await this.sleep(delay);
747
+ }
748
+ }
749
+ }
750
+ throw lastError;
751
+ }
752
+ /**
753
+ * Sleep utility
754
+ */
755
+ sleep(ms) {
756
+ return new Promise((resolve) => {
757
+ const t = setTimeout(resolve, ms);
758
+ if (typeof t.unref === "function") {
759
+ try {
760
+ t.unref();
761
+ } catch {
762
+ }
763
+ }
764
+ });
765
+ }
766
+ /**
767
+ * Group results by specified criteria
768
+ */
769
+ groupResults(results, groupBy) {
770
+ const grouped = {};
771
+ for (const result of results) {
772
+ const key = groupBy === "check" ? result.checkType : this.getSeverityGroup(result.score);
773
+ if (!grouped[key]) {
774
+ grouped[key] = [];
775
+ }
776
+ grouped[key].push(result);
777
+ }
778
+ return grouped;
779
+ }
780
+ /**
781
+ * Get severity group based on score
782
+ */
783
+ getSeverityGroup(score) {
784
+ if (!score) return "Unknown";
785
+ if (score >= 90) return "Excellent";
786
+ if (score >= 75) return "Good";
787
+ if (score >= 50) return "Needs Improvement";
788
+ return "Critical Issues";
789
+ }
790
+ // Emoji helper removed: plain titles are used in group headers
791
+ /**
792
+ * Format group title with score and issue count
793
+ */
794
+ formatGroupTitle(groupKey, score, issuesFound) {
795
+ const formattedScore = Math.round(score);
796
+ return `${groupKey} Review (Score: ${formattedScore}/100)${issuesFound > 0 ? ` - ${issuesFound} issues found` : ""}`;
797
+ }
798
+ };
799
+ }
800
+ });
801
+
802
+ // src/frontends/github-frontend.ts
803
+ init_logger();
804
+
805
+ // src/utils/json-text-extractor.ts
806
+ function extractTextFieldFromMalformedJson(content) {
807
+ const fieldPatterns = [
808
+ /^\s*\{\s*"text"\s*:\s*"/i,
809
+ /^\s*\{\s*"response"\s*:\s*"/i,
810
+ /^\s*\{\s*"message"\s*:\s*"/i
811
+ ];
812
+ for (const pattern of fieldPatterns) {
813
+ const match = pattern.exec(content);
814
+ if (match) {
815
+ const valueStart = match[0].length;
816
+ const remaining = content.substring(valueStart);
817
+ let value = "";
818
+ let i = 0;
819
+ while (i < remaining.length) {
820
+ const char = remaining[i];
821
+ if (char === "\\" && i + 1 < remaining.length) {
822
+ const nextChar = remaining[i + 1];
823
+ if (nextChar === "n") {
824
+ value += "\n";
825
+ } else if (nextChar === "r") {
826
+ value += "\r";
827
+ } else if (nextChar === "t") {
828
+ value += " ";
829
+ } else if (nextChar === '"') {
830
+ value += '"';
831
+ } else if (nextChar === "\\") {
832
+ value += "\\";
833
+ } else {
834
+ value += char + nextChar;
835
+ }
836
+ i += 2;
837
+ } else if (char === '"') {
838
+ break;
839
+ } else {
840
+ value += char;
841
+ i++;
842
+ }
843
+ }
844
+ if (value.trim().length > 0) {
845
+ return value.trim();
846
+ }
847
+ }
848
+ }
849
+ return void 0;
850
+ }
851
+ function extractTextFromJson(content) {
852
+ if (content === void 0 || content === null) return void 0;
853
+ let parsed = content;
854
+ if (typeof content === "string") {
855
+ const trimmed = content.trim();
856
+ if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) {
857
+ return trimmed.length > 0 ? trimmed : void 0;
858
+ }
859
+ try {
860
+ parsed = JSON.parse(trimmed);
861
+ } catch {
862
+ const extracted = extractTextFieldFromMalformedJson(trimmed);
863
+ if (extracted) {
864
+ return extracted;
865
+ }
866
+ return trimmed.length > 0 ? trimmed : void 0;
867
+ }
868
+ }
869
+ if (parsed && typeof parsed === "object") {
870
+ const txt = parsed.text || parsed.response || parsed.message;
871
+ if (typeof txt === "string" && txt.trim()) {
872
+ return txt.trim();
873
+ }
874
+ }
875
+ if (typeof content === "string") {
876
+ const trimmed = content.trim();
877
+ return trimmed.length > 0 ? trimmed : void 0;
878
+ }
879
+ return void 0;
880
+ }
881
+
882
+ // src/frontends/github-frontend.ts
883
+ var GitHubFrontend = class {
884
+ name = "github";
885
+ subs = [];
886
+ checkRunIds = /* @__PURE__ */ new Map();
887
+ revision = 0;
888
+ cachedCommentId;
889
+ // legacy single-thread id (kept for compatibility)
890
+ // Group → (checkId → SectionState)
891
+ stepStatusByGroup = /* @__PURE__ */ new Map();
892
+ // Debounce/coalescing state
893
+ debounceMs = 400;
894
+ maxWaitMs = 2e3;
895
+ _timer = null;
896
+ _lastFlush = 0;
897
+ _pendingIds = /* @__PURE__ */ new Set();
898
+ // Mutex for serializing comment updates per group
899
+ updateLocks = /* @__PURE__ */ new Map();
900
+ minUpdateDelayMs = 1e3;
901
+ // Minimum delay between updates (public for testing)
902
+ // Cache of created GitHub comment IDs per group to handle API eventual consistency
903
+ createdCommentGithubIds = /* @__PURE__ */ new Map();
904
+ start(ctx) {
905
+ const log = ctx.logger;
906
+ const bus = ctx.eventBus;
907
+ const octokit = ctx.octokit;
908
+ const repo = ctx.run.repo;
909
+ const pr = ctx.run.pr;
910
+ const headSha = ctx.run.headSha;
911
+ const canPostComments = !!(octokit && repo && pr);
912
+ const canPostChecks = !!(octokit && repo && pr && headSha);
913
+ const svc = canPostChecks ? new (init_github_check_service(), __toCommonJS(github_check_service_exports)).GitHubCheckService(octokit) : null;
914
+ const CommentManager2 = (init_github_comments(), __toCommonJS(github_comments_exports)).CommentManager;
915
+ const comments = canPostComments ? new CommentManager2(octokit) : null;
916
+ const threadKey = repo && pr && headSha ? `${repo.owner}/${repo.name}#${pr}@${(headSha || "").substring(0, 7)}` : ctx.run.runId;
917
+ this.cachedCommentId = `visor-thread-${threadKey}`;
918
+ this.subs.push(
919
+ bus.on("CheckScheduled", async (env) => {
920
+ const ev = env && env.payload || env;
921
+ try {
922
+ if (!canPostChecks || !svc) return;
923
+ if (this.checkRunIds.has(ev.checkId)) return;
924
+ const group = this.getGroupForCheck(ctx, ev.checkId);
925
+ this.upsertSectionState(group, ev.checkId, {
926
+ status: "queued",
927
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
928
+ });
929
+ const res = await svc.createCheckRun(
930
+ {
931
+ owner: repo.owner,
932
+ repo: repo.name,
933
+ head_sha: headSha,
934
+ name: `Visor: ${ev.checkId}`,
935
+ external_id: `visor:${ctx.run.runId}:${ev.checkId}`,
936
+ engine_mode: "state-machine"
937
+ },
938
+ { title: `${ev.checkId}`, summary: "Queued" }
939
+ );
940
+ this.checkRunIds.set(ev.checkId, res.id);
941
+ } catch (e) {
942
+ log.warn(
943
+ `[github-frontend] createCheckRun failed for ${ev.checkId}: ${e instanceof Error ? e.message : e}`
944
+ );
945
+ }
946
+ })
947
+ );
948
+ this.subs.push(
949
+ bus.on("CheckCompleted", async (env) => {
950
+ const ev = env && env.payload || env;
951
+ try {
952
+ if (canPostChecks && svc && this.checkRunIds.has(ev.checkId)) {
953
+ const id = this.checkRunIds.get(ev.checkId);
954
+ const issues = Array.isArray(ev.result?.issues) ? ev.result.issues : [];
955
+ const failureResults = await this.evaluateFailureResults(ctx, ev.checkId, ev.result);
956
+ await svc.completeCheckRun(
957
+ repo.owner,
958
+ repo.name,
959
+ id,
960
+ ev.checkId,
961
+ failureResults,
962
+ issues,
963
+ void 0,
964
+ void 0,
965
+ pr,
966
+ headSha
967
+ );
968
+ }
969
+ if (canPostComments && comments) {
970
+ const count = Array.isArray(ev.result?.issues) ? ev.result.issues.length : 0;
971
+ const failureResults = await this.evaluateFailureResults(ctx, ev.checkId, ev.result);
972
+ const failed = Array.isArray(failureResults) ? failureResults.some((r) => r && r.failed) : false;
973
+ const group = this.getGroupForCheck(ctx, ev.checkId);
974
+ const rawContent = ev?.result?.content;
975
+ const extractedContent = extractTextFromJson(rawContent);
976
+ this.upsertSectionState(group, ev.checkId, {
977
+ status: "completed",
978
+ conclusion: failed ? "failure" : "success",
979
+ issues: count,
980
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
981
+ content: extractedContent
982
+ });
983
+ await this.updateGroupedComment(ctx, comments, group, ev.checkId);
984
+ }
985
+ } catch (e) {
986
+ log.warn(
987
+ `[github-frontend] handle CheckCompleted failed: ${e instanceof Error ? e.message : e}`
988
+ );
989
+ }
990
+ })
991
+ );
992
+ this.subs.push(
993
+ bus.on("CheckErrored", async (env) => {
994
+ const ev = env && env.payload || env;
995
+ try {
996
+ if (canPostChecks && svc && this.checkRunIds.has(ev.checkId)) {
997
+ const id = this.checkRunIds.get(ev.checkId);
998
+ await svc.completeCheckRun(
999
+ repo.owner,
1000
+ repo.name,
1001
+ id,
1002
+ ev.checkId,
1003
+ [],
1004
+ [],
1005
+ ev.error?.message || "Execution error",
1006
+ void 0,
1007
+ pr,
1008
+ headSha
1009
+ );
1010
+ }
1011
+ if (canPostComments && comments) {
1012
+ const group = this.getGroupForCheck(ctx, ev.checkId);
1013
+ this.upsertSectionState(group, ev.checkId, {
1014
+ status: "errored",
1015
+ conclusion: "failure",
1016
+ issues: 0,
1017
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
1018
+ error: ev.error?.message || "Execution error"
1019
+ });
1020
+ await this.updateGroupedComment(ctx, comments, group, ev.checkId);
1021
+ }
1022
+ } catch (e) {
1023
+ log.warn(
1024
+ `[github-frontend] handle CheckErrored failed: ${e instanceof Error ? e.message : e}`
1025
+ );
1026
+ }
1027
+ })
1028
+ );
1029
+ this.subs.push(
1030
+ bus.on("StateTransition", async (env) => {
1031
+ const ev = env && env.payload || env;
1032
+ try {
1033
+ if (ev.to === "Completed" || ev.to === "Error") {
1034
+ if (canPostComments && comments) {
1035
+ for (const group of this.stepStatusByGroup.keys()) {
1036
+ await this.updateGroupedComment(ctx, comments, group);
1037
+ }
1038
+ }
1039
+ }
1040
+ } catch (e) {
1041
+ log.warn(
1042
+ `[github-frontend] handle StateTransition failed: ${e instanceof Error ? e.message : e}`
1043
+ );
1044
+ }
1045
+ })
1046
+ );
1047
+ }
1048
+ stop() {
1049
+ for (const s of this.subs) s.unsubscribe();
1050
+ this.subs = [];
1051
+ }
1052
+ async buildFullBody(ctx, group) {
1053
+ const header = this.renderThreadHeader(ctx, group);
1054
+ const sections = this.renderSections(ctx, group);
1055
+ return `${header}
1056
+
1057
+ ${sections}
1058
+
1059
+ <!-- visor:thread-end key="${this.threadKeyFor(ctx)}" -->`;
1060
+ }
1061
+ threadKeyFor(ctx) {
1062
+ const r = ctx.run;
1063
+ return r.repo && r.pr && r.headSha ? `${r.repo.owner}/${r.repo.name}#${r.pr}@${(r.headSha || "").substring(0, 7)}` : r.runId;
1064
+ }
1065
+ renderThreadHeader(ctx, group) {
1066
+ const header = {
1067
+ key: this.threadKeyFor(ctx),
1068
+ runId: ctx.run.runId,
1069
+ workflowId: ctx.run.workflowId,
1070
+ revision: this.revision,
1071
+ group,
1072
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString()
1073
+ };
1074
+ return `<!-- visor:thread=${JSON.stringify(header)} -->`;
1075
+ }
1076
+ renderSections(ctx, group) {
1077
+ const lines = [];
1078
+ const groupMap = this.stepStatusByGroup.get(group) || /* @__PURE__ */ new Map();
1079
+ for (const [checkId, st] of groupMap.entries()) {
1080
+ const start = `<!-- visor:section=${JSON.stringify({ id: checkId, revision: this.revision })} -->`;
1081
+ const end = `<!-- visor:section-end id="${checkId}" -->`;
1082
+ const body = st.content && st.content.toString().trim().length > 0 ? st.content.toString().trim() : "";
1083
+ lines.push(`${start}
1084
+ ${body}
1085
+ ${end}`);
1086
+ }
1087
+ return lines.join("\\n\\n");
1088
+ }
1089
+ /**
1090
+ * Acquires a mutex lock for the given group and executes the update.
1091
+ * This ensures only one comment update happens at a time per group,
1092
+ * preventing race conditions where updates overwrite each other.
1093
+ *
1094
+ * Uses a proper queue-based mutex: each new caller chains onto the previous
1095
+ * lock, ensuring strict serialization even when multiple callers wait
1096
+ * simultaneously.
1097
+ */
1098
+ async updateGroupedComment(ctx, comments, group, changedIds) {
1099
+ const existingLock = this.updateLocks.get(group);
1100
+ let resolveLock;
1101
+ const ourLock = new Promise((resolve) => {
1102
+ resolveLock = resolve;
1103
+ });
1104
+ this.updateLocks.set(group, ourLock);
1105
+ try {
1106
+ if (existingLock) {
1107
+ try {
1108
+ await existingLock;
1109
+ } catch (error) {
1110
+ logger.warn(
1111
+ `[github-frontend] Previous update for group ${group} failed: ${error instanceof Error ? error.message : error}`
1112
+ );
1113
+ }
1114
+ }
1115
+ await this.performGroupedCommentUpdate(ctx, comments, group, changedIds);
1116
+ } finally {
1117
+ if (this.updateLocks.get(group) === ourLock) {
1118
+ this.updateLocks.delete(group);
1119
+ }
1120
+ resolveLock();
1121
+ }
1122
+ }
1123
+ /**
1124
+ * Performs the actual comment update with delay enforcement.
1125
+ */
1126
+ async performGroupedCommentUpdate(ctx, comments, group, changedIds) {
1127
+ try {
1128
+ if (!ctx.run.repo || !ctx.run.pr) return;
1129
+ const config = ctx.config;
1130
+ const prCommentEnabled = config?.output?.pr_comment?.enabled !== false;
1131
+ if (!prCommentEnabled) {
1132
+ logger.debug(
1133
+ `[github-frontend] PR comments disabled in config, skipping comment for group: ${group}`
1134
+ );
1135
+ return;
1136
+ }
1137
+ const timeSinceLastFlush = Date.now() - this._lastFlush;
1138
+ if (this._lastFlush > 0 && timeSinceLastFlush < this.minUpdateDelayMs) {
1139
+ const delay = this.minUpdateDelayMs - timeSinceLastFlush;
1140
+ logger.debug(
1141
+ `[github-frontend] Waiting ${delay}ms before next update to prevent rate limiting`
1142
+ );
1143
+ await this.sleep(delay);
1144
+ }
1145
+ this.revision++;
1146
+ const commentId = this.commentIdForGroup(ctx, group);
1147
+ const mergedBody = await this.mergeIntoExistingBody(ctx, comments, group, changedIds);
1148
+ const cachedGithubId = this.createdCommentGithubIds.get(commentId);
1149
+ const result = await comments.updateOrCreateComment(
1150
+ ctx.run.repo.owner,
1151
+ ctx.run.repo.name,
1152
+ ctx.run.pr,
1153
+ mergedBody,
1154
+ {
1155
+ commentId,
1156
+ triggeredBy: this.deriveTriggeredBy(ctx),
1157
+ commitSha: ctx.run.headSha,
1158
+ // Pass the cached GitHub comment ID if available
1159
+ cachedGithubCommentId: cachedGithubId
1160
+ }
1161
+ );
1162
+ if (result && result.id) {
1163
+ this.createdCommentGithubIds.set(commentId, result.id);
1164
+ }
1165
+ this._lastFlush = Date.now();
1166
+ } catch (e) {
1167
+ logger.debug(
1168
+ `[github-frontend] updateGroupedComment failed: ${e instanceof Error ? e.message : e}`
1169
+ );
1170
+ }
1171
+ }
1172
+ deriveTriggeredBy(ctx) {
1173
+ const ev = ctx.run.event || "";
1174
+ const actor = ctx.run.actor;
1175
+ const commentEvents = /* @__PURE__ */ new Set([
1176
+ "issue_comment",
1177
+ "issue_comment_created",
1178
+ "pr_comment",
1179
+ "comment",
1180
+ "pull_request_review_comment"
1181
+ ]);
1182
+ if (commentEvents.has(ev) && actor) return actor;
1183
+ if (ev) return ev;
1184
+ return actor || "unknown";
1185
+ }
1186
+ async mergeIntoExistingBody(ctx, comments, group, changedIds) {
1187
+ const repo = ctx.run.repo;
1188
+ const pr = ctx.run.pr;
1189
+ const existing = await comments.findVisorComment(
1190
+ repo.owner,
1191
+ repo.name,
1192
+ pr,
1193
+ this.commentIdForGroup(ctx, group)
1194
+ );
1195
+ if (!existing || !existing.body) return this.buildFullBody(ctx, group);
1196
+ const body = String(existing.body);
1197
+ const doc = this.parseSections(body);
1198
+ doc.header = {
1199
+ ...doc.header || {},
1200
+ key: this.threadKeyFor(ctx),
1201
+ revision: this.revision,
1202
+ group
1203
+ };
1204
+ if (changedIds) {
1205
+ const ids = Array.isArray(changedIds) ? changedIds : [changedIds];
1206
+ const fresh = this.renderSections(ctx, group);
1207
+ for (const id of ids) {
1208
+ const block = this.extractSectionById(fresh, id);
1209
+ if (block) doc.sections.set(id, block);
1210
+ }
1211
+ } else {
1212
+ const fresh = this.renderSections(ctx, group);
1213
+ const map = this.stepStatusByGroup.get(group) || /* @__PURE__ */ new Map();
1214
+ for (const [checkId] of map.entries()) {
1215
+ if (!doc.sections.has(checkId)) {
1216
+ const block = this.extractSectionById(fresh, checkId);
1217
+ if (block) doc.sections.set(checkId, block);
1218
+ }
1219
+ }
1220
+ }
1221
+ return this.serializeSections(doc);
1222
+ }
1223
+ parseSections(body) {
1224
+ const sections = /* @__PURE__ */ new Map();
1225
+ const headerRe = /<!--\s*visor:thread=(\{[\s\S]*?\})\s*-->/m;
1226
+ const startRe = /<!--\s*visor:section=(\{[\s\S]*?\})\s*-->/g;
1227
+ const endRe = /<!--\s*visor:section-end\s+id=\"([^\"]+)\"\s*-->/g;
1228
+ const safePick = (obj, allowed) => {
1229
+ if (!obj || typeof obj !== "object" || Array.isArray(obj)) return void 0;
1230
+ const out = /* @__PURE__ */ Object.create(null);
1231
+ for (const [k, t] of Object.entries(allowed)) {
1232
+ if (Object.prototype.hasOwnProperty.call(obj, k)) {
1233
+ const v = obj[k];
1234
+ if (t === "string" && typeof v === "string") out[k] = v;
1235
+ else if (t === "number" && typeof v === "number" && Number.isFinite(v)) out[k] = v;
1236
+ }
1237
+ }
1238
+ return out;
1239
+ };
1240
+ const safeParse = (text) => {
1241
+ try {
1242
+ return JSON.parse(text);
1243
+ } catch {
1244
+ return void 0;
1245
+ }
1246
+ };
1247
+ let header;
1248
+ try {
1249
+ const h = headerRe.exec(body);
1250
+ if (h) {
1251
+ const parsed = safeParse(h[1]);
1252
+ const picked = safePick(parsed, {
1253
+ key: "string",
1254
+ runId: "string",
1255
+ workflowId: "string",
1256
+ revision: "number",
1257
+ group: "string",
1258
+ generatedAt: "string"
1259
+ });
1260
+ header = picked;
1261
+ }
1262
+ } catch {
1263
+ }
1264
+ let cursor = 0;
1265
+ while (true) {
1266
+ const s = startRe.exec(body);
1267
+ if (!s) break;
1268
+ const metaRaw = safeParse(s[1]);
1269
+ const meta = safePick(metaRaw, { id: "string", revision: "number" }) || { id: "" };
1270
+ const startIdx = startRe.lastIndex;
1271
+ endRe.lastIndex = startIdx;
1272
+ const e = endRe.exec(body);
1273
+ if (!e) break;
1274
+ const id = typeof meta.id === "string" && meta.id ? String(meta.id) : String(e[1]);
1275
+ const content = body.substring(startIdx, e.index).trim();
1276
+ const block = `<!-- visor:section=${JSON.stringify(meta)} -->
1277
+ ${content}
1278
+ <!-- visor:section-end id="${id}" -->`;
1279
+ sections.set(id, block);
1280
+ cursor = endRe.lastIndex;
1281
+ startRe.lastIndex = cursor;
1282
+ }
1283
+ return { header, sections };
1284
+ }
1285
+ serializeSections(doc) {
1286
+ const header = `<!-- visor:thread=${JSON.stringify({ ...doc.header || {}, generatedAt: (/* @__PURE__ */ new Date()).toISOString() })} -->`;
1287
+ const blocks = Array.from(doc.sections.values()).join("\n\n");
1288
+ const key = doc.header && doc.header.key || "";
1289
+ return `${header}
1290
+
1291
+ ${blocks}
1292
+
1293
+ <!-- visor:thread-end key="${key}" -->`;
1294
+ }
1295
+ extractSectionById(rendered, id) {
1296
+ const rx = new RegExp(
1297
+ `<!--\\s*visor:section=(\\{[\\s\\S]*?\\})\\s*-->[\\s\\S]*?<!--\\s*visor:section-end\\s+id=\\"${this.escapeRegExp(id)}\\"\\s*-->`,
1298
+ "m"
1299
+ );
1300
+ const m = rx.exec(rendered);
1301
+ return m ? m[0] : void 0;
1302
+ }
1303
+ escapeRegExp(s) {
1304
+ return s.replace(/[.*+?^${}()|[\\]\\]/g, "\\$&");
1305
+ }
1306
+ getGroupForCheck(ctx, checkId) {
1307
+ try {
1308
+ const cfg = ctx.config || {};
1309
+ const g = cfg?.checks?.[checkId]?.group || cfg?.steps?.[checkId]?.group;
1310
+ if (typeof g === "string" && g.trim().length > 0) return g;
1311
+ } catch {
1312
+ }
1313
+ return "review";
1314
+ }
1315
+ upsertSectionState(group, checkId, patch) {
1316
+ let groupMap = this.stepStatusByGroup.get(group);
1317
+ if (!groupMap) {
1318
+ groupMap = /* @__PURE__ */ new Map();
1319
+ this.stepStatusByGroup.set(group, groupMap);
1320
+ }
1321
+ const prev = groupMap.get(checkId) || { status: "queued", lastUpdated: (/* @__PURE__ */ new Date()).toISOString() };
1322
+ groupMap.set(checkId, { ...prev, ...patch });
1323
+ }
1324
+ commentIdForGroup(ctx, group) {
1325
+ if (group === "dynamic") {
1326
+ return `visor-thread-dynamic-${ctx.run.runId}`;
1327
+ }
1328
+ const r = ctx.run;
1329
+ const base = r.repo && r.pr ? `${r.repo.owner}/${r.repo.name}#${r.pr}` : r.runId;
1330
+ return `visor-thread-${group}-${base}`;
1331
+ }
1332
+ /**
1333
+ * Compute failure condition results for a completed check so Check Runs map to the
1334
+ * correct GitHub conclusion. This mirrors the engine's evaluation for fail_if.
1335
+ */
1336
+ async evaluateFailureResults(ctx, checkId, result) {
1337
+ try {
1338
+ const config = ctx.config || {};
1339
+ const checks = config && config.checks || {};
1340
+ const checkCfg = checks[checkId] || {};
1341
+ const checkSchema = typeof checkCfg.schema === "string" ? checkCfg.schema : "code-review";
1342
+ const checkGroup = checkCfg.group || "default";
1343
+ const { FailureConditionEvaluator } = (init_failure_condition_evaluator(), __toCommonJS(failure_condition_evaluator_exports));
1344
+ const evaluator = new FailureConditionEvaluator();
1345
+ const reviewSummary = { issues: Array.isArray(result?.issues) ? result.issues : [] };
1346
+ const failures = [];
1347
+ if (config.fail_if) {
1348
+ const failed = await evaluator.evaluateSimpleCondition(
1349
+ checkId,
1350
+ checkSchema,
1351
+ checkGroup,
1352
+ reviewSummary,
1353
+ config.fail_if
1354
+ );
1355
+ failures.push({
1356
+ conditionName: "global_fail_if",
1357
+ failed,
1358
+ expression: config.fail_if,
1359
+ severity: "error",
1360
+ haltExecution: false
1361
+ });
1362
+ }
1363
+ if (checkCfg.fail_if) {
1364
+ const failed = await evaluator.evaluateSimpleCondition(
1365
+ checkId,
1366
+ checkSchema,
1367
+ checkGroup,
1368
+ reviewSummary,
1369
+ checkCfg.fail_if
1370
+ );
1371
+ failures.push({
1372
+ conditionName: `${checkId}_fail_if`,
1373
+ failed,
1374
+ expression: checkCfg.fail_if,
1375
+ severity: "error",
1376
+ haltExecution: false
1377
+ });
1378
+ }
1379
+ return failures;
1380
+ } catch {
1381
+ return [];
1382
+ }
1383
+ }
1384
+ // Debounce helpers
1385
+ scheduleUpdate(ctx, comments, group, id) {
1386
+ if (id) this._pendingIds.add(id);
1387
+ const now = Date.now();
1388
+ const since = now - this._lastFlush;
1389
+ const remaining = this.maxWaitMs - since;
1390
+ if (this._timer) clearTimeout(this._timer);
1391
+ const wait = Math.max(0, Math.min(this.debounceMs, remaining));
1392
+ this._timer = setTimeout(async () => {
1393
+ const ids = Array.from(this._pendingIds);
1394
+ this._pendingIds.clear();
1395
+ this._timer = null;
1396
+ await this.updateGroupedComment(ctx, comments, group, ids.length > 0 ? ids : void 0);
1397
+ this._lastFlush = Date.now();
1398
+ }, wait);
1399
+ }
1400
+ async flushNow(ctx, comments, group) {
1401
+ if (this._timer) {
1402
+ clearTimeout(this._timer);
1403
+ this._timer = null;
1404
+ }
1405
+ const ids = Array.from(this._pendingIds);
1406
+ this._pendingIds.clear();
1407
+ await this.updateGroupedComment(ctx, comments, group, ids.length > 0 ? ids : void 0);
1408
+ this._lastFlush = Date.now();
1409
+ }
1410
+ /**
1411
+ * Sleep utility for enforcing delays
1412
+ */
1413
+ sleep(ms) {
1414
+ return new Promise((resolve) => setTimeout(resolve, ms));
1415
+ }
1416
+ };
1417
+ export {
1418
+ GitHubFrontend
1419
+ };
1420
+ //# sourceMappingURL=github-frontend-SIAEOCON.mjs.map