@probelabs/visor 0.1.130 → 0.1.131

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 (134) hide show
  1. package/README.md +7 -0
  2. package/defaults/visor.yaml +5 -2
  3. package/dist/ai-review-service.d.ts +2 -0
  4. package/dist/ai-review-service.d.ts.map +1 -1
  5. package/dist/cli-main.d.ts.map +1 -1
  6. package/dist/cli.d.ts.map +1 -1
  7. package/dist/config/cli-handler.d.ts +5 -0
  8. package/dist/config/cli-handler.d.ts.map +1 -0
  9. package/dist/config/config-reloader.d.ts +24 -0
  10. package/dist/config/config-reloader.d.ts.map +1 -0
  11. package/dist/config/config-snapshot-store.d.ts +21 -0
  12. package/dist/config/config-snapshot-store.d.ts.map +1 -0
  13. package/dist/config/config-watcher.d.ts +19 -0
  14. package/dist/config/config-watcher.d.ts.map +1 -0
  15. package/dist/config/types.d.ts +16 -0
  16. package/dist/config/types.d.ts.map +1 -0
  17. package/dist/defaults/visor.yaml +5 -2
  18. package/dist/docs/ai-configuration.md +139 -0
  19. package/dist/docs/ai-custom-tools.md +30 -0
  20. package/dist/docs/capacity-planning.md +359 -0
  21. package/dist/docs/commands.md +35 -0
  22. package/dist/docs/database-operations.md +487 -0
  23. package/dist/docs/index.md +6 -1
  24. package/dist/docs/licensing.md +372 -0
  25. package/dist/docs/production-deployment.md +583 -0
  26. package/dist/examples/ai-with-bash.yaml +17 -0
  27. package/dist/generated/config-schema.d.ts +4 -0
  28. package/dist/generated/config-schema.d.ts.map +1 -1
  29. package/dist/index.js +9945 -10907
  30. package/dist/liquid-extensions.d.ts +7 -0
  31. package/dist/liquid-extensions.d.ts.map +1 -1
  32. package/dist/output/traces/{run-2026-02-11T16-20-59-999Z.ndjson → run-2026-02-15T19-14-20-379Z.ndjson} +84 -84
  33. package/dist/{traces/run-2026-02-11T16-21-47-711Z.ndjson → output/traces/run-2026-02-15T19-15-09-410Z.ndjson} +1019 -1019
  34. package/dist/providers/ai-check-provider.d.ts +5 -0
  35. package/dist/providers/ai-check-provider.d.ts.map +1 -1
  36. package/dist/providers/command-check-provider.d.ts.map +1 -1
  37. package/dist/providers/workflow-check-provider.d.ts.map +1 -1
  38. package/dist/scheduler/schedule-tool.d.ts.map +1 -1
  39. package/dist/sdk/{check-provider-registry-PANIXYRB.mjs → check-provider-registry-AAPPJ4CP.mjs} +7 -7
  40. package/dist/sdk/{check-provider-registry-M3Y6JMTW.mjs → check-provider-registry-S7BMQ2FC.mjs} +7 -7
  41. package/dist/sdk/check-provider-registry-ZOLEYDKM.mjs +28 -0
  42. package/dist/sdk/{chunk-VMLORODQ.mjs → chunk-2GCSK3PD.mjs} +4 -4
  43. package/dist/sdk/{chunk-EUUAQBTW.mjs → chunk-6ZZ4DPAA.mjs} +240 -48
  44. package/dist/sdk/chunk-6ZZ4DPAA.mjs.map +1 -0
  45. package/dist/sdk/{chunk-HOKQOO3G.mjs → chunk-EBTD2D4L.mjs} +2 -2
  46. package/dist/sdk/chunk-LDFUW34H.mjs +39912 -0
  47. package/dist/sdk/chunk-LDFUW34H.mjs.map +1 -0
  48. package/dist/sdk/{chunk-UCNT3PDT.mjs → chunk-LQ5B4T6L.mjs} +5 -1
  49. package/dist/sdk/chunk-LQ5B4T6L.mjs.map +1 -0
  50. package/dist/sdk/{chunk-S6CD7GFM.mjs → chunk-MQ57AB4U.mjs} +211 -35
  51. package/dist/sdk/chunk-MQ57AB4U.mjs.map +1 -0
  52. package/dist/sdk/chunk-N4I6ZDCJ.mjs +436 -0
  53. package/dist/sdk/chunk-N4I6ZDCJ.mjs.map +1 -0
  54. package/dist/sdk/chunk-OMFPM576.mjs +739 -0
  55. package/dist/sdk/chunk-OMFPM576.mjs.map +1 -0
  56. package/dist/sdk/chunk-RI77TA6V.mjs +436 -0
  57. package/dist/sdk/chunk-RI77TA6V.mjs.map +1 -0
  58. package/dist/sdk/chunk-VO4N6TEL.mjs +1502 -0
  59. package/dist/sdk/chunk-VO4N6TEL.mjs.map +1 -0
  60. package/dist/sdk/{chunk-V2IV3ILA.mjs → chunk-XJQKTK6V.mjs} +31 -5
  61. package/dist/sdk/chunk-XJQKTK6V.mjs.map +1 -0
  62. package/dist/sdk/{config-OGOS4ZU4.mjs → config-4EG7IQIU.mjs} +2 -2
  63. package/dist/sdk/{failure-condition-evaluator-HC3M5377.mjs → failure-condition-evaluator-GLHZZF47.mjs} +3 -3
  64. package/dist/sdk/failure-condition-evaluator-KN55WXRO.mjs +17 -0
  65. package/dist/sdk/{github-frontend-E2KJSC3Y.mjs → github-frontend-F4TE2JY7.mjs} +3 -3
  66. package/dist/sdk/github-frontend-HCOKL53D.mjs +1356 -0
  67. package/dist/sdk/github-frontend-HCOKL53D.mjs.map +1 -0
  68. package/dist/sdk/{host-EE6EJ2FM.mjs → host-SAT6RHDX.mjs} +2 -2
  69. package/dist/sdk/host-VA3ET7N6.mjs +63 -0
  70. package/dist/sdk/host-VA3ET7N6.mjs.map +1 -0
  71. package/dist/sdk/{liquid-extensions-E4EUOCES.mjs → liquid-extensions-YDIIH33Q.mjs} +2 -2
  72. package/dist/sdk/{routing-OZQWAGAI.mjs → routing-KFYQGOYU.mjs} +5 -5
  73. package/dist/sdk/routing-OXQKETSA.mjs +25 -0
  74. package/dist/sdk/{schedule-tool-handler-IEB2VS7O.mjs → schedule-tool-handler-G353DHS6.mjs} +7 -7
  75. package/dist/sdk/{schedule-tool-handler-B7TMSG6A.mjs → schedule-tool-handler-OQF57URO.mjs} +7 -7
  76. package/dist/sdk/schedule-tool-handler-PJVKWSYX.mjs +38 -0
  77. package/dist/sdk/schedule-tool-handler-PJVKWSYX.mjs.map +1 -0
  78. package/dist/sdk/sdk.d.mts +15 -0
  79. package/dist/sdk/sdk.d.ts +15 -0
  80. package/dist/sdk/sdk.js +621 -183
  81. package/dist/sdk/sdk.js.map +1 -1
  82. package/dist/sdk/sdk.mjs +6 -6
  83. package/dist/sdk/{trace-helpers-PP3YHTAM.mjs → trace-helpers-LOPBHYYX.mjs} +4 -2
  84. package/dist/sdk/trace-helpers-LOPBHYYX.mjs.map +1 -0
  85. package/dist/sdk/trace-helpers-R2ETIEC2.mjs +25 -0
  86. package/dist/sdk/trace-helpers-R2ETIEC2.mjs.map +1 -0
  87. package/dist/sdk/{workflow-check-provider-2ET3SFZH.mjs → workflow-check-provider-57KAR4Y4.mjs} +7 -7
  88. package/dist/sdk/workflow-check-provider-57KAR4Y4.mjs.map +1 -0
  89. package/dist/sdk/{workflow-check-provider-HB4XTD4Z.mjs → workflow-check-provider-LRWD52WN.mjs} +7 -7
  90. package/dist/sdk/workflow-check-provider-LRWD52WN.mjs.map +1 -0
  91. package/dist/sdk/workflow-check-provider-N2DRFQDB.mjs +28 -0
  92. package/dist/sdk/workflow-check-provider-N2DRFQDB.mjs.map +1 -0
  93. package/dist/slack/socket-runner.d.ts.map +1 -1
  94. package/dist/state-machine/context/build-engine-context.d.ts.map +1 -1
  95. package/dist/state-machine/runner.d.ts.map +1 -1
  96. package/dist/state-machine/states/completed.d.ts.map +1 -1
  97. package/dist/telemetry/trace-helpers.d.ts +5 -0
  98. package/dist/telemetry/trace-helpers.d.ts.map +1 -1
  99. package/dist/test-runner/evaluators.d.ts.map +1 -1
  100. package/dist/test-runner/index.d.ts +7 -0
  101. package/dist/test-runner/index.d.ts.map +1 -1
  102. package/dist/test-runner/validator.d.ts.map +1 -1
  103. package/dist/traces/{run-2026-02-11T16-20-59-999Z.ndjson → run-2026-02-15T19-14-20-379Z.ndjson} +84 -84
  104. package/dist/{output/traces/run-2026-02-11T16-21-47-711Z.ndjson → traces/run-2026-02-15T19-15-09-410Z.ndjson} +1019 -1019
  105. package/dist/tui/chat-runner.d.ts.map +1 -1
  106. package/dist/types/cli.d.ts +2 -0
  107. package/dist/types/cli.d.ts.map +1 -1
  108. package/dist/types/config.d.ts +15 -0
  109. package/dist/types/config.d.ts.map +1 -1
  110. package/dist/types/engine.d.ts +2 -0
  111. package/dist/types/engine.d.ts.map +1 -1
  112. package/package.json +3 -3
  113. package/defaults/.visor.yaml +0 -420
  114. package/dist/sdk/chunk-EUUAQBTW.mjs.map +0 -1
  115. package/dist/sdk/chunk-S6CD7GFM.mjs.map +0 -1
  116. package/dist/sdk/chunk-UCNT3PDT.mjs.map +0 -1
  117. package/dist/sdk/chunk-V2IV3ILA.mjs.map +0 -1
  118. package/dist/sdk/chunk-YJRBN3XS.mjs +0 -217
  119. package/dist/sdk/chunk-YJRBN3XS.mjs.map +0 -1
  120. /package/dist/sdk/{check-provider-registry-M3Y6JMTW.mjs.map → check-provider-registry-AAPPJ4CP.mjs.map} +0 -0
  121. /package/dist/sdk/{check-provider-registry-PANIXYRB.mjs.map → check-provider-registry-S7BMQ2FC.mjs.map} +0 -0
  122. /package/dist/sdk/{config-OGOS4ZU4.mjs.map → check-provider-registry-ZOLEYDKM.mjs.map} +0 -0
  123. /package/dist/sdk/{chunk-VMLORODQ.mjs.map → chunk-2GCSK3PD.mjs.map} +0 -0
  124. /package/dist/sdk/{chunk-HOKQOO3G.mjs.map → chunk-EBTD2D4L.mjs.map} +0 -0
  125. /package/dist/sdk/{failure-condition-evaluator-HC3M5377.mjs.map → config-4EG7IQIU.mjs.map} +0 -0
  126. /package/dist/sdk/{liquid-extensions-E4EUOCES.mjs.map → failure-condition-evaluator-GLHZZF47.mjs.map} +0 -0
  127. /package/dist/sdk/{routing-OZQWAGAI.mjs.map → failure-condition-evaluator-KN55WXRO.mjs.map} +0 -0
  128. /package/dist/sdk/{github-frontend-E2KJSC3Y.mjs.map → github-frontend-F4TE2JY7.mjs.map} +0 -0
  129. /package/dist/sdk/{host-EE6EJ2FM.mjs.map → host-SAT6RHDX.mjs.map} +0 -0
  130. /package/dist/sdk/{schedule-tool-handler-B7TMSG6A.mjs.map → liquid-extensions-YDIIH33Q.mjs.map} +0 -0
  131. /package/dist/sdk/{schedule-tool-handler-IEB2VS7O.mjs.map → routing-KFYQGOYU.mjs.map} +0 -0
  132. /package/dist/sdk/{trace-helpers-PP3YHTAM.mjs.map → routing-OXQKETSA.mjs.map} +0 -0
  133. /package/dist/sdk/{workflow-check-provider-2ET3SFZH.mjs.map → schedule-tool-handler-G353DHS6.mjs.map} +0 -0
  134. /package/dist/sdk/{workflow-check-provider-HB4XTD4Z.mjs.map → schedule-tool-handler-OQF57URO.mjs.map} +0 -0
package/dist/sdk/sdk.js CHANGED
@@ -641,6 +641,205 @@ var init_fallback_ndjson = __esm({
641
641
  }
642
642
  });
643
643
 
