@probelabs/visor 0.1.106 → 0.1.107

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