644
+ // package.json
645
+ var require_package = __commonJS({
646
+ "package.json"(exports2, module2) {
647
+ module2.exports = {
648
+ name: "@probelabs/visor",
649
+ version: "0.1.131",
650
+ main: "dist/index.js",
651
+ bin: {
652
+ visor: "./dist/index.js"
653
+ },
654
+ exports: {
655
+ ".": {
656
+ require: "./dist/index.js",
657
+ import: "./dist/index.js"
658
+ },
659
+ "./sdk": {
660
+ types: "./dist/sdk/sdk.d.ts",
661
+ import: "./dist/sdk/sdk.mjs",
662
+ require: "./dist/sdk/sdk.js"
663
+ },
664
+ "./cli": {
665
+ require: "./dist/index.js"
666
+ }
667
+ },
668
+ files: [
669
+ "dist/",
670
+ "defaults/",
671
+ "action.yml",
672
+ "README.md",
673
+ "LICENSE"
674
+ ],
675
+ publishConfig: {
676
+ access: "public",
677
+ registry: "https://registry.npmjs.org/"
678
+ },
679
+ scripts: {
680
+ "build:cli": "ncc build src/index.ts -o dist && cp -r defaults dist/ && cp -r output dist/ && cp -r docs dist/ && cp -r examples dist/ && cp -r src/debug-visualizer/ui dist/debug-visualizer/ && node scripts/inject-version.js && echo '#!/usr/bin/env node' | cat - dist/index.js > temp && mv temp dist/index.js && chmod +x dist/index.js",
681
+ "build:sdk": "tsup src/sdk.ts --dts --sourcemap --format esm,cjs --out-dir dist/sdk",
682
+ build: "./scripts/build-oss.sh",
683
+ "build:ee": "npm run build:cli && npm run build:sdk",
684
+ test: "jest && npm run test:yaml",
685
+ "test:unit": "jest",
686
+ prepublishOnly: "npm run build",
687
+ "test:watch": "jest --watch",
688
+ "test:coverage": "jest --coverage",
689
+ "test:ee": "jest --testPathPatterns='tests/ee' --testPathIgnorePatterns='/node_modules/' --no-coverage",
690
+ "test:manual:bash": "RUN_MANUAL_TESTS=true jest tests/manual/bash-config-manual.test.ts",
691
+ lint: "eslint src tests --ext .ts",
692
+ "lint:fix": "eslint src tests --ext .ts --fix",
693
+ format: "prettier --write src tests",
694
+ "format:check": "prettier --check src tests",
695
+ clean: "",
696
+ prebuild: "npm run clean && node scripts/generate-config-schema.js",
697
+ pretest: "node scripts/generate-config-schema.js && npm run build:cli",
698
+ "pretest:unit": "node scripts/generate-config-schema.js && npm run build:cli",
699
+ "test:with-build": "npm run build:cli && jest",
700
+ "test:yaml": "node dist/index.js test --progress compact",
701
+ "test:yaml:parallel": "node dist/index.js test --progress compact --max-parallel 4",
702
+ prepare: "husky",
703
+ "pre-commit": "lint-staged",
704
+ "deploy:site": "cd site && npx wrangler pages deploy . --project-name=visor-site --commit-dirty=true",
705
+ "deploy:worker": "npx wrangler deploy",
706
+ deploy: "npm run deploy:site && npm run deploy:worker",
707
+ "publish:ee": "./scripts/publish-ee.sh",
708
+ release: "./scripts/release.sh",
709
+ "release:patch": "./scripts/release.sh patch",
710
+ "release:minor": "./scripts/release.sh minor",
711
+ "release:major": "./scripts/release.sh major",
712
+ "release:prerelease": "./scripts/release.sh prerelease",
713
+ "docs:validate": "node scripts/validate-readme-links.js",
714
+ "workshop:setup": "npm install -D reveal-md@6.1.2",
715
+ "workshop:serve": "cd workshop && reveal-md slides.md -w",
716
+ "workshop:export": "reveal-md workshop/slides.md --static workshop/build",
717
+ "workshop:pdf": "reveal-md workshop/slides.md --print workshop/Visor-Workshop.pdf --print-size letter",
718
+ "workshop:pdf:ci": 'reveal-md workshop/slides.md --print workshop/Visor-Workshop.pdf --print-size letter --puppeteer-launch-args="--no-sandbox --disable-dev-shm-usage"',
719
+ "workshop:pdf:a4": "reveal-md workshop/slides.md --print workshop/Visor-Workshop-A4.pdf --print-size A4",
720
+ "workshop:build": "npm run workshop:export && npm run workshop:pdf",
721
+ "simulate:issue": "TS_NODE_TRANSPILE_ONLY=1 ts-node scripts/simulate-gh-run.ts --event issues --action opened --debug",
722
+ "simulate:comment": "TS_NODE_TRANSPILE_ONLY=1 ts-node scripts/simulate-gh-run.ts --event issue_comment --action created --debug"
723
+ },
724
+ keywords: [
725
+ "code-review",
726
+ "ai",
727
+ "github-action",
728
+ "cli",
729
+ "pr-review",
730
+ "visor"
731
+ ],
732
+ author: "Probe Labs",
733
+ license: "MIT",
734
+ description: "AI-powered code review tool for GitHub Pull Requests - CLI and GitHub Action",
735
+ repository: {
736
+ type: "git",
737
+ url: "git+https://github.com/probelabs/visor.git"
738
+ },
739
+ bugs: {
740
+ url: "https://github.com/probelabs/visor/issues"
741
+ },
742
+ homepage: "https://github.com/probelabs/visor#readme",
743
+ dependencies: {
744
+ "@actions/core": "^1.11.1",
745
+ "@modelcontextprotocol/sdk": "^1.25.3",
746
+ "@nyariv/sandboxjs": "github:probelabs/SandboxJS#f1c13b8eee98734a8ea024061eada4aa9a9ff2e9",
747
+ "@octokit/action": "^8.0.2",
748
+ "@octokit/auth-app": "^8.1.0",
749
+ "@octokit/core": "^7.0.3",
750
+ "@octokit/rest": "^22.0.0",
751
+ "@probelabs/probe": "^0.6.0-rc232",
752
+ "@types/commander": "^2.12.0",
753
+ "@types/uuid": "^10.0.0",
754
+ ajv: "^8.17.1",
755
+ "ajv-formats": "^3.0.1",
756
+ "better-sqlite3": "^11.0.0",
757
+ blessed: "^0.1.81",
758
+ "cli-table3": "^0.6.5",
759
+ commander: "^14.0.0",
760
+ dotenv: "^17.2.3",
761
+ ignore: "^7.0.5",
762
+ "js-yaml": "^4.1.0",
763
+ liquidjs: "^10.21.1",
764
+ "node-cron": "^3.0.3",
765
+ open: "^9.1.0",
766
+ "simple-git": "^3.28.0",
767
+ uuid: "^11.1.0",
768
+ ws: "^8.18.3"
769
+ },
770
+ optionalDependencies: {
771
+ "@anthropic/claude-code-sdk": "npm:null@*",
772
+ "@open-policy-agent/opa-wasm": "^1.10.0",
773
+ "@opentelemetry/api": "^1.9.0",
774
+ "@opentelemetry/core": "^1.30.1",
775
+ "@opentelemetry/exporter-trace-otlp-grpc": "^0.203.0",
776
+ "@opentelemetry/exporter-trace-otlp-http": "^0.203.0",
777
+ "@opentelemetry/instrumentation": "^0.203.0",
778
+ "@opentelemetry/resources": "^1.30.1",
779
+ "@opentelemetry/sdk-metrics": "^1.30.1",
780
+ "@opentelemetry/sdk-node": "^0.203.0",
781
+ "@opentelemetry/sdk-trace-base": "^1.30.1",
782
+ "@opentelemetry/semantic-conventions": "^1.30.1",
783
+ knex: "^3.1.0",
784
+ mysql2: "^3.11.0",
785
+ pg: "^8.13.0",
786
+ tedious: "^19.0.0"
787
+ },
788
+ devDependencies: {
789
+ "@eslint/js": "^9.34.0",
790
+ "@kie/act-js": "^2.6.2",
791
+ "@kie/mock-github": "^2.0.1",
792
+ "@swc/core": "^1.13.2",
793
+ "@swc/jest": "^0.2.37",
794
+ "@types/better-sqlite3": "^7.6.0",
795
+ "@types/blessed": "^0.1.27",
796
+ "@types/jest": "^30.0.0",
797
+ "@types/js-yaml": "^4.0.9",
798
+ "@types/node": "^24.3.0",
799
+ "@types/node-cron": "^3.0.11",
800
+ "@types/ws": "^8.18.1",
801
+ "@typescript-eslint/eslint-plugin": "^8.42.0",
802
+ "@typescript-eslint/parser": "^8.42.0",
803
+ "@vercel/ncc": "^0.38.4",
804
+ eslint: "^9.34.0",
805
+ "eslint-config-prettier": "^10.1.8",
806
+ "eslint-plugin-prettier": "^5.5.4",
807
+ husky: "^9.1.7",
808
+ jest: "^30.1.3",
809
+ "lint-staged": "^16.1.6",
810
+ prettier: "^3.6.2",
811
+ "reveal-md": "^6.1.2",
812
+ "ts-json-schema-generator": "^1.5.1",
813
+ "ts-node": "^10.9.2",
814
+ tsup: "^8.5.0",
815
+ typescript: "^5.9.2",
816
+ wrangler: "^3.0.0"
817
+ },
818
+ peerDependenciesMeta: {
819
+ "@anthropic/claude-code-sdk": {
820
+ optional: true
821
+ }
822
+ },
823
+ directories: {
824
+ test: "tests"
825
+ },
826
+ "lint-staged": {
827
+ "src/**/*.{ts,js}": [
828
+ "eslint --fix",
829
+ "prettier --write"
830
+ ],
831
+ "tests/**/*.{ts,js}": [
832
+ "eslint --fix",
833
+ "prettier --write"
834
+ ],
835
+ "*.{json,md,yml,yaml}": [
836
+ "prettier --write"
837
+ ]
838
+ }
839
+ };
840
+ }
841
+ });
842
+
644
843
  // src/telemetry/trace-helpers.ts
645
844
  var trace_helpers_exports = {};
646
845
  __export(trace_helpers_exports, {
@@ -648,6 +847,7 @@ __export(trace_helpers_exports, {
648
847
  _appendRunMarker: () => _appendRunMarker,
649
848
  addEvent: () => addEvent,
650
849
  getTracer: () => getTracer,
850
+ getVisorRunAttributes: () => getVisorRunAttributes,
651
851
  setSpanAttributes: () => setSpanAttributes,
652
852
  setSpanError: () => setSpanError,
653
853
  withActiveSpan: () => withActiveSpan
@@ -717,20 +917,37 @@ function setSpanError(err) {
717
917
  } catch {
718
918
  }
719
919
  }
920
+ function getVisorRunAttributes() {
921
+ const attrs = {};
922
+ try {
923
+ attrs["visor.version"] = process.env.VISOR_VERSION || (require_package()?.version ?? "dev");
924
+ } catch {
925
+ attrs["visor.version"] = "dev";
926
+ }
927
+ const commitShort = process.env.VISOR_COMMIT_SHORT || "";
928
+ const commitFull = process.env.VISOR_COMMIT_SHA || process.env.VISOR_COMMIT || "";
929
+ if (commitShort) {
930
+ attrs["visor.commit"] = commitShort;
931
+ }
932
+ if (commitFull) {
933
+ attrs["visor.commit.sha"] = commitFull;
934
+ }
935
+ return attrs;
936
+ }
720
937
  function __getOrCreateNdjsonPath() {
721
938
  try {
722
939
  if (process.env.VISOR_TELEMETRY_SINK && process.env.VISOR_TELEMETRY_SINK !== "file")
723
940
  return null;
724
941
  const path25 = require("path");
725
- const fs22 = require("fs");
942
+ const fs21 = require("fs");
726
943
  if (process.env.VISOR_FALLBACK_TRACE_FILE) {
727
944
  __ndjsonPath = process.env.VISOR_FALLBACK_TRACE_FILE;
728
945
  const dir = path25.dirname(__ndjsonPath);
729
- if (!fs22.existsSync(dir)) fs22.mkdirSync(dir, { recursive: true });
946
+ if (!fs21.existsSync(dir)) fs21.mkdirSync(dir, { recursive: true });
730
947
  return __ndjsonPath;
731
948
  }
732
949
  const outDir = process.env.VISOR_TRACE_DIR || path25.join(process.cwd(), "output", "traces");
733
- if (!fs22.existsSync(outDir)) fs22.mkdirSync(outDir, { recursive: true });
950
+ if (!fs21.existsSync(outDir)) fs21.mkdirSync(outDir, { recursive: true });
734
951
  if (!__ndjsonPath) {
735
952
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
736
953
  __ndjsonPath = path25.join(outDir, `${ts}.ndjson`);
@@ -742,11 +959,11 @@ function __getOrCreateNdjsonPath() {
742
959
  }
743
960
  function _appendRunMarker() {
744
961
  try {
745
- const fs22 = require("fs");
962
+ const fs21 = require("fs");
746
963
  const p = __getOrCreateNdjsonPath();
747
964
  if (!p) return;
748
965
  const line = { name: "visor.run", attributes: { started: true } };
749
- fs22.appendFileSync(p, JSON.stringify(line) + "\n", "utf8");
966
+ fs21.appendFileSync(p, JSON.stringify(line) + "\n", "utf8");
750
967
  } catch {
751
968
  }
752
969
  }
@@ -3709,25 +3926,45 @@ function createExtendedLiquid(options = {}) {
3709
3926
  configureLiquidWithExtensions(liquid);
3710
3927
  return liquid;
3711
3928
  }
3712
- var import_liquidjs, import_async_hooks, import_promises2, import_path2, ReadFileTag, permissionsALS;
3929
+ var import_liquidjs, import_async_hooks, import_path2, ReadFileTag, permissionsALS;
3713
3930
  var init_liquid_extensions = __esm({
3714
3931
  "src/liquid-extensions.ts"() {
3715
3932
  "use strict";
3716
3933
  import_liquidjs = require("liquidjs");
3717
3934
  import_async_hooks = require("async_hooks");
3718
- import_promises2 = __toESM(require("fs/promises"));
3719
3935
  import_path2 = __toESM(require("path"));
3720
3936
  init_author_permissions();
3721
3937
  init_memory_store();
3722
3938
  init_sandbox();
3723
3939
  ReadFileTag = class extends import_liquidjs.Tag {
3724
3940
  filepath;
3941
+ indentValue = null;
3725
3942
  constructor(token, remainTokens, liquid) {
3726
3943
  super(token, remainTokens, liquid);
3727
- this.filepath = new import_liquidjs.Value(token.args, liquid);
3944
+ const argsStr = token.args.trim();
3945
+ const indentMatch = argsStr.match(/^(.+?)\s+indent:\s*(\d+)\s*$/);
3946
+ if (indentMatch) {
3947
+ this.filepath = new import_liquidjs.Value(indentMatch[1].trim(), liquid);
3948
+ this.indentValue = new import_liquidjs.Value(indentMatch[2], liquid);
3949
+ } else {
3950
+ this.filepath = new import_liquidjs.Value(argsStr, liquid);
3951
+ }
3728
3952
  }
3729
3953
  *render(ctx, emitter) {
3730
3954
  const filePath = yield this.filepath.value(ctx, false);
3955
+ let indent = 0;
3956
+ if (this.indentValue) {
3957
+ const indentAmount = yield this.indentValue.value(ctx, false);
3958
+ indent = typeof indentAmount === "number" ? indentAmount : parseInt(String(indentAmount), 10) || 0;
3959
+ } else {
3960
+ const output = emitter.buffer || "";
3961
+ const lastNewline = output.lastIndexOf("\n");
3962
+ if (lastNewline >= 0) {
3963
+ const lineStart = output.substring(lastNewline + 1);
3964
+ const match = lineStart.match(/^(\s*)/);
3965
+ indent = match ? match[1].length : 0;
3966
+ }
3967
+ }
3731
3968
  if (!filePath || typeof filePath !== "string") {
3732
3969
  emitter.write("[Error: Invalid file path]");
3733
3970
  return;
@@ -3741,7 +3978,12 @@ var init_liquid_extensions = __esm({
3741
3978
  return;
3742
3979
  }
3743
3980
  try {
3744
- const content = yield import_promises2.default.readFile(resolvedPath, "utf-8");
3981
+ let content = require("fs").readFileSync(resolvedPath, "utf-8");
3982
+ if (indent > 0) {
3983
+ const indentStr = " ".repeat(indent);
3984
+ const lines = content.split("\n");
3985
+ content = lines.map((line, i) => i === 0 ? line : indentStr + line).join("\n");
3986
+ }
3745
3987
  emitter.write(content);
3746
3988
  } catch (error) {
3747
3989
  const errorMessage = error instanceof Error ? error.message : error?.code || "Unknown error";
@@ -5453,14 +5695,14 @@ function emitMermaidFromMarkdown(checkName, markdown, origin) {
5453
5695
  if (process.env.VISOR_TRACE_REPORT === "true") {
5454
5696
  const outDir = process.env.VISOR_TRACE_DIR || path4.join(process.cwd(), "output", "traces");
5455
5697
  try {
5456
- if (!fs4.existsSync(outDir)) fs4.mkdirSync(outDir, { recursive: true });
5698
+ if (!fs3.existsSync(outDir)) fs3.mkdirSync(outDir, { recursive: true });
5457
5699
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
5458
5700
  const jsonPath = path4.join(outDir, `${ts}.trace.json`);
5459
5701
  const htmlPath = path4.join(outDir, `${ts}.report.html`);
5460
5702
  let data = { spans: [] };
5461
- if (fs4.existsSync(jsonPath)) {
5703
+ if (fs3.existsSync(jsonPath)) {
5462
5704
  try {
5463
- data = JSON.parse(fs4.readFileSync(jsonPath, "utf8"));
5705
+ data = JSON.parse(fs3.readFileSync(jsonPath, "utf8"));
5464
5706
  } catch {
5465
5707
  data = { spans: [] };
5466
5708
  }
@@ -5468,9 +5710,9 @@ function emitMermaidFromMarkdown(checkName, markdown, origin) {
5468
5710
  data.spans.push({
5469
5711
  events: [{ name: "diagram.block", attrs: { check: checkName, origin, code } }]
5470
5712
  });
5471
- fs4.writeFileSync(jsonPath, JSON.stringify(data, null, 2), "utf8");
5472
- if (!fs4.existsSync(htmlPath)) {
5473
- fs4.writeFileSync(
5713
+ fs3.writeFileSync(jsonPath, JSON.stringify(data, null, 2), "utf8");
5714
+ if (!fs3.existsSync(htmlPath)) {
5715
+ fs3.writeFileSync(
5474
5716
  htmlPath,
5475
5717
  '<!doctype html><html><head><meta charset="utf-8"/><title>Visor Trace Report</title></head><body><h2>Visor Trace Report</h2></body></html>',
5476
5718
  "utf8"
@@ -5486,13 +5728,13 @@ function emitMermaidFromMarkdown(checkName, markdown, origin) {
5486
5728
  }
5487
5729
  return count;
5488
5730
  }
5489
- var fs4, path4, MERMAID_RE;
5731
+ var fs3, path4, MERMAID_RE;
5490
5732
  var init_mermaid_telemetry = __esm({
5491
5733
  "src/utils/mermaid-telemetry.ts"() {
5492
5734
  "use strict";
5493
5735
  init_trace_helpers();
5494
5736
  init_metrics();
5495
- fs4 = __toESM(require("fs"));
5737
+ fs3 = __toESM(require("fs"));
5496
5738
  path4 = __toESM(require("path"));
5497
5739
  MERMAID_RE = /```mermaid\s*\n([\s\S]*?)\n```/gi;
5498
5740
  }
@@ -6130,7 +6372,7 @@ var init_dependency_gating = __esm({
6130
6372
  async function renderTemplateContent(checkId, checkConfig, reviewSummary) {
6131
6373
  try {
6132
6374
  const { createExtendedLiquid: createExtendedLiquid2 } = await Promise.resolve().then(() => (init_liquid_extensions(), liquid_extensions_exports));
6133
- const fs22 = await import("fs/promises");
6375
+ const fs21 = await import("fs/promises");
6134
6376
  const path25 = await import("path");
6135
6377
  const schemaRaw = checkConfig.schema || "plain";
6136
6378
  const schema = typeof schemaRaw === "string" ? schemaRaw : "code-review";
@@ -6140,7 +6382,7 @@ async function renderTemplateContent(checkId, checkConfig, reviewSummary) {
6140
6382
  } else if (checkConfig.template && checkConfig.template.file) {
6141
6383
  const file = String(checkConfig.template.file);
6142
6384
  const resolved = path25.resolve(process.cwd(), file);
6143
- templateContent = await fs22.readFile(resolved, "utf-8");
6385
+ templateContent = await fs21.readFile(resolved, "utf-8");
6144
6386
  } else if (schema && schema !== "plain") {
6145
6387
  const sanitized = String(schema).replace(/[^a-zA-Z0-9-]/g, "");
6146
6388
  if (sanitized) {
@@ -6156,7 +6398,7 @@ async function renderTemplateContent(checkId, checkConfig, reviewSummary) {
6156
6398
  ];
6157
6399
  for (const p of candidatePaths) {
6158
6400
  try {
6159
- templateContent = await fs22.readFile(p, "utf-8");
6401
+ templateContent = await fs21.readFile(p, "utf-8");
6160
6402
  if (templateContent) break;
6161
6403
  } catch {
6162
6404
  }
@@ -6312,8 +6554,8 @@ async function initializeTracer(sessionId, checkName) {
6312
6554
  const sanitizedCheckName = checkName ? path5.basename(checkName) : "check";
6313
6555
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
6314
6556
  const traceDir = process.env.GITHUB_WORKSPACE ? path5.join(process.env.GITHUB_WORKSPACE, "debug-artifacts") : path5.join(process.cwd(), "debug-artifacts");
6315
- if (!fs5.existsSync(traceDir)) {
6316
- fs5.mkdirSync(traceDir, { recursive: true });
6557
+ if (!fs4.existsSync(traceDir)) {
6558
+ fs4.mkdirSync(traceDir, { recursive: true });
6317
6559
  }
6318
6560
  const traceFilePath = path5.join(traceDir, `trace-${sanitizedCheckName}-${timestamp}.jsonl`);
6319
6561
  const resolvedTracePath = path5.resolve(traceFilePath);
@@ -6358,12 +6600,12 @@ async function initializeTracer(sessionId, checkName) {
6358
6600
  return null;
6359
6601
  }
6360
6602
  }
6361
- var path5, fs5;
6603
+ var path5, fs4;
6362
6604
  var init_tracer_init = __esm({
6363
6605
  "src/utils/tracer-init.ts"() {
6364
6606
  "use strict";
6365
6607
  path5 = __toESM(require("path"));
6366
- fs5 = __toESM(require("fs"));
6608
+ fs4 = __toESM(require("fs"));
6367
6609
  }
6368
6610
  });
6369
6611
 
@@ -6561,7 +6803,7 @@ async function processDiffWithOutline(diffContent) {
6561
6803
  }
6562
6804
  try {
6563
6805
  const originalProbePath = process.env.PROBE_PATH;
6564
- const fs22 = require("fs");
6806
+ const fs21 = require("fs");
6565
6807
  const possiblePaths = [
6566
6808
  // Relative to current working directory (most common in production)
6567
6809
  path6.join(process.cwd(), "node_modules/@probelabs/probe/bin/probe-binary"),
@@ -6572,7 +6814,7 @@ async function processDiffWithOutline(diffContent) {
6572
6814
  ];
6573
6815
  let probeBinaryPath;
6574
6816
  for (const candidatePath of possiblePaths) {
6575
- if (fs22.existsSync(candidatePath)) {
6817
+ if (fs21.existsSync(candidatePath)) {
6576
6818
  probeBinaryPath = candidatePath;
6577
6819
  break;
6578
6820
  }
@@ -7129,6 +7371,8 @@ var init_ai_review_service = __esm({
7129
7371
  const isCodeReviewSchema = schema === "code-review";
7130
7372
  const prContext = skipPRContext ? "" : await this.formatPRContext(prInfo, isCodeReviewSchema);
7131
7373
  const slackContextXml = skipSlackContext === true ? "" : this.formatSlackContextFromPRInfo(prInfo);
7374
+ const traceIdXml = prInfo.otelTraceId ? `
7375
+ <trace_id>${this.escapeXml(String(prInfo.otelTraceId))}</trace_id>` : "";
7132
7376
  const isIssue = prInfo.isIssue === true;
7133
7377
  if (isIssue) {
7134
7378
  if (skipPRContext && !slackContextXml) {
@@ -7142,7 +7386,7 @@ ${customInstructions}
7142
7386
  </instructions>
7143
7387
 
7144
7388
  <context>
7145
- ${getCurrentDateXml()}
7389
+ ${getCurrentDateXml()}${traceIdXml}
7146
7390
  ${prContext}${slackContextXml}
7147
7391
  </context>
7148
7392
 
@@ -7184,7 +7428,7 @@ ${customInstructions}
7184
7428
  </instructions>
7185
7429
 
7186
7430
  <context>
7187
- ${getCurrentDateXml()}
7431
+ ${getCurrentDateXml()}${traceIdXml}
7188
7432
  ${prContext}${slackContextXml}
7189
7433
  </context>
7190
7434
 
@@ -7212,7 +7456,7 @@ ${customInstructions}
7212
7456
  </instructions>
7213
7457
 
7214
7458
  <context>
7215
- ${getCurrentDateXml()}
7459
+ ${getCurrentDateXml()}${traceIdXml}
7216
7460
  ${prContext}${slackContextXml}
7217
7461
  </context>`;
7218
7462
  }
@@ -7637,7 +7881,7 @@ ${schemaString}`);
7637
7881
  }
7638
7882
  if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
7639
7883
  try {
7640
- const fs22 = require("fs");
7884
+ const fs21 = require("fs");
7641
7885
  const path25 = require("path");
7642
7886
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
7643
7887
  const provider = this.config.provider || "auto";
@@ -7753,19 +7997,19 @@ ${"=".repeat(60)}
7753
7997
  readableVersion += `${"=".repeat(60)}
7754
7998
  `;
7755
7999
  const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path25.join(process.cwd(), "debug-artifacts");
7756
- if (!fs22.existsSync(debugArtifactsDir)) {
7757
- fs22.mkdirSync(debugArtifactsDir, { recursive: true });
8000
+ if (!fs21.existsSync(debugArtifactsDir)) {
8001
+ fs21.mkdirSync(debugArtifactsDir, { recursive: true });
7758
8002
  }
7759
8003
  const debugFile = path25.join(
7760
8004
  debugArtifactsDir,
7761
8005
  `prompt-${_checkName || "unknown"}-${timestamp}.json`
7762
8006
  );
7763
- fs22.writeFileSync(debugFile, debugJson, "utf-8");
8007
+ fs21.writeFileSync(debugFile, debugJson, "utf-8");
7764
8008
  const readableFile = path25.join(
7765
8009
  debugArtifactsDir,
7766
8010
  `prompt-${_checkName || "unknown"}-${timestamp}.txt`
7767
8011
  );
7768
- fs22.writeFileSync(readableFile, readableVersion, "utf-8");
8012
+ fs21.writeFileSync(readableFile, readableVersion, "utf-8");
7769
8013
  log(`
7770
8014
  \u{1F4BE} Full debug info saved to:`);
7771
8015
  log(` JSON: ${debugFile}`);
@@ -7798,7 +8042,7 @@ ${"=".repeat(60)}
7798
8042
  log(`\u{1F4E4} Response length: ${response.length} characters`);
7799
8043
  if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
7800
8044
  try {
7801
- const fs22 = require("fs");
8045
+ const fs21 = require("fs");
7802
8046
  const path25 = require("path");
7803
8047
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
7804
8048
  const agentAny2 = agent;
@@ -7823,7 +8067,7 @@ ${"=".repeat(60)}
7823
8067
  schema: effectiveSchema,
7824
8068
  totalMessages: fullHistory.length
7825
8069
  };
7826
- fs22.writeFileSync(sessionBase + ".json", JSON.stringify(sessionData, null, 2), "utf-8");
8070
+ fs21.writeFileSync(sessionBase + ".json", JSON.stringify(sessionData, null, 2), "utf-8");
7827
8071
  let readable = `=============================================================
7828
8072
  `;
7829
8073
  readable += `COMPLETE AI SESSION HISTORY (AFTER RESPONSE)
@@ -7850,7 +8094,7 @@ ${"=".repeat(60)}
7850
8094
  `;
7851
8095
  readable += content + "\n";
7852
8096
  });
7853
- fs22.writeFileSync(sessionBase + ".summary.txt", readable, "utf-8");
8097
+ fs21.writeFileSync(sessionBase + ".summary.txt", readable, "utf-8");
7854
8098
  log(`\u{1F4BE} Complete session history saved:`);
7855
8099
  log(` - Contains ALL ${fullHistory.length} messages (prompts + responses)`);
7856
8100
  } catch (error) {
@@ -7859,7 +8103,7 @@ ${"=".repeat(60)}
7859
8103
  }
7860
8104
  if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
7861
8105
  try {
7862
- const fs22 = require("fs");
8106
+ const fs21 = require("fs");
7863
8107
  const path25 = require("path");
7864
8108
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
7865
8109
  const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path25.join(process.cwd(), "debug-artifacts");
@@ -7896,7 +8140,7 @@ ${"=".repeat(60)}
7896
8140
  `;
7897
8141
  responseContent += `${"=".repeat(60)}
7898
8142
  `;
7899
- fs22.writeFileSync(responseFile, responseContent, "utf-8");
8143
+ fs21.writeFileSync(responseFile, responseContent, "utf-8");
7900
8144
  log(`\u{1F4BE} Response saved to: ${responseFile}`);
7901
8145
  } catch (error) {
7902
8146
  log(`\u26A0\uFE0F Could not save response file: ${error}`);
@@ -7912,9 +8156,9 @@ ${"=".repeat(60)}
7912
8156
  await agentAny._telemetryConfig.shutdown();
7913
8157
  log(`\u{1F4CA} OpenTelemetry trace saved to: ${agentAny._traceFilePath}`);
7914
8158
  if (process.env.GITHUB_ACTIONS) {
7915
- const fs22 = require("fs");
7916
- if (fs22.existsSync(agentAny._traceFilePath)) {
7917
- const stats = fs22.statSync(agentAny._traceFilePath);
8159
+ const fs21 = require("fs");
8160
+ if (fs21.existsSync(agentAny._traceFilePath)) {
8161
+ const stats = fs21.statSync(agentAny._traceFilePath);
7918
8162
  console.log(
7919
8163
  `::notice title=AI Trace Saved::${agentAny._traceFilePath} (${stats.size} bytes)`
7920
8164
  );
@@ -8046,6 +8290,9 @@ ${"=".repeat(60)}
8046
8290
  if (this.config.completionPrompt !== void 0) {
8047
8291
  options.completionPrompt = this.config.completionPrompt;
8048
8292
  }
8293
+ if (this.config.concurrencyLimiter) {
8294
+ options.concurrencyLimiter = this.config.concurrencyLimiter;
8295
+ }
8049
8296
  try {
8050
8297
  const cfgAny = this.config;
8051
8298
  const allowedFolders = cfgAny.allowedFolders;
@@ -8109,7 +8356,7 @@ ${schemaString}`);
8109
8356
  const model = this.config.model || "default";
8110
8357
  if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
8111
8358
  try {
8112
- const fs22 = require("fs");
8359
+ const fs21 = require("fs");
8113
8360
  const path25 = require("path");
8114
8361
  const os2 = require("os");
8115
8362
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
@@ -8185,7 +8432,7 @@ ${"=".repeat(60)}
8185
8432
  `;
8186
8433
  const tempDir = os2.tmpdir();
8187
8434
  const promptFile = path25.join(tempDir, `visor-prompt-${timestamp}.txt`);
8188
- fs22.writeFileSync(promptFile, prompt, "utf-8");
8435
+ fs21.writeFileSync(promptFile, prompt, "utf-8");
8189
8436
  log(`
8190
8437
  \u{1F4BE} Prompt saved to: ${promptFile}`);
8191
8438
  const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path25.join(process.cwd(), "debug-artifacts");
@@ -8194,8 +8441,8 @@ ${"=".repeat(60)}
8194
8441
  debugArtifactsDir,
8195
8442
  `prompt-${_checkName || "unknown"}-${timestamp}`
8196
8443
  );
8197
- fs22.writeFileSync(base + ".json", debugJson, "utf-8");
8198
- fs22.writeFileSync(base + ".summary.txt", readableVersion, "utf-8");
8444
+ fs21.writeFileSync(base + ".json", debugJson, "utf-8");
8445
+ fs21.writeFileSync(base + ".summary.txt", readableVersion, "utf-8");
8199
8446
  log(`
8200
8447
  \u{1F4BE} Full debug info saved to directory: ${debugArtifactsDir}`);
8201
8448
  } catch {
@@ -8240,7 +8487,7 @@ $ ${cliCommand}
8240
8487
  log(`\u{1F4E4} Response length: ${response.length} characters`);
8241
8488
  if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
8242
8489
  try {
8243
- const fs22 = require("fs");
8490
+ const fs21 = require("fs");
8244
8491
  const path25 = require("path");
8245
8492
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
8246
8493
  const agentAny = agent;
@@ -8265,7 +8512,7 @@ $ ${cliCommand}
8265
8512
  schema: effectiveSchema,
8266
8513
  totalMessages: fullHistory.length
8267
8514
  };
8268
- fs22.writeFileSync(sessionBase + ".json", JSON.stringify(sessionData, null, 2), "utf-8");
8515
+ fs21.writeFileSync(sessionBase + ".json", JSON.stringify(sessionData, null, 2), "utf-8");
8269
8516
  let readable = `=============================================================
8270
8517
  `;
8271
8518
  readable += `COMPLETE AI SESSION HISTORY (AFTER RESPONSE)
@@ -8292,7 +8539,7 @@ ${"=".repeat(60)}
8292
8539
  `;
8293
8540
  readable += content + "\n";
8294
8541
  });
8295
- fs22.writeFileSync(sessionBase + ".summary.txt", readable, "utf-8");
8542
+ fs21.writeFileSync(sessionBase + ".summary.txt", readable, "utf-8");
8296
8543
  log(`\u{1F4BE} Complete session history saved:`);
8297
8544
  log(` - Contains ALL ${fullHistory.length} messages (prompts + responses)`);
8298
8545
  } catch (error) {
@@ -8301,7 +8548,7 @@ ${"=".repeat(60)}
8301
8548
  }
8302
8549
  if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
8303
8550
  try {
8304
- const fs22 = require("fs");
8551
+ const fs21 = require("fs");
8305
8552
  const path25 = require("path");
8306
8553
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
8307
8554
  const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path25.join(process.cwd(), "debug-artifacts");
@@ -8338,7 +8585,7 @@ ${"=".repeat(60)}
8338
8585
  `;
8339
8586
  responseContent += `${"=".repeat(60)}
8340
8587
  `;
8341
- fs22.writeFileSync(responseFile, responseContent, "utf-8");
8588
+ fs21.writeFileSync(responseFile, responseContent, "utf-8");
8342
8589
  log(`\u{1F4BE} Response saved to: ${responseFile}`);
8343
8590
  } catch (error) {
8344
8591
  log(`\u26A0\uFE0F Could not save response file: ${error}`);
@@ -8356,9 +8603,9 @@ ${"=".repeat(60)}
8356
8603
  await telemetry.shutdown();
8357
8604
  log(`\u{1F4CA} OpenTelemetry trace saved to: ${traceFilePath}`);
8358
8605
  if (process.env.GITHUB_ACTIONS) {
8359
- const fs22 = require("fs");
8360
- if (fs22.existsSync(traceFilePath)) {
8361
- const stats = fs22.statSync(traceFilePath);
8606
+ const fs21 = require("fs");
8607
+ if (fs21.existsSync(traceFilePath)) {
8608
+ const stats = fs21.statSync(traceFilePath);
8362
8609
  console.log(
8363
8610
  `::notice title=AI Trace Saved::OpenTelemetry trace file size: ${stats.size} bytes`
8364
8611
  );
@@ -8396,7 +8643,7 @@ ${"=".repeat(60)}
8396
8643
  * Load schema content from schema files or inline definitions
8397
8644
  */
8398
8645
  async loadSchemaContent(schema) {
8399
- const fs22 = require("fs").promises;
8646
+ const fs21 = require("fs").promises;
8400
8647
  const path25 = require("path");
8401
8648
  if (typeof schema === "object" && schema !== null) {
8402
8649
  log("\u{1F4CB} Using inline schema object from configuration");
@@ -8417,7 +8664,7 @@ ${"=".repeat(60)}
8417
8664
  try {
8418
8665
  const schemaPath = path25.resolve(process.cwd(), schema);
8419
8666
  log(`\u{1F4CB} Loading custom schema from file: ${schemaPath}`);
8420
- const schemaContent = await fs22.readFile(schemaPath, "utf-8");
8667
+ const schemaContent = await fs21.readFile(schemaPath, "utf-8");
8421
8668
  return schemaContent.trim();
8422
8669
  } catch (error) {
8423
8670
  throw new Error(
@@ -8439,7 +8686,7 @@ ${"=".repeat(60)}
8439
8686
  ];
8440
8687
  for (const schemaPath of candidatePaths) {
8441
8688
  try {
8442
- const schemaContent = await fs22.readFile(schemaPath, "utf-8");
8689
+ const schemaContent = await fs21.readFile(schemaPath, "utf-8");
8443
8690
  return schemaContent.trim();
8444
8691
  } catch {
8445
8692
  }
@@ -8846,11 +9093,11 @@ var issue_filter_exports = {};
8846
9093
  __export(issue_filter_exports, {
8847
9094
  IssueFilter: () => IssueFilter
8848
9095
  });
8849
- var fs6, path7, IssueFilter;
9096
+ var fs5, path7, IssueFilter;
8850
9097
  var init_issue_filter = __esm({
8851
9098
  "src/issue-filter.ts"() {
8852
9099
  "use strict";
8853
- fs6 = __toESM(require("fs"));
9100
+ fs5 = __toESM(require("fs"));
8854
9101
  path7 = __toESM(require("path"));
8855
9102
  IssueFilter = class {
8856
9103
  fileCache = /* @__PURE__ */ new Map();
@@ -8920,16 +9167,16 @@ var init_issue_filter = __esm({
8920
9167
  }
8921
9168
  try {
8922
9169
  const resolvedPath = path7.isAbsolute(filePath) ? filePath : path7.join(workingDir, filePath);
8923
- if (!fs6.existsSync(resolvedPath)) {
8924
- if (fs6.existsSync(filePath)) {
8925
- const content2 = fs6.readFileSync(filePath, "utf8");
9170
+ if (!fs5.existsSync(resolvedPath)) {
9171
+ if (fs5.existsSync(filePath)) {
9172
+ const content2 = fs5.readFileSync(filePath, "utf8");
8926
9173
  const lines2 = content2.split("\n");
8927
9174
  this.fileCache.set(filePath, lines2);
8928
9175
  return lines2;
8929
9176
  }
8930
9177
  return null;
8931
9178
  }
8932
- const content = fs6.readFileSync(resolvedPath, "utf8");
9179
+ const content = fs5.readFileSync(resolvedPath, "utf8");
8933
9180
  const lines = content.split("\n");
8934
9181
  this.fileCache.set(filePath, lines);
8935
9182
  return lines;
@@ -10369,11 +10616,11 @@ var init_config_merger = __esm({
10369
10616
  });
10370
10617
 
10371
10618
  // src/utils/config-loader.ts
10372
- var fs8, path9, yaml2, ConfigLoader;
10619
+ var fs7, path9, yaml2, ConfigLoader;
10373
10620
  var init_config_loader = __esm({
10374
10621
  "src/utils/config-loader.ts"() {
10375
10622
  "use strict";
10376
- fs8 = __toESM(require("fs"));
10623
+ fs7 = __toESM(require("fs"));
10377
10624
  path9 = __toESM(require("path"));
10378
10625
  yaml2 = __toESM(require("js-yaml"));
10379
10626
  ConfigLoader = class {
@@ -10468,7 +10715,7 @@ var init_config_loader = __esm({
10468
10715
  const resolvedPath = path9.resolve(basePath, filePath);
10469
10716
  this.validateLocalPath(resolvedPath);
10470
10717
  try {
10471
- const content = fs8.readFileSync(resolvedPath, "utf8");
10718
+ const content = fs7.readFileSync(resolvedPath, "utf8");
10472
10719
  const config = yaml2.load(content);
10473
10720
  if (!config || typeof config !== "object") {
10474
10721
  throw new Error(`Invalid YAML in configuration file: ${resolvedPath}`);
@@ -10573,14 +10820,14 @@ var init_config_loader = __esm({
10573
10820
  ].filter((p) => p);
10574
10821
  let defaultConfigPath;
10575
10822
  for (const possiblePath of possiblePaths) {
10576
- if (fs8.existsSync(possiblePath)) {
10823
+ if (fs7.existsSync(possiblePath)) {
10577
10824
  defaultConfigPath = possiblePath;
10578
10825
  break;
10579
10826
  }
10580
10827
  }
10581
10828
  if (defaultConfigPath) {
10582
10829
  console.error(`\u{1F4E6} Loading bundled default configuration from ${defaultConfigPath}`);
10583
- const content = fs8.readFileSync(defaultConfigPath, "utf8");
10830
+ const content = fs7.readFileSync(defaultConfigPath, "utf8");
10584
10831
  let config = yaml2.load(content);
10585
10832
  if (!config || typeof config !== "object") {
10586
10833
  throw new Error("Invalid default configuration");
@@ -10701,9 +10948,9 @@ var init_config_loader = __esm({
10701
10948
  const root = path9.parse(currentDir).root;
10702
10949
  while (currentDir !== root) {
10703
10950
  const packageJsonPath = path9.join(currentDir, "package.json");
10704
- if (fs8.existsSync(packageJsonPath)) {
10951
+ if (fs7.existsSync(packageJsonPath)) {
10705
10952
  try {
10706
- const packageJson = JSON.parse(fs8.readFileSync(packageJsonPath, "utf8"));
10953
+ const packageJson = JSON.parse(fs7.readFileSync(packageJsonPath, "utf8"));
10707
10954
  if (packageJson.name === "@probelabs/visor") {
10708
10955
  return currentDir;
10709
10956
  }
@@ -11222,6 +11469,10 @@ var init_config_schema = __esm({
11222
11469
  type: "string",
11223
11470
  description: "JavaScript expression to dynamically compute custom tools for this AI check. Expression has access to: outputs, inputs, pr, files, env, memory Must return an array of tool names (strings) or WorkflowToolReference objects ({ workflow: string, args?: Record<string, unknown> })\n\nExample: ``` const tools = []; if (outputs['route-intent'].intent === 'engineer') { tools.push({ workflow: 'engineer', args: { projects: ['tyk'] } }); } return tools; ```"
11224
11471
  },
11472
+ ai_bash_config_js: {
11473
+ type: "string",
11474
+ description: "JavaScript expression to dynamically compute bash configuration for this AI check. Expression has access to: outputs, inputs, pr, files, env, memory. Must return a BashConfig object with optional allow/deny string arrays.\n\nExample: ``` return outputs['build-config']?.bash_config ?? {}; ```"
11475
+ },
11225
11476
  claude_code: {
11226
11477
  $ref: "#/definitions/ClaudeCodeConfig",
11227
11478
  description: "Claude Code configuration (for claude-code type checks)"
@@ -13126,12 +13377,12 @@ __export(config_exports, {
13126
13377
  ConfigManager: () => ConfigManager,
13127
13378
  VALID_EVENT_TRIGGERS: () => VALID_EVENT_TRIGGERS
13128
13379
  });
13129
- var yaml3, fs9, path10, import_simple_git, import_ajv3, import_ajv_formats2, VALID_EVENT_TRIGGERS, ConfigManager, __ajvValidate, __ajvErrors;
13380
+ var yaml3, fs8, path10, import_simple_git, import_ajv3, import_ajv_formats2, VALID_EVENT_TRIGGERS, ConfigManager, __ajvValidate, __ajvErrors;
13130
13381
  var init_config = __esm({
13131
13382
  "src/config.ts"() {
13132
13383
  "use strict";
13133
13384
  yaml3 = __toESM(require("js-yaml"));
13134
- fs9 = __toESM(require("fs"));
13385
+ fs8 = __toESM(require("fs"));
13135
13386
  path10 = __toESM(require("path"));
13136
13387
  init_logger();
13137
13388
  import_simple_git = __toESM(require("simple-git"));
@@ -13180,7 +13431,7 @@ var init_config = __esm({
13180
13431
  try {
13181
13432
  let configContent;
13182
13433
  try {
13183
- configContent = fs9.readFileSync(resolvedPath, "utf8");
13434
+ configContent = fs8.readFileSync(resolvedPath, "utf8");
13184
13435
  } catch (readErr) {
13185
13436
  if (readErr && (readErr.code === "ENOENT" || readErr.code === "ENOTDIR")) {
13186
13437
  throw new Error(`Configuration file not found: ${resolvedPath}`);
@@ -13306,7 +13557,7 @@ var init_config = __esm({
13306
13557
  );
13307
13558
  for (const p of candidates) {
13308
13559
  try {
13309
- const st = fs9.statSync(p);
13560
+ const st = fs8.statSync(p);
13310
13561
  if (!st.isFile()) continue;
13311
13562
  const isLegacy = path10.basename(p).startsWith(".");
13312
13563
  if (isLegacy) {
@@ -13390,7 +13641,7 @@ var init_config = __esm({
13390
13641
  }
13391
13642
  let bundledConfigPath;
13392
13643
  for (const possiblePath of possiblePaths) {
13393
- if (fs9.existsSync(possiblePath)) {
13644
+ if (fs8.existsSync(possiblePath)) {
13394
13645
  bundledConfigPath = possiblePath;
13395
13646
  break;
13396
13647
  }
@@ -13398,7 +13649,7 @@ var init_config = __esm({
13398
13649
  if (bundledConfigPath) {
13399
13650
  console.error(`\u{1F4E6} Loading bundled default configuration from ${bundledConfigPath}`);
13400
13651
  const readAndParse = (p) => {
13401
- const raw = fs9.readFileSync(p, "utf8");
13652
+ const raw = fs8.readFileSync(p, "utf8");
13402
13653
  const obj = yaml3.load(raw);
13403
13654
  if (!obj || typeof obj !== "object") return {};
13404
13655
  if (obj.include && !obj.extends) {
@@ -13446,9 +13697,9 @@ var init_config = __esm({
13446
13697
  let currentDir = __dirname;
13447
13698
  while (currentDir !== path10.dirname(currentDir)) {
13448
13699
  const packageJsonPath = path10.join(currentDir, "package.json");
13449
- if (fs9.existsSync(packageJsonPath)) {
13700
+ if (fs8.existsSync(packageJsonPath)) {
13450
13701
  try {
13451
- const packageJson = JSON.parse(fs9.readFileSync(packageJsonPath, "utf8"));
13702
+ const packageJson = JSON.parse(fs8.readFileSync(packageJsonPath, "utf8"));
13452
13703
  if (packageJson.name === "@probelabs/visor") {
13453
13704
  return currentDir;
13454
13705
  }
@@ -14212,7 +14463,7 @@ ${errors}`);
14212
14463
  if (policy.engine === "local" && policy.rules) {
14213
14464
  const rulesPath = Array.isArray(policy.rules) ? policy.rules : [policy.rules];
14214
14465
  for (const rp of rulesPath) {
14215
- if (typeof rp === "string" && !fs9.existsSync(path10.resolve(rp))) {
14466
+ if (typeof rp === "string" && !fs8.existsSync(path10.resolve(rp))) {
14216
14467
  warnings.push({
14217
14468
  field: "policy.rules",
14218
14469
  message: `Policy rules path does not exist: ${rp}. It will be resolved at runtime.`,
@@ -14262,7 +14513,7 @@ ${errors}`);
14262
14513
  });
14263
14514
  }
14264
14515
  }
14265
- if (policy.data && typeof policy.data === "string" && !fs9.existsSync(path10.resolve(policy.data))) {
14516
+ if (policy.data && typeof policy.data === "string" && !fs8.existsSync(path10.resolve(policy.data))) {
14266
14517
  warnings.push({
14267
14518
  field: "policy.data",
14268
14519
  message: `Policy data file does not exist: ${policy.data}. It will be resolved at runtime.`,
@@ -15045,6 +15296,31 @@ var init_workflow_check_provider = __esm({
15045
15296
  `[WorkflowProvider] NO WORKSPACE from parent - nested checkouts won't be added to workspace!`
15046
15297
  );
15047
15298
  }
15299
+ const stepPrefix = config.checkName || workflow.id;
15300
+ const parentExecCtx = parentContext?.executionContext;
15301
+ let childExecCtx = parentExecCtx;
15302
+ if (parentExecCtx?.hooks) {
15303
+ const parentHooks = parentExecCtx.hooks;
15304
+ childExecCtx = {
15305
+ ...parentExecCtx,
15306
+ hooks: {
15307
+ ...parentHooks,
15308
+ // Wrap onPromptCaptured: emit with prefixed step name so parent sees "route-intent.classify"
15309
+ onPromptCaptured: parentHooks.onPromptCaptured ? (info) => {
15310
+ parentHooks.onPromptCaptured({
15311
+ ...info,
15312
+ step: `${stepPrefix}.${info.step}`
15313
+ });
15314
+ } : void 0,
15315
+ // Wrap mockForStep: try prefixed name first, fall back to unprefixed for backward compat
15316
+ mockForStep: parentHooks.mockForStep ? (step) => {
15317
+ const prefixed = parentHooks.mockForStep(`${stepPrefix}.${step}`);
15318
+ if (prefixed !== void 0) return prefixed;
15319
+ return parentHooks.mockForStep(step);
15320
+ } : void 0
15321
+ }
15322
+ };
15323
+ }
15048
15324
  const childContext = {
15049
15325
  mode: "state-machine",
15050
15326
  config: workflowConfig,
@@ -15066,9 +15342,9 @@ var init_workflow_check_provider = __esm({
15066
15342
  debug: parentContext?.debug || false,
15067
15343
  maxParallelism: parentContext?.maxParallelism,
15068
15344
  failFast: parentContext?.failFast,
15069
- // Propagate execution hooks (mocks, octokit, etc.) into the child so
15070
- // nested steps can be mocked/observed by the YAML test runner.
15071
- executionContext: parentContext?.executionContext,
15345
+ // Propagate wrapped execution hooks (mocks, octokit, etc.) into the child so
15346
+ // nested steps can be mocked/observed by the YAML test runner with dotted prefixes.
15347
+ executionContext: childExecCtx,
15072
15348
  // Ensure all workflow steps are considered requested to avoid tag/event filtering surprises
15073
15349
  requestedChecks: Object.keys(checksMetadata)
15074
15350
  };
@@ -15082,6 +15358,26 @@ var init_workflow_check_provider = __esm({
15082
15358
  `[WorkflowProvider] Executing nested workflow '${workflow.id}' at depth ${currentDepth + 1}`
15083
15359
  );
15084
15360
  const result = await runner.run();
15361
+ if (parentContext?.journal && parentContext?.sessionId) {
15362
+ const parentJournal = parentContext.journal;
15363
+ const childSnapshot = childJournal.beginSnapshot();
15364
+ const childSessionId = childContext.sessionId;
15365
+ const childEntries = childJournal.readVisible(childSessionId, childSnapshot, void 0);
15366
+ for (const entry of childEntries) {
15367
+ parentJournal.commitEntry({
15368
+ sessionId: parentContext.sessionId,
15369
+ scope: entry.scope,
15370
+ checkId: `${stepPrefix}.${entry.checkId}`,
15371
+ result: entry.result,
15372
+ event: entry.event
15373
+ });
15374
+ }
15375
+ if (childEntries.length > 0) {
15376
+ logger.debug(
15377
+ `[WorkflowProvider] Propagated ${childEntries.length} child journal entries to parent with prefix '${stepPrefix}.'`
15378
+ );
15379
+ }
15380
+ }
15085
15381
  const bubbledEvents = childContext._bubbledEvents || [];
15086
15382
  if (bubbledEvents.length > 0 && parentContext) {
15087
15383
  if (parentContext.debug) {
@@ -15285,13 +15581,13 @@ var init_workflow_check_provider = __esm({
15285
15581
  */
15286
15582
  async loadWorkflowFromConfigPath(sourcePath, baseDir) {
15287
15583
  const path25 = require("path");
15288
- const fs22 = require("fs");
15584
+ const fs21 = require("fs");
15289
15585
  const yaml5 = require("js-yaml");
15290
15586
  const resolved = path25.isAbsolute(sourcePath) ? sourcePath : path25.resolve(baseDir, sourcePath);
15291
- if (!fs22.existsSync(resolved)) {
15587
+ if (!fs21.existsSync(resolved)) {
15292
15588
  throw new Error(`Workflow config not found at: ${resolved}`);
15293
15589
  }
15294
- const rawContent = fs22.readFileSync(resolved, "utf8");
15590
+ const rawContent = fs21.readFileSync(resolved, "utf8");
15295
15591
  const rawData = yaml5.load(rawContent);
15296
15592
  if (rawData.imports && Array.isArray(rawData.imports)) {
15297
15593
  const configDir = path25.dirname(resolved);
@@ -15971,7 +16267,7 @@ async function migrateJsonToBackend(jsonPath, backend) {
15971
16267
  const resolvedPath = import_path5.default.resolve(process.cwd(), jsonPath);
15972
16268
  let content;
15973
16269
  try {
15974
- content = await import_promises3.default.readFile(resolvedPath, "utf-8");
16270
+ content = await import_promises2.default.readFile(resolvedPath, "utf-8");
15975
16271
  } catch (err) {
15976
16272
  if (err.code === "ENOENT") {
15977
16273
  return 0;
@@ -16018,7 +16314,7 @@ async function migrateJsonToBackend(jsonPath, backend) {
16018
16314
  async function renameToMigrated(resolvedPath) {
16019
16315
  const migratedPath = `${resolvedPath}.migrated`;
16020
16316
  try {
16021
- await import_promises3.default.rename(resolvedPath, migratedPath);
16317
+ await import_promises2.default.rename(resolvedPath, migratedPath);
16022
16318
  logger.info(`[JsonMigrator] Backed up ${resolvedPath} \u2192 ${migratedPath}`);
16023
16319
  } catch (err) {
16024
16320
  logger.warn(
@@ -16026,11 +16322,11 @@ async function renameToMigrated(resolvedPath) {
16026
16322
  );
16027
16323
  }
16028
16324
  }
16029
- var import_promises3, import_path5;
16325
+ var import_promises2, import_path5;
16030
16326
  var init_json_migrator = __esm({
16031
16327
  "src/scheduler/store/json-migrator.ts"() {
16032
16328
  "use strict";
16033
- import_promises3 = __toESM(require("fs/promises"));
16329
+ import_promises2 = __toESM(require("fs/promises"));
16034
16330
  import_path5 = __toESM(require("path"));
16035
16331
  init_logger();
16036
16332
  }
@@ -16439,7 +16735,7 @@ function formatScheduleList(schedules) {
16439
16735
  if (schedules.length === 0) {
16440
16736
  return `You don't have any active schedules.
16441
16737
 
16442
- To create one: "schedule daily-report every Monday at 9am"`;
16738
+ To create one: "remind me every Monday at 9am to check PRs" or "schedule %daily-report every Monday at 9am"`;
16443
16739
  }
16444
16740
  const lines = schedules.map((s, i) => `${i + 1}. ${formatSchedule(s)}`);
16445
16741
  return `**Your active schedules:**
@@ -16715,6 +17011,12 @@ function getScheduleToolDefinition() {
16715
17011
 
16716
17012
  YOU (the AI) must extract and structure all scheduling parameters. Do NOT pass natural language time expressions - convert them to cron or ISO timestamps.
16717
17013
 
17014
+ CRITICAL WORKFLOW RULE:
17015
+ - To schedule a WORKFLOW, the user MUST use a '%' prefix (e.g., "schedule %my-workflow daily").
17016
+ - If the '%' prefix is present, extract the word following it as the 'workflow' parameter (without the '%').
17017
+ - If the '%' prefix is NOT present, the request is a simple text reminder. The ENTIRE user request (excluding the schedule expression) MUST be placed in the 'reminder_text' parameter.
17018
+ - DO NOT guess or infer a workflow name from a user's request without the '%' prefix.
17019
+
16718
17020
  ACTIONS:
16719
17021
  - create: Schedule a new reminder or workflow
16720
17022
  - list: Show user's active schedules
@@ -16722,7 +17024,9 @@ ACTIONS:
16722
17024
  - pause/resume: Temporarily disable/enable a schedule
16723
17025
 
16724
17026
  FOR CREATE ACTION - Extract these from user's request:
16725
- 1. WHAT: Either reminder_text (message to send) OR workflow (workflow ID to run)
17027
+ 1. WHAT:
17028
+ - If user says "schedule %some-workflow ...", populate 'workflow' with "some-workflow".
17029
+ - Otherwise, populate 'reminder_text' with the user's full request text.
16726
17030
  2. WHERE: Use the CURRENT channel from context
16727
17031
  - target_id: The channel ID from context (C... for channels, D... for DMs)
16728
17032
  - target_type: "channel" for public/private channels, "dm" for direct messages
@@ -16757,7 +17061,7 @@ User in DM: "remind me to check builds every day at 9am"
16757
17061
  "original_expression": "every day at 9am"
16758
17062
  }
16759
17063
 
16760
- User in #security channel: "run security-scan every Monday at 10am"
17064
+ User in #security channel: "schedule %security-scan every Monday at 10am"
16761
17065
  \u2192 {
16762
17066
  "action": "create",
16763
17067
  "workflow": "security-scan",
@@ -16768,6 +17072,17 @@ User in #security channel: "run security-scan every Monday at 10am"
16768
17072
  "original_expression": "every Monday at 10am"
16769
17073
  }
16770
17074
 
17075
+ User in #security channel: "run security-scan every Monday at 10am" (NO % prefix!)
17076
+ \u2192 {
17077
+ "action": "create",
17078
+ "reminder_text": "run security-scan every Monday at 10am",
17079
+ "is_recurring": true,
17080
+ "cron": "0 10 * * 1",
17081
+ "target_type": "channel",
17082
+ "target_id": "<channel ID from context, e.g., C05ABC123>",
17083
+ "original_expression": "every Monday at 10am"
17084
+ }
17085
+
16771
17086
  User in DM: "remind me in 2 hours to review the PR"
16772
17087
  \u2192 {
16773
17088
  "action": "create",
@@ -16811,7 +17126,7 @@ User: "cancel schedule abc123"
16811
17126
  },
16812
17127
  workflow: {
16813
17128
  type: "string",
16814
- description: "For create: workflow/check ID to run (use instead of reminder_text for workflow execution)"
17129
+ description: 'For create: workflow ID to run. ONLY populate this if the user used the % prefix (e.g., "%my-workflow"). Extract the name without the % symbol. If no % prefix, use reminder_text instead.'
16815
17130
  },
16816
17131
  workflow_inputs: {
16817
17132
  type: "object",
@@ -17785,16 +18100,13 @@ var init_mcp_custom_sse_server = __esm({
17785
18100
  logger.error(
17786
18101
  `[CustomToolsSSEServer:${this.sessionId}] Tool execution failed: ${toolName} - ${errorMsg}`
17787
18102
  );
18103
+ const isValidationError = errorMsg.startsWith("Invalid workflow inputs:");
17788
18104
  return {
17789
18105
  jsonrpc: "2.0",
17790
18106
  id,
17791
18107
  error: {
17792
- code: -32603,
17793
- message: "Internal error",
17794
- data: {
17795
- tool: toolName,
17796
- error: errorMsg
17797
- }
18108
+ code: isValidationError ? -32602 : -32603,
18109
+ message: isValidationError ? `Invalid tool parameters: ${errorMsg}` : `Internal error during tool execution: ${errorMsg}`
17798
18110
  }
17799
18111
  };
17800
18112
  } finally {
@@ -17831,7 +18143,7 @@ var init_mcp_custom_sse_server = __esm({
17831
18143
  });
17832
18144
 
17833
18145
  // src/providers/ai-check-provider.ts
17834
- var import_promises4, import_path6, AICheckProvider;
18146
+ var import_promises3, import_path6, AICheckProvider;
17835
18147
  var init_ai_check_provider = __esm({
17836
18148
  "src/providers/ai-check-provider.ts"() {
17837
18149
  "use strict";
@@ -17840,7 +18152,7 @@ var init_ai_check_provider = __esm({
17840
18152
  init_env_resolver();
17841
18153
  init_issue_filter();
17842
18154
  init_liquid_extensions();
17843
- import_promises4 = __toESM(require("fs/promises"));
18155
+ import_promises3 = __toESM(require("fs/promises"));
17844
18156
  import_path6 = __toESM(require("path"));
17845
18157
  init_lazy_otel();
17846
18158
  init_state_capture();
@@ -18020,9 +18332,9 @@ var init_ai_check_provider = __esm({
18020
18332
  } else {
18021
18333
  resolvedPath = import_path6.default.resolve(process.cwd(), str);
18022
18334
  }
18023
- const fs22 = require("fs").promises;
18335
+ const fs21 = require("fs").promises;
18024
18336
  try {
18025
- const stat = await fs22.stat(resolvedPath);
18337
+ const stat = await fs21.stat(resolvedPath);
18026
18338
  return stat.isFile();
18027
18339
  } catch {
18028
18340
  return hasFileExtension && (isRelativePath || isAbsolutePath || hasPathSeparators);
@@ -18055,7 +18367,7 @@ var init_ai_check_provider = __esm({
18055
18367
  throw new Error("Invalid prompt file path: path traversal detected");
18056
18368
  }
18057
18369
  try {
18058
- const promptContent = await import_promises4.default.readFile(resolvedPath, "utf-8");
18370
+ const promptContent = await import_promises3.default.readFile(resolvedPath, "utf-8");
18059
18371
  return promptContent;
18060
18372
  } catch (error) {
18061
18373
  throw new Error(
@@ -18560,6 +18872,10 @@ ${preview}`);
18560
18872
  if (config.ai_max_iterations !== void 0 && aiConfig.maxIterations === void 0) {
18561
18873
  aiConfig.maxIterations = config.ai_max_iterations;
18562
18874
  }
18875
+ const sharedLimiter = sessionInfo?._parentContext?.sharedConcurrencyLimiter;
18876
+ if (sharedLimiter) {
18877
+ aiConfig.concurrencyLimiter = sharedLimiter;
18878
+ }
18563
18879
  const policyEngine = sessionInfo?._parentContext?.policyEngine;
18564
18880
  if (policyEngine) {
18565
18881
  try {
@@ -18791,13 +19107,13 @@ ${preview}`);
18791
19107
  `[AICheckProvider] Started custom tools SSE server '${customToolsServerName}' on port ${port} for ${customTools.size} tools`
18792
19108
  );
18793
19109
  }
19110
+ const workflowToolTimeout = config.ai?.timeout || 18e5;
18794
19111
  mcpServers[customToolsServerName] = {
18795
19112
  command: "",
18796
19113
  args: [],
18797
19114
  url: `http://localhost:${port}/sse`,
18798
19115
  transport: "sse",
18799
- timeout: 6e5
18800
- // 10 minutes for workflow tools
19116
+ timeout: workflowToolTimeout
18801
19117
  };
18802
19118
  }
18803
19119
  } catch (error) {
@@ -18810,6 +19126,37 @@ ${preview}`);
18810
19126
  aiConfig.mcpServers = mcpServers;
18811
19127
  } else if (config.ai?.disableTools) {
18812
19128
  }
19129
+ const bashConfigJsExpr = config.ai_bash_config_js;
19130
+ if (bashConfigJsExpr && _dependencyResults) {
19131
+ try {
19132
+ const dynamicBashConfig = this.evaluateBashConfigJs(
19133
+ bashConfigJsExpr,
19134
+ prInfo,
19135
+ _dependencyResults,
19136
+ config
19137
+ );
19138
+ if (!aiConfig.bashConfig) aiConfig.bashConfig = {};
19139
+ if (dynamicBashConfig.allow?.length) {
19140
+ aiConfig.bashConfig.allow = [
19141
+ ...aiConfig.bashConfig.allow || [],
19142
+ ...dynamicBashConfig.allow
19143
+ ];
19144
+ }
19145
+ if (dynamicBashConfig.deny?.length) {
19146
+ aiConfig.bashConfig.deny = [
19147
+ ...aiConfig.bashConfig.deny || [],
19148
+ ...dynamicBashConfig.deny
19149
+ ];
19150
+ }
19151
+ if (dynamicBashConfig.allow?.length || dynamicBashConfig.deny?.length) {
19152
+ aiConfig.allowBash = true;
19153
+ }
19154
+ } catch (error) {
19155
+ logger.error(
19156
+ `[AICheckProvider] Failed to evaluate ai_bash_config_js: ${error instanceof Error ? error.message : "Unknown error"}`
19157
+ );
19158
+ }
19159
+ }
18813
19160
  const templateContext = {
18814
19161
  pr: {
18815
19162
  number: prInfo.number,
@@ -18831,6 +19178,10 @@ ${preview}`);
18831
19178
  const span = trace.getSpan(context.active());
18832
19179
  if (span) {
18833
19180
  captureCheckInputContext(span, templateContext);
19181
+ const spanTraceId = span.spanContext()?.traceId;
19182
+ if (spanTraceId) {
19183
+ prInfo.otelTraceId = spanTraceId;
19184
+ }
18834
19185
  }
18835
19186
  } catch {
18836
19187
  }
@@ -19235,6 +19586,80 @@ ${processedPrompt}` : processedPrompt;
19235
19586
  return {};
19236
19587
  }
19237
19588
  }
19589
+ /**
19590
+ * Evaluate ai_bash_config_js expression to dynamically compute bash configuration.
19591
+ * Returns a partial BashConfig object with optional allow/deny arrays.
19592
+ */
19593
+ evaluateBashConfigJs(expression, prInfo, dependencyResults, config) {
19594
+ if (!this.sandbox) {
19595
+ this.sandbox = createSecureSandbox();
19596
+ }
19597
+ const outputs = {};
19598
+ for (const [checkId, result] of dependencyResults.entries()) {
19599
+ const summary = result;
19600
+ outputs[checkId] = summary.output !== void 0 ? summary.output : summary;
19601
+ }
19602
+ const jsContext = {
19603
+ outputs,
19604
+ inputs: config.inputs || {},
19605
+ pr: {
19606
+ number: prInfo.number,
19607
+ title: prInfo.title,
19608
+ description: prInfo.body,
19609
+ author: prInfo.author,
19610
+ branch: prInfo.head,
19611
+ base: prInfo.base,
19612
+ authorAssociation: prInfo.authorAssociation
19613
+ },
19614
+ files: prInfo.files?.map((f) => ({
19615
+ filename: f.filename,
19616
+ status: f.status,
19617
+ additions: f.additions,
19618
+ deletions: f.deletions,
19619
+ changes: f.changes
19620
+ })) || [],
19621
+ env: this.buildSafeEnv(),
19622
+ memory: config.__memoryAccessor || {}
19623
+ };
19624
+ try {
19625
+ const result = compileAndRun(this.sandbox, expression, jsContext, {
19626
+ injectLog: true,
19627
+ wrapFunction: true,
19628
+ logPrefix: "[ai_bash_config_js]"
19629
+ });
19630
+ if (typeof result !== "object" || result === null || Array.isArray(result)) {
19631
+ logger.warn(
19632
+ `[AICheckProvider] ai_bash_config_js must return an object, got ${Array.isArray(result) ? "array" : typeof result}`
19633
+ );
19634
+ return {};
19635
+ }
19636
+ const cfg = result;
19637
+ const validConfig = {};
19638
+ if (cfg.allow !== void 0) {
19639
+ if (Array.isArray(cfg.allow) && cfg.allow.every((item) => typeof item === "string")) {
19640
+ validConfig.allow = cfg.allow;
19641
+ } else {
19642
+ logger.warn(`[AICheckProvider] ai_bash_config_js: 'allow' must be a string array`);
19643
+ }
19644
+ }
19645
+ if (cfg.deny !== void 0) {
19646
+ if (Array.isArray(cfg.deny) && cfg.deny.every((item) => typeof item === "string")) {
19647
+ validConfig.deny = cfg.deny;
19648
+ } else {
19649
+ logger.warn(`[AICheckProvider] ai_bash_config_js: 'deny' must be a string array`);
19650
+ }
19651
+ }
19652
+ logger.debug(
19653
+ `[AICheckProvider] ai_bash_config_js evaluated: allow=${validConfig.allow?.length ?? 0}, deny=${validConfig.deny?.length ?? 0}`
19654
+ );
19655
+ return validConfig;
19656
+ } catch (error) {
19657
+ logger.error(
19658
+ `[AICheckProvider] Failed to evaluate ai_bash_config_js: ${error instanceof Error ? error.message : "Unknown error"}`
19659
+ );
19660
+ return {};
19661
+ }
19662
+ }
19238
19663
  /**
19239
19664
  * Build a safe subset of environment variables for sandbox access.
19240
19665
  * Excludes sensitive keys like API keys, secrets, tokens.
@@ -19331,6 +19756,7 @@ ${processedPrompt}` : processedPrompt;
19331
19756
  "ai_mcp_servers_js",
19332
19757
  "ai_custom_tools",
19333
19758
  "ai_custom_tools_js",
19759
+ "ai_bash_config_js",
19334
19760
  "env"
19335
19761
  ];
19336
19762
  }
@@ -19825,7 +20251,7 @@ var init_template_context = __esm({
19825
20251
  });
19826
20252
 
19827
20253
  // src/providers/http-client-provider.ts
19828
- var fs13, path15, HttpClientProvider;
20254
+ var fs12, path15, HttpClientProvider;
19829
20255
  var init_http_client_provider = __esm({
19830
20256
  "src/providers/http-client-provider.ts"() {
19831
20257
  "use strict";
@@ -19835,7 +20261,7 @@ var init_http_client_provider = __esm({
19835
20261
  init_sandbox();
19836
20262
  init_template_context();
19837
20263
  init_logger();
19838
- fs13 = __toESM(require("fs"));
20264
+ fs12 = __toESM(require("fs"));
19839
20265
  path15 = __toESM(require("path"));
19840
20266
  HttpClientProvider = class extends CheckProvider {
19841
20267
  liquid;
@@ -19937,8 +20363,8 @@ var init_http_client_provider = __esm({
19937
20363
  `[http_client] Resolved relative output_file to workspace: ${resolvedOutputFile}`
19938
20364
  );
19939
20365
  }
19940
- if (skipIfExists && fs13.existsSync(resolvedOutputFile)) {
19941
- const stats = fs13.statSync(resolvedOutputFile);
20366
+ if (skipIfExists && fs12.existsSync(resolvedOutputFile)) {
20367
+ const stats = fs12.statSync(resolvedOutputFile);
19942
20368
  logger.verbose(`[http_client] File cached: ${resolvedOutputFile} (${stats.size} bytes)`);
19943
20369
  return {
19944
20370
  issues: [],
@@ -20150,12 +20576,12 @@ var init_http_client_provider = __esm({
20150
20576
  };
20151
20577
  }
20152
20578
  const parentDir = path15.dirname(outputFile);
20153
- if (parentDir && !fs13.existsSync(parentDir)) {
20154
- fs13.mkdirSync(parentDir, { recursive: true });
20579
+ if (parentDir && !fs12.existsSync(parentDir)) {
20580
+ fs12.mkdirSync(parentDir, { recursive: true });
20155
20581
  }
20156
20582
  const arrayBuffer = await response.arrayBuffer();
20157
20583
  const buffer = Buffer.from(arrayBuffer);
20158
- fs13.writeFileSync(outputFile, buffer);
20584
+ fs12.writeFileSync(outputFile, buffer);
20159
20585
  const contentType = response.headers.get("content-type") || "application/octet-stream";
20160
20586
  logger.verbose(`[http_client] Downloaded: ${outputFile} (${buffer.length} bytes)`);
20161
20587
  return {
@@ -20914,7 +21340,7 @@ var init_claude_code_types = __esm({
20914
21340
  function isClaudeCodeConstructor(value) {
20915
21341
  return typeof value === "function";
20916
21342
  }
20917
- var import_promises5, import_path7, ClaudeCodeSDKNotInstalledError, ClaudeCodeAPIKeyMissingError, ClaudeCodeCheckProvider;
21343
+ var import_promises4, import_path7, ClaudeCodeSDKNotInstalledError, ClaudeCodeAPIKeyMissingError, ClaudeCodeCheckProvider;
20918
21344
  var init_claude_code_check_provider = __esm({
20919
21345
  "src/providers/claude-code-check-provider.ts"() {
20920
21346
  "use strict";
@@ -20922,7 +21348,7 @@ var init_claude_code_check_provider = __esm({
20922
21348
  init_env_resolver();
20923
21349
  init_issue_filter();
20924
21350
  init_liquid_extensions();
20925
- import_promises5 = __toESM(require("fs/promises"));
21351
+ import_promises4 = __toESM(require("fs/promises"));
20926
21352
  import_path7 = __toESM(require("path"));
20927
21353
  init_claude_code_types();
20928
21354
  ClaudeCodeSDKNotInstalledError = class extends Error {
@@ -21087,7 +21513,7 @@ var init_claude_code_check_provider = __esm({
21087
21513
  resolvedPath = import_path7.default.resolve(process.cwd(), str);
21088
21514
  }
21089
21515
  try {
21090
- const stat = await import_promises5.default.stat(resolvedPath);
21516
+ const stat = await import_promises4.default.stat(resolvedPath);
21091
21517
  return stat.isFile();
21092
21518
  } catch {
21093
21519
  return hasFileExtension && (isRelativePath || isAbsolutePath || hasPathSeparators);
@@ -21120,7 +21546,7 @@ var init_claude_code_check_provider = __esm({
21120
21546
  throw new Error("Invalid prompt file path: path traversal detected");
21121
21547
  }
21122
21548
  try {
21123
- const promptContent = await import_promises5.default.readFile(resolvedPath, "utf-8");
21549
+ const promptContent = await import_promises4.default.readFile(resolvedPath, "utf-8");
21124
21550
  return promptContent;
21125
21551
  } catch (error) {
21126
21552
  throw new Error(
@@ -21678,19 +22104,7 @@ var init_command_check_provider = __esm({
21678
22104
  }
21679
22105
  }
21680
22106
  const timeoutMs = config.timeout || 6e4;
21681
- const normalizeNodeEval = (cmd) => {
21682
- const re = /^(?<prefix>\s*(?:\/usr\/bin\/env\s+)?node(?:\.exe)?\s+(?:-e|--eval)\s+)(['"])([\s\S]*?)\2(?<suffix>\s|$)/;
21683
- const m = cmd.match(re);
21684
- if (!m || !m.groups) return cmd;
21685
- const prefix = m.groups.prefix;
21686
- const quote = m[2];
21687
- const code = m[3];
21688
- const suffix = m.groups.suffix || "";
21689
- if (!code.includes("\n")) return cmd;
21690
- const escaped = code.replace(/\n/g, "\\n");
21691
- return cmd.replace(re, `${prefix}${quote}${escaped}${quote}${suffix}`);
21692
- };
21693
- const safeCommand = normalizeNodeEval(renderedCommand);
22107
+ const safeCommand = renderedCommand;
21694
22108
  const parentContext = context2?._parentContext;
21695
22109
  const workingDirectory = parentContext?.workingDirectory;
21696
22110
  const workspaceEnabled = parentContext?.workspace?.isEnabled?.();
@@ -41197,7 +41611,7 @@ var init_stdin_reader = __esm({
41197
41611
  });
41198
41612
 
41199
41613
  // src/providers/human-input-check-provider.ts
41200
- var fs15, path17, HumanInputCheckProvider;
41614
+ var fs14, path17, HumanInputCheckProvider;
41201
41615
  var init_human_input_check_provider = __esm({
41202
41616
  "src/providers/human-input-check-provider.ts"() {
41203
41617
  "use strict";
@@ -41206,7 +41620,7 @@ var init_human_input_check_provider = __esm({
41206
41620
  init_prompt_state();
41207
41621
  init_liquid_extensions();
41208
41622
  init_stdin_reader();
41209
- fs15 = __toESM(require("fs"));
41623
+ fs14 = __toESM(require("fs"));
41210
41624
  path17 = __toESM(require("path"));
41211
41625
  HumanInputCheckProvider = class _HumanInputCheckProvider extends CheckProvider {
41212
41626
  liquid;
@@ -41388,12 +41802,12 @@ var init_human_input_check_provider = __esm({
41388
41802
  return null;
41389
41803
  }
41390
41804
  try {
41391
- await fs15.promises.access(normalizedPath, fs15.constants.R_OK);
41392
- const stats = await fs15.promises.stat(normalizedPath);
41805
+ await fs14.promises.access(normalizedPath, fs14.constants.R_OK);
41806
+ const stats = await fs14.promises.stat(normalizedPath);
41393
41807
  if (!stats.isFile()) {
41394
41808
  return null;
41395
41809
  }
41396
- const content = await fs15.promises.readFile(normalizedPath, "utf-8");
41810
+ const content = await fs14.promises.readFile(normalizedPath, "utf-8");
41397
41811
  return content.trim();
41398
41812
  } catch {
41399
41813
  return null;
@@ -41835,11 +42249,11 @@ var init_script_check_provider = __esm({
41835
42249
  });
41836
42250
 
41837
42251
  // src/utils/worktree-manager.ts
41838
- var fs16, fsp, path18, crypto, WorktreeManager, worktreeManager;
42252
+ var fs15, fsp, path18, crypto, WorktreeManager, worktreeManager;
41839
42253
  var init_worktree_manager = __esm({
41840
42254
  "src/utils/worktree-manager.ts"() {
41841
42255
  "use strict";
41842
- fs16 = __toESM(require("fs"));
42256
+ fs15 = __toESM(require("fs"));
41843
42257
  fsp = __toESM(require("fs/promises"));
41844
42258
  path18 = __toESM(require("path"));
41845
42259
  crypto = __toESM(require("crypto"));
@@ -41894,12 +42308,12 @@ var init_worktree_manager = __esm({
41894
42308
  }
41895
42309
  const reposDir = this.getReposDir();
41896
42310
  const worktreesDir = this.getWorktreesDir();
41897
- if (!fs16.existsSync(reposDir)) {
41898
- fs16.mkdirSync(reposDir, { recursive: true });
42311
+ if (!fs15.existsSync(reposDir)) {
42312
+ fs15.mkdirSync(reposDir, { recursive: true });
41899
42313
  logger.debug(`Created repos directory: ${reposDir}`);
41900
42314
  }
41901
- if (!fs16.existsSync(worktreesDir)) {
41902
- fs16.mkdirSync(worktreesDir, { recursive: true });
42315
+ if (!fs15.existsSync(worktreesDir)) {
42316
+ fs15.mkdirSync(worktreesDir, { recursive: true });
41903
42317
  logger.debug(`Created worktrees directory: ${worktreesDir}`);
41904
42318
  }
41905
42319
  }
@@ -41926,7 +42340,7 @@ var init_worktree_manager = __esm({
41926
42340
  const reposDir = this.getReposDir();
41927
42341
  const repoName = repository.replace(/\//g, "-");
41928
42342
  const bareRepoPath = path18.join(reposDir, `${repoName}.git`);
41929
- if (fs16.existsSync(bareRepoPath)) {
42343
+ if (fs15.existsSync(bareRepoPath)) {
41930
42344
  logger.debug(`Bare repository already exists: ${bareRepoPath}`);
41931
42345
  const verifyResult = await this.verifyBareRepoRemote(bareRepoPath, repoUrl);
41932
42346
  if (verifyResult === "timeout") {
@@ -42049,7 +42463,7 @@ var init_worktree_manager = __esm({
42049
42463
  if (options.workingDirectory) {
42050
42464
  worktreePath = this.validatePath(options.workingDirectory);
42051
42465
  }
42052
- if (fs16.existsSync(worktreePath)) {
42466
+ if (fs15.existsSync(worktreePath)) {
42053
42467
  logger.debug(`Worktree already exists: ${worktreePath}`);
42054
42468
  const metadata2 = await this.loadMetadata(worktreePath);
42055
42469
  if (metadata2) {
@@ -42290,9 +42704,9 @@ var init_worktree_manager = __esm({
42290
42704
  const result = await this.executeGitCommand(removeCmd, { timeout: 3e4 });
42291
42705
  if (result.exitCode !== 0) {
42292
42706
  logger.warn(`Failed to remove worktree via git: ${result.stderr}`);
42293
- if (fs16.existsSync(worktree_path)) {
42707
+ if (fs15.existsSync(worktree_path)) {
42294
42708
  logger.debug(`Manually removing worktree directory`);
42295
- fs16.rmSync(worktree_path, { recursive: true, force: true });
42709
+ fs15.rmSync(worktree_path, { recursive: true, force: true });
42296
42710
  }
42297
42711
  }
42298
42712
  this.activeWorktrees.delete(worktreeId);
@@ -42303,18 +42717,18 @@ var init_worktree_manager = __esm({
42303
42717
  */
42304
42718
  async saveMetadata(worktreePath, metadata) {
42305
42719
  const metadataPath = path18.join(worktreePath, ".visor-metadata.json");
42306
- fs16.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2), "utf8");
42720
+ fs15.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2), "utf8");
42307
42721
  }
42308
42722
  /**
42309
42723
  * Load worktree metadata
42310
42724
  */
42311
42725
  async loadMetadata(worktreePath) {
42312
42726
  const metadataPath = path18.join(worktreePath, ".visor-metadata.json");
42313
- if (!fs16.existsSync(metadataPath)) {
42727
+ if (!fs15.existsSync(metadataPath)) {
42314
42728
  return null;
42315
42729
  }
42316
42730
  try {
42317
- const content = fs16.readFileSync(metadataPath, "utf8");
42731
+ const content = fs15.readFileSync(metadataPath, "utf8");
42318
42732
  return JSON.parse(content);
42319
42733
  } catch (error) {
42320
42734
  logger.warn(`Failed to load metadata: ${error}`);
@@ -42326,10 +42740,10 @@ var init_worktree_manager = __esm({
42326
42740
  */
42327
42741
  async listWorktrees() {
42328
42742
  const worktreesDir = this.getWorktreesDir();
42329
- if (!fs16.existsSync(worktreesDir)) {
42743
+ if (!fs15.existsSync(worktreesDir)) {
42330
42744
  return [];
42331
42745
  }
42332
- const entries = fs16.readdirSync(worktreesDir, { withFileTypes: true });
42746
+ const entries = fs15.readdirSync(worktreesDir, { withFileTypes: true });
42333
42747
  const worktrees = [];
42334
42748
  for (const entry of entries) {
42335
42749
  if (!entry.isDirectory()) continue;
@@ -44492,7 +44906,7 @@ __export(renderer_schema_exports, {
44492
44906
  });
44493
44907
  async function loadRendererSchema(name) {
44494
44908
  try {
44495
- const fs22 = await import("fs/promises");
44909
+ const fs21 = await import("fs/promises");
44496
44910
  const path25 = await import("path");
44497
44911
  const sanitized = String(name).replace(/[^a-zA-Z0-9-]/g, "");
44498
44912
  if (!sanitized) return void 0;
@@ -44508,7 +44922,7 @@ async function loadRendererSchema(name) {
44508
44922
  ];
44509
44923
  for (const p of candidates) {
44510
44924
  try {
44511
- const raw = await fs22.readFile(p, "utf-8");
44925
+ const raw = await fs21.readFile(p, "utf-8");
44512
44926
  return JSON.parse(raw);
44513
44927
  } catch {
44514
44928
  }
@@ -46927,7 +47341,7 @@ function updateStats2(results, state, isForEachIteration = false) {
46927
47341
  async function renderTemplateContent2(checkId, checkConfig, reviewSummary) {
46928
47342
  try {
46929
47343
  const { createExtendedLiquid: createExtendedLiquid2 } = await Promise.resolve().then(() => (init_liquid_extensions(), liquid_extensions_exports));
46930
- const fs22 = await import("fs/promises");
47344
+ const fs21 = await import("fs/promises");
46931
47345
  const path25 = await import("path");
46932
47346
  const schemaRaw = checkConfig.schema || "plain";
46933
47347
  const schema = typeof schemaRaw === "string" ? schemaRaw : "code-review";
@@ -46938,7 +47352,7 @@ async function renderTemplateContent2(checkId, checkConfig, reviewSummary) {
46938
47352
  } else if (checkConfig.template && checkConfig.template.file) {
46939
47353
  const file = String(checkConfig.template.file);
46940
47354
  const resolved = path25.resolve(process.cwd(), file);
46941
- templateContent = await fs22.readFile(resolved, "utf-8");
47355
+ templateContent = await fs21.readFile(resolved, "utf-8");
46942
47356
  logger.debug(`[LevelDispatch] Using template file for ${checkId}: ${resolved}`);
46943
47357
  } else if (schema && schema !== "plain") {
46944
47358
  const sanitized = String(schema).replace(/[^a-zA-Z0-9-]/g, "");
@@ -46957,7 +47371,7 @@ async function renderTemplateContent2(checkId, checkConfig, reviewSummary) {
46957
47371
  ];
46958
47372
  for (const p of candidatePaths) {
46959
47373
  try {
46960
- templateContent = await fs22.readFile(p, "utf-8");
47374
+ templateContent = await fs21.readFile(p, "utf-8");
46961
47375
  if (templateContent) {
46962
47376
  logger.debug(`[LevelDispatch] Using schema template for ${checkId}: ${p}`);
46963
47377
  break;
@@ -47157,6 +47571,9 @@ var init_runner = __esm({
47157
47571
  break;
47158
47572
  }
47159
47573
  }
47574
+ if (this.state.currentState === "Completed" && !this.context._parentContext && !process.env.VISOR_TEST_MODE) {
47575
+ logger.info("All workflows finished \u2014 waiting for events");
47576
+ }
47160
47577
  return this.buildExecutionResult();
47161
47578
  } catch (error) {
47162
47579
  const errorMsg = error instanceof Error ? error.message : String(error);
@@ -48195,12 +48612,12 @@ var init_sandbox_manager = __esm({
48195
48612
  });
48196
48613
 
48197
48614
  // src/utils/file-exclusion.ts
48198
- var import_ignore, fs17, path19, DEFAULT_EXCLUSION_PATTERNS, FileExclusionHelper;
48615
+ var import_ignore, fs16, path19, DEFAULT_EXCLUSION_PATTERNS, FileExclusionHelper;
48199
48616
  var init_file_exclusion = __esm({
48200
48617
  "src/utils/file-exclusion.ts"() {
48201
48618
  "use strict";
48202
48619
  import_ignore = __toESM(require("ignore"));
48203
- fs17 = __toESM(require("fs"));
48620
+ fs16 = __toESM(require("fs"));
48204
48621
  path19 = __toESM(require("path"));
48205
48622
  DEFAULT_EXCLUSION_PATTERNS = [
48206
48623
  "dist/",
@@ -48246,8 +48663,8 @@ var init_file_exclusion = __esm({
48246
48663
  if (additionalPatterns && additionalPatterns.length > 0) {
48247
48664
  this.gitignore.add(additionalPatterns);
48248
48665
  }
48249
- if (fs17.existsSync(gitignorePath)) {
48250
- const rawContent = fs17.readFileSync(gitignorePath, "utf8");
48666
+ if (fs16.existsSync(gitignorePath)) {
48667
+ const rawContent = fs16.readFileSync(gitignorePath, "utf8");
48251
48668
  const gitignoreContent = rawContent.replace(/[\r\n]+/g, "\n").replace(/[\x00-\x09\x0B-\x1F\x7F]/g, "").split("\n").filter((line) => line.length < 1e3).join("\n").trim();
48252
48669
  this.gitignore.add(gitignoreContent);
48253
48670
  if (process.env.VISOR_DEBUG === "true") {
@@ -48279,13 +48696,13 @@ var git_repository_analyzer_exports = {};
48279
48696
  __export(git_repository_analyzer_exports, {
48280
48697
  GitRepositoryAnalyzer: () => GitRepositoryAnalyzer
48281
48698
  });
48282
- var import_simple_git2, path20, fs18, MAX_PATCH_SIZE, GitRepositoryAnalyzer;
48699
+ var import_simple_git2, path20, fs17, MAX_PATCH_SIZE, GitRepositoryAnalyzer;
48283
48700
  var init_git_repository_analyzer = __esm({
48284
48701
  "src/git-repository-analyzer.ts"() {
48285
48702
  "use strict";
48286
48703
  import_simple_git2 = require("simple-git");
48287
48704
  path20 = __toESM(require("path"));
48288
- fs18 = __toESM(require("fs"));
48705
+ fs17 = __toESM(require("fs"));
48289
48706
  init_file_exclusion();
48290
48707
  MAX_PATCH_SIZE = 50 * 1024;
48291
48708
  GitRepositoryAnalyzer = class {
@@ -48550,7 +48967,7 @@ ${file.patch}`).join("\n\n");
48550
48967
  let content;
48551
48968
  let truncated = false;
48552
48969
  try {
48553
- if (includeContext && status !== "added" && fs18.existsSync(filePath)) {
48970
+ if (includeContext && status !== "added" && fs17.existsSync(filePath)) {
48554
48971
  const diff = await this.git.diff(["--", filename]).catch(() => "");
48555
48972
  if (diff) {
48556
48973
  const result = this.truncatePatch(diff, filename);
@@ -48560,7 +48977,7 @@ ${file.patch}`).join("\n\n");
48560
48977
  additions = lines.filter((line) => line.startsWith("+")).length;
48561
48978
  deletions = lines.filter((line) => line.startsWith("-")).length;
48562
48979
  }
48563
- } else if (status !== "added" && fs18.existsSync(filePath)) {
48980
+ } else if (status !== "added" && fs17.existsSync(filePath)) {
48564
48981
  const diff = await this.git.diff(["--", filename]).catch(() => "");
48565
48982
  if (diff) {
48566
48983
  const lines = diff.split("\n");
@@ -48568,17 +48985,17 @@ ${file.patch}`).join("\n\n");
48568
48985
  deletions = lines.filter((line) => line.startsWith("-")).length;
48569
48986
  }
48570
48987
  }
48571
- if (status === "added" && fs18.existsSync(filePath)) {
48988
+ if (status === "added" && fs17.existsSync(filePath)) {
48572
48989
  try {
48573
- const stats = fs18.statSync(filePath);
48990
+ const stats = fs17.statSync(filePath);
48574
48991
  if (stats.isFile() && stats.size < 1024 * 1024) {
48575
48992
  if (includeContext) {
48576
- content = fs18.readFileSync(filePath, "utf8");
48993
+ content = fs17.readFileSync(filePath, "utf8");
48577
48994
  const result = this.truncatePatch(content, filename);
48578
48995
  patch = result.patch;
48579
48996
  truncated = result.truncated;
48580
48997
  }
48581
- const fileContent = includeContext ? content : fs18.readFileSync(filePath, "utf8");
48998
+ const fileContent = includeContext ? content : fs17.readFileSync(filePath, "utf8");
48582
48999
  additions = fileContent.split("\n").length;
48583
49000
  }
48584
49001
  } catch {
@@ -49082,6 +49499,17 @@ function buildEngineContextForRun(workingDirectory, config, prInfo, debug, maxPa
49082
49499
  }
49083
49500
  const journal = new ExecutionJournal();
49084
49501
  const memory = MemoryStore.getInstance(clonedConfig.memory);
49502
+ let sharedConcurrencyLimiter = void 0;
49503
+ if (clonedConfig.max_ai_concurrency && _DelegationManager) {
49504
+ sharedConcurrencyLimiter = new _DelegationManager({
49505
+ maxConcurrent: clonedConfig.max_ai_concurrency,
49506
+ maxPerSession: 999
49507
+ // No per-session limit needed for global AI gating
49508
+ });
49509
+ logger.debug(
49510
+ `[EngineContext] Created shared AI concurrency limiter (max: ${clonedConfig.max_ai_concurrency})`
49511
+ );
49512
+ }
49085
49513
  return {
49086
49514
  mode: "state-machine",
49087
49515
  config: clonedConfig,
@@ -49094,6 +49522,7 @@ function buildEngineContextForRun(workingDirectory, config, prInfo, debug, maxPa
49094
49522
  event: prInfo.eventType,
49095
49523
  debug,
49096
49524
  maxParallelism,
49525
+ sharedConcurrencyLimiter,
49097
49526
  failFast,
49098
49527
  requestedChecks: requestedChecks && requestedChecks.length > 0 ? requestedChecks : void 0,
49099
49528
  // Store prInfo for later access (e.g., in getOutputHistorySnapshot)
@@ -49140,6 +49569,7 @@ async function initializeWorkspace(context2) {
49140
49569
  return context2;
49141
49570
  }
49142
49571
  }
49572
+ var _DelegationManager;
49143
49573
  var init_build_engine_context = __esm({
49144
49574
  "src/state-machine/context/build-engine-context.ts"() {
49145
49575
  "use strict";
@@ -49148,6 +49578,14 @@ var init_build_engine_context = __esm({
49148
49578
  init_human_id();
49149
49579
  init_logger();
49150
49580
  init_workspace_manager();
49581
+ _DelegationManager = null;
49582
+ try {
49583
+ const probe = require("@probelabs/probe");
49584
+ if (probe && typeof probe.DelegationManager === "function") {
49585
+ _DelegationManager = probe.DelegationManager;
49586
+ }
49587
+ } catch {
49588
+ }
49151
49589
  }
49152
49590
  });
49153
49591
 
@@ -50956,7 +51394,7 @@ async function renderMermaidToPng(mermaidCode) {
50956
51394
  `mermaid-${Date.now()}-${Math.random().toString(36).slice(2)}.png`
50957
51395
  );
50958
51396
  try {
50959
- fs20.writeFileSync(inputFile, mermaidCode, "utf-8");
51397
+ fs19.writeFileSync(inputFile, mermaidCode, "utf-8");
50960
51398
  const chromiumPaths = [
50961
51399
  "/usr/bin/chromium",
50962
51400
  "/usr/bin/chromium-browser",
@@ -50965,7 +51403,7 @@ async function renderMermaidToPng(mermaidCode) {
50965
51403
  ];
50966
51404
  let chromiumPath;
50967
51405
  for (const p of chromiumPaths) {
50968
- if (fs20.existsSync(p)) {
51406
+ if (fs19.existsSync(p)) {
50969
51407
  chromiumPath = p;
50970
51408
  break;
50971
51409
  }
@@ -51017,19 +51455,19 @@ async function renderMermaidToPng(mermaidCode) {
51017
51455
  console.warn(`Mermaid rendering failed: ${result.error}`);
51018
51456
  return null;
51019
51457
  }
51020
- if (!fs20.existsSync(outputFile)) {
51458
+ if (!fs19.existsSync(outputFile)) {
51021
51459
  console.warn("Mermaid output file not created");
51022
51460
  return null;
51023
51461
  }
51024
- const pngBuffer = fs20.readFileSync(outputFile);
51462
+ const pngBuffer = fs19.readFileSync(outputFile);
51025
51463
  return pngBuffer;
51026
51464
  } catch (e) {
51027
51465
  console.warn(`Mermaid rendering error: ${e instanceof Error ? e.message : String(e)}`);
51028
51466
  return null;
51029
51467
  } finally {
51030
51468
  try {
51031
- if (fs20.existsSync(inputFile)) fs20.unlinkSync(inputFile);
51032
- if (fs20.existsSync(outputFile)) fs20.unlinkSync(outputFile);
51469
+ if (fs19.existsSync(inputFile)) fs19.unlinkSync(inputFile);
51470
+ if (fs19.existsSync(outputFile)) fs19.unlinkSync(outputFile);
51033
51471
  } catch {
51034
51472
  }
51035
51473
  }
@@ -51093,12 +51531,12 @@ function markdownToSlack(text) {
51093
51531
  function formatSlackText(text) {
51094
51532
  return markdownToSlack(text);
51095
51533
  }
51096
- var import_child_process5, fs20, path23, os;
51534
+ var import_child_process5, fs19, path23, os;
51097
51535
  var init_markdown = __esm({
51098
51536
  "src/slack/markdown.ts"() {
51099
51537
  "use strict";
51100
51538
  import_child_process5 = require("child_process");
51101
- fs20 = __toESM(require("fs"));
51539
+ fs19 = __toESM(require("fs"));
51102
51540
  path23 = __toESM(require("path"));
51103
51541
  os = __toESM(require("os"));
51104
51542
  }
@@ -52040,7 +52478,7 @@ function serializeRunState(state) {
52040
52478
  ])
52041
52479
  };
52042
52480
  }
52043
- var path24, fs21, StateMachineExecutionEngine;
52481
+ var path24, fs20, StateMachineExecutionEngine;
52044
52482
  var init_state_machine_execution_engine = __esm({
52045
52483
  "src/state-machine-execution-engine.ts"() {
52046
52484
  "use strict";
@@ -52048,7 +52486,7 @@ var init_state_machine_execution_engine = __esm({
52048
52486
  init_logger();
52049
52487
  init_sandbox_manager();
52050
52488
  path24 = __toESM(require("path"));
52051
- fs21 = __toESM(require("fs"));
52489
+ fs20 = __toESM(require("fs"));
52052
52490
  StateMachineExecutionEngine = class _StateMachineExecutionEngine {
52053
52491
  workingDirectory;
52054
52492
  executionContext;
@@ -52425,7 +52863,7 @@ var init_state_machine_execution_engine = __esm({
52425
52863
  const checkId = String(ev?.checkId || "unknown");
52426
52864
  const threadKey = ev?.threadKey || (channel && threadTs ? `${channel}:${threadTs}` : "session");
52427
52865
  const baseDir = process.env.VISOR_SNAPSHOT_DIR || path24.resolve(process.cwd(), ".visor", "snapshots");
52428
- fs21.mkdirSync(baseDir, { recursive: true });
52866
+ fs20.mkdirSync(baseDir, { recursive: true });
52429
52867
  const filePath = path24.join(baseDir, `${threadKey}-${checkId}.json`);
52430
52868
  await this.saveSnapshotToFile(filePath);
52431
52869
  logger.info(`[Snapshot] Saved run snapshot: ${filePath}`);
@@ -52567,7 +53005,7 @@ var init_state_machine_execution_engine = __esm({
52567
53005
  * Does not include secrets. Intended for debugging and future resume support.
52568
53006
  */
52569
53007
  async saveSnapshotToFile(filePath) {
52570
- const fs22 = await import("fs/promises");
53008
+ const fs21 = await import("fs/promises");
52571
53009
  const ctx = this._lastContext;
52572
53010
  const runner = this._lastRunner;
52573
53011
  if (!ctx || !runner) {
@@ -52587,14 +53025,14 @@ var init_state_machine_execution_engine = __esm({
52587
53025
  journal: entries,
52588
53026
  requestedChecks: ctx.requestedChecks || []
52589
53027
  };
52590
- await fs22.writeFile(filePath, JSON.stringify(payload, null, 2), "utf8");
53028
+ await fs21.writeFile(filePath, JSON.stringify(payload, null, 2), "utf8");
52591
53029
  }
52592
53030
  /**
52593
53031
  * Load a snapshot JSON from file and return it. Resume support can build on this.
52594
53032
  */
52595
53033
  async loadSnapshotFromFile(filePath) {
52596
- const fs22 = await import("fs/promises");
52597
- const raw = await fs22.readFile(filePath, "utf8");
53034
+ const fs21 = await import("fs/promises");
53035
+ const raw = await fs21.readFile(filePath, "utf8");
52598
53036
  return JSON.parse(raw);
52599
53037
  }
52600
53038
  /**