@probelabs/visor 0.1.97 → 0.1.100

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 (110) hide show
  1. package/README.md +16 -15
  2. package/action.yml +7 -2
  3. package/defaults/.visor.yaml +7 -6
  4. package/dist/action-cli-bridge.d.ts +1 -0
  5. package/dist/action-cli-bridge.d.ts.map +1 -1
  6. package/dist/ai-review-service.d.ts.map +1 -1
  7. package/dist/check-execution-engine.d.ts +8 -2
  8. package/dist/check-execution-engine.d.ts.map +1 -1
  9. package/dist/cli-main.d.ts.map +1 -1
  10. package/dist/cli.d.ts.map +1 -1
  11. package/dist/config.d.ts +5 -0
  12. package/dist/config.d.ts.map +1 -1
  13. package/dist/debug-visualizer/debug-span-exporter.d.ts +47 -0
  14. package/dist/debug-visualizer/debug-span-exporter.d.ts.map +1 -0
  15. package/dist/debug-visualizer/trace-reader.d.ts +117 -0
  16. package/dist/debug-visualizer/trace-reader.d.ts.map +1 -0
  17. package/dist/debug-visualizer/ui/index.html +2568 -0
  18. package/dist/debug-visualizer/ws-server.d.ts +99 -0
  19. package/dist/debug-visualizer/ws-server.d.ts.map +1 -0
  20. package/dist/defaults/.visor.yaml +7 -6
  21. package/dist/failure-condition-evaluator.d.ts.map +1 -1
  22. package/dist/generated/config-schema.d.ts +7 -3
  23. package/dist/generated/config-schema.d.ts.map +1 -1
  24. package/dist/generated/config-schema.json +7 -3
  25. package/dist/git-repository-analyzer.d.ts +1 -7
  26. package/dist/git-repository-analyzer.d.ts.map +1 -1
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +17668 -1760
  29. package/dist/liquid-extensions.d.ts +1 -1
  30. package/dist/liquid-extensions.d.ts.map +1 -1
  31. package/dist/output/code-review/schema.json +2 -2
  32. package/dist/output/traces/run-2025-10-22T10-40-34-055Z.ndjson +218 -0
  33. package/dist/pr-analyzer.d.ts +2 -1
  34. package/dist/pr-analyzer.d.ts.map +1 -1
  35. package/dist/providers/ai-check-provider.d.ts.map +1 -1
  36. package/dist/providers/check-provider-registry.d.ts.map +1 -1
  37. package/dist/providers/check-provider.interface.d.ts +17 -6
  38. package/dist/providers/check-provider.interface.d.ts.map +1 -1
  39. package/dist/providers/command-check-provider.d.ts.map +1 -1
  40. package/dist/providers/github-ops-provider.d.ts.map +1 -1
  41. package/dist/providers/http-check-provider.d.ts.map +1 -1
  42. package/dist/providers/human-input-check-provider.d.ts +78 -0
  43. package/dist/providers/human-input-check-provider.d.ts.map +1 -0
  44. package/dist/providers/index.d.ts +2 -1
  45. package/dist/providers/index.d.ts.map +1 -1
  46. package/dist/providers/mcp-check-provider.d.ts.map +1 -1
  47. package/dist/providers/memory-check-provider.d.ts.map +1 -1
  48. package/dist/sdk/check-execution-engine-F3662LY7.mjs +11 -0
  49. package/dist/sdk/{chunk-I3GQJIR7.mjs → chunk-B5QBV2QJ.mjs} +2 -2
  50. package/dist/sdk/chunk-B5QBV2QJ.mjs.map +1 -0
  51. package/dist/sdk/{chunk-IG3BFIIN.mjs → chunk-FVS5CJ5S.mjs} +30 -1
  52. package/dist/sdk/chunk-FVS5CJ5S.mjs.map +1 -0
  53. package/dist/sdk/{chunk-YXOWIDEF.mjs → chunk-TUTOLSFV.mjs} +15 -3
  54. package/dist/sdk/chunk-TUTOLSFV.mjs.map +1 -0
  55. package/dist/sdk/{chunk-4VK6WTYU.mjs → chunk-X2JKUOE5.mjs} +1375 -570
  56. package/dist/sdk/chunk-X2JKUOE5.mjs.map +1 -0
  57. package/dist/sdk/{liquid-extensions-GMEGEGC3.mjs → liquid-extensions-KVL4MKRH.mjs} +2 -2
  58. package/dist/sdk/{mermaid-telemetry-4DUEYCLE.mjs → mermaid-telemetry-FBF6D35S.mjs} +2 -2
  59. package/dist/sdk/sdk.d.mts +62 -4
  60. package/dist/sdk/sdk.d.ts +62 -4
  61. package/dist/sdk/sdk.js +1658 -723
  62. package/dist/sdk/sdk.js.map +1 -1
  63. package/dist/sdk/sdk.mjs +60 -15
  64. package/dist/sdk/sdk.mjs.map +1 -1
  65. package/dist/sdk/{tracer-init-RJGAIOBP.mjs → tracer-init-WC75N5NW.mjs} +2 -2
  66. package/dist/sdk.d.ts +5 -2
  67. package/dist/sdk.d.ts.map +1 -1
  68. package/dist/telemetry/file-span-exporter.d.ts.map +1 -1
  69. package/dist/telemetry/opentelemetry.d.ts +2 -0
  70. package/dist/telemetry/opentelemetry.d.ts.map +1 -1
  71. package/dist/telemetry/state-capture.d.ts +53 -0
  72. package/dist/telemetry/state-capture.d.ts.map +1 -0
  73. package/dist/telemetry/trace-helpers.d.ts.map +1 -1
  74. package/dist/traces/run-2025-10-22T10-40-34-055Z.ndjson +218 -0
  75. package/dist/types/cli.d.ts +6 -0
  76. package/dist/types/cli.d.ts.map +1 -1
  77. package/dist/types/config.d.ts +44 -3
  78. package/dist/types/config.d.ts.map +1 -1
  79. package/dist/utils/config-loader.d.ts +5 -0
  80. package/dist/utils/config-loader.d.ts.map +1 -1
  81. package/dist/utils/file-exclusion.d.ts +50 -0
  82. package/dist/utils/file-exclusion.d.ts.map +1 -0
  83. package/dist/utils/interactive-prompt.d.ts +26 -0
  84. package/dist/utils/interactive-prompt.d.ts.map +1 -0
  85. package/dist/utils/sandbox.d.ts +26 -0
  86. package/dist/utils/sandbox.d.ts.map +1 -0
  87. package/dist/utils/stdin-reader.d.ts +22 -0
  88. package/dist/utils/stdin-reader.d.ts.map +1 -0
  89. package/dist/utils/tracer-init.d.ts +0 -5
  90. package/dist/utils/tracer-init.d.ts.map +1 -1
  91. package/package.json +8 -4
  92. package/dist/output/traces/run-2025-10-19T14-24-36-341Z.ndjson +0 -40
  93. package/dist/output/traces/run-2025-10-19T14-24-48-674Z.ndjson +0 -40
  94. package/dist/output/traces/run-2025-10-19T14-24-49-238Z.ndjson +0 -40
  95. package/dist/output/traces/run-2025-10-19T14-24-49-761Z.ndjson +0 -40
  96. package/dist/output/traces/run-2025-10-19T14-24-50-279Z.ndjson +0 -12
  97. package/dist/sdk/check-execution-engine-S7BFPVWA.mjs +0 -11
  98. package/dist/sdk/chunk-4VK6WTYU.mjs.map +0 -1
  99. package/dist/sdk/chunk-I3GQJIR7.mjs.map +0 -1
  100. package/dist/sdk/chunk-IG3BFIIN.mjs.map +0 -1
  101. package/dist/sdk/chunk-YXOWIDEF.mjs.map +0 -1
  102. package/dist/traces/run-2025-10-19T14-24-36-341Z.ndjson +0 -40
  103. package/dist/traces/run-2025-10-19T14-24-48-674Z.ndjson +0 -40
  104. package/dist/traces/run-2025-10-19T14-24-49-238Z.ndjson +0 -40
  105. package/dist/traces/run-2025-10-19T14-24-49-761Z.ndjson +0 -40
  106. package/dist/traces/run-2025-10-19T14-24-50-279Z.ndjson +0 -12
  107. /package/dist/sdk/{check-execution-engine-S7BFPVWA.mjs.map → check-execution-engine-F3662LY7.mjs.map} +0 -0
  108. /package/dist/sdk/{liquid-extensions-GMEGEGC3.mjs.map → liquid-extensions-KVL4MKRH.mjs.map} +0 -0
  109. /package/dist/sdk/{mermaid-telemetry-4DUEYCLE.mjs.map → mermaid-telemetry-FBF6D35S.mjs.map} +0 -0
  110. /package/dist/sdk/{tracer-init-RJGAIOBP.mjs.map → tracer-init-WC75N5NW.mjs.map} +0 -0
package/dist/sdk/sdk.js CHANGED
@@ -406,7 +406,7 @@ ${content}
406
406
  * Sleep utility
407
407
  */
408
408
  sleep(ms) {
409
- return new Promise((resolve4) => setTimeout(resolve4, ms));
409
+ return new Promise((resolve7) => setTimeout(resolve7, ms));
410
410
  }
411
411
  /**
412
412
  * Group results by specified criteria
@@ -468,7 +468,19 @@ __export(tracer_init_exports, {
468
468
  });
469
469
  async function initializeTracer(sessionId, checkName) {
470
470
  try {
471
- if (import_probe.SimpleTelemetry && import_probe.SimpleAppTracer) {
471
+ let ProbeLib;
472
+ try {
473
+ ProbeLib = await import("@probelabs/probe");
474
+ } catch {
475
+ try {
476
+ ProbeLib = require("@probelabs/probe");
477
+ } catch {
478
+ ProbeLib = {};
479
+ }
480
+ }
481
+ const SimpleTelemetry = ProbeLib?.SimpleTelemetry;
482
+ const SimpleAppTracer = ProbeLib?.SimpleAppTracer;
483
+ if (SimpleTelemetry && SimpleAppTracer) {
472
484
  const sanitizedCheckName = checkName ? path.basename(checkName) : "check";
473
485
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
474
486
  const traceDir = process.env.GITHUB_WORKSPACE ? path.join(process.env.GITHUB_WORKSPACE, "debug-artifacts") : path.join(process.cwd(), "debug-artifacts");
@@ -484,12 +496,12 @@ async function initializeTracer(sessionId, checkName) {
484
496
  );
485
497
  return null;
486
498
  }
487
- const telemetry = new import_probe.SimpleTelemetry({
499
+ const telemetry = new SimpleTelemetry({
488
500
  enableFile: true,
489
501
  filePath: traceFilePath,
490
502
  enableConsole: false
491
503
  });
492
- const tracer = new import_probe.SimpleAppTracer(telemetry, sessionId);
504
+ const tracer = new SimpleAppTracer(telemetry, sessionId);
493
505
  console.error(`\u{1F4CA} Simple tracing enabled, will save to: ${traceFilePath}`);
494
506
  if (process.env.GITHUB_ACTIONS) {
495
507
  console.log(`::notice title=AI Trace::Trace will be saved to ${traceFilePath}`);
@@ -508,13 +520,12 @@ async function initializeTracer(sessionId, checkName) {
508
520
  return null;
509
521
  }
510
522
  }
511
- var path, fs, import_probe;
523
+ var path, fs;
512
524
  var init_tracer_init = __esm({
513
525
  "src/utils/tracer-init.ts"() {
514
526
  "use strict";
515
527
  path = __toESM(require("path"));
516
528
  fs = __toESM(require("fs"));
517
- import_probe = require("@probelabs/probe");
518
529
  }
519
530
  });
520
531
 
@@ -712,7 +723,7 @@ async function processDiffWithOutline(diffContent) {
712
723
  }
713
724
  try {
714
725
  const originalProbePath = process.env.PROBE_PATH;
715
- const fs12 = require("fs");
726
+ const fs14 = require("fs");
716
727
  const possiblePaths = [
717
728
  // Relative to current working directory (most common in production)
718
729
  path2.join(process.cwd(), "node_modules/@probelabs/probe/bin/probe-binary"),
@@ -723,7 +734,7 @@ async function processDiffWithOutline(diffContent) {
723
734
  ];
724
735
  let probeBinaryPath;
725
736
  for (const candidatePath of possiblePaths) {
726
- if (fs12.existsSync(candidatePath)) {
737
+ if (fs14.existsSync(candidatePath)) {
727
738
  probeBinaryPath = candidatePath;
728
739
  break;
729
740
  }
@@ -735,7 +746,7 @@ async function processDiffWithOutline(diffContent) {
735
746
  return diffContent;
736
747
  }
737
748
  process.env.PROBE_PATH = probeBinaryPath;
738
- const extractPromise = (0, import_probe2.extract)({
749
+ const extractPromise = (0, import_probe.extract)({
739
750
  content: diffContent,
740
751
  format: "outline-diff",
741
752
  allowTests: true
@@ -758,11 +769,11 @@ async function processDiffWithOutline(diffContent) {
758
769
  return diffContent;
759
770
  }
760
771
  }
761
- var import_probe2, path2;
772
+ var import_probe, path2;
762
773
  var init_diff_processor = __esm({
763
774
  "src/utils/diff-processor.ts"() {
764
775
  "use strict";
765
- import_probe2 = require("@probelabs/probe");
776
+ import_probe = require("@probelabs/probe");
766
777
  path2 = __toESM(require("path"));
767
778
  }
768
779
  });
@@ -771,11 +782,11 @@ var init_diff_processor = __esm({
771
782
  function log(...args) {
772
783
  logger.debug(args.join(" "));
773
784
  }
774
- var import_probe3, AIReviewService;
785
+ var import_probe2, AIReviewService;
775
786
  var init_ai_review_service = __esm({
776
787
  "src/ai-review-service.ts"() {
777
788
  "use strict";
778
- import_probe3 = require("@probelabs/probe");
789
+ import_probe2 = require("@probelabs/probe");
779
790
  init_session_registry();
780
791
  init_logger();
781
792
  init_tracer_init();
@@ -815,6 +826,7 @@ var init_ai_review_service = __esm({
815
826
  this.config.model = process.env.MODEL_NAME;
816
827
  }
817
828
  }
829
+ // NOTE: per request, no additional redaction/encryption helpers are used.
818
830
  /**
819
831
  * Execute AI review using probe agent
820
832
  */
@@ -1131,14 +1143,12 @@ ${prContext}
1131
1143
  const isIssue = prContextInfo.isIssue === true;
1132
1144
  const isPRContext = prContextInfo.isPRContext === true;
1133
1145
  const includeCodeContext = isPRContext || prContextInfo.includeCodeContext !== false;
1134
- const log2 = this.config.debug ? console.error : () => {
1135
- };
1136
1146
  if (isPRContext) {
1137
- log2("\u{1F50D} Including full code diffs in AI context (PR mode)");
1147
+ log("\u{1F50D} Including full code diffs in AI context (PR mode)");
1138
1148
  } else if (!includeCodeContext) {
1139
- log2("\u{1F4CA} Including only file summary in AI context (no diffs)");
1149
+ log("\u{1F4CA} Including only file summary in AI context (no diffs)");
1140
1150
  } else {
1141
- log2("\u{1F50D} Including code diffs in AI context");
1151
+ log("\u{1F50D} Including code diffs in AI context");
1142
1152
  }
1143
1153
  if (isIssue) {
1144
1154
  let context2 = `<issue>
@@ -1386,8 +1396,8 @@ ${schemaString}`);
1386
1396
  }
1387
1397
  if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
1388
1398
  try {
1389
- const fs12 = require("fs");
1390
- const path13 = require("path");
1399
+ const fs14 = require("fs");
1400
+ const path16 = require("path");
1391
1401
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
1392
1402
  const provider = this.config.provider || "auto";
1393
1403
  const model = this.config.model || "default";
@@ -1501,20 +1511,20 @@ ${"=".repeat(60)}
1501
1511
  `;
1502
1512
  readableVersion += `${"=".repeat(60)}
1503
1513
  `;
1504
- const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path13.join(process.cwd(), "debug-artifacts");
1505
- if (!fs12.existsSync(debugArtifactsDir)) {
1506
- fs12.mkdirSync(debugArtifactsDir, { recursive: true });
1514
+ const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path16.join(process.cwd(), "debug-artifacts");
1515
+ if (!fs14.existsSync(debugArtifactsDir)) {
1516
+ fs14.mkdirSync(debugArtifactsDir, { recursive: true });
1507
1517
  }
1508
- const debugFile = path13.join(
1518
+ const debugFile = path16.join(
1509
1519
  debugArtifactsDir,
1510
1520
  `prompt-${_checkName || "unknown"}-${timestamp}.json`
1511
1521
  );
1512
- fs12.writeFileSync(debugFile, debugJson, "utf-8");
1513
- const readableFile = path13.join(
1522
+ fs14.writeFileSync(debugFile, debugJson, "utf-8");
1523
+ const readableFile = path16.join(
1514
1524
  debugArtifactsDir,
1515
1525
  `prompt-${_checkName || "unknown"}-${timestamp}.txt`
1516
1526
  );
1517
- fs12.writeFileSync(readableFile, readableVersion, "utf-8");
1527
+ fs14.writeFileSync(readableFile, readableVersion, "utf-8");
1518
1528
  log(`
1519
1529
  \u{1F4BE} Full debug info saved to:`);
1520
1530
  log(` JSON: ${debugFile}`);
@@ -1546,8 +1556,8 @@ ${"=".repeat(60)}
1546
1556
  log(`\u{1F4E4} Response length: ${response.length} characters`);
1547
1557
  if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
1548
1558
  try {
1549
- const fs12 = require("fs");
1550
- const path13 = require("path");
1559
+ const fs14 = require("fs");
1560
+ const path16 = require("path");
1551
1561
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
1552
1562
  const agentAny2 = agent;
1553
1563
  let fullHistory = [];
@@ -1558,10 +1568,10 @@ ${"=".repeat(60)}
1558
1568
  } else if (agentAny2._messages) {
1559
1569
  fullHistory = agentAny2._messages;
1560
1570
  }
1561
- const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path13.join(process.cwd(), "debug-artifacts");
1562
- const sessionFile = path13.join(
1571
+ const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path16.join(process.cwd(), "debug-artifacts");
1572
+ const sessionBase = path16.join(
1563
1573
  debugArtifactsDir,
1564
- `session-${_checkName || "unknown"}-${timestamp}.json`
1574
+ `session-${_checkName || "unknown"}-${timestamp}`
1565
1575
  );
1566
1576
  const sessionData = {
1567
1577
  timestamp,
@@ -1569,15 +1579,9 @@ ${"=".repeat(60)}
1569
1579
  provider: this.config.provider || "auto",
1570
1580
  model: this.config.model || "default",
1571
1581
  schema: effectiveSchema,
1572
- fullConversationHistory: fullHistory,
1573
- totalMessages: fullHistory.length,
1574
- latestResponse: response
1582
+ totalMessages: fullHistory.length
1575
1583
  };
1576
- fs12.writeFileSync(sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
1577
- const sessionTxtFile = path13.join(
1578
- debugArtifactsDir,
1579
- `session-${_checkName || "unknown"}-${timestamp}.txt`
1580
- );
1584
+ fs14.writeFileSync(sessionBase + ".json", JSON.stringify(sessionData, null, 2), "utf-8");
1581
1585
  let readable = `=============================================================
1582
1586
  `;
1583
1587
  readable += `COMPLETE AI SESSION HISTORY (AFTER RESPONSE)
@@ -1594,22 +1598,18 @@ ${"=".repeat(60)}
1594
1598
 
1595
1599
  `;
1596
1600
  fullHistory.forEach((msg, idx) => {
1601
+ const role = msg.role || "unknown";
1602
+ const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content, null, 2);
1597
1603
  readable += `
1598
1604
  ${"=".repeat(60)}
1605
+ MESSAGE ${idx + 1}/${fullHistory.length}
1606
+ Role: ${role}
1607
+ ${"=".repeat(60)}
1599
1608
  `;
1600
- readable += `MESSAGE ${idx + 1}/${fullHistory.length}
1601
- `;
1602
- readable += `Role: ${msg.role || "unknown"}
1603
- `;
1604
- readable += `${"=".repeat(60)}
1605
- `;
1606
- const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content, null, 2);
1607
1609
  readable += content + "\n";
1608
1610
  });
1609
- fs12.writeFileSync(sessionTxtFile, readable, "utf-8");
1611
+ fs14.writeFileSync(sessionBase + ".summary.txt", readable, "utf-8");
1610
1612
  log(`\u{1F4BE} Complete session history saved:`);
1611
- log(` JSON: ${sessionFile}`);
1612
- log(` TXT: ${sessionTxtFile}`);
1613
1613
  log(` - Contains ALL ${fullHistory.length} messages (prompts + responses)`);
1614
1614
  } catch (error) {
1615
1615
  log(`\u26A0\uFE0F Could not save complete session history: ${error}`);
@@ -1617,11 +1617,11 @@ ${"=".repeat(60)}
1617
1617
  }
1618
1618
  if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
1619
1619
  try {
1620
- const fs12 = require("fs");
1621
- const path13 = require("path");
1620
+ const fs14 = require("fs");
1621
+ const path16 = require("path");
1622
1622
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
1623
- const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path13.join(process.cwd(), "debug-artifacts");
1624
- const responseFile = path13.join(
1623
+ const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path16.join(process.cwd(), "debug-artifacts");
1624
+ const responseFile = path16.join(
1625
1625
  debugArtifactsDir,
1626
1626
  `response-${_checkName || "unknown"}-${timestamp}.txt`
1627
1627
  );
@@ -1654,7 +1654,7 @@ ${"=".repeat(60)}
1654
1654
  `;
1655
1655
  responseContent += `${"=".repeat(60)}
1656
1656
  `;
1657
- fs12.writeFileSync(responseFile, responseContent, "utf-8");
1657
+ fs14.writeFileSync(responseFile, responseContent, "utf-8");
1658
1658
  log(`\u{1F4BE} Response saved to: ${responseFile}`);
1659
1659
  } catch (error) {
1660
1660
  log(`\u26A0\uFE0F Could not save response file: ${error}`);
@@ -1670,9 +1670,9 @@ ${"=".repeat(60)}
1670
1670
  await agentAny._telemetryConfig.shutdown();
1671
1671
  log(`\u{1F4CA} OpenTelemetry trace saved to: ${agentAny._traceFilePath}`);
1672
1672
  if (process.env.GITHUB_ACTIONS) {
1673
- const fs12 = require("fs");
1674
- if (fs12.existsSync(agentAny._traceFilePath)) {
1675
- const stats = fs12.statSync(agentAny._traceFilePath);
1673
+ const fs14 = require("fs");
1674
+ if (fs14.existsSync(agentAny._traceFilePath)) {
1675
+ const stats = fs14.statSync(agentAny._traceFilePath);
1676
1676
  console.log(
1677
1677
  `::notice title=AI Trace Saved::${agentAny._traceFilePath} (${stats.size} bytes)`
1678
1678
  );
@@ -1683,12 +1683,14 @@ ${"=".repeat(60)}
1683
1683
  log(`\u{1F4CA} Trace saved to: ${agentAny._traceFilePath}`);
1684
1684
  }
1685
1685
  } catch (exportError) {
1686
- console.error("\u26A0\uFE0F Warning: Failed to export trace for cloned session:", exportError);
1686
+ logger.warn(`\u26A0\uFE0F Warning: Failed to export trace for cloned session: ${exportError}`);
1687
1687
  }
1688
1688
  }
1689
1689
  return { response, effectiveSchema };
1690
1690
  } catch (error) {
1691
- console.error("\u274C ProbeAgent session reuse failed:", error);
1691
+ logger.error(
1692
+ `\u274C ProbeAgent session reuse failed: ${error instanceof Error ? error.message : "Unknown error"}`
1693
+ );
1692
1694
  throw new Error(
1693
1695
  `ProbeAgent session reuse failed: ${error instanceof Error ? error.message : "Unknown error"}`
1694
1696
  );
@@ -1759,7 +1761,7 @@ ${"=".repeat(60)}
1759
1761
  if (this.config.model) {
1760
1762
  options.model = this.config.model;
1761
1763
  }
1762
- const agent = new import_probe3.ProbeAgent(options);
1764
+ const agent = new import_probe2.ProbeAgent(options);
1763
1765
  log("\u{1F680} Calling ProbeAgent...");
1764
1766
  let schemaString = void 0;
1765
1767
  let effectiveSchema = typeof schema === "object" ? "custom" : schema;
@@ -1792,8 +1794,8 @@ ${schemaString}`);
1792
1794
  const model = this.config.model || "default";
1793
1795
  if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
1794
1796
  try {
1795
- const fs12 = require("fs");
1796
- const path13 = require("path");
1797
+ const fs14 = require("fs");
1798
+ const path16 = require("path");
1797
1799
  const os = require("os");
1798
1800
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
1799
1801
  const debugData = {
@@ -1867,30 +1869,20 @@ ${"=".repeat(60)}
1867
1869
  readableVersion += `${"=".repeat(60)}
1868
1870
  `;
1869
1871
  const tempDir = os.tmpdir();
1870
- const promptFile = path13.join(tempDir, `visor-prompt-${timestamp}.txt`);
1871
- fs12.writeFileSync(promptFile, prompt, "utf-8");
1872
+ const promptFile = path16.join(tempDir, `visor-prompt-${timestamp}.txt`);
1873
+ fs14.writeFileSync(promptFile, prompt, "utf-8");
1872
1874
  log(`
1873
1875
  \u{1F4BE} Prompt saved to: ${promptFile}`);
1874
- const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path13.join(process.cwd(), "debug-artifacts");
1876
+ const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path16.join(process.cwd(), "debug-artifacts");
1875
1877
  try {
1876
- if (!fs12.existsSync(debugArtifactsDir)) {
1877
- fs12.mkdirSync(debugArtifactsDir, { recursive: true });
1878
- }
1879
- const debugFile = path13.join(
1880
- debugArtifactsDir,
1881
- `prompt-${_checkName || "unknown"}-${timestamp}.json`
1882
- );
1883
- fs12.writeFileSync(debugFile, debugJson, "utf-8");
1884
- const readableFile = path13.join(
1878
+ const base = path16.join(
1885
1879
  debugArtifactsDir,
1886
- `prompt-${_checkName || "unknown"}-${timestamp}.txt`
1880
+ `prompt-${_checkName || "unknown"}-${timestamp}`
1887
1881
  );
1888
- fs12.writeFileSync(readableFile, readableVersion, "utf-8");
1882
+ fs14.writeFileSync(base + ".json", debugJson, "utf-8");
1883
+ fs14.writeFileSync(base + ".summary.txt", readableVersion, "utf-8");
1889
1884
  log(`
1890
- \u{1F4BE} Full debug info saved to:`);
1891
- log(` JSON: ${debugFile}`);
1892
- log(` TXT: ${readableFile}`);
1893
- log(` - Includes: prompt, schema, provider, model, and schema options`);
1885
+ \u{1F4BE} Full debug info saved to directory: ${debugArtifactsDir}`);
1894
1886
  } catch {
1895
1887
  }
1896
1888
  log(`
@@ -1933,8 +1925,8 @@ $ ${cliCommand}
1933
1925
  log(`\u{1F4E4} Response length: ${response.length} characters`);
1934
1926
  if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
1935
1927
  try {
1936
- const fs12 = require("fs");
1937
- const path13 = require("path");
1928
+ const fs14 = require("fs");
1929
+ const path16 = require("path");
1938
1930
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
1939
1931
  const agentAny = agent;
1940
1932
  let fullHistory = [];
@@ -1945,10 +1937,10 @@ $ ${cliCommand}
1945
1937
  } else if (agentAny._messages) {
1946
1938
  fullHistory = agentAny._messages;
1947
1939
  }
1948
- const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path13.join(process.cwd(), "debug-artifacts");
1949
- const sessionFile = path13.join(
1940
+ const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path16.join(process.cwd(), "debug-artifacts");
1941
+ const sessionBase = path16.join(
1950
1942
  debugArtifactsDir,
1951
- `session-${_checkName || "unknown"}-${timestamp}.json`
1943
+ `session-${_checkName || "unknown"}-${timestamp}`
1952
1944
  );
1953
1945
  const sessionData = {
1954
1946
  timestamp,
@@ -1956,15 +1948,9 @@ $ ${cliCommand}
1956
1948
  provider: this.config.provider || "auto",
1957
1949
  model: this.config.model || "default",
1958
1950
  schema: effectiveSchema,
1959
- fullConversationHistory: fullHistory,
1960
- totalMessages: fullHistory.length,
1961
- latestResponse: response
1951
+ totalMessages: fullHistory.length
1962
1952
  };
1963
- fs12.writeFileSync(sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
1964
- const sessionTxtFile = path13.join(
1965
- debugArtifactsDir,
1966
- `session-${_checkName || "unknown"}-${timestamp}.txt`
1967
- );
1953
+ fs14.writeFileSync(sessionBase + ".json", JSON.stringify(sessionData, null, 2), "utf-8");
1968
1954
  let readable = `=============================================================
1969
1955
  `;
1970
1956
  readable += `COMPLETE AI SESSION HISTORY (AFTER RESPONSE)
@@ -1981,22 +1967,18 @@ $ ${cliCommand}
1981
1967
 
1982
1968
  `;
1983
1969
  fullHistory.forEach((msg, idx) => {
1970
+ const role = msg.role || "unknown";
1971
+ const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content, null, 2);
1984
1972
  readable += `
1985
1973
  ${"=".repeat(60)}
1974
+ MESSAGE ${idx + 1}/${fullHistory.length}
1975
+ Role: ${role}
1976
+ ${"=".repeat(60)}
1986
1977
  `;
1987
- readable += `MESSAGE ${idx + 1}/${fullHistory.length}
1988
- `;
1989
- readable += `Role: ${msg.role || "unknown"}
1990
- `;
1991
- readable += `${"=".repeat(60)}
1992
- `;
1993
- const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content, null, 2);
1994
1978
  readable += content + "\n";
1995
1979
  });
1996
- fs12.writeFileSync(sessionTxtFile, readable, "utf-8");
1980
+ fs14.writeFileSync(sessionBase + ".summary.txt", readable, "utf-8");
1997
1981
  log(`\u{1F4BE} Complete session history saved:`);
1998
- log(` JSON: ${sessionFile}`);
1999
- log(` TXT: ${sessionTxtFile}`);
2000
1982
  log(` - Contains ALL ${fullHistory.length} messages (prompts + responses)`);
2001
1983
  } catch (error) {
2002
1984
  log(`\u26A0\uFE0F Could not save complete session history: ${error}`);
@@ -2004,11 +1986,11 @@ ${"=".repeat(60)}
2004
1986
  }
2005
1987
  if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
2006
1988
  try {
2007
- const fs12 = require("fs");
2008
- const path13 = require("path");
1989
+ const fs14 = require("fs");
1990
+ const path16 = require("path");
2009
1991
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
2010
- const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path13.join(process.cwd(), "debug-artifacts");
2011
- const responseFile = path13.join(
1992
+ const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path16.join(process.cwd(), "debug-artifacts");
1993
+ const responseFile = path16.join(
2012
1994
  debugArtifactsDir,
2013
1995
  `response-${_checkName || "unknown"}-${timestamp}.txt`
2014
1996
  );
@@ -2041,7 +2023,7 @@ ${"=".repeat(60)}
2041
2023
  `;
2042
2024
  responseContent += `${"=".repeat(60)}
2043
2025
  `;
2044
- fs12.writeFileSync(responseFile, responseContent, "utf-8");
2026
+ fs14.writeFileSync(responseFile, responseContent, "utf-8");
2045
2027
  log(`\u{1F4BE} Response saved to: ${responseFile}`);
2046
2028
  } catch (error) {
2047
2029
  log(`\u26A0\uFE0F Could not save response file: ${error}`);
@@ -2059,9 +2041,9 @@ ${"=".repeat(60)}
2059
2041
  await telemetry.shutdown();
2060
2042
  log(`\u{1F4CA} OpenTelemetry trace saved to: ${traceFilePath}`);
2061
2043
  if (process.env.GITHUB_ACTIONS) {
2062
- const fs12 = require("fs");
2063
- if (fs12.existsSync(traceFilePath)) {
2064
- const stats = fs12.statSync(traceFilePath);
2044
+ const fs14 = require("fs");
2045
+ if (fs14.existsSync(traceFilePath)) {
2046
+ const stats = fs14.statSync(traceFilePath);
2065
2047
  console.log(
2066
2048
  `::notice title=AI Trace Saved::OpenTelemetry trace file size: ${stats.size} bytes`
2067
2049
  );
@@ -2072,7 +2054,7 @@ ${"=".repeat(60)}
2072
2054
  log(`\u{1F4CA} Trace saved to: ${traceFilePath}`);
2073
2055
  }
2074
2056
  } catch (exportError) {
2075
- console.error("\u26A0\uFE0F Warning: Failed to export trace:", exportError);
2057
+ logger.warn(`\u26A0\uFE0F Warning: Failed to export trace: ${exportError}`);
2076
2058
  }
2077
2059
  }
2078
2060
  if (_checkName) {
@@ -2099,8 +2081,8 @@ ${"=".repeat(60)}
2099
2081
  * Load schema content from schema files or inline definitions
2100
2082
  */
2101
2083
  async loadSchemaContent(schema) {
2102
- const fs12 = require("fs").promises;
2103
- const path13 = require("path");
2084
+ const fs14 = require("fs").promises;
2085
+ const path16 = require("path");
2104
2086
  if (typeof schema === "object" && schema !== null) {
2105
2087
  log("\u{1F4CB} Using inline schema object from configuration");
2106
2088
  return JSON.stringify(schema);
@@ -2113,14 +2095,14 @@ ${"=".repeat(60)}
2113
2095
  }
2114
2096
  } catch {
2115
2097
  }
2116
- if ((schema.startsWith("./") || schema.includes(".json")) && !path13.isAbsolute(schema)) {
2098
+ if ((schema.startsWith("./") || schema.includes(".json")) && !path16.isAbsolute(schema)) {
2117
2099
  if (schema.includes("..") || schema.includes("\0")) {
2118
2100
  throw new Error("Invalid schema path: path traversal not allowed");
2119
2101
  }
2120
2102
  try {
2121
- const schemaPath2 = path13.resolve(process.cwd(), schema);
2103
+ const schemaPath2 = path16.resolve(process.cwd(), schema);
2122
2104
  log(`\u{1F4CB} Loading custom schema from file: ${schemaPath2}`);
2123
- const schemaContent = await fs12.readFile(schemaPath2, "utf-8");
2105
+ const schemaContent = await fs14.readFile(schemaPath2, "utf-8");
2124
2106
  return schemaContent.trim();
2125
2107
  } catch (error) {
2126
2108
  throw new Error(
@@ -2132,9 +2114,9 @@ ${"=".repeat(60)}
2132
2114
  if (!sanitizedSchemaName || sanitizedSchemaName !== schema) {
2133
2115
  throw new Error("Invalid schema name");
2134
2116
  }
2135
- const schemaPath = path13.join(process.cwd(), "output", sanitizedSchemaName, "schema.json");
2117
+ const schemaPath = path16.join(process.cwd(), "output", sanitizedSchemaName, "schema.json");
2136
2118
  try {
2137
- const schemaContent = await fs12.readFile(schemaPath, "utf-8");
2119
+ const schemaContent = await fs14.readFile(schemaPath, "utf-8");
2138
2120
  return schemaContent.trim();
2139
2121
  } catch (error) {
2140
2122
  throw new Error(
@@ -2244,6 +2226,25 @@ ${"=".repeat(60)}
2244
2226
  }
2245
2227
  }
2246
2228
  const isCustomSchema = _schema === "custom" || _schema && (_schema.startsWith("./") || _schema.endsWith(".json")) || _schema && _schema !== "code-review" && !_schema.includes("output/");
2229
+ const _debugSchemaLogging = this.config.debug === true || process.env.VISOR_DEBUG_AI_SESSIONS === "true";
2230
+ if (_debugSchemaLogging) {
2231
+ const details = {
2232
+ schema: _schema,
2233
+ isCustomSchema,
2234
+ isCustomLiteral: _schema === "custom",
2235
+ startsWithDotSlash: typeof _schema === "string" ? _schema.startsWith("./") : false,
2236
+ endsWithJson: typeof _schema === "string" ? _schema.endsWith(".json") : false,
2237
+ notCodeReview: _schema !== "code-review",
2238
+ noOutputPrefix: typeof _schema === "string" ? !_schema.includes("output/") : false
2239
+ };
2240
+ try {
2241
+ log(`\u{1F50D} Schema detection: ${JSON.stringify(details)}`);
2242
+ } catch {
2243
+ log(
2244
+ `\u{1F50D} Schema detection: _schema="${String(_schema)}", isCustomSchema=${isCustomSchema}`
2245
+ );
2246
+ }
2247
+ }
2247
2248
  if (isCustomSchema) {
2248
2249
  log("\u{1F4CB} Custom schema detected - preserving all fields from parsed JSON");
2249
2250
  log(`\u{1F4CA} Schema: ${_schema}`);
@@ -2289,33 +2290,39 @@ ${"=".repeat(60)}
2289
2290
  log("\u2705 Successfully created ReviewSummary");
2290
2291
  return result;
2291
2292
  } catch (error) {
2292
- console.error("\u274C Failed to parse AI response:", error);
2293
- console.error("\u{1F4C4} FULL RAW RESPONSE:");
2294
- console.error("=".repeat(80));
2295
- console.error(response);
2296
- console.error("=".repeat(80));
2297
- console.error(`\u{1F4CF} Response length: ${response.length} characters`);
2298
- if (error instanceof SyntaxError) {
2299
- console.error("\u{1F50D} JSON parsing error - the response may not be valid JSON");
2300
- console.error("\u{1F50D} Error details:", error.message);
2301
- const errorMatch = error.message.match(/position (\d+)/);
2302
- if (errorMatch) {
2303
- const position = parseInt(errorMatch[1]);
2304
- console.error(`\u{1F50D} Error at position ${position}:`);
2305
- const start = Math.max(0, position - 50);
2306
- const end = Math.min(response.length, position + 50);
2307
- console.error(`\u{1F50D} Context: "${response.substring(start, end)}"`);
2308
- console.error(`\u{1F50D} Response beginning: "${response.substring(0, 100)}"`);
2309
- }
2310
- if (response.includes("I cannot")) {
2311
- console.error("\u{1F50D} Response appears to be a refusal/explanation rather than JSON");
2312
- }
2313
- if (response.includes("```")) {
2314
- console.error("\u{1F50D} Response appears to contain markdown code blocks");
2315
- }
2316
- if (response.startsWith("<")) {
2317
- console.error("\u{1F50D} Response appears to start with XML/HTML");
2293
+ const detailed = this.config.debug === true || process.env.VISOR_DEBUG_AI_SESSIONS === "true";
2294
+ const message = error instanceof Error ? error.message : String(error);
2295
+ if (detailed) {
2296
+ logger.debug(`\u274C Failed to parse AI response: ${message}`);
2297
+ logger.debug("\u{1F4C4} FULL RAW RESPONSE:");
2298
+ logger.debug("=".repeat(80));
2299
+ logger.debug(response);
2300
+ logger.debug("=".repeat(80));
2301
+ logger.debug(`\u{1F4CF} Response length: ${response.length} characters`);
2302
+ if (error instanceof SyntaxError) {
2303
+ logger.debug("\u{1F50D} JSON parsing error - the response may not be valid JSON");
2304
+ logger.debug(`\u{1F50D} Error details: ${error.message}`);
2305
+ const errorMatch = error.message.match(/position (\d+)/);
2306
+ if (errorMatch) {
2307
+ const position = parseInt(errorMatch[1]);
2308
+ logger.debug(`\u{1F50D} Error at position ${position}:`);
2309
+ const start = Math.max(0, position - 50);
2310
+ const end = Math.min(response.length, position + 50);
2311
+ logger.debug(`\u{1F50D} Context: "${response.substring(start, end)}"`);
2312
+ logger.debug(`\u{1F50D} Response beginning: "${response.substring(0, 100)}"`);
2313
+ }
2314
+ if (response.includes("I cannot")) {
2315
+ logger.debug("\u{1F50D} Response appears to be a refusal/explanation rather than JSON");
2316
+ }
2317
+ if (response.includes("```")) {
2318
+ logger.debug("\u{1F50D} Response appears to contain markdown code blocks");
2319
+ }
2320
+ if (response.startsWith("<")) {
2321
+ logger.debug("\u{1F50D} Response appears to start with XML/HTML");
2322
+ }
2318
2323
  }
2324
+ } else {
2325
+ logger.error(`\u274C Failed to parse AI response: ${message}`);
2319
2326
  }
2320
2327
  throw new Error(
2321
2328
  `Invalid AI response format: ${error instanceof Error ? error.message : "Unknown error"}`
@@ -2380,7 +2387,7 @@ ${"=".repeat(60)}
2380
2387
  * Generate mock response for testing
2381
2388
  */
2382
2389
  async generateMockResponse(_prompt) {
2383
- await new Promise((resolve4) => setTimeout(resolve4, 500));
2390
+ await new Promise((resolve7) => setTimeout(resolve7, 500));
2384
2391
  const mockResponse = {
2385
2392
  content: JSON.stringify({
2386
2393
  issues: [
@@ -2501,15 +2508,15 @@ var init_reviewer = __esm({
2501
2508
  if (["code-review", "overview", "plain", "text"].includes(schema)) {
2502
2509
  return true;
2503
2510
  }
2504
- const fs12 = require("fs").promises;
2505
- const path13 = require("path");
2511
+ const fs14 = require("fs").promises;
2512
+ const path16 = require("path");
2506
2513
  const sanitizedSchemaName = schema.replace(/[^a-zA-Z0-9-]/g, "");
2507
2514
  if (!sanitizedSchemaName || sanitizedSchemaName !== schema) {
2508
2515
  return false;
2509
2516
  }
2510
- const schemaPath = path13.join(process.cwd(), "output", sanitizedSchemaName, "schema.json");
2517
+ const schemaPath = path16.join(process.cwd(), "output", sanitizedSchemaName, "schema.json");
2511
2518
  try {
2512
- const schemaContent = await fs12.readFile(schemaPath, "utf-8");
2519
+ const schemaContent = await fs14.readFile(schemaPath, "utf-8");
2513
2520
  const schemaObj = JSON.parse(schemaContent);
2514
2521
  const properties = schemaObj.properties;
2515
2522
  return !!(properties && "text" in properties);
@@ -2575,16 +2582,16 @@ var init_reviewer = __esm({
2575
2582
  }
2576
2583
  }
2577
2584
  async formatGroupComment(checkResults, _options, _githubContext) {
2578
- const normalize2 = (s) => s.replace(/\\n/g, "\n");
2585
+ const normalize3 = (s) => s.replace(/\\n/g, "\n");
2579
2586
  const checkContents = checkResults.map((result) => {
2580
2587
  const trimmed = result.content?.trim();
2581
- if (trimmed) return normalize2(trimmed);
2588
+ if (trimmed) return normalize3(trimmed);
2582
2589
  const out = result.output;
2583
2590
  if (out) {
2584
- if (typeof out === "string" && out.trim()) return normalize2(out.trim());
2591
+ if (typeof out === "string" && out.trim()) return normalize3(out.trim());
2585
2592
  if (typeof out === "object") {
2586
2593
  const txt = out.text || out.response || out.message;
2587
- if (typeof txt === "string" && txt.trim()) return normalize2(txt.trim());
2594
+ if (typeof txt === "string" && txt.trim()) return normalize3(txt.trim());
2588
2595
  }
2589
2596
  }
2590
2597
  return "";
@@ -2680,15 +2687,15 @@ var init_reviewer = __esm({
2680
2687
  }
2681
2688
  saveDebugArtifact(debug) {
2682
2689
  try {
2683
- const fs12 = require("fs");
2684
- const path13 = require("path");
2685
- const debugDir = path13.join(process.cwd(), "debug-artifacts");
2686
- if (!fs12.existsSync(debugDir)) {
2687
- fs12.mkdirSync(debugDir, { recursive: true });
2690
+ const fs14 = require("fs");
2691
+ const path16 = require("path");
2692
+ const debugDir = path16.join(process.cwd(), "debug-artifacts");
2693
+ if (!fs14.existsSync(debugDir)) {
2694
+ fs14.mkdirSync(debugDir, { recursive: true });
2688
2695
  }
2689
2696
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
2690
2697
  const filename = `visor-debug-${timestamp}.md`;
2691
- const filepath = path13.join(debugDir, filename);
2698
+ const filepath = path16.join(debugDir, filename);
2692
2699
  const content = [
2693
2700
  `# Visor Debug Information`,
2694
2701
  ``,
@@ -2709,7 +2716,7 @@ var init_reviewer = __esm({
2709
2716
  debug.rawResponse,
2710
2717
  "```"
2711
2718
  ].join("\n");
2712
- fs12.writeFileSync(filepath, content, "utf8");
2719
+ fs14.writeFileSync(filepath, content, "utf8");
2713
2720
  return filename;
2714
2721
  } catch (error) {
2715
2722
  console.error("Failed to save debug artifact:", error);
@@ -2720,21 +2727,101 @@ var init_reviewer = __esm({
2720
2727
  }
2721
2728
  });
2722
2729
 
2730
+ // src/utils/file-exclusion.ts
2731
+ var import_ignore, fs2, path3, DEFAULT_EXCLUSION_PATTERNS, FileExclusionHelper;
2732
+ var init_file_exclusion = __esm({
2733
+ "src/utils/file-exclusion.ts"() {
2734
+ "use strict";
2735
+ import_ignore = __toESM(require("ignore"));
2736
+ fs2 = __toESM(require("fs"));
2737
+ path3 = __toESM(require("path"));
2738
+ DEFAULT_EXCLUSION_PATTERNS = [
2739
+ "dist/",
2740
+ "build/",
2741
+ ".next/",
2742
+ "out/",
2743
+ "node_modules/",
2744
+ "coverage/",
2745
+ ".turbo/",
2746
+ "bundled/"
2747
+ ];
2748
+ FileExclusionHelper = class {
2749
+ gitignore = null;
2750
+ workingDirectory;
2751
+ /**
2752
+ * @param workingDirectory - Directory to search for .gitignore
2753
+ * @param additionalPatterns - Additional patterns to include (optional, defaults to common build artifacts)
2754
+ */
2755
+ constructor(workingDirectory = process.cwd(), additionalPatterns = DEFAULT_EXCLUSION_PATTERNS) {
2756
+ const normalizedPath = path3.resolve(workingDirectory);
2757
+ if (normalizedPath.includes("\0")) {
2758
+ throw new Error("Invalid workingDirectory: contains null bytes");
2759
+ }
2760
+ this.workingDirectory = normalizedPath;
2761
+ this.loadGitignore(additionalPatterns);
2762
+ }
2763
+ /**
2764
+ * Load .gitignore patterns from the working directory (called once in constructor)
2765
+ * @param additionalPatterns - Additional patterns to add to gitignore rules
2766
+ */
2767
+ loadGitignore(additionalPatterns) {
2768
+ const gitignorePath = path3.resolve(this.workingDirectory, ".gitignore");
2769
+ const resolvedWorkingDir = path3.resolve(this.workingDirectory);
2770
+ try {
2771
+ const relativePath = path3.relative(resolvedWorkingDir, gitignorePath);
2772
+ if (relativePath.startsWith("..") || path3.isAbsolute(relativePath)) {
2773
+ throw new Error("Invalid gitignore path: path traversal detected");
2774
+ }
2775
+ if (relativePath !== ".gitignore") {
2776
+ throw new Error("Invalid gitignore path: must be .gitignore in working directory");
2777
+ }
2778
+ this.gitignore = (0, import_ignore.default)();
2779
+ if (additionalPatterns && additionalPatterns.length > 0) {
2780
+ this.gitignore.add(additionalPatterns);
2781
+ }
2782
+ if (fs2.existsSync(gitignorePath)) {
2783
+ const rawContent = fs2.readFileSync(gitignorePath, "utf8");
2784
+ 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();
2785
+ this.gitignore.add(gitignoreContent);
2786
+ console.error("\u2705 Loaded .gitignore patterns for file filtering");
2787
+ } else if (additionalPatterns && additionalPatterns.length > 0) {
2788
+ console.error("\u26A0\uFE0F No .gitignore found, using default exclusion patterns");
2789
+ }
2790
+ } catch (error) {
2791
+ console.warn("\u26A0\uFE0F Failed to load .gitignore:", error instanceof Error ? error.message : error);
2792
+ }
2793
+ }
2794
+ /**
2795
+ * Check if a file should be excluded based on .gitignore patterns
2796
+ */
2797
+ shouldExcludeFile(filename) {
2798
+ if (this.gitignore) {
2799
+ return this.gitignore.ignores(filename);
2800
+ }
2801
+ return false;
2802
+ }
2803
+ };
2804
+ }
2805
+ });
2806
+
2723
2807
  // src/git-repository-analyzer.ts
2724
- var import_simple_git, path3, fs2, MAX_PATCH_SIZE, GitRepositoryAnalyzer;
2808
+ var import_simple_git, path4, fs3, MAX_PATCH_SIZE, GitRepositoryAnalyzer;
2725
2809
  var init_git_repository_analyzer = __esm({
2726
2810
  "src/git-repository-analyzer.ts"() {
2727
2811
  "use strict";
2728
2812
  import_simple_git = require("simple-git");
2729
- path3 = __toESM(require("path"));
2730
- fs2 = __toESM(require("fs"));
2813
+ path4 = __toESM(require("path"));
2814
+ fs3 = __toESM(require("fs"));
2815
+ init_file_exclusion();
2731
2816
  MAX_PATCH_SIZE = 50 * 1024;
2732
2817
  GitRepositoryAnalyzer = class {
2733
2818
  git;
2734
2819
  cwd;
2820
+ fileExclusionHelper;
2735
2821
  constructor(workingDirectory = process.cwd()) {
2736
2822
  this.cwd = workingDirectory;
2737
2823
  this.git = (0, import_simple_git.simpleGit)(workingDirectory);
2824
+ this.fileExclusionHelper = new FileExclusionHelper(workingDirectory);
2738
2825
  }
2739
2826
  /**
2740
2827
  * Analyze the current git repository state and return data compatible with PRInfo interface
@@ -2869,34 +2956,6 @@ ${file.patch}`).join("\n\n");
2869
2956
  return "main";
2870
2957
  }
2871
2958
  }
2872
- /**
2873
- * Check if a file should be excluded from analysis
2874
- * This includes:
2875
- * - Files in .gitignore (even if force-added)
2876
- * - Built/generated files (dist/, build/, .next/, etc.)
2877
- */
2878
- async shouldExcludeFile(filename) {
2879
- const excludePatterns = [
2880
- /^dist\//,
2881
- /^build\//,
2882
- /^\.next\//,
2883
- /^out\//,
2884
- /^node_modules\//,
2885
- /^coverage\//,
2886
- /^\.turbo\//
2887
- ];
2888
- for (const pattern of excludePatterns) {
2889
- if (pattern.test(filename)) {
2890
- return true;
2891
- }
2892
- }
2893
- try {
2894
- const result = await this.git.raw(["check-ignore", filename]);
2895
- return result.trim().length > 0;
2896
- } catch {
2897
- return false;
2898
- }
2899
- }
2900
2959
  /**
2901
2960
  * Truncate a patch if it exceeds MAX_PATCH_SIZE
2902
2961
  */
@@ -2937,11 +2996,11 @@ ${file.patch}`).join("\n\n");
2937
2996
  }))
2938
2997
  ];
2939
2998
  for (const { file, status: status2 } of fileChanges) {
2940
- if (await this.shouldExcludeFile(file)) {
2999
+ if (this.fileExclusionHelper.shouldExcludeFile(file)) {
2941
3000
  console.error(`\u23ED\uFE0F Skipping excluded file: ${file}`);
2942
3001
  continue;
2943
3002
  }
2944
- const filePath = path3.join(this.cwd, file);
3003
+ const filePath = path4.join(this.cwd, file);
2945
3004
  const fileChange = await this.analyzeFileChange(file, status2, filePath, includeContext);
2946
3005
  changes.push(fileChange);
2947
3006
  }
@@ -2962,7 +3021,7 @@ ${file.patch}`).join("\n\n");
2962
3021
  return [];
2963
3022
  }
2964
3023
  for (const file of diffSummary.files) {
2965
- if (await this.shouldExcludeFile(file.file)) {
3024
+ if (this.fileExclusionHelper.shouldExcludeFile(file.file)) {
2966
3025
  console.error(`\u23ED\uFE0F Skipping excluded file: ${file.file}`);
2967
3026
  continue;
2968
3027
  }
@@ -3017,7 +3076,7 @@ ${file.patch}`).join("\n\n");
3017
3076
  let content;
3018
3077
  let truncated = false;
3019
3078
  try {
3020
- if (includeContext && status !== "added" && fs2.existsSync(filePath)) {
3079
+ if (includeContext && status !== "added" && fs3.existsSync(filePath)) {
3021
3080
  const diff = await this.git.diff(["--", filename]).catch(() => "");
3022
3081
  if (diff) {
3023
3082
  const result = this.truncatePatch(diff, filename);
@@ -3027,7 +3086,7 @@ ${file.patch}`).join("\n\n");
3027
3086
  additions = lines.filter((line) => line.startsWith("+")).length;
3028
3087
  deletions = lines.filter((line) => line.startsWith("-")).length;
3029
3088
  }
3030
- } else if (status !== "added" && fs2.existsSync(filePath)) {
3089
+ } else if (status !== "added" && fs3.existsSync(filePath)) {
3031
3090
  const diff = await this.git.diff(["--", filename]).catch(() => "");
3032
3091
  if (diff) {
3033
3092
  const lines = diff.split("\n");
@@ -3035,17 +3094,17 @@ ${file.patch}`).join("\n\n");
3035
3094
  deletions = lines.filter((line) => line.startsWith("-")).length;
3036
3095
  }
3037
3096
  }
3038
- if (status === "added" && fs2.existsSync(filePath)) {
3097
+ if (status === "added" && fs3.existsSync(filePath)) {
3039
3098
  try {
3040
- const stats = fs2.statSync(filePath);
3099
+ const stats = fs3.statSync(filePath);
3041
3100
  if (stats.isFile() && stats.size < 1024 * 1024) {
3042
3101
  if (includeContext) {
3043
- content = fs2.readFileSync(filePath, "utf8");
3102
+ content = fs3.readFileSync(filePath, "utf8");
3044
3103
  const result = this.truncatePatch(content, filename);
3045
3104
  patch = result.patch;
3046
3105
  truncated = result.truncated;
3047
3106
  }
3048
- const fileContent = includeContext ? content : fs2.readFileSync(filePath, "utf8");
3107
+ const fileContent = includeContext ? content : fs3.readFileSync(filePath, "utf8");
3049
3108
  additions = fileContent.split("\n").length;
3050
3109
  }
3051
3110
  } catch {
@@ -3130,15 +3189,19 @@ ${file.patch}`).join("\n\n");
3130
3189
  });
3131
3190
 
3132
3191
  // src/pr-analyzer.ts
3133
- var PRAnalyzer;
3192
+ var path5, PRAnalyzer;
3134
3193
  var init_pr_analyzer = __esm({
3135
3194
  "src/pr-analyzer.ts"() {
3136
3195
  "use strict";
3196
+ path5 = __toESM(require("path"));
3197
+ init_file_exclusion();
3137
3198
  PRAnalyzer = class {
3138
- constructor(octokit, maxRetries = 3) {
3199
+ constructor(octokit, maxRetries = 3, workingDirectory = path5.resolve(process.cwd())) {
3139
3200
  this.octokit = octokit;
3140
3201
  this.maxRetries = maxRetries;
3202
+ this.fileExclusionHelper = new FileExclusionHelper(workingDirectory);
3141
3203
  }
3204
+ fileExclusionHelper;
3142
3205
  /**
3143
3206
  * Fetch commit diff for incremental analysis
3144
3207
  */
@@ -3194,14 +3257,25 @@ ${file.patch}`).join("\n\n");
3194
3257
  const authorAssociation = pr.author_association && typeof pr.author_association === "string" ? pr.author_association : void 0;
3195
3258
  const base = pr.base && typeof pr.base === "object" && pr.base.ref ? typeof pr.base.ref === "string" ? pr.base.ref : String(pr.base.ref) : "main";
3196
3259
  const head = pr.head && typeof pr.head === "object" && pr.head.ref ? typeof pr.head.ref === "string" ? pr.head.ref : String(pr.head.ref) : "feature";
3197
- const validFiles = files ? files.filter((file) => file && typeof file === "object" && file.filename).map((file) => ({
3260
+ let skippedCount = 0;
3261
+ const validFiles = files ? files.filter((file) => file && typeof file === "object" && file.filename).filter((file) => {
3262
+ const filename = typeof file.filename === "string" ? file.filename : String(file.filename || "unknown");
3263
+ if (!filename || this.fileExclusionHelper.shouldExcludeFile(filename)) {
3264
+ skippedCount++;
3265
+ return false;
3266
+ }
3267
+ return true;
3268
+ }).map((file) => ({
3198
3269
  filename: typeof file.filename === "string" ? file.filename : String(file.filename || "unknown"),
3199
3270
  additions: typeof file.additions === "number" ? Math.max(0, file.additions) : 0,
3200
3271
  deletions: typeof file.deletions === "number" ? Math.max(0, file.deletions) : 0,
3201
3272
  changes: typeof file.changes === "number" ? Math.max(0, file.changes) : 0,
3202
3273
  patch: typeof file.patch === "string" ? file.patch : void 0,
3203
3274
  status: ["added", "removed", "modified", "renamed"].includes(file.status) ? file.status : "modified"
3204
- })).filter((file) => file.filename.length > 0) : [];
3275
+ })) : [];
3276
+ if (skippedCount > 0) {
3277
+ console.log(`\u23ED\uFE0F Skipped ${skippedCount} excluded file(s)`);
3278
+ }
3205
3279
  const prInfo = {
3206
3280
  number: typeof pr.number === "number" ? pr.number : parseInt(String(pr.number || 1), 10),
3207
3281
  title,
@@ -3280,7 +3354,7 @@ ${file.patch}`).join("\n\n");
3280
3354
  }
3281
3355
  if (this.isRetryableError(error)) {
3282
3356
  const delay = Math.min(1e3 * Math.pow(2, attempt), 5e3);
3283
- await new Promise((resolve4) => setTimeout(resolve4, delay));
3357
+ await new Promise((resolve7) => setTimeout(resolve7, delay));
3284
3358
  } else {
3285
3359
  throw error;
3286
3360
  }
@@ -3421,12 +3495,12 @@ var init_env_resolver = __esm({
3421
3495
  });
3422
3496
 
3423
3497
  // src/issue-filter.ts
3424
- var fs3, path4, IssueFilter;
3498
+ var fs4, path6, IssueFilter;
3425
3499
  var init_issue_filter = __esm({
3426
3500
  "src/issue-filter.ts"() {
3427
3501
  "use strict";
3428
- fs3 = __toESM(require("fs"));
3429
- path4 = __toESM(require("path"));
3502
+ fs4 = __toESM(require("fs"));
3503
+ path6 = __toESM(require("path"));
3430
3504
  IssueFilter = class {
3431
3505
  fileCache = /* @__PURE__ */ new Map();
3432
3506
  suppressionEnabled;
@@ -3494,17 +3568,17 @@ var init_issue_filter = __esm({
3494
3568
  return this.fileCache.get(filePath);
3495
3569
  }
3496
3570
  try {
3497
- const resolvedPath = path4.isAbsolute(filePath) ? filePath : path4.join(workingDir, filePath);
3498
- if (!fs3.existsSync(resolvedPath)) {
3499
- if (fs3.existsSync(filePath)) {
3500
- const content2 = fs3.readFileSync(filePath, "utf8");
3571
+ const resolvedPath = path6.isAbsolute(filePath) ? filePath : path6.join(workingDir, filePath);
3572
+ if (!fs4.existsSync(resolvedPath)) {
3573
+ if (fs4.existsSync(filePath)) {
3574
+ const content2 = fs4.readFileSync(filePath, "utf8");
3501
3575
  const lines2 = content2.split("\n");
3502
3576
  this.fileCache.set(filePath, lines2);
3503
3577
  return lines2;
3504
3578
  }
3505
3579
  return null;
3506
3580
  }
3507
- const content = fs3.readFileSync(resolvedPath, "utf8");
3581
+ const content = fs4.readFileSync(resolvedPath, "utf8");
3508
3582
  const lines = content.split("\n");
3509
3583
  this.fileCache.set(filePath, lines);
3510
3584
  return lines;
@@ -4047,7 +4121,7 @@ __export(liquid_extensions_exports, {
4047
4121
  function sanitizeLabel(value) {
4048
4122
  if (value == null) return "";
4049
4123
  const s = String(value);
4050
- return s.replace(/[^A-Za-z0-9:\/]/g, "").replace(/\/{2,}/g, "/");
4124
+ return s.replace(/[^A-Za-z0-9:\/\- ]/g, "").replace(/\/{2,}/g, "/").trim();
4051
4125
  }
4052
4126
  function sanitizeLabelList(labels) {
4053
4127
  if (!Array.isArray(labels)) return [];
@@ -4174,8 +4248,209 @@ var init_liquid_extensions = __esm({
4174
4248
  }
4175
4249
  });
4176
4250
 
4251
+ // src/telemetry/state-capture.ts
4252
+ function safeSerialize(value, maxLength = MAX_ATTRIBUTE_LENGTH) {
4253
+ try {
4254
+ if (value === void 0 || value === null) return String(value);
4255
+ const seen = /* @__PURE__ */ new WeakSet();
4256
+ const json = JSON.stringify(value, (key, val) => {
4257
+ if (typeof val === "object" && val !== null) {
4258
+ if (seen.has(val)) return "[Circular]";
4259
+ seen.add(val);
4260
+ }
4261
+ if (typeof val === "string" && val.length > maxLength) {
4262
+ return val.substring(0, maxLength) + "...[truncated]";
4263
+ }
4264
+ return val;
4265
+ });
4266
+ if (json.length > maxLength) {
4267
+ return json.substring(0, maxLength) + "...[truncated]";
4268
+ }
4269
+ return json;
4270
+ } catch (err) {
4271
+ return `[Error serializing: ${err instanceof Error ? err.message : String(err)}]`;
4272
+ }
4273
+ }
4274
+ function captureCheckInputContext(span, context) {
4275
+ try {
4276
+ const keys = Object.keys(context);
4277
+ span.setAttribute("visor.check.input.keys", keys.join(","));
4278
+ span.setAttribute("visor.check.input.count", keys.length);
4279
+ span.setAttribute("visor.check.input.context", safeSerialize(context));
4280
+ if (context.pr) {
4281
+ span.setAttribute("visor.check.input.pr", safeSerialize(context.pr, 1e3));
4282
+ }
4283
+ if (context.outputs) {
4284
+ span.setAttribute("visor.check.input.outputs", safeSerialize(context.outputs, 5e3));
4285
+ }
4286
+ if (context.env) {
4287
+ span.setAttribute("visor.check.input.env_keys", Object.keys(context.env).join(","));
4288
+ }
4289
+ } catch (err) {
4290
+ try {
4291
+ span.setAttribute("visor.check.input.error", String(err));
4292
+ } catch {
4293
+ }
4294
+ }
4295
+ }
4296
+ function captureCheckOutput(span, output) {
4297
+ try {
4298
+ span.setAttribute("visor.check.output.type", typeof output);
4299
+ if (Array.isArray(output)) {
4300
+ span.setAttribute("visor.check.output.length", output.length);
4301
+ const preview = output.slice(0, 10);
4302
+ span.setAttribute("visor.check.output.preview", safeSerialize(preview, 2e3));
4303
+ }
4304
+ span.setAttribute("visor.check.output", safeSerialize(output));
4305
+ } catch (err) {
4306
+ try {
4307
+ span.setAttribute("visor.check.output.error", String(err));
4308
+ } catch {
4309
+ }
4310
+ }
4311
+ }
4312
+ function captureForEachState(span, items, index, currentItem) {
4313
+ try {
4314
+ span.setAttribute("visor.foreach.total", items.length);
4315
+ span.setAttribute("visor.foreach.index", index);
4316
+ span.setAttribute("visor.foreach.current_item", safeSerialize(currentItem, 500));
4317
+ if (items.length <= MAX_ARRAY_ITEMS) {
4318
+ span.setAttribute("visor.foreach.items", safeSerialize(items));
4319
+ } else {
4320
+ span.setAttribute(
4321
+ "visor.foreach.items.preview",
4322
+ safeSerialize(items.slice(0, MAX_ARRAY_ITEMS))
4323
+ );
4324
+ span.setAttribute("visor.foreach.items.truncated", true);
4325
+ }
4326
+ } catch (err) {
4327
+ span.setAttribute("visor.foreach.error", String(err));
4328
+ }
4329
+ }
4330
+ function captureTransformJS(span, code, input, output) {
4331
+ try {
4332
+ const codePreview = code.length > 2e3 ? code.substring(0, 2e3) + "...[truncated]" : code;
4333
+ span.setAttribute("visor.transform.code", codePreview);
4334
+ span.setAttribute("visor.transform.code.length", code.length);
4335
+ span.setAttribute("visor.transform.input", safeSerialize(input, 2e3));
4336
+ span.setAttribute("visor.transform.output", safeSerialize(output, 2e3));
4337
+ } catch (err) {
4338
+ span.setAttribute("visor.transform.error", String(err));
4339
+ }
4340
+ }
4341
+ function captureProviderCall(span, providerType, request, response) {
4342
+ try {
4343
+ span.setAttribute("visor.provider.type", providerType);
4344
+ if (request.model) span.setAttribute("visor.provider.request.model", String(request.model));
4345
+ if (request.prompt) {
4346
+ span.setAttribute("visor.provider.request.prompt.length", request.prompt.length);
4347
+ span.setAttribute("visor.provider.request.prompt.preview", request.prompt.substring(0, 500));
4348
+ }
4349
+ if (response.content) {
4350
+ span.setAttribute("visor.provider.response.length", response.content.length);
4351
+ span.setAttribute("visor.provider.response.preview", response.content.substring(0, 500));
4352
+ }
4353
+ if (response.tokens) {
4354
+ span.setAttribute("visor.provider.response.tokens", response.tokens);
4355
+ }
4356
+ } catch (err) {
4357
+ span.setAttribute("visor.provider.error", String(err));
4358
+ }
4359
+ }
4360
+ function captureStateSnapshot(span, checkId, outputs, memory) {
4361
+ try {
4362
+ span.addEvent("state.snapshot", {
4363
+ "visor.snapshot.check_id": checkId,
4364
+ "visor.snapshot.outputs": safeSerialize(outputs, 5e3),
4365
+ "visor.snapshot.memory": safeSerialize(memory, 5e3),
4366
+ "visor.snapshot.timestamp": (/* @__PURE__ */ new Date()).toISOString()
4367
+ });
4368
+ } catch (err) {
4369
+ span.setAttribute("visor.snapshot.error", String(err));
4370
+ }
4371
+ }
4372
+ var MAX_ATTRIBUTE_LENGTH, MAX_ARRAY_ITEMS;
4373
+ var init_state_capture = __esm({
4374
+ "src/telemetry/state-capture.ts"() {
4375
+ "use strict";
4376
+ MAX_ATTRIBUTE_LENGTH = 1e4;
4377
+ MAX_ARRAY_ITEMS = 100;
4378
+ }
4379
+ });
4380
+
4381
+ // src/telemetry/fallback-ndjson.ts
4382
+ var fallback_ndjson_exports = {};
4383
+ __export(fallback_ndjson_exports, {
4384
+ emitNdjsonFallback: () => emitNdjsonFallback,
4385
+ emitNdjsonSpanWithEvents: () => emitNdjsonSpanWithEvents,
4386
+ flushNdjson: () => flushNdjson
4387
+ });
4388
+ function resolveTargetPath(outDir) {
4389
+ if (process.env.VISOR_FALLBACK_TRACE_FILE) {
4390
+ CURRENT_FILE = process.env.VISOR_FALLBACK_TRACE_FILE;
4391
+ return CURRENT_FILE;
4392
+ }
4393
+ if (CURRENT_FILE) return CURRENT_FILE;
4394
+ const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
4395
+ CURRENT_FILE = path9.join(outDir, `${ts}.ndjson`);
4396
+ return CURRENT_FILE;
4397
+ }
4398
+ function isEnabled() {
4399
+ if (process.env.VISOR_FALLBACK_TRACE_FILE) return true;
4400
+ return process.env.VISOR_TELEMETRY_ENABLED === "true" && (process.env.VISOR_TELEMETRY_SINK || "file") === "file";
4401
+ }
4402
+ function appendAsync(outDir, line) {
4403
+ writeChain = writeChain.then(async () => {
4404
+ if (!dirReady) {
4405
+ try {
4406
+ await fs7.promises.mkdir(outDir, { recursive: true });
4407
+ } catch {
4408
+ }
4409
+ dirReady = true;
4410
+ }
4411
+ const target = resolveTargetPath(outDir);
4412
+ await fs7.promises.appendFile(target, line, "utf8");
4413
+ }).catch(() => {
4414
+ });
4415
+ }
4416
+ async function flushNdjson() {
4417
+ try {
4418
+ await writeChain;
4419
+ } catch {
4420
+ }
4421
+ }
4422
+ function emitNdjsonFallback(name, attrs) {
4423
+ try {
4424
+ if (!isEnabled()) return;
4425
+ const outDir = process.env.VISOR_TRACE_DIR || path9.join(process.cwd(), "output", "traces");
4426
+ const line = JSON.stringify({ name, attributes: attrs }) + "\n";
4427
+ appendAsync(outDir, line);
4428
+ } catch {
4429
+ }
4430
+ }
4431
+ function emitNdjsonSpanWithEvents(name, attrs, events) {
4432
+ try {
4433
+ if (!isEnabled()) return;
4434
+ const outDir = process.env.VISOR_TRACE_DIR || path9.join(process.cwd(), "output", "traces");
4435
+ const line = JSON.stringify({ name, attributes: attrs, events }) + "\n";
4436
+ appendAsync(outDir, line);
4437
+ } catch {
4438
+ }
4439
+ }
4440
+ var fs7, path9, CURRENT_FILE, dirReady, writeChain;
4441
+ var init_fallback_ndjson = __esm({
4442
+ "src/telemetry/fallback-ndjson.ts"() {
4443
+ "use strict";
4444
+ fs7 = __toESM(require("fs"));
4445
+ path9 = __toESM(require("path"));
4446
+ CURRENT_FILE = null;
4447
+ dirReady = false;
4448
+ writeChain = Promise.resolve();
4449
+ }
4450
+ });
4451
+
4177
4452
  // src/providers/ai-check-provider.ts
4178
- var import_promises3, import_path3, AICheckProvider;
4453
+ var import_promises3, import_path3, import_api, AICheckProvider;
4179
4454
  var init_ai_check_provider = __esm({
4180
4455
  "src/providers/ai-check-provider.ts"() {
4181
4456
  "use strict";
@@ -4186,6 +4461,8 @@ var init_ai_check_provider = __esm({
4186
4461
  init_liquid_extensions();
4187
4462
  import_promises3 = __toESM(require("fs/promises"));
4188
4463
  import_path3 = __toESM(require("path"));
4464
+ import_api = require("@opentelemetry/api");
4465
+ init_state_capture();
4189
4466
  AICheckProvider = class extends CheckProvider {
4190
4467
  aiReviewService;
4191
4468
  liquidEngine;
@@ -4317,9 +4594,9 @@ var init_ai_check_provider = __esm({
4317
4594
  } else {
4318
4595
  resolvedPath = import_path3.default.resolve(process.cwd(), str);
4319
4596
  }
4320
- const fs12 = require("fs").promises;
4597
+ const fs14 = require("fs").promises;
4321
4598
  try {
4322
- const stat = await fs12.stat(resolvedPath);
4599
+ const stat = await fs14.stat(resolvedPath);
4323
4600
  return stat.isFile();
4324
4601
  } catch {
4325
4602
  return hasFileExtension && (isRelativePath || isAbsolutePath || hasPathSeparators);
@@ -4525,6 +4802,40 @@ var init_ai_check_provider = __esm({
4525
4802
  );
4526
4803
  }
4527
4804
  }
4805
+ const templateContext = {
4806
+ pr: {
4807
+ number: prInfo.number,
4808
+ title: prInfo.title,
4809
+ author: prInfo.author,
4810
+ branch: prInfo.head,
4811
+ base: prInfo.base
4812
+ },
4813
+ files: prInfo.files,
4814
+ outputs: _dependencyResults ? Object.fromEntries(
4815
+ Array.from(_dependencyResults.entries()).map(([checkName, result]) => [
4816
+ checkName,
4817
+ result.output !== void 0 ? result.output : result
4818
+ ])
4819
+ ) : {}
4820
+ };
4821
+ try {
4822
+ const span = import_api.trace.getSpan(import_api.context.active());
4823
+ if (span) {
4824
+ captureCheckInputContext(span, templateContext);
4825
+ }
4826
+ } catch {
4827
+ }
4828
+ try {
4829
+ const checkId = config.checkName || config.id || "unknown";
4830
+ const ctxJson = JSON.stringify(templateContext);
4831
+ const { emitNdjsonSpanWithEvents: emitNdjsonSpanWithEvents2 } = (init_fallback_ndjson(), __toCommonJS(fallback_ndjson_exports));
4832
+ emitNdjsonSpanWithEvents2(
4833
+ "visor.check",
4834
+ { "visor.check.id": checkId, "visor.check.input.context": ctxJson },
4835
+ []
4836
+ );
4837
+ } catch {
4838
+ }
4528
4839
  const processedPrompt = await this.processPrompt(
4529
4840
  customPrompt,
4530
4841
  prInfo,
@@ -4577,10 +4888,43 @@ var init_ai_check_provider = __esm({
4577
4888
  const suppressionEnabled = config.suppressionEnabled !== false;
4578
4889
  const issueFilter = new IssueFilter(suppressionEnabled);
4579
4890
  const filteredIssues = issueFilter.filterIssues(result.issues || [], process.cwd());
4580
- return {
4891
+ const finalResult = {
4581
4892
  ...result,
4582
4893
  issues: filteredIssues
4583
4894
  };
4895
+ try {
4896
+ const span = import_api.trace.getSpan(import_api.context.active());
4897
+ if (span) {
4898
+ captureProviderCall(
4899
+ span,
4900
+ "ai",
4901
+ {
4902
+ prompt: processedPrompt.substring(0, 500),
4903
+ // Preview only
4904
+ model: aiConfig.model
4905
+ },
4906
+ {
4907
+ content: JSON.stringify(finalResult).substring(0, 500),
4908
+ tokens: result.usage?.totalTokens
4909
+ }
4910
+ );
4911
+ const outputForSpan = finalResult.output ?? finalResult;
4912
+ captureCheckOutput(span, outputForSpan);
4913
+ }
4914
+ } catch {
4915
+ }
4916
+ try {
4917
+ const checkId = config.checkName || config.id || "unknown";
4918
+ const outJson = JSON.stringify(finalResult.output ?? finalResult);
4919
+ const { emitNdjsonSpanWithEvents: emitNdjsonSpanWithEvents2 } = (init_fallback_ndjson(), __toCommonJS(fallback_ndjson_exports));
4920
+ emitNdjsonSpanWithEvents2(
4921
+ "visor.check",
4922
+ { "visor.check.id": checkId, "visor.check.output": outJson },
4923
+ []
4924
+ );
4925
+ } catch {
4926
+ }
4927
+ return finalResult;
4584
4928
  } catch (error) {
4585
4929
  const errorMessage = error instanceof Error ? error.message : String(error);
4586
4930
  console.error(`\u274C AI Check Provider Error for check: ${errorMessage}`);
@@ -4627,13 +4971,15 @@ var init_ai_check_provider = __esm({
4627
4971
  });
4628
4972
 
4629
4973
  // src/providers/http-check-provider.ts
4630
- var HttpCheckProvider;
4974
+ var import_api2, HttpCheckProvider;
4631
4975
  var init_http_check_provider = __esm({
4632
4976
  "src/providers/http-check-provider.ts"() {
4633
4977
  "use strict";
4634
4978
  init_check_provider_interface();
4635
4979
  init_issue_filter();
4636
4980
  init_liquid_extensions();
4981
+ import_api2 = require("@opentelemetry/api");
4982
+ init_state_capture();
4637
4983
  HttpCheckProvider = class extends CheckProvider {
4638
4984
  liquid;
4639
4985
  constructor() {
@@ -4695,6 +5041,13 @@ var init_http_check_provider = __esm({
4695
5041
  outputs: dependencyResults ? Object.fromEntries(dependencyResults) : {},
4696
5042
  metadata: config.metadata || {}
4697
5043
  };
5044
+ try {
5045
+ const span = import_api2.trace.getSpan(import_api2.context.active());
5046
+ if (span) {
5047
+ captureCheckInputContext(span, templateContext);
5048
+ }
5049
+ } catch {
5050
+ }
4698
5051
  let payload;
4699
5052
  try {
4700
5053
  const renderedBody = await this.liquid.parseAndRender(bodyTemplate, templateContext);
@@ -4717,10 +5070,31 @@ var init_http_check_provider = __esm({
4717
5070
  const suppressionEnabled = config.suppressionEnabled !== false;
4718
5071
  const issueFilter = new IssueFilter(suppressionEnabled);
4719
5072
  const filteredIssues = issueFilter.filterIssues(result.issues || [], process.cwd());
4720
- return {
5073
+ const finalResult = {
4721
5074
  ...result,
4722
5075
  issues: filteredIssues
4723
5076
  };
5077
+ try {
5078
+ const span = import_api2.trace.getSpan(import_api2.context.active());
5079
+ if (span) {
5080
+ captureProviderCall(
5081
+ span,
5082
+ "http",
5083
+ {
5084
+ url,
5085
+ method,
5086
+ body: JSON.stringify(payload).substring(0, 500)
5087
+ },
5088
+ {
5089
+ content: JSON.stringify(response).substring(0, 500)
5090
+ }
5091
+ );
5092
+ const outputForSpan = finalResult.output ?? finalResult;
5093
+ captureCheckOutput(span, outputForSpan);
5094
+ }
5095
+ } catch {
5096
+ }
5097
+ return finalResult;
4724
5098
  } catch (error) {
4725
5099
  return this.createErrorResult(url, error);
4726
5100
  }
@@ -5402,13 +5776,123 @@ var init_log_check_provider = __esm({
5402
5776
  }
5403
5777
  });
5404
5778
 
5779
+ // src/utils/sandbox.ts
5780
+ function createSecureSandbox() {
5781
+ const globals = {
5782
+ ...import_sandboxjs.default.SAFE_GLOBALS,
5783
+ Math,
5784
+ JSON,
5785
+ // Provide console with limited surface. Calls are harmless in CI logs and
5786
+ // help with debugging value_js / transform_js expressions.
5787
+ console: {
5788
+ log: console.log,
5789
+ warn: console.warn,
5790
+ error: console.error
5791
+ }
5792
+ };
5793
+ const prototypeWhitelist = new Map(import_sandboxjs.default.SAFE_PROTOTYPES);
5794
+ const arrayMethods = /* @__PURE__ */ new Set([
5795
+ "some",
5796
+ "every",
5797
+ "filter",
5798
+ "map",
5799
+ "reduce",
5800
+ "find",
5801
+ "includes",
5802
+ "indexOf",
5803
+ "length",
5804
+ "slice",
5805
+ "concat",
5806
+ "join",
5807
+ "push",
5808
+ "pop",
5809
+ "shift",
5810
+ "unshift",
5811
+ "sort",
5812
+ "reverse",
5813
+ "flat",
5814
+ "flatMap"
5815
+ ]);
5816
+ prototypeWhitelist.set(Array.prototype, arrayMethods);
5817
+ const stringMethods = /* @__PURE__ */ new Set([
5818
+ "toLowerCase",
5819
+ "toUpperCase",
5820
+ "includes",
5821
+ "indexOf",
5822
+ "startsWith",
5823
+ "endsWith",
5824
+ "slice",
5825
+ "substring",
5826
+ "length",
5827
+ "trim",
5828
+ "split",
5829
+ "replace",
5830
+ "match",
5831
+ "padStart",
5832
+ "padEnd"
5833
+ ]);
5834
+ prototypeWhitelist.set(String.prototype, stringMethods);
5835
+ const objectMethods = /* @__PURE__ */ new Set([
5836
+ "hasOwnProperty",
5837
+ "toString",
5838
+ "valueOf",
5839
+ "keys",
5840
+ "values"
5841
+ ]);
5842
+ prototypeWhitelist.set(Object.prototype, objectMethods);
5843
+ return new import_sandboxjs.default({ globals, prototypeWhitelist });
5844
+ }
5845
+ function compileAndRun(sandbox, userCode, scope, opts = { injectLog: true, wrapFunction: true, logPrefix: "[sandbox]" }) {
5846
+ const inject = opts?.injectLog === true;
5847
+ let safePrefix = String(opts?.logPrefix ?? "[sandbox]");
5848
+ safePrefix = safePrefix.replace(/[\r\n\t\0]/g, "").replace(/[`$\\]/g, "").replace(/\$\{/g, "").slice(0, 64);
5849
+ const header = inject ? `const __lp = ${JSON.stringify(safePrefix)}; const log = (...a) => { try { console.log(__lp, ...a); } catch {} };
5850
+ ` : "";
5851
+ const body = opts.wrapFunction ? `const __fn = () => {
5852
+ ${userCode}
5853
+ };
5854
+ return __fn();
5855
+ ` : `${userCode}`;
5856
+ const code = `${header}${body}`;
5857
+ let exec;
5858
+ try {
5859
+ exec = sandbox.compile(code);
5860
+ } catch (e) {
5861
+ const msg = e instanceof Error ? e.message : String(e);
5862
+ throw new Error(`sandbox_compile_error: ${msg}`);
5863
+ }
5864
+ let out;
5865
+ try {
5866
+ out = exec(scope);
5867
+ } catch (e) {
5868
+ const msg = e instanceof Error ? e.message : String(e);
5869
+ throw new Error(`sandbox_execution_error: ${msg}`);
5870
+ }
5871
+ if (out && typeof out.run === "function") {
5872
+ try {
5873
+ return out.run();
5874
+ } catch (e) {
5875
+ const msg = e instanceof Error ? e.message : String(e);
5876
+ throw new Error(`sandbox_runner_error: ${msg}`);
5877
+ }
5878
+ }
5879
+ return out;
5880
+ }
5881
+ var import_sandboxjs;
5882
+ var init_sandbox = __esm({
5883
+ "src/utils/sandbox.ts"() {
5884
+ "use strict";
5885
+ import_sandboxjs = __toESM(require("@nyariv/sandboxjs"));
5886
+ }
5887
+ });
5888
+
5405
5889
  // src/providers/github-ops-provider.ts
5406
- var import_sandboxjs, GitHubOpsProvider;
5890
+ var GitHubOpsProvider;
5407
5891
  var init_github_ops_provider = __esm({
5408
5892
  "src/providers/github-ops-provider.ts"() {
5409
5893
  "use strict";
5410
5894
  init_check_provider_interface();
5411
- import_sandboxjs = __toESM(require("@nyariv/sandboxjs"));
5895
+ init_sandbox();
5412
5896
  init_liquid_extensions();
5413
5897
  GitHubOpsProvider = class extends CheckProvider {
5414
5898
  sandbox;
@@ -5436,25 +5920,20 @@ var init_github_ops_provider = __esm({
5436
5920
  }
5437
5921
  async execute(prInfo, config, dependencyResults) {
5438
5922
  const cfg = config;
5439
- let octokit = config.eventContext?.octokit;
5923
+ const octokit = config.eventContext?.octokit;
5440
5924
  if (!octokit) {
5441
- const token = process.env["INPUT_GITHUB-TOKEN"] || process.env["GITHUB_TOKEN"];
5442
- if (!token) {
5443
- return {
5444
- issues: [
5445
- {
5446
- file: "system",
5447
- line: 0,
5448
- ruleId: "github/missing_token",
5449
- message: "No GitHub token available; set GITHUB_TOKEN or pass github-token input for native GitHub operations",
5450
- severity: "error",
5451
- category: "logic"
5452
- }
5453
- ]
5454
- };
5455
- }
5456
- const { Octokit } = await import("@octokit/rest");
5457
- octokit = new Octokit({ auth: token });
5925
+ return {
5926
+ issues: [
5927
+ {
5928
+ file: "system",
5929
+ line: 0,
5930
+ ruleId: "github/missing_octokit",
5931
+ message: "No authenticated Octokit instance available in event context. GitHub operations require proper authentication context.",
5932
+ severity: "error",
5933
+ category: "logic"
5934
+ }
5935
+ ]
5936
+ };
5458
5937
  }
5459
5938
  const repoEnv = process.env.GITHUB_REPOSITORY || "";
5460
5939
  const [owner, repo] = repoEnv.split("/");
@@ -5532,14 +6011,19 @@ var init_github_ops_provider = __esm({
5532
6011
  if (cfg.value_js && cfg.value_js.trim()) {
5533
6012
  try {
5534
6013
  const sandbox = this.getSecureSandbox();
5535
- const code = `
5536
- const __fn = () => {
5537
- ${cfg.value_js}
5538
- };
5539
- return __fn();
5540
- `;
5541
- const exec = sandbox.compile(code);
5542
- const res = exec({ pr: prInfo, values });
6014
+ const depOutputs = {};
6015
+ if (dependencyResults) {
6016
+ for (const [name, result] of dependencyResults.entries()) {
6017
+ const summary = result;
6018
+ depOutputs[name] = summary.output !== void 0 ? summary.output : summary;
6019
+ }
6020
+ }
6021
+ const res = compileAndRun(
6022
+ sandbox,
6023
+ cfg.value_js,
6024
+ { pr: prInfo, values, outputs: depOutputs },
6025
+ { injectLog: true, wrapFunction: true, logPrefix: "[github:value_js]" }
6026
+ );
5543
6027
  if (typeof res === "string") values = [res];
5544
6028
  else if (Array.isArray(res)) values = res.map((v) => String(v));
5545
6029
  } catch (e) {
@@ -5630,44 +6114,7 @@ ${cfg.value_js}
5630
6114
  */
5631
6115
  getSecureSandbox() {
5632
6116
  if (this.sandbox) return this.sandbox;
5633
- const globals = {
5634
- ...import_sandboxjs.default.SAFE_GLOBALS,
5635
- Math
5636
- };
5637
- const prototypeWhitelist = new Map(import_sandboxjs.default.SAFE_PROTOTYPES);
5638
- const arrayMethods = /* @__PURE__ */ new Set([
5639
- "some",
5640
- "every",
5641
- "filter",
5642
- "map",
5643
- "reduce",
5644
- "find",
5645
- "includes",
5646
- "indexOf",
5647
- "length",
5648
- "slice",
5649
- "concat",
5650
- "join"
5651
- ]);
5652
- prototypeWhitelist.set(Array.prototype, arrayMethods);
5653
- const stringMethods = /* @__PURE__ */ new Set([
5654
- "toLowerCase",
5655
- "toUpperCase",
5656
- "includes",
5657
- "indexOf",
5658
- "startsWith",
5659
- "endsWith",
5660
- "slice",
5661
- "substring",
5662
- "length",
5663
- "trim",
5664
- "split",
5665
- "replace"
5666
- ]);
5667
- prototypeWhitelist.set(String.prototype, stringMethods);
5668
- const objectMethods = /* @__PURE__ */ new Set(["hasOwnProperty", "toString", "valueOf"]);
5669
- prototypeWhitelist.set(Object.prototype, objectMethods);
5670
- this.sandbox = new import_sandboxjs.default({ globals, prototypeWhitelist });
6117
+ this.sandbox = createSecureSandbox();
5671
6118
  return this.sandbox;
5672
6119
  }
5673
6120
  };
@@ -6142,15 +6589,17 @@ var init_claude_code_check_provider = __esm({
6142
6589
  });
6143
6590
 
6144
6591
  // src/providers/command-check-provider.ts
6145
- var import_sandboxjs2, CommandCheckProvider;
6592
+ var import_api3, CommandCheckProvider;
6146
6593
  var init_command_check_provider = __esm({
6147
6594
  "src/providers/command-check-provider.ts"() {
6148
6595
  "use strict";
6149
6596
  init_check_provider_interface();
6150
- import_sandboxjs2 = __toESM(require("@nyariv/sandboxjs"));
6597
+ init_sandbox();
6151
6598
  init_liquid_extensions();
6152
6599
  init_logger();
6153
6600
  init_author_permissions();
6601
+ import_api3 = require("@opentelemetry/api");
6602
+ init_state_capture();
6154
6603
  CommandCheckProvider = class extends CheckProvider {
6155
6604
  liquid;
6156
6605
  sandbox;
@@ -6163,13 +6612,7 @@ var init_command_check_provider = __esm({
6163
6612
  });
6164
6613
  }
6165
6614
  createSecureSandbox() {
6166
- const globals = {
6167
- ...import_sandboxjs2.default.SAFE_GLOBALS,
6168
- console,
6169
- JSON
6170
- };
6171
- const prototypeWhitelist = new Map(import_sandboxjs2.default.SAFE_PROTOTYPES);
6172
- return new import_sandboxjs2.default({ globals, prototypeWhitelist });
6615
+ return createSecureSandbox();
6173
6616
  }
6174
6617
  getName() {
6175
6618
  return "command";
@@ -6218,6 +6661,24 @@ var init_command_check_provider = __esm({
6218
6661
  logger.debug(
6219
6662
  `\u{1F527} Debug: Template outputs keys: ${Object.keys(templateContext.outputs || {}).join(", ")}`
6220
6663
  );
6664
+ try {
6665
+ const span = import_api3.trace.getSpan(import_api3.context.active());
6666
+ if (span) {
6667
+ captureCheckInputContext(span, templateContext);
6668
+ }
6669
+ } catch {
6670
+ }
6671
+ try {
6672
+ const checkId = config.checkName || config.id || "unknown";
6673
+ const ctxJson = JSON.stringify(templateContext);
6674
+ const { emitNdjsonSpanWithEvents: emitNdjsonSpanWithEvents2 } = (init_fallback_ndjson(), __toCommonJS(fallback_ndjson_exports));
6675
+ emitNdjsonSpanWithEvents2(
6676
+ "visor.check",
6677
+ { "visor.check.id": checkId, "visor.check.input.context": ctxJson },
6678
+ [{ name: "check.started" }, { name: "check.completed" }]
6679
+ );
6680
+ } catch {
6681
+ }
6221
6682
  try {
6222
6683
  let renderedCommand = command;
6223
6684
  if (command.includes("{{") || command.includes("{%")) {
@@ -6412,8 +6873,12 @@ ${bodyWithReturn}
6412
6873
  if (parsedFromSandboxJson !== void 0) {
6413
6874
  finalOutput = parsedFromSandboxJson;
6414
6875
  } else {
6415
- const exec2 = this.sandbox.compile(code);
6416
- finalOutput = exec2({ scope: jsContext }).run();
6876
+ finalOutput = compileAndRun(
6877
+ this.sandbox,
6878
+ code,
6879
+ { scope: jsContext },
6880
+ { injectLog: false, wrapFunction: false }
6881
+ );
6417
6882
  }
6418
6883
  try {
6419
6884
  if (finalOutput && typeof finalOutput === "object" && !Array.isArray(finalOutput) && (finalOutput.error === void 0 || finalOutput.issues === void 0)) {
@@ -6824,6 +7289,27 @@ ${bodyWithReturn}
6824
7289
  ...content ? { content } : {},
6825
7290
  ...promoted
6826
7291
  };
7292
+ try {
7293
+ const span = import_api3.trace.getSpan(import_api3.context.active());
7294
+ if (span) {
7295
+ captureCheckOutput(span, outputForDependents);
7296
+ if (transformJs && output !== finalOutput) {
7297
+ captureTransformJS(span, transformJs, output, finalOutput);
7298
+ }
7299
+ }
7300
+ } catch {
7301
+ }
7302
+ try {
7303
+ const checkId = config.checkName || config.id || "unknown";
7304
+ const outJson = JSON.stringify(result.output ?? result);
7305
+ const { emitNdjsonSpanWithEvents: emitNdjsonSpanWithEvents2 } = (init_fallback_ndjson(), __toCommonJS(fallback_ndjson_exports));
7306
+ emitNdjsonSpanWithEvents2(
7307
+ "visor.check",
7308
+ { "visor.check.id": checkId, "visor.check.output": outJson },
7309
+ [{ name: "check.started" }, { name: "check.completed" }]
7310
+ );
7311
+ } catch {
7312
+ }
6827
7313
  try {
6828
7314
  if (transformJs) {
6829
7315
  const rawObj = snapshotForExtraction || finalOutput;
@@ -7389,7 +7875,7 @@ ${stderrOutput}` : `Command execution failed: ${errorMessage}`;
7389
7875
  });
7390
7876
 
7391
7877
  // src/providers/memory-check-provider.ts
7392
- var import_sandboxjs3, MemoryCheckProvider;
7878
+ var MemoryCheckProvider;
7393
7879
  var init_memory_check_provider = __esm({
7394
7880
  "src/providers/memory-check-provider.ts"() {
7395
7881
  "use strict";
@@ -7397,7 +7883,7 @@ var init_memory_check_provider = __esm({
7397
7883
  init_memory_store();
7398
7884
  init_liquid_extensions();
7399
7885
  init_logger();
7400
- import_sandboxjs3 = __toESM(require("@nyariv/sandboxjs"));
7886
+ init_sandbox();
7401
7887
  MemoryCheckProvider = class extends CheckProvider {
7402
7888
  liquid;
7403
7889
  sandbox;
@@ -7412,61 +7898,7 @@ var init_memory_check_provider = __esm({
7412
7898
  * Create a secure sandbox for JavaScript execution
7413
7899
  */
7414
7900
  createSecureSandbox() {
7415
- const globals = {
7416
- ...import_sandboxjs3.default.SAFE_GLOBALS,
7417
- Math,
7418
- console: {
7419
- log: console.log,
7420
- warn: console.warn,
7421
- error: console.error
7422
- }
7423
- };
7424
- const prototypeWhitelist = new Map(import_sandboxjs3.default.SAFE_PROTOTYPES);
7425
- const arrayMethods = /* @__PURE__ */ new Set([
7426
- "some",
7427
- "every",
7428
- "filter",
7429
- "map",
7430
- "reduce",
7431
- "find",
7432
- "includes",
7433
- "indexOf",
7434
- "length",
7435
- "slice",
7436
- "concat",
7437
- "join",
7438
- "push",
7439
- "pop",
7440
- "shift",
7441
- "unshift",
7442
- "sort",
7443
- "reverse"
7444
- ]);
7445
- prototypeWhitelist.set(Array.prototype, arrayMethods);
7446
- const stringMethods = /* @__PURE__ */ new Set([
7447
- "toLowerCase",
7448
- "toUpperCase",
7449
- "includes",
7450
- "indexOf",
7451
- "startsWith",
7452
- "endsWith",
7453
- "slice",
7454
- "substring",
7455
- "length",
7456
- "trim",
7457
- "split",
7458
- "replace",
7459
- "match",
7460
- "padStart",
7461
- "padEnd"
7462
- ]);
7463
- prototypeWhitelist.set(String.prototype, stringMethods);
7464
- const objectMethods = /* @__PURE__ */ new Set(["hasOwnProperty", "toString", "valueOf"]);
7465
- prototypeWhitelist.set(Object.prototype, objectMethods);
7466
- return new import_sandboxjs3.default({
7467
- globals,
7468
- prototypeWhitelist
7469
- });
7901
+ return createSecureSandbox();
7470
7902
  }
7471
7903
  getName() {
7472
7904
  return "memory";
@@ -7758,15 +8190,12 @@ var init_memory_check_provider = __esm({
7758
8190
  this.sandbox = this.createSecureSandbox();
7759
8191
  }
7760
8192
  try {
7761
- const log2 = (...args) => {
7762
- logger.info(`\u{1F50D} [memory-js] ${args.map((a) => JSON.stringify(a)).join(" ")}`);
7763
- };
7764
- const scope = {
7765
- ...context,
7766
- log: log2
7767
- };
7768
- const exec = this.sandbox.compile(`return (${expression});`);
7769
- return exec(scope).run();
8193
+ const scope = { ...context };
8194
+ return compileAndRun(this.sandbox, `return (${expression});`, scope, {
8195
+ injectLog: true,
8196
+ wrapFunction: false,
8197
+ logPrefix: "[memory:value_js]"
8198
+ });
7770
8199
  } catch (error) {
7771
8200
  const errorMsg = error instanceof Error ? error.message : "Unknown error";
7772
8201
  throw new Error(`Failed to evaluate value_js: ${errorMsg}`);
@@ -7781,16 +8210,12 @@ var init_memory_check_provider = __esm({
7781
8210
  this.sandbox = this.createSecureSandbox();
7782
8211
  }
7783
8212
  try {
7784
- const log2 = (...args) => {
7785
- logger.info(`\u{1F50D} [memory-js] ${args.map((a) => JSON.stringify(a)).join(" ")}`);
7786
- };
7787
- const scope = {
7788
- ...context,
7789
- log: log2
7790
- };
7791
- const exec = this.sandbox.compile(script);
7792
- const result = exec(scope).run();
7793
- return result;
8213
+ const scope = { ...context };
8214
+ return compileAndRun(this.sandbox, script, scope, {
8215
+ injectLog: true,
8216
+ wrapFunction: false,
8217
+ logPrefix: "[memory:exec_js]"
8218
+ });
7794
8219
  } catch (error) {
7795
8220
  const errorMsg = error instanceof Error ? error.message : "Unknown error";
7796
8221
  logger.error(`[memory-js] Script execution error: ${errorMsg}`);
@@ -7879,7 +8304,7 @@ var init_memory_check_provider = __esm({
7879
8304
  });
7880
8305
 
7881
8306
  // src/providers/mcp-check-provider.ts
7882
- var import_client, import_stdio, import_sse, import_streamableHttp, import_sandboxjs4, McpCheckProvider;
8307
+ var import_client, import_stdio, import_sse, import_streamableHttp, McpCheckProvider;
7883
8308
  var init_mcp_check_provider = __esm({
7884
8309
  "src/providers/mcp-check-provider.ts"() {
7885
8310
  "use strict";
@@ -7890,7 +8315,7 @@ var init_mcp_check_provider = __esm({
7890
8315
  import_stdio = require("@modelcontextprotocol/sdk/client/stdio.js");
7891
8316
  import_sse = require("@modelcontextprotocol/sdk/client/sse.js");
7892
8317
  import_streamableHttp = require("@modelcontextprotocol/sdk/client/streamableHttp.js");
7893
- import_sandboxjs4 = __toESM(require("@nyariv/sandboxjs"));
8318
+ init_sandbox();
7894
8319
  McpCheckProvider = class extends CheckProvider {
7895
8320
  liquid;
7896
8321
  sandbox;
@@ -7909,67 +8334,7 @@ var init_mcp_check_provider = __esm({
7909
8334
  * - No access to filesystem, network, or system resources
7910
8335
  */
7911
8336
  createSecureSandbox() {
7912
- const log2 = (...args) => {
7913
- logger.debug(`[transform_js] ${args.map((a) => JSON.stringify(a)).join(" ")}`);
7914
- };
7915
- const globals = {
7916
- ...import_sandboxjs4.default.SAFE_GLOBALS,
7917
- // Excludes Function, eval, require, process, etc.
7918
- Math,
7919
- JSON,
7920
- console: {
7921
- log: log2
7922
- }
7923
- };
7924
- const prototypeWhitelist = new Map(import_sandboxjs4.default.SAFE_PROTOTYPES);
7925
- const arrayMethods = /* @__PURE__ */ new Set([
7926
- "some",
7927
- "every",
7928
- "filter",
7929
- "map",
7930
- "reduce",
7931
- "find",
7932
- "includes",
7933
- "indexOf",
7934
- "length",
7935
- "slice",
7936
- "concat",
7937
- "join",
7938
- "push",
7939
- "pop",
7940
- "shift",
7941
- "unshift",
7942
- "sort",
7943
- "reverse",
7944
- "flat",
7945
- "flatMap"
7946
- ]);
7947
- prototypeWhitelist.set(Array.prototype, arrayMethods);
7948
- const stringMethods = /* @__PURE__ */ new Set([
7949
- "toLowerCase",
7950
- "toUpperCase",
7951
- "includes",
7952
- "indexOf",
7953
- "startsWith",
7954
- "endsWith",
7955
- "slice",
7956
- "substring",
7957
- "length",
7958
- "trim",
7959
- "split",
7960
- "replace",
7961
- // String.prototype.replace for text manipulation (e.g., "hello".replace("h", "H"))
7962
- "match",
7963
- "padStart",
7964
- "padEnd"
7965
- ]);
7966
- prototypeWhitelist.set(String.prototype, stringMethods);
7967
- const objectMethods = /* @__PURE__ */ new Set(["hasOwnProperty", "toString", "valueOf", "keys", "values"]);
7968
- prototypeWhitelist.set(Object.prototype, objectMethods);
7969
- return new import_sandboxjs4.default({
7970
- globals,
7971
- prototypeWhitelist
7972
- });
8337
+ return createSecureSandbox();
7973
8338
  }
7974
8339
  getName() {
7975
8340
  return "mcp";
@@ -8098,8 +8463,12 @@ var init_mcp_check_provider = __esm({
8098
8463
  outputs: templateContext.outputs,
8099
8464
  env: templateContext.env
8100
8465
  };
8101
- const exec = this.sandbox.compile(`return (${cfg.transform_js});`);
8102
- finalOutput = exec(scope).run();
8466
+ finalOutput = compileAndRun(
8467
+ this.sandbox,
8468
+ `return (${cfg.transform_js});`,
8469
+ scope,
8470
+ { injectLog: true, wrapFunction: false, logPrefix: "[mcp:transform_js]" }
8471
+ );
8103
8472
  } catch (error) {
8104
8473
  logger.error(`Failed to apply JavaScript transform: ${error}`);
8105
8474
  return {
@@ -8412,44 +8781,538 @@ var init_mcp_check_provider = __esm({
8412
8781
  replacement: replacement || void 0
8413
8782
  };
8414
8783
  }
8415
- toTrimmedString(value) {
8416
- if (typeof value === "string") {
8417
- const trimmed = value.trim();
8418
- return trimmed.length > 0 ? trimmed : null;
8784
+ toTrimmedString(value) {
8785
+ if (typeof value === "string") {
8786
+ const trimmed = value.trim();
8787
+ return trimmed.length > 0 ? trimmed : null;
8788
+ }
8789
+ if (value !== null && value !== void 0 && typeof value.toString === "function") {
8790
+ const converted = String(value).trim();
8791
+ return converted.length > 0 ? converted : null;
8792
+ }
8793
+ return null;
8794
+ }
8795
+ toNumber(value) {
8796
+ if (value === null || value === void 0) {
8797
+ return null;
8798
+ }
8799
+ const num = Number(value);
8800
+ if (Number.isFinite(num)) {
8801
+ return Math.trunc(num);
8802
+ }
8803
+ return null;
8804
+ }
8805
+ getSupportedConfigKeys() {
8806
+ return [
8807
+ "type",
8808
+ "transport",
8809
+ "command",
8810
+ "args",
8811
+ "env",
8812
+ "workingDirectory",
8813
+ "url",
8814
+ "headers",
8815
+ "sessionId",
8816
+ "method",
8817
+ "methodArgs",
8818
+ "argsTransform",
8819
+ "transform",
8820
+ "transform_js",
8821
+ "timeout",
8822
+ "depends_on",
8823
+ "on",
8824
+ "if",
8825
+ "group"
8826
+ ];
8827
+ }
8828
+ async isAvailable() {
8829
+ return true;
8830
+ }
8831
+ getRequirements() {
8832
+ return ["MCP method name specified", "Transport configuration (stdio: command, sse/http: url)"];
8833
+ }
8834
+ };
8835
+ }
8836
+ });
8837
+
8838
+ // src/utils/interactive-prompt.ts
8839
+ function formatTime(ms) {
8840
+ const seconds = Math.ceil(ms / 1e3);
8841
+ const mins = Math.floor(seconds / 60);
8842
+ const secs = seconds % 60;
8843
+ return `${mins}:${secs.toString().padStart(2, "0")}`;
8844
+ }
8845
+ function drawLine(char, width) {
8846
+ return char.repeat(width);
8847
+ }
8848
+ function wrapText(text, width) {
8849
+ const words = text.split(" ");
8850
+ const lines = [];
8851
+ let currentLine = "";
8852
+ for (const word of words) {
8853
+ if (currentLine.length + word.length + 1 <= width) {
8854
+ currentLine += (currentLine ? " " : "") + word;
8855
+ } else {
8856
+ if (currentLine) lines.push(currentLine);
8857
+ currentLine = word;
8858
+ }
8859
+ }
8860
+ if (currentLine) lines.push(currentLine);
8861
+ return lines;
8862
+ }
8863
+ function displayPromptUI(options, remainingMs) {
8864
+ const width = Math.min(process.stdout.columns || 80, 80) - 4;
8865
+ const icon = supportsUnicode ? "\u{1F4AC}" : ">";
8866
+ console.log("\n");
8867
+ console.log(`${box.topLeft}${drawLine(box.horizontal, width + 2)}${box.topRight}`);
8868
+ console.log(
8869
+ `${box.vertical} ${colors.bold}${icon} Human Input Required${colors.reset}${" ".repeat(
8870
+ width - 22
8871
+ )} ${box.vertical}`
8872
+ );
8873
+ console.log(`${box.leftT}${drawLine(box.horizontal, width + 2)}${box.rightT}`);
8874
+ console.log(`${box.vertical} ${" ".repeat(width)} ${box.vertical}`);
8875
+ const promptLines = wrapText(options.prompt, width - 2);
8876
+ for (const line of promptLines) {
8877
+ console.log(
8878
+ `${box.vertical} ${colors.cyan}${line}${colors.reset}${" ".repeat(
8879
+ width - line.length
8880
+ )} ${box.vertical}`
8881
+ );
8882
+ }
8883
+ console.log(`${box.vertical} ${" ".repeat(width)} ${box.vertical}`);
8884
+ const instruction = options.multiline ? "(Type your response, press Ctrl+D when done)" : "(Type your response and press Enter)";
8885
+ console.log(
8886
+ `${box.vertical} ${colors.dim}${instruction}${colors.reset}${" ".repeat(
8887
+ width - instruction.length
8888
+ )} ${box.vertical}`
8889
+ );
8890
+ if (options.placeholder && !options.multiline) {
8891
+ console.log(
8892
+ `${box.vertical} ${colors.dim}${options.placeholder}${colors.reset}${" ".repeat(
8893
+ width - options.placeholder.length
8894
+ )} ${box.vertical}`
8895
+ );
8896
+ }
8897
+ console.log(`${box.vertical} ${" ".repeat(width)} ${box.vertical}`);
8898
+ if (remainingMs !== void 0 && options.timeout) {
8899
+ const timeIcon = supportsUnicode ? "\u23F1 " : "Time: ";
8900
+ const timeStr = `${timeIcon} ${formatTime(remainingMs)} remaining`;
8901
+ console.log(
8902
+ `${box.vertical} ${colors.yellow}${timeStr}${colors.reset}${" ".repeat(
8903
+ width - timeStr.length
8904
+ )} ${box.vertical}`
8905
+ );
8906
+ }
8907
+ console.log(`${box.bottomLeft}${drawLine(box.horizontal, width + 2)}${box.bottomRight}`);
8908
+ console.log("");
8909
+ process.stdout.write(`${colors.green}>${colors.reset} `);
8910
+ }
8911
+ async function interactivePrompt(options) {
8912
+ return new Promise((resolve7, reject) => {
8913
+ let input = "";
8914
+ let timeoutId;
8915
+ let countdownInterval;
8916
+ let remainingMs = options.timeout;
8917
+ const rl = readline.createInterface({
8918
+ input: process.stdin,
8919
+ output: process.stdout,
8920
+ terminal: true
8921
+ });
8922
+ displayPromptUI(options, remainingMs);
8923
+ const cleanup = () => {
8924
+ if (timeoutId) clearTimeout(timeoutId);
8925
+ if (countdownInterval) clearInterval(countdownInterval);
8926
+ rl.close();
8927
+ };
8928
+ const finish = (value) => {
8929
+ cleanup();
8930
+ console.log("");
8931
+ resolve7(value);
8932
+ };
8933
+ if (options.timeout) {
8934
+ timeoutId = setTimeout(() => {
8935
+ cleanup();
8936
+ console.log(`
8937
+ ${colors.yellow}\u23F1 Timeout reached${colors.reset}`);
8938
+ if (options.defaultValue !== void 0) {
8939
+ console.log(
8940
+ `${colors.gray}Using default value: ${options.defaultValue}${colors.reset}
8941
+ `
8942
+ );
8943
+ resolve7(options.defaultValue);
8944
+ } else {
8945
+ reject(new Error("Input timeout"));
8946
+ }
8947
+ }, options.timeout);
8948
+ if (remainingMs) {
8949
+ countdownInterval = setInterval(() => {
8950
+ remainingMs = remainingMs - 1e3;
8951
+ if (remainingMs <= 0) {
8952
+ if (countdownInterval) clearInterval(countdownInterval);
8953
+ }
8954
+ }, 1e3);
8955
+ }
8956
+ }
8957
+ if (options.multiline) {
8958
+ rl.on("line", (line) => {
8959
+ input += (input ? "\n" : "") + line;
8960
+ });
8961
+ rl.on("close", () => {
8962
+ cleanup();
8963
+ const trimmed = input.trim();
8964
+ if (!trimmed && !options.allowEmpty) {
8965
+ console.log(`${colors.yellow}\u26A0 Empty input not allowed${colors.reset}`);
8966
+ reject(new Error("Empty input not allowed"));
8967
+ } else {
8968
+ finish(trimmed);
8969
+ }
8970
+ });
8971
+ } else {
8972
+ rl.question("", (answer) => {
8973
+ const trimmed = answer.trim();
8974
+ if (!trimmed && !options.allowEmpty && !options.defaultValue) {
8975
+ cleanup();
8976
+ console.log(`${colors.yellow}\u26A0 Empty input not allowed${colors.reset}`);
8977
+ reject(new Error("Empty input not allowed"));
8978
+ } else {
8979
+ finish(trimmed || options.defaultValue || "");
8980
+ }
8981
+ });
8982
+ }
8983
+ rl.on("SIGINT", () => {
8984
+ cleanup();
8985
+ console.log("\n\n" + colors.yellow + "\u26A0 Cancelled by user" + colors.reset);
8986
+ reject(new Error("Cancelled by user"));
8987
+ });
8988
+ });
8989
+ }
8990
+ async function simplePrompt(prompt) {
8991
+ return new Promise((resolve7) => {
8992
+ const rl = readline.createInterface({
8993
+ input: process.stdin,
8994
+ output: process.stdout
8995
+ });
8996
+ rl.question(`${prompt}
8997
+ > `, (answer) => {
8998
+ rl.close();
8999
+ resolve7(answer.trim());
9000
+ });
9001
+ });
9002
+ }
9003
+ var readline, colors, supportsUnicode, box;
9004
+ var init_interactive_prompt = __esm({
9005
+ "src/utils/interactive-prompt.ts"() {
9006
+ "use strict";
9007
+ readline = __toESM(require("readline"));
9008
+ colors = {
9009
+ reset: "\x1B[0m",
9010
+ dim: "\x1B[2m",
9011
+ bold: "\x1B[1m",
9012
+ cyan: "\x1B[36m",
9013
+ green: "\x1B[32m",
9014
+ yellow: "\x1B[33m",
9015
+ gray: "\x1B[90m"
9016
+ };
9017
+ supportsUnicode = process.env.LANG?.includes("UTF-8") || process.platform === "darwin";
9018
+ box = supportsUnicode ? {
9019
+ topLeft: "\u250C",
9020
+ topRight: "\u2510",
9021
+ bottomLeft: "\u2514",
9022
+ bottomRight: "\u2518",
9023
+ horizontal: "\u2500",
9024
+ vertical: "\u2502",
9025
+ leftT: "\u251C",
9026
+ rightT: "\u2524"
9027
+ } : {
9028
+ topLeft: "+",
9029
+ topRight: "+",
9030
+ bottomLeft: "+",
9031
+ bottomRight: "+",
9032
+ horizontal: "-",
9033
+ vertical: "|",
9034
+ leftT: "+",
9035
+ rightT: "+"
9036
+ };
9037
+ }
9038
+ });
9039
+
9040
+ // src/utils/stdin-reader.ts
9041
+ function isStdinAvailable() {
9042
+ return !process.stdin.isTTY;
9043
+ }
9044
+ async function readStdin(timeout, maxSize = 1024 * 1024) {
9045
+ return new Promise((resolve7, reject) => {
9046
+ let data = "";
9047
+ let timeoutId;
9048
+ if (timeout) {
9049
+ timeoutId = setTimeout(() => {
9050
+ cleanup();
9051
+ reject(new Error(`Stdin read timeout after ${timeout}ms`));
9052
+ }, timeout);
9053
+ }
9054
+ const cleanup = () => {
9055
+ if (timeoutId) {
9056
+ clearTimeout(timeoutId);
9057
+ }
9058
+ process.stdin.removeListener("data", onData);
9059
+ process.stdin.removeListener("end", onEnd);
9060
+ process.stdin.removeListener("error", onError);
9061
+ process.stdin.pause();
9062
+ };
9063
+ const onData = (chunk) => {
9064
+ data += chunk.toString();
9065
+ if (data.length > maxSize) {
9066
+ cleanup();
9067
+ reject(new Error(`Input exceeds maximum size of ${maxSize} bytes`));
9068
+ }
9069
+ };
9070
+ const onEnd = () => {
9071
+ cleanup();
9072
+ resolve7(data.trim());
9073
+ };
9074
+ const onError = (err) => {
9075
+ cleanup();
9076
+ reject(err);
9077
+ };
9078
+ process.stdin.setEncoding("utf8");
9079
+ process.stdin.on("data", onData);
9080
+ process.stdin.on("end", onEnd);
9081
+ process.stdin.on("error", onError);
9082
+ process.stdin.resume();
9083
+ });
9084
+ }
9085
+ async function tryReadStdin(timeout, maxSize = 1024 * 1024) {
9086
+ if (!isStdinAvailable()) {
9087
+ return null;
9088
+ }
9089
+ try {
9090
+ return await readStdin(timeout, maxSize);
9091
+ } catch {
9092
+ return null;
9093
+ }
9094
+ }
9095
+ var init_stdin_reader = __esm({
9096
+ "src/utils/stdin-reader.ts"() {
9097
+ "use strict";
9098
+ }
9099
+ });
9100
+
9101
+ // src/providers/human-input-check-provider.ts
9102
+ var fs10, path12, HumanInputCheckProvider;
9103
+ var init_human_input_check_provider = __esm({
9104
+ "src/providers/human-input-check-provider.ts"() {
9105
+ "use strict";
9106
+ init_check_provider_interface();
9107
+ init_interactive_prompt();
9108
+ init_stdin_reader();
9109
+ fs10 = __toESM(require("fs"));
9110
+ path12 = __toESM(require("path"));
9111
+ HumanInputCheckProvider = class _HumanInputCheckProvider extends CheckProvider {
9112
+ /**
9113
+ * @deprecated Use ExecutionContext.cliMessage instead
9114
+ * Kept for backward compatibility
9115
+ */
9116
+ static cliMessage;
9117
+ /**
9118
+ * @deprecated Use ExecutionContext.hooks instead
9119
+ * Kept for backward compatibility
9120
+ */
9121
+ static hooks = {};
9122
+ /**
9123
+ * Set the CLI message value (from --message argument)
9124
+ * @deprecated Use ExecutionContext.cliMessage instead
9125
+ */
9126
+ static setCLIMessage(message) {
9127
+ _HumanInputCheckProvider.cliMessage = message;
9128
+ }
9129
+ /**
9130
+ * Get the current CLI message value
9131
+ * @deprecated Use ExecutionContext.cliMessage instead
9132
+ */
9133
+ static getCLIMessage() {
9134
+ return _HumanInputCheckProvider.cliMessage;
9135
+ }
9136
+ /**
9137
+ * Set hooks for SDK mode
9138
+ * @deprecated Use ExecutionContext.hooks instead
9139
+ */
9140
+ static setHooks(hooks) {
9141
+ _HumanInputCheckProvider.hooks = hooks;
9142
+ }
9143
+ getName() {
9144
+ return "human-input";
9145
+ }
9146
+ getDescription() {
9147
+ return "Prompts for human input during workflow execution (CLI interactive or SDK hook)";
9148
+ }
9149
+ async validateConfig(config) {
9150
+ if (!config || typeof config !== "object") {
9151
+ return false;
9152
+ }
9153
+ const cfg = config;
9154
+ if (cfg.type !== "human-input") {
9155
+ return false;
9156
+ }
9157
+ if (!cfg.prompt || typeof cfg.prompt !== "string") {
9158
+ console.error('human-input check requires a "prompt" field');
9159
+ return false;
9160
+ }
9161
+ return true;
9162
+ }
9163
+ /**
9164
+ * Check if a string looks like a file path
9165
+ */
9166
+ looksLikePath(str) {
9167
+ return str.includes("/") || str.includes("\\");
9168
+ }
9169
+ /**
9170
+ * Sanitize user input to prevent injection attacks in dependent checks
9171
+ * Removes potentially dangerous characters while preserving useful input
9172
+ */
9173
+ sanitizeInput(input) {
9174
+ let sanitized = input.replace(/\0/g, "");
9175
+ sanitized = sanitized.replace(/[\x00-\x08\x0B-\x0C\x0E-\x1F\x7F]/g, "");
9176
+ const maxLength = 100 * 1024;
9177
+ if (sanitized.length > maxLength) {
9178
+ sanitized = sanitized.substring(0, maxLength);
8419
9179
  }
8420
- if (value !== null && value !== void 0 && typeof value.toString === "function") {
8421
- const converted = String(value).trim();
8422
- return converted.length > 0 ? converted : null;
9180
+ return sanitized;
9181
+ }
9182
+ /**
9183
+ * Try to read message from file if it exists
9184
+ * Validates path to prevent directory traversal attacks
9185
+ */
9186
+ async tryReadFile(filePath) {
9187
+ try {
9188
+ const absolutePath = path12.isAbsolute(filePath) ? filePath : path12.resolve(process.cwd(), filePath);
9189
+ const normalizedPath = path12.normalize(absolutePath);
9190
+ const cwd = process.cwd();
9191
+ if (!normalizedPath.startsWith(cwd + path12.sep) && normalizedPath !== cwd) {
9192
+ return null;
9193
+ }
9194
+ try {
9195
+ await fs10.promises.access(normalizedPath, fs10.constants.R_OK);
9196
+ const stats = await fs10.promises.stat(normalizedPath);
9197
+ if (!stats.isFile()) {
9198
+ return null;
9199
+ }
9200
+ const content = await fs10.promises.readFile(normalizedPath, "utf-8");
9201
+ return content.trim();
9202
+ } catch {
9203
+ return null;
9204
+ }
9205
+ } catch {
8423
9206
  }
8424
9207
  return null;
8425
9208
  }
8426
- toNumber(value) {
8427
- if (value === null || value === void 0) {
8428
- return null;
9209
+ /**
9210
+ * Get user input through various methods
9211
+ */
9212
+ async getUserInput(checkName, config, context) {
9213
+ const prompt = config.prompt || "Please provide input:";
9214
+ const placeholder = config.placeholder || "Enter your response...";
9215
+ const allowEmpty = config.allow_empty ?? false;
9216
+ const multiline = config.multiline ?? false;
9217
+ const timeout = config.timeout ? config.timeout * 1e3 : void 0;
9218
+ const defaultValue = config.default;
9219
+ const cliMessage = context?.cliMessage ?? _HumanInputCheckProvider.cliMessage;
9220
+ if (cliMessage !== void 0) {
9221
+ const message = cliMessage;
9222
+ if (this.looksLikePath(message)) {
9223
+ const fileContent = await this.tryReadFile(message);
9224
+ if (fileContent !== null) {
9225
+ return fileContent;
9226
+ }
9227
+ }
9228
+ return message;
8429
9229
  }
8430
- const num = Number(value);
8431
- if (Number.isFinite(num)) {
8432
- return Math.trunc(num);
9230
+ const stdinInput = await tryReadStdin(timeout);
9231
+ if (stdinInput !== null && stdinInput.length > 0) {
9232
+ return stdinInput;
9233
+ }
9234
+ const hooks = context?.hooks ?? _HumanInputCheckProvider.hooks;
9235
+ if (hooks?.onHumanInput) {
9236
+ const request = {
9237
+ checkId: checkName,
9238
+ prompt,
9239
+ placeholder,
9240
+ allowEmpty,
9241
+ multiline,
9242
+ timeout,
9243
+ default: defaultValue
9244
+ };
9245
+ try {
9246
+ const result = await hooks.onHumanInput(request);
9247
+ return result;
9248
+ } catch (error) {
9249
+ throw new Error(
9250
+ `Hook onHumanInput failed: ${error instanceof Error ? error.message : String(error)}`
9251
+ );
9252
+ }
9253
+ }
9254
+ if (process.stdin.isTTY) {
9255
+ try {
9256
+ const result = await interactivePrompt({
9257
+ prompt,
9258
+ placeholder,
9259
+ multiline,
9260
+ timeout,
9261
+ defaultValue,
9262
+ allowEmpty
9263
+ });
9264
+ return result;
9265
+ } catch (error) {
9266
+ throw new Error(
9267
+ `Interactive prompt failed: ${error instanceof Error ? error.message : String(error)}`
9268
+ );
9269
+ }
9270
+ }
9271
+ try {
9272
+ const result = await simplePrompt(prompt);
9273
+ if (!result && !allowEmpty && !defaultValue) {
9274
+ throw new Error("Empty input not allowed");
9275
+ }
9276
+ return result || defaultValue || "";
9277
+ } catch (error) {
9278
+ throw new Error(
9279
+ `Simple prompt failed: ${error instanceof Error ? error.message : String(error)}`
9280
+ );
9281
+ }
9282
+ }
9283
+ async execute(_prInfo, config, _dependencyResults, context) {
9284
+ const checkName = config.checkName || "human-input";
9285
+ try {
9286
+ const userInput = await this.getUserInput(checkName, config, context);
9287
+ const sanitizedInput = this.sanitizeInput(userInput);
9288
+ return {
9289
+ issues: [],
9290
+ output: sanitizedInput
9291
+ };
9292
+ } catch (error) {
9293
+ return {
9294
+ issues: [
9295
+ {
9296
+ file: "",
9297
+ line: 0,
9298
+ ruleId: "human-input-error",
9299
+ message: `Failed to get user input: ${error instanceof Error ? error.message : String(error)}`,
9300
+ severity: "error",
9301
+ category: "logic"
9302
+ }
9303
+ ]
9304
+ };
8433
9305
  }
8434
- return null;
8435
9306
  }
8436
9307
  getSupportedConfigKeys() {
8437
9308
  return [
8438
9309
  "type",
8439
- "transport",
8440
- "command",
8441
- "args",
8442
- "env",
8443
- "workingDirectory",
8444
- "url",
8445
- "headers",
8446
- "sessionId",
8447
- "method",
8448
- "methodArgs",
8449
- "argsTransform",
8450
- "transform",
8451
- "transform_js",
9310
+ "prompt",
9311
+ "placeholder",
9312
+ "allow_empty",
9313
+ "multiline",
8452
9314
  "timeout",
9315
+ "default",
8453
9316
  "depends_on",
8454
9317
  "on",
8455
9318
  "if",
@@ -8460,7 +9323,11 @@ var init_mcp_check_provider = __esm({
8460
9323
  return true;
8461
9324
  }
8462
9325
  getRequirements() {
8463
- return ["MCP method name specified", "Transport configuration (stdio: command, sse/http: url)"];
9326
+ return [
9327
+ "No external dependencies required",
9328
+ "Works in CLI mode with --message argument, piped stdin, or interactive prompts",
9329
+ "SDK mode requires onHumanInput hook to be configured"
9330
+ ];
8464
9331
  }
8465
9332
  };
8466
9333
  }
@@ -8482,6 +9349,7 @@ var init_check_provider_registry = __esm({
8482
9349
  init_command_check_provider();
8483
9350
  init_memory_check_provider();
8484
9351
  init_mcp_check_provider();
9352
+ init_human_input_check_provider();
8485
9353
  CheckProviderRegistry = class _CheckProviderRegistry {
8486
9354
  providers = /* @__PURE__ */ new Map();
8487
9355
  static instance;
@@ -8510,6 +9378,7 @@ var init_check_provider_registry = __esm({
8510
9378
  this.register(new LogCheckProvider());
8511
9379
  this.register(new MemoryCheckProvider());
8512
9380
  this.register(new GitHubOpsProvider());
9381
+ this.register(new HumanInputCheckProvider());
8513
9382
  try {
8514
9383
  this.register(new ClaudeCodeCheckProvider());
8515
9384
  } catch (error) {
@@ -8821,80 +9690,37 @@ var init_dependency_resolver = __esm({
8821
9690
  }
8822
9691
  });
8823
9692
 
8824
- // src/telemetry/fallback-ndjson.ts
8825
- var fallback_ndjson_exports = {};
8826
- __export(fallback_ndjson_exports, {
8827
- emitNdjsonFallback: () => emitNdjsonFallback,
8828
- emitNdjsonSpanWithEvents: () => emitNdjsonSpanWithEvents,
8829
- flushNdjson: () => flushNdjson
8830
- });
8831
- function resolveTargetPath(outDir) {
8832
- if (process.env.VISOR_FALLBACK_TRACE_FILE) {
8833
- CURRENT_FILE = process.env.VISOR_FALLBACK_TRACE_FILE;
8834
- return CURRENT_FILE;
8835
- }
8836
- if (CURRENT_FILE) return CURRENT_FILE;
8837
- const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
8838
- CURRENT_FILE = path9.join(outDir, `${ts}.ndjson`);
8839
- return CURRENT_FILE;
8840
- }
8841
- function isEnabled() {
8842
- if (process.env.VISOR_FALLBACK_TRACE_FILE) return true;
8843
- return process.env.VISOR_TELEMETRY_ENABLED === "true" && (process.env.VISOR_TELEMETRY_SINK || "file") === "file";
9693
+ // src/telemetry/trace-helpers.ts
9694
+ function getTracer() {
9695
+ return import_api4.trace.getTracer("visor");
8844
9696
  }
8845
- function appendAsync(outDir, line) {
8846
- writeChain = writeChain.then(async () => {
8847
- if (!dirReady) {
9697
+ async function withActiveSpan(name, attrs, fn) {
9698
+ const tracer = getTracer();
9699
+ return await new Promise((resolve7, reject) => {
9700
+ const callback = async (span) => {
8848
9701
  try {
8849
- await fs8.promises.mkdir(outDir, { recursive: true });
8850
- } catch {
9702
+ const res = await fn(span);
9703
+ resolve7(res);
9704
+ } catch (err) {
9705
+ try {
9706
+ if (err instanceof Error) span.recordException(err);
9707
+ span.setStatus({ code: import_api4.SpanStatusCode.ERROR });
9708
+ } catch {
9709
+ }
9710
+ reject(err);
9711
+ } finally {
9712
+ try {
9713
+ span.end();
9714
+ } catch {
9715
+ }
8851
9716
  }
8852
- dirReady = true;
8853
- }
8854
- const target = resolveTargetPath(outDir);
8855
- await fs8.promises.appendFile(target, line, "utf8");
8856
- }).catch(() => {
9717
+ };
9718
+ const options = attrs ? { attributes: attrs } : {};
9719
+ tracer.startActiveSpan(name, options, callback);
8857
9720
  });
8858
9721
  }
8859
- async function flushNdjson() {
8860
- try {
8861
- await writeChain;
8862
- } catch {
8863
- }
8864
- }
8865
- function emitNdjsonFallback(name, attrs) {
8866
- try {
8867
- if (!isEnabled()) return;
8868
- const outDir = process.env.VISOR_TRACE_DIR || path9.join(process.cwd(), "output", "traces");
8869
- const line = JSON.stringify({ name, attributes: attrs }) + "\n";
8870
- appendAsync(outDir, line);
8871
- } catch {
8872
- }
8873
- }
8874
- function emitNdjsonSpanWithEvents(name, attrs, events) {
8875
- try {
8876
- if (!isEnabled()) return;
8877
- const outDir = process.env.VISOR_TRACE_DIR || path9.join(process.cwd(), "output", "traces");
8878
- const line = JSON.stringify({ name, attributes: attrs, events }) + "\n";
8879
- appendAsync(outDir, line);
8880
- } catch {
8881
- }
8882
- }
8883
- var fs8, path9, CURRENT_FILE, dirReady, writeChain;
8884
- var init_fallback_ndjson = __esm({
8885
- "src/telemetry/fallback-ndjson.ts"() {
8886
- "use strict";
8887
- fs8 = __toESM(require("fs"));
8888
- path9 = __toESM(require("path"));
8889
- CURRENT_FILE = null;
8890
- dirReady = false;
8891
- writeChain = Promise.resolve();
8892
- }
8893
- });
8894
-
8895
- // src/telemetry/trace-helpers.ts
8896
9722
  function addEvent(name, attrs) {
8897
- const span = import_api.trace.getSpan(import_api.context.active());
9723
+ const span = import_api4.trace.getSpan(import_api4.context.active());
8898
9724
  if (span) {
8899
9725
  try {
8900
9726
  span.addEvent(name, attrs);
@@ -8913,11 +9739,11 @@ function addEvent(name, attrs) {
8913
9739
  } catch {
8914
9740
  }
8915
9741
  }
8916
- var import_api;
9742
+ var import_api4;
8917
9743
  var init_trace_helpers = __esm({
8918
9744
  "src/telemetry/trace-helpers.ts"() {
8919
9745
  "use strict";
8920
- import_api = require("@opentelemetry/api");
9746
+ import_api4 = require("@opentelemetry/api");
8921
9747
  }
8922
9748
  });
8923
9749
 
@@ -8972,26 +9798,26 @@ function addDiagramBlock(origin) {
8972
9798
  } catch {
8973
9799
  }
8974
9800
  }
8975
- var import_api2, initialized, meter, TEST_ENABLED, TEST_SNAPSHOT, checkDurationHist, providerDurationHist, foreachDurationHist, issuesCounter, activeChecks, failIfCounter, diagramBlocks;
9801
+ var import_api5, initialized, meter, TEST_ENABLED, TEST_SNAPSHOT, checkDurationHist, providerDurationHist, foreachDurationHist, issuesCounter, activeChecks, failIfCounter, diagramBlocks;
8976
9802
  var init_metrics = __esm({
8977
9803
  "src/telemetry/metrics.ts"() {
8978
9804
  "use strict";
8979
- import_api2 = require("@opentelemetry/api");
9805
+ import_api5 = require("@opentelemetry/api");
8980
9806
  initialized = false;
8981
- meter = import_api2.metrics.getMeter("visor");
9807
+ meter = import_api5.metrics.getMeter("visor");
8982
9808
  TEST_ENABLED = process.env.VISOR_TEST_METRICS === "true";
8983
9809
  TEST_SNAPSHOT = { fail_if_triggered: 0 };
8984
9810
  }
8985
9811
  });
8986
9812
 
8987
9813
  // src/failure-condition-evaluator.ts
8988
- var import_sandboxjs5, FailureConditionEvaluator;
9814
+ var FailureConditionEvaluator;
8989
9815
  var init_failure_condition_evaluator = __esm({
8990
9816
  "src/failure-condition-evaluator.ts"() {
8991
9817
  "use strict";
8992
9818
  init_trace_helpers();
8993
9819
  init_metrics();
8994
- import_sandboxjs5 = __toESM(require("@nyariv/sandboxjs"));
9820
+ init_sandbox();
8995
9821
  init_author_permissions();
8996
9822
  init_memory_store();
8997
9823
  FailureConditionEvaluator = class _FailureConditionEvaluator {
@@ -9002,54 +9828,7 @@ var init_failure_condition_evaluator = __esm({
9002
9828
  * Create a secure sandbox with whitelisted functions and globals
9003
9829
  */
9004
9830
  createSecureSandbox() {
9005
- const globals = {
9006
- ...import_sandboxjs5.default.SAFE_GLOBALS,
9007
- // Allow Math for calculations
9008
- Math,
9009
- // Allow console for debugging (in controlled environment)
9010
- console: {
9011
- log: console.log,
9012
- warn: console.warn,
9013
- error: console.error
9014
- }
9015
- };
9016
- const prototypeWhitelist = new Map(import_sandboxjs5.default.SAFE_PROTOTYPES);
9017
- const arrayMethods = /* @__PURE__ */ new Set([
9018
- "some",
9019
- "every",
9020
- "filter",
9021
- "map",
9022
- "reduce",
9023
- "find",
9024
- "includes",
9025
- "indexOf",
9026
- "length",
9027
- "slice",
9028
- "concat",
9029
- "join"
9030
- ]);
9031
- prototypeWhitelist.set(Array.prototype, arrayMethods);
9032
- const stringMethods = /* @__PURE__ */ new Set([
9033
- "toLowerCase",
9034
- "toUpperCase",
9035
- "includes",
9036
- "indexOf",
9037
- "startsWith",
9038
- "endsWith",
9039
- "slice",
9040
- "substring",
9041
- "length",
9042
- "trim",
9043
- "split",
9044
- "replace"
9045
- ]);
9046
- prototypeWhitelist.set(String.prototype, stringMethods);
9047
- const objectMethods = /* @__PURE__ */ new Set(["hasOwnProperty", "toString", "valueOf"]);
9048
- prototypeWhitelist.set(Object.prototype, objectMethods);
9049
- return new import_sandboxjs5.default({
9050
- globals,
9051
- prototypeWhitelist
9052
- });
9831
+ return createSecureSandbox();
9053
9832
  }
9054
9833
  /**
9055
9834
  * Evaluate simple fail_if condition
@@ -9322,7 +10101,7 @@ var init_failure_condition_evaluator = __esm({
9322
10101
  */
9323
10102
  evaluateExpression(condition, context) {
9324
10103
  try {
9325
- const normalize2 = (expr) => {
10104
+ const normalize3 = (expr) => {
9326
10105
  const trimmed = expr.trim();
9327
10106
  if (!/[\n;]/.test(trimmed)) return trimmed;
9328
10107
  const parts = trimmed.split(/[\n;]+/).map((s) => s.trim()).filter((s) => s.length > 0 && !s.startsWith("//"));
@@ -9465,7 +10244,7 @@ var init_failure_condition_evaluator = __esm({
9465
10244
  try {
9466
10245
  exec = this.sandbox.compile(`return (${raw});`);
9467
10246
  } catch {
9468
- const normalizedExpr = normalize2(condition);
10247
+ const normalizedExpr = normalize3(condition);
9469
10248
  exec = this.sandbox.compile(`return (${normalizedExpr});`);
9470
10249
  }
9471
10250
  const result = exec(scope).run();
@@ -10175,16 +10954,16 @@ function emitMermaidFromMarkdown(checkName, markdown, origin) {
10175
10954
  addEvent("diagram.block", { check: checkName, origin, code });
10176
10955
  addDiagramBlock(origin);
10177
10956
  if (process.env.VISOR_TRACE_REPORT === "true") {
10178
- const outDir = process.env.VISOR_TRACE_DIR || path10.join(process.cwd(), "output", "traces");
10957
+ const outDir = process.env.VISOR_TRACE_DIR || path13.join(process.cwd(), "output", "traces");
10179
10958
  try {
10180
- if (!fs9.existsSync(outDir)) fs9.mkdirSync(outDir, { recursive: true });
10959
+ if (!fs11.existsSync(outDir)) fs11.mkdirSync(outDir, { recursive: true });
10181
10960
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
10182
- const jsonPath = path10.join(outDir, `${ts}.trace.json`);
10183
- const htmlPath = path10.join(outDir, `${ts}.report.html`);
10961
+ const jsonPath = path13.join(outDir, `${ts}.trace.json`);
10962
+ const htmlPath = path13.join(outDir, `${ts}.report.html`);
10184
10963
  let data = { spans: [] };
10185
- if (fs9.existsSync(jsonPath)) {
10964
+ if (fs11.existsSync(jsonPath)) {
10186
10965
  try {
10187
- data = JSON.parse(fs9.readFileSync(jsonPath, "utf8"));
10966
+ data = JSON.parse(fs11.readFileSync(jsonPath, "utf8"));
10188
10967
  } catch {
10189
10968
  data = { spans: [] };
10190
10969
  }
@@ -10192,9 +10971,9 @@ function emitMermaidFromMarkdown(checkName, markdown, origin) {
10192
10971
  data.spans.push({
10193
10972
  events: [{ name: "diagram.block", attrs: { check: checkName, origin, code } }]
10194
10973
  });
10195
- fs9.writeFileSync(jsonPath, JSON.stringify(data, null, 2), "utf8");
10196
- if (!fs9.existsSync(htmlPath)) {
10197
- fs9.writeFileSync(
10974
+ fs11.writeFileSync(jsonPath, JSON.stringify(data, null, 2), "utf8");
10975
+ if (!fs11.existsSync(htmlPath)) {
10976
+ fs11.writeFileSync(
10198
10977
  htmlPath,
10199
10978
  '<!doctype html><html><head><meta charset="utf-8"/><title>Visor Trace Report</title></head><body><h2>Visor Trace Report</h2></body></html>',
10200
10979
  "utf8"
@@ -10210,14 +10989,14 @@ function emitMermaidFromMarkdown(checkName, markdown, origin) {
10210
10989
  }
10211
10990
  return count;
10212
10991
  }
10213
- var fs9, path10, MERMAID_RE;
10992
+ var fs11, path13, MERMAID_RE;
10214
10993
  var init_mermaid_telemetry = __esm({
10215
10994
  "src/utils/mermaid-telemetry.ts"() {
10216
10995
  "use strict";
10217
10996
  init_trace_helpers();
10218
10997
  init_metrics();
10219
- fs9 = __toESM(require("fs"));
10220
- path10 = __toESM(require("path"));
10998
+ fs11 = __toESM(require("fs"));
10999
+ path13 = __toESM(require("path"));
10221
11000
  MERMAID_RE = /```mermaid\s*\n([\s\S]*?)\n```/gi;
10222
11001
  }
10223
11002
  });
@@ -10250,7 +11029,7 @@ function getSafeEnvironmentVariables() {
10250
11029
  }
10251
11030
  return safeEnv;
10252
11031
  }
10253
- var import_sandboxjs6, CheckExecutionEngine;
11032
+ var import_api6, CheckExecutionEngine;
10254
11033
  var init_check_execution_engine = __esm({
10255
11034
  "src/check-execution-engine.ts"() {
10256
11035
  "use strict";
@@ -10263,12 +11042,14 @@ var init_check_execution_engine = __esm({
10263
11042
  init_github_check_service();
10264
11043
  init_issue_filter();
10265
11044
  init_logger();
10266
- import_sandboxjs6 = __toESM(require("@nyariv/sandboxjs"));
11045
+ init_sandbox();
10267
11046
  init_author_permissions();
10268
11047
  init_memory_store();
10269
11048
  init_fallback_ndjson();
10270
11049
  init_trace_helpers();
10271
11050
  init_metrics();
11051
+ import_api6 = require("@opentelemetry/api");
11052
+ init_state_capture();
10272
11053
  CheckExecutionEngine = class _CheckExecutionEngine {
10273
11054
  gitAnalyzer;
10274
11055
  mockOctokit;
@@ -10287,6 +11068,8 @@ var init_check_execution_engine = __esm({
10287
11068
  outputHistory = /* @__PURE__ */ new Map();
10288
11069
  // Event override to simulate alternate event (used during routing goto)
10289
11070
  routingEventOverride;
11071
+ // Execution context for providers (CLI message, hooks, etc.)
11072
+ executionContext;
10290
11073
  // Cached GitHub context for context elevation when running in Actions
10291
11074
  actionContext;
10292
11075
  constructor(workingDirectory, octokit) {
@@ -10316,19 +11099,19 @@ var init_check_execution_engine = __esm({
10316
11099
  }
10317
11100
  return baseContext;
10318
11101
  }
11102
+ /**
11103
+ * Set execution context for providers (CLI message, hooks, etc.)
11104
+ * This allows passing state without using static properties
11105
+ */
11106
+ setExecutionContext(context) {
11107
+ this.executionContext = context;
11108
+ }
10319
11109
  /**
10320
11110
  * Lazily create a secure sandbox for routing JS (goto_js, run_js)
10321
11111
  */
10322
11112
  getRoutingSandbox() {
10323
11113
  if (this.routingSandbox) return this.routingSandbox;
10324
- const globals = {
10325
- ...import_sandboxjs6.default.SAFE_GLOBALS,
10326
- Math,
10327
- JSON,
10328
- console: { log: console.log }
10329
- };
10330
- const prototypeWhitelist = new Map(import_sandboxjs6.default.SAFE_PROTOTYPES);
10331
- this.routingSandbox = new import_sandboxjs6.default({ globals, prototypeWhitelist });
11114
+ this.routingSandbox = createSecureSandbox();
10332
11115
  return this.routingSandbox;
10333
11116
  }
10334
11117
  redact(str, limit = 200) {
@@ -10340,7 +11123,7 @@ var init_check_execution_engine = __esm({
10340
11123
  }
10341
11124
  }
10342
11125
  async sleep(ms) {
10343
- return new Promise((resolve4) => setTimeout(resolve4, ms));
11126
+ return new Promise((resolve7) => setTimeout(resolve7, ms));
10344
11127
  }
10345
11128
  deterministicJitter(baseMs, seedStr) {
10346
11129
  let h = 2166136261;
@@ -10401,16 +11184,16 @@ var init_check_execution_engine = __esm({
10401
11184
  ),
10402
11185
  event: eventObj
10403
11186
  };
10404
- const code = `
10405
- const step = scope.step; const attempt = scope.attempt; const loop = scope.loop; const error = scope.error; const foreach = scope.foreach; const outputs = scope.outputs; const output = scope.output; const pr = scope.pr; const files = scope.files; const env = scope.env; const event = scope.event; const log = (...a)=>console.log('\u{1F50D} Debug:',...a); const hasMinPermission = scope.permissions.hasMinPermission; const isOwner = scope.permissions.isOwner; const isMember = scope.permissions.isMember; const isCollaborator = scope.permissions.isCollaborator; const isContributor = scope.permissions.isContributor; const isFirstTimer = scope.permissions.isFirstTimer;
10406
- const __fn = () => {
10407
- ${expr}
10408
- };
10409
- const __res = __fn();
10410
- return Array.isArray(__res) ? __res : (__res ? [__res] : []);
10411
- `;
10412
- const exec = sandbox.compile(code);
10413
- const res = exec({ scope }).run();
11187
+ const prelude = `const step = scope.step; const attempt = scope.attempt; const loop = scope.loop; const error = scope.error; const foreach = scope.foreach; const outputs = scope.outputs; const output = scope.output; const pr = scope.pr; const files = scope.files; const env = scope.env; const event = scope.event; const hasMinPermission = scope.permissions.hasMinPermission; const isOwner = scope.permissions.isOwner; const isMember = scope.permissions.isMember; const isCollaborator = scope.permissions.isCollaborator; const isContributor = scope.permissions.isContributor; const isFirstTimer = scope.permissions.isFirstTimer;`;
11188
+ const code = `${prelude}
11189
+ ${expr}`;
11190
+ const result = compileAndRun(
11191
+ sandbox,
11192
+ code,
11193
+ { scope },
11194
+ { injectLog: false, wrapFunction: true }
11195
+ );
11196
+ const res = Array.isArray(result) ? result : result ? [result] : [];
10414
11197
  if (debug) {
10415
11198
  log2(`\u{1F527} Debug: run_js evaluated \u2192 [${this.redact(res)}]`);
10416
11199
  }
@@ -10454,16 +11237,15 @@ ${expr}
10454
11237
  ),
10455
11238
  event: eventObj
10456
11239
  };
10457
- const code = `
10458
- const step = scope.step; const attempt = scope.attempt; const loop = scope.loop; const error = scope.error; const foreach = scope.foreach; const outputs = scope.outputs; const output = scope.output; const pr = scope.pr; const files = scope.files; const env = scope.env; const event = scope.event; const log = (...a)=>console.log('\u{1F50D} Debug:',...a); const hasMinPermission = scope.permissions.hasMinPermission; const isOwner = scope.permissions.isOwner; const isMember = scope.permissions.isMember; const isCollaborator = scope.permissions.isCollaborator; const isContributor = scope.permissions.isContributor; const isFirstTimer = scope.permissions.isFirstTimer;
10459
- const __fn = () => {
10460
- ${expr}
10461
- };
10462
- const __res = __fn();
10463
- return (typeof __res === 'string' && __res) ? __res : null;
10464
- `;
10465
- const exec = sandbox.compile(code);
10466
- const res = exec({ scope }).run();
11240
+ const prelude2 = `const step = scope.step; const attempt = scope.attempt; const loop = scope.loop; const error = scope.error; const foreach = scope.foreach; const outputs = scope.outputs; const output = scope.output; const pr = scope.pr; const files = scope.files; const env = scope.env; const event = scope.event; const hasMinPermission = scope.permissions.hasMinPermission; const isOwner = scope.permissions.isOwner; const isMember = scope.permissions.isMember; const isCollaborator = scope.permissions.isCollaborator; const isContributor = scope.permissions.isContributor; const isFirstTimer = scope.permissions.isFirstTimer;`;
11241
+ const code2 = `${prelude2}
11242
+ ${expr}`;
11243
+ const res = compileAndRun(
11244
+ sandbox,
11245
+ code2,
11246
+ { scope },
11247
+ { injectLog: false, wrapFunction: true }
11248
+ );
10467
11249
  if (debug) {
10468
11250
  log2(`\u{1F527} Debug: goto_js evaluated \u2192 ${this.redact(res)}`);
10469
11251
  }
@@ -10583,7 +11365,11 @@ ${expr}
10583
11365
  }
10584
11366
  let r;
10585
11367
  try {
10586
- r = await prov.execute(prInfoForInline, provCfg, depResults, sessionInfo);
11368
+ const inlineContext = {
11369
+ ...sessionInfo,
11370
+ ...this.executionContext
11371
+ };
11372
+ r = await prov.execute(prInfoForInline, provCfg, depResults, inlineContext);
10587
11373
  } finally {
10588
11374
  this.routingEventOverride = prevEventOverride;
10589
11375
  }
@@ -10614,7 +11400,31 @@ ${expr}
10614
11400
  });
10615
11401
  } catch {
10616
11402
  }
10617
- const res = await provider.execute(prInfo, providerConfig, dependencyResults, sessionInfo);
11403
+ const context = {
11404
+ ...sessionInfo,
11405
+ ...this.executionContext
11406
+ };
11407
+ const res = await withActiveSpan(
11408
+ `visor.check.${checkName}`,
11409
+ {
11410
+ "visor.check.id": checkName,
11411
+ "visor.check.type": providerConfig.type || "ai",
11412
+ "visor.check.attempt": attempt
11413
+ },
11414
+ async () => {
11415
+ try {
11416
+ return await provider.execute(prInfo, providerConfig, dependencyResults, context);
11417
+ } finally {
11418
+ try {
11419
+ emitNdjsonSpanWithEvents("visor.check", { "visor.check.id": checkName }, [
11420
+ { name: "check.started" },
11421
+ { name: "check.completed" }
11422
+ ]);
11423
+ } catch {
11424
+ }
11425
+ }
11426
+ }
11427
+ );
10618
11428
  try {
10619
11429
  currentRouteOutput = res?.output;
10620
11430
  } catch {
@@ -11242,7 +12052,7 @@ ${expr}
11242
12052
  /**
11243
12053
  * Execute review checks and return grouped results with statistics for new architecture
11244
12054
  */
11245
- async executeGroupedChecks(prInfo, checks, timeout, config, outputFormat, debug, maxParallelism, failFast, tagFilter) {
12055
+ async executeGroupedChecks(prInfo, checks, timeout, config, outputFormat, debug, maxParallelism, failFast, tagFilter, _pauseGate) {
11246
12056
  const logFn = outputFormat === "json" || outputFormat === "sarif" ? debug ? console.error : () => {
11247
12057
  } : console.log;
11248
12058
  if (debug) {
@@ -11315,7 +12125,8 @@ ${expr}
11315
12125
  logFn,
11316
12126
  debug,
11317
12127
  maxParallelism,
11318
- failFast
12128
+ failFast,
12129
+ _pauseGate
11319
12130
  );
11320
12131
  }
11321
12132
  if (checks.length === 1) {
@@ -11328,7 +12139,8 @@ ${expr}
11328
12139
  timeout,
11329
12140
  config,
11330
12141
  logFn,
11331
- debug
12142
+ debug,
12143
+ _pauseGate
11332
12144
  );
11333
12145
  const groupedResults = {};
11334
12146
  groupedResults[checkResult.group] = [checkResult];
@@ -11345,7 +12157,7 @@ ${expr}
11345
12157
  /**
11346
12158
  * Execute single check and return grouped result
11347
12159
  */
11348
- async executeSingleGroupedCheck(prInfo, checkName, timeout, config, logFn, debug) {
12160
+ async executeSingleGroupedCheck(prInfo, checkName, timeout, config, logFn, debug, _pauseGate) {
11349
12161
  if (!config?.checks?.[checkName]) {
11350
12162
  throw new Error(`No configuration found for check: ${checkName}`);
11351
12163
  }
@@ -11359,6 +12171,8 @@ ${expr}
11359
12171
  focus: checkConfig.focus || this.mapCheckNameToFocus(checkName),
11360
12172
  schema: checkConfig.schema,
11361
12173
  group: checkConfig.group,
12174
+ checkName,
12175
+ // propagate for fallback NDJSON attribution
11362
12176
  eventContext: this.enrichEventContext(prInfo.eventContext),
11363
12177
  ai: {
11364
12178
  timeout: timeout || 6e5,
@@ -11473,7 +12287,7 @@ ${expr}
11473
12287
  /**
11474
12288
  * Execute multiple checks with dependency awareness - return grouped results with statistics
11475
12289
  */
11476
- async executeGroupedDependencyAwareChecks(prInfo, checks, timeout, config, logFn, debug, maxParallelism, failFast) {
12290
+ async executeGroupedDependencyAwareChecks(prInfo, checks, timeout, config, logFn, debug, maxParallelism, failFast, pauseGate) {
11477
12291
  const reviewSummary = await this.executeDependencyAwareChecks(
11478
12292
  prInfo,
11479
12293
  checks,
@@ -11482,7 +12296,8 @@ ${expr}
11482
12296
  logFn,
11483
12297
  debug,
11484
12298
  maxParallelism,
11485
- failFast
12299
+ failFast,
12300
+ pauseGate
11486
12301
  );
11487
12302
  const executionStatistics = this.buildExecutionStatistics();
11488
12303
  const groupedResults = await this.convertReviewSummaryToGroupedResults(
@@ -11583,7 +12398,7 @@ ${expr}
11583
12398
  * - Enforcing .liquid file extension
11584
12399
  */
11585
12400
  async validateTemplatePath(templatePath) {
11586
- const path13 = await import("path");
12401
+ const path16 = await import("path");
11587
12402
  if (!templatePath || typeof templatePath !== "string" || templatePath.trim() === "") {
11588
12403
  throw new Error("Template path must be a non-empty string");
11589
12404
  }
@@ -11593,7 +12408,7 @@ ${expr}
11593
12408
  if (!templatePath.endsWith(".liquid")) {
11594
12409
  throw new Error("Template file must have .liquid extension");
11595
12410
  }
11596
- if (path13.isAbsolute(templatePath)) {
12411
+ if (path16.isAbsolute(templatePath)) {
11597
12412
  throw new Error("Template path must be relative to project directory");
11598
12413
  }
11599
12414
  if (templatePath.includes("..")) {
@@ -11607,14 +12422,14 @@ ${expr}
11607
12422
  if (!projectRoot || typeof projectRoot !== "string") {
11608
12423
  throw new Error("Unable to determine project root directory");
11609
12424
  }
11610
- const resolvedPath = path13.resolve(projectRoot, templatePath);
11611
- const resolvedProjectRoot = path13.resolve(projectRoot);
12425
+ const resolvedPath = path16.resolve(projectRoot, templatePath);
12426
+ const resolvedProjectRoot = path16.resolve(projectRoot);
11612
12427
  if (!resolvedPath || !resolvedProjectRoot || resolvedPath === "" || resolvedProjectRoot === "") {
11613
12428
  throw new Error(
11614
12429
  `Unable to resolve template path: projectRoot="${projectRoot}", templatePath="${templatePath}", resolvedPath="${resolvedPath}", resolvedProjectRoot="${resolvedProjectRoot}"`
11615
12430
  );
11616
12431
  }
11617
- if (!resolvedPath.startsWith(resolvedProjectRoot + path13.sep) && resolvedPath !== resolvedProjectRoot) {
12432
+ if (!resolvedPath.startsWith(resolvedProjectRoot + path16.sep) && resolvedPath !== resolvedProjectRoot) {
11618
12433
  throw new Error("Template path escapes project directory");
11619
12434
  }
11620
12435
  return resolvedPath;
@@ -11658,8 +12473,8 @@ ${expr}
11658
12473
  return directContent.trim();
11659
12474
  }
11660
12475
  const { createExtendedLiquid: createExtendedLiquid2 } = await Promise.resolve().then(() => (init_liquid_extensions(), liquid_extensions_exports));
11661
- const fs12 = await import("fs/promises");
11662
- const path13 = await import("path");
12476
+ const fs14 = await import("fs/promises");
12477
+ const path16 = await import("path");
11663
12478
  const liquid = createExtendedLiquid2({
11664
12479
  trimTagLeft: false,
11665
12480
  trimTagRight: false,
@@ -11682,7 +12497,7 @@ ${expr}
11682
12497
  templateContent = checkConfig.template.content;
11683
12498
  } else if (checkConfig.template.file) {
11684
12499
  const validatedPath = await this.validateTemplatePath(checkConfig.template.file);
11685
- templateContent = await fs12.readFile(validatedPath, "utf-8");
12500
+ templateContent = await fs14.readFile(validatedPath, "utf-8");
11686
12501
  } else {
11687
12502
  throw new Error('Custom template must specify either "file" or "content"');
11688
12503
  }
@@ -11693,8 +12508,8 @@ ${expr}
11693
12508
  if (!sanitizedSchema) {
11694
12509
  throw new Error("Invalid schema name");
11695
12510
  }
11696
- const templatePath = path13.join(__dirname, `output/${sanitizedSchema}/template.liquid`);
11697
- templateContent = await fs12.readFile(templatePath, "utf-8");
12511
+ const templatePath = path16.join(__dirname, `output/${sanitizedSchema}/template.liquid`);
12512
+ templateContent = await fs14.readFile(templatePath, "utf-8");
11698
12513
  if (sanitizedSchema === "issue-assistant") {
11699
12514
  enrichAssistantContext = true;
11700
12515
  }
@@ -11802,7 +12617,7 @@ ${expr}
11802
12617
  /**
11803
12618
  * Execute multiple checks with dependency awareness - intelligently parallel and sequential
11804
12619
  */
11805
- async executeDependencyAwareChecks(prInfo, checks, timeout, config, logFn, debug, maxParallelism, failFast) {
12620
+ async executeDependencyAwareChecks(prInfo, checks, timeout, config, logFn, debug, maxParallelism, failFast, pauseGate) {
11806
12621
  const log2 = logFn || console.error;
11807
12622
  if (debug) {
11808
12623
  log2(`\u{1F527} Debug: Starting dependency-aware execution of ${checks.length} checks`);
@@ -11937,6 +12752,13 @@ ${expr}
11937
12752
  }
11938
12753
  const levelChecks = executionGroup.parallel.filter((name) => !results.has(name));
11939
12754
  const levelTaskFunctions = levelChecks.map((checkName) => async () => {
12755
+ if (pauseGate) {
12756
+ try {
12757
+ await pauseGate();
12758
+ } catch {
12759
+ return { checkName, error: "__STOP__", result: null, skipped: true };
12760
+ }
12761
+ }
11940
12762
  if (results.has(checkName)) {
11941
12763
  if (debug) log2(`\u{1F527} Debug: Skipping ${checkName} (already satisfied earlier)`);
11942
12764
  return { checkName, error: null, result: results.get(checkName) };
@@ -12025,7 +12847,7 @@ ${expr}
12025
12847
  });
12026
12848
  if (!hasFatalFailure && config && (config.fail_if || config.checks[depId]?.fail_if)) {
12027
12849
  try {
12028
- hasFatalFailure = await this.failIfTriggered(depId, depRes, config);
12850
+ hasFatalFailure = await this.failIfTriggered(depId, depRes, config, results);
12029
12851
  } catch {
12030
12852
  }
12031
12853
  }
@@ -12258,7 +13080,9 @@ ${expr}
12258
13080
  const fRes = await this.evaluateFailureConditions(
12259
13081
  childName,
12260
13082
  childItemRes,
12261
- config
13083
+ config,
13084
+ prInfo,
13085
+ results
12262
13086
  );
12263
13087
  if (fRes.length > 0) {
12264
13088
  const fIssues = fRes.filter((f) => f.failed).map((f) => ({
@@ -12301,6 +13125,13 @@ ${expr}
12301
13125
  }
12302
13126
  };
12303
13127
  const itemTasks = forEachItems.map((item, itemIndex) => async () => {
13128
+ if (pauseGate) {
13129
+ try {
13130
+ await pauseGate();
13131
+ } catch {
13132
+ throw new Error("__STOP__");
13133
+ }
13134
+ }
12304
13135
  try {
12305
13136
  emitNdjsonSpanWithEvents(
12306
13137
  "visor.foreach.item",
@@ -12313,6 +13144,13 @@ ${expr}
12313
13144
  );
12314
13145
  } catch {
12315
13146
  }
13147
+ try {
13148
+ const span = import_api6.trace.getSpan(import_api6.context.active());
13149
+ if (span) {
13150
+ captureForEachState(span, forEachItems, itemIndex, item);
13151
+ }
13152
+ } catch {
13153
+ }
12316
13154
  const forEachDependencyResults = /* @__PURE__ */ new Map();
12317
13155
  for (const [depName, depResult] of dependencyResults) {
12318
13156
  if (forEachParents.includes(depName)) {
@@ -12363,7 +13201,9 @@ ${expr}
12363
13201
  const depFailures = await this.evaluateFailureConditions(
12364
13202
  depId,
12365
13203
  depItemRes,
12366
- config
13204
+ config,
13205
+ prInfo,
13206
+ results
12367
13207
  );
12368
13208
  hasFatalDepFailure = depFailures.some((f) => f.failed);
12369
13209
  } catch {
@@ -12439,7 +13279,9 @@ ${expr}
12439
13279
  const itemFailures = await this.evaluateFailureConditions(
12440
13280
  checkName,
12441
13281
  itemResult,
12442
- config
13282
+ config,
13283
+ prInfo,
13284
+ results
12443
13285
  );
12444
13286
  if (itemFailures.length > 0) {
12445
13287
  const failureIssues = itemFailures.filter((f) => f.failed).map((f) => ({
@@ -12609,7 +13451,13 @@ ${expr}
12609
13451
  { index: itemIndex, total: forEachItems.length, parent: forEachParentName }
12610
13452
  );
12611
13453
  if (config && (config.fail_if || nodeCfg.fail_if)) {
12612
- const fRes = await this.evaluateFailureConditions(node, nodeItemRes, config);
13454
+ const fRes = await this.evaluateFailureConditions(
13455
+ node,
13456
+ nodeItemRes,
13457
+ config,
13458
+ prInfo,
13459
+ results
13460
+ );
12613
13461
  if (fRes.length > 0) {
12614
13462
  const fIssues = fRes.filter((f) => f.failed).map((f) => ({
12615
13463
  file: "system",
@@ -12704,7 +13552,13 @@ ${expr}
12704
13552
  rForEval = { ...r, output: parsed };
12705
13553
  }
12706
13554
  }
12707
- const failures = await this.evaluateFailureConditions(parent, rForEval, config);
13555
+ const failures = await this.evaluateFailureConditions(
13556
+ parent,
13557
+ rForEval,
13558
+ config,
13559
+ prInfo,
13560
+ results
13561
+ );
12708
13562
  if (failures.some((f) => f.failed)) {
12709
13563
  }
12710
13564
  if (failures.some((f) => f.failed)) return true;
@@ -12823,7 +13677,9 @@ ${expr}
12823
13677
  const failures = await this.evaluateFailureConditions(
12824
13678
  checkName,
12825
13679
  r,
12826
- config
13680
+ config,
13681
+ prInfo,
13682
+ results
12827
13683
  );
12828
13684
  hadFatal = failures.some((f) => f.failed);
12829
13685
  } catch {
@@ -12930,7 +13786,9 @@ ${expr}
12930
13786
  const failureResults = await this.evaluateFailureConditions(
12931
13787
  checkName,
12932
13788
  finalResult,
12933
- config
13789
+ config,
13790
+ prInfo,
13791
+ results
12934
13792
  );
12935
13793
  if (failureResults.length > 0) {
12936
13794
  const failureIssues = failureResults.filter((f) => f.failed).map((f) => ({
@@ -13045,6 +13903,14 @@ ${error.stack || ""}` : String(error);
13045
13903
  const checkName = levelChecksList[i];
13046
13904
  const result = levelResults[i];
13047
13905
  const checkConfig = config.checks[checkName];
13906
+ if (result.status === "fulfilled" && result.value?.error === "__STOP__") {
13907
+ shouldStopExecution = true;
13908
+ break;
13909
+ }
13910
+ if (result.status === "rejected" && result.reason instanceof Error && result.reason.message === "__STOP__") {
13911
+ shouldStopExecution = true;
13912
+ break;
13913
+ }
13048
13914
  if (result.status === "fulfilled" && result.value.result && !result.value.error) {
13049
13915
  if (result.value.skipped) {
13050
13916
  if (debug) {
@@ -13105,6 +13971,21 @@ ${error.stack || ""}` : String(error);
13105
13971
  this.trackOutputHistory(checkName, reviewResultWithOutput.output);
13106
13972
  }
13107
13973
  results.set(checkName, reviewResult);
13974
+ try {
13975
+ const span = import_api6.trace.getSpan(import_api6.context.active());
13976
+ if (span) {
13977
+ const allOutputs = {};
13978
+ results.forEach((result2, name) => {
13979
+ if (result2.output !== void 0) {
13980
+ allOutputs[name] = result2.output;
13981
+ }
13982
+ });
13983
+ const memoryStore = MemoryStore.getInstance();
13984
+ const memoryData = await memoryStore.getAll();
13985
+ captureStateSnapshot(span, checkName, allOutputs, memoryData);
13986
+ }
13987
+ } catch {
13988
+ }
13108
13989
  } else {
13109
13990
  const errorSummary = {
13110
13991
  issues: [
@@ -13823,13 +14704,14 @@ ${result.value.result.debug.rawResponse}`;
13823
14704
  /**
13824
14705
  * Evaluate failure conditions for a check result
13825
14706
  */
13826
- async evaluateFailureConditions(checkName, reviewSummary, config, prInfo) {
14707
+ async evaluateFailureConditions(checkName, reviewSummary, config, prInfo, previousOutputs) {
13827
14708
  if (!config) {
13828
14709
  return [];
13829
14710
  }
13830
14711
  const checkConfig = config.checks[checkName];
13831
14712
  const checkSchema = typeof checkConfig?.schema === "object" ? "custom" : checkConfig?.schema || "";
13832
14713
  const checkGroup = checkConfig?.group || "";
14714
+ const outputsRecord = previousOutputs ? previousOutputs instanceof Map ? Object.fromEntries(previousOutputs.entries()) : previousOutputs : void 0;
13833
14715
  const globalFailIf = config.fail_if;
13834
14716
  const checkFailIf = checkConfig?.fail_if;
13835
14717
  if (globalFailIf || checkFailIf) {
@@ -13840,7 +14722,8 @@ ${result.value.result.debug.rawResponse}`;
13840
14722
  checkSchema,
13841
14723
  checkGroup,
13842
14724
  reviewSummary,
13843
- globalFailIf
14725
+ globalFailIf,
14726
+ outputsRecord
13844
14727
  );
13845
14728
  try {
13846
14729
  addEvent("fail_if.evaluated", {
@@ -13905,7 +14788,8 @@ ${result.value.result.debug.rawResponse}`;
13905
14788
  checkSchema,
13906
14789
  checkGroup,
13907
14790
  reviewSummary,
13908
- checkFailIf
14791
+ checkFailIf,
14792
+ outputsRecord
13909
14793
  );
13910
14794
  try {
13911
14795
  addEvent("fail_if.evaluated", {
@@ -14434,9 +15318,15 @@ ${result.value.result.debug.rawResponse}`;
14434
15318
  if (!issues || issues.length === 0) return false;
14435
15319
  return issues.some((i) => this.isFatalRule(i.ruleId || "", i.severity));
14436
15320
  }
14437
- async failIfTriggered(checkName, result, config) {
15321
+ async failIfTriggered(checkName, result, config, previousOutputs) {
14438
15322
  if (!config) return false;
14439
- const failures = await this.evaluateFailureConditions(checkName, result, config);
15323
+ const failures = await this.evaluateFailureConditions(
15324
+ checkName,
15325
+ result,
15326
+ config,
15327
+ void 0,
15328
+ previousOutputs
15329
+ );
14440
15330
  return failures.some((f) => f.failed);
14441
15331
  }
14442
15332
  /**
@@ -14834,9 +15724,13 @@ var init_config_schema = __esm({
14834
15724
  ],
14835
15725
  description: 'Extends from other configurations - can be file path, HTTP(S) URL, or "default"'
14836
15726
  },
15727
+ steps: {
15728
+ $ref: "#/definitions/Record%3Cstring%2CCheckConfig%3E",
15729
+ description: "Step configurations (recommended)"
15730
+ },
14837
15731
  checks: {
14838
15732
  $ref: "#/definitions/Record%3Cstring%2CCheckConfig%3E",
14839
- description: "Check configurations"
15733
+ description: "Check configurations (legacy, use 'steps' instead) - always populated after normalization"
14840
15734
  },
14841
15735
  output: {
14842
15736
  $ref: "#/definitions/OutputConfig",
@@ -14891,7 +15785,7 @@ var init_config_schema = __esm({
14891
15785
  description: "Optional routing defaults for retry/goto/run policies"
14892
15786
  }
14893
15787
  },
14894
- required: ["version", "checks", "output"],
15788
+ required: ["version", "output"],
14895
15789
  additionalProperties: false,
14896
15790
  description: "Main Visor configuration",
14897
15791
  patternProperties: {
@@ -15175,7 +16069,8 @@ var init_config_schema = __esm({
15175
16069
  "memory",
15176
16070
  "github",
15177
16071
  "claude-code",
15178
- "mcp"
16072
+ "mcp",
16073
+ "human-input"
15179
16074
  ],
15180
16075
  description: "Valid check types in configuration"
15181
16076
  },
@@ -15879,14 +16774,14 @@ init_check_execution_engine();
15879
16774
 
15880
16775
  // src/config.ts
15881
16776
  var yaml2 = __toESM(require("js-yaml"));
15882
- var fs11 = __toESM(require("fs"));
15883
- var path12 = __toESM(require("path"));
16777
+ var fs13 = __toESM(require("fs"));
16778
+ var path15 = __toESM(require("path"));
15884
16779
  init_logger();
15885
16780
  var import_simple_git2 = __toESM(require("simple-git"));
15886
16781
 
15887
16782
  // src/utils/config-loader.ts
15888
- var fs10 = __toESM(require("fs"));
15889
- var path11 = __toESM(require("path"));
16783
+ var fs12 = __toESM(require("fs"));
16784
+ var path14 = __toESM(require("path"));
15890
16785
  var yaml = __toESM(require("js-yaml"));
15891
16786
  var ConfigLoader = class {
15892
16787
  constructor(options = {}) {
@@ -15967,7 +16862,7 @@ var ConfigLoader = class {
15967
16862
  return source.toLowerCase();
15968
16863
  case "local" /* LOCAL */:
15969
16864
  const basePath = this.options.baseDir || process.cwd();
15970
- return path11.resolve(basePath, source);
16865
+ return path14.resolve(basePath, source);
15971
16866
  default:
15972
16867
  return source;
15973
16868
  }
@@ -15977,19 +16872,19 @@ var ConfigLoader = class {
15977
16872
  */
15978
16873
  async fetchLocalConfig(filePath) {
15979
16874
  const basePath = this.options.baseDir || process.cwd();
15980
- const resolvedPath = path11.resolve(basePath, filePath);
16875
+ const resolvedPath = path14.resolve(basePath, filePath);
15981
16876
  this.validateLocalPath(resolvedPath);
15982
- if (!fs10.existsSync(resolvedPath)) {
16877
+ if (!fs12.existsSync(resolvedPath)) {
15983
16878
  throw new Error(`Configuration file not found: ${resolvedPath}`);
15984
16879
  }
15985
16880
  try {
15986
- const content = fs10.readFileSync(resolvedPath, "utf8");
16881
+ const content = fs12.readFileSync(resolvedPath, "utf8");
15987
16882
  const config = yaml.load(content);
15988
16883
  if (!config || typeof config !== "object") {
15989
16884
  throw new Error(`Invalid YAML in configuration file: ${resolvedPath}`);
15990
16885
  }
15991
16886
  const previousBaseDir = this.options.baseDir;
15992
- this.options.baseDir = path11.dirname(resolvedPath);
16887
+ this.options.baseDir = path14.dirname(resolvedPath);
15993
16888
  try {
15994
16889
  if (config.extends) {
15995
16890
  const processedConfig = await this.processExtends(config);
@@ -16069,29 +16964,30 @@ var ConfigLoader = class {
16069
16964
  async fetchDefaultConfig() {
16070
16965
  const possiblePaths = [
16071
16966
  // When running as GitHub Action (bundled in dist/)
16072
- path11.join(__dirname, "defaults", ".visor.yaml"),
16967
+ path14.join(__dirname, "defaults", ".visor.yaml"),
16073
16968
  // When running from source
16074
- path11.join(__dirname, "..", "..", "defaults", ".visor.yaml"),
16969
+ path14.join(__dirname, "..", "..", "defaults", ".visor.yaml"),
16075
16970
  // Try via package root
16076
- this.findPackageRoot() ? path11.join(this.findPackageRoot(), "defaults", ".visor.yaml") : "",
16971
+ this.findPackageRoot() ? path14.join(this.findPackageRoot(), "defaults", ".visor.yaml") : "",
16077
16972
  // GitHub Action environment variable
16078
- process.env.GITHUB_ACTION_PATH ? path11.join(process.env.GITHUB_ACTION_PATH, "defaults", ".visor.yaml") : "",
16079
- process.env.GITHUB_ACTION_PATH ? path11.join(process.env.GITHUB_ACTION_PATH, "dist", "defaults", ".visor.yaml") : ""
16973
+ process.env.GITHUB_ACTION_PATH ? path14.join(process.env.GITHUB_ACTION_PATH, "defaults", ".visor.yaml") : "",
16974
+ process.env.GITHUB_ACTION_PATH ? path14.join(process.env.GITHUB_ACTION_PATH, "dist", "defaults", ".visor.yaml") : ""
16080
16975
  ].filter((p) => p);
16081
16976
  let defaultConfigPath;
16082
16977
  for (const possiblePath of possiblePaths) {
16083
- if (fs10.existsSync(possiblePath)) {
16978
+ if (fs12.existsSync(possiblePath)) {
16084
16979
  defaultConfigPath = possiblePath;
16085
16980
  break;
16086
16981
  }
16087
16982
  }
16088
- if (defaultConfigPath && fs10.existsSync(defaultConfigPath)) {
16983
+ if (defaultConfigPath && fs12.existsSync(defaultConfigPath)) {
16089
16984
  console.error(`\u{1F4E6} Loading bundled default configuration from ${defaultConfigPath}`);
16090
- const content = fs10.readFileSync(defaultConfigPath, "utf8");
16091
- const config = yaml.load(content);
16985
+ const content = fs12.readFileSync(defaultConfigPath, "utf8");
16986
+ let config = yaml.load(content);
16092
16987
  if (!config || typeof config !== "object") {
16093
16988
  throw new Error("Invalid default configuration");
16094
16989
  }
16990
+ config = this.normalizeStepsAndChecks(config);
16095
16991
  if (config.extends) {
16096
16992
  return await this.processExtends(config);
16097
16993
  }
@@ -16166,8 +17062,8 @@ var ConfigLoader = class {
16166
17062
  */
16167
17063
  validateLocalPath(resolvedPath) {
16168
17064
  const projectRoot = this.options.projectRoot || process.cwd();
16169
- const normalizedPath = path11.normalize(resolvedPath);
16170
- const normalizedRoot = path11.normalize(projectRoot);
17065
+ const normalizedPath = path14.normalize(resolvedPath);
17066
+ const normalizedRoot = path14.normalize(projectRoot);
16171
17067
  if (!normalizedPath.startsWith(normalizedRoot)) {
16172
17068
  throw new Error(
16173
17069
  `Security error: Path traversal detected. Cannot access files outside project root: ${projectRoot}`
@@ -16193,19 +17089,19 @@ var ConfigLoader = class {
16193
17089
  */
16194
17090
  findPackageRoot() {
16195
17091
  let currentDir = __dirname;
16196
- const root = path11.parse(currentDir).root;
17092
+ const root = path14.parse(currentDir).root;
16197
17093
  while (currentDir !== root) {
16198
- const packageJsonPath = path11.join(currentDir, "package.json");
16199
- if (fs10.existsSync(packageJsonPath)) {
17094
+ const packageJsonPath = path14.join(currentDir, "package.json");
17095
+ if (fs12.existsSync(packageJsonPath)) {
16200
17096
  try {
16201
- const packageJson = JSON.parse(fs10.readFileSync(packageJsonPath, "utf8"));
17097
+ const packageJson = JSON.parse(fs12.readFileSync(packageJsonPath, "utf8"));
16202
17098
  if (packageJson.name === "@probelabs/visor") {
16203
17099
  return currentDir;
16204
17100
  }
16205
17101
  } catch {
16206
17102
  }
16207
17103
  }
16208
- currentDir = path11.dirname(currentDir);
17104
+ currentDir = path14.dirname(currentDir);
16209
17105
  }
16210
17106
  return null;
16211
17107
  }
@@ -16222,6 +17118,20 @@ var ConfigLoader = class {
16222
17118
  this.loadedConfigs.clear();
16223
17119
  this.clearCache();
16224
17120
  }
17121
+ /**
17122
+ * Normalize 'checks' and 'steps' keys for backward compatibility
17123
+ * Ensures both keys are present and contain the same data
17124
+ */
17125
+ normalizeStepsAndChecks(config) {
17126
+ if (config.steps && config.checks) {
17127
+ config.checks = config.steps;
17128
+ } else if (config.steps && !config.checks) {
17129
+ config.checks = config.steps;
17130
+ } else if (config.checks && !config.steps) {
17131
+ config.steps = config.checks;
17132
+ }
17133
+ return config;
17134
+ }
16225
17135
  };
16226
17136
 
16227
17137
  // src/config.ts
@@ -16242,6 +17152,7 @@ var ConfigManager = class {
16242
17152
  validCheckTypes = [
16243
17153
  "ai",
16244
17154
  "claude-code",
17155
+ "mcp",
16245
17156
  "command",
16246
17157
  "http",
16247
17158
  "http_input",
@@ -16250,7 +17161,8 @@ var ConfigManager = class {
16250
17161
  "noop",
16251
17162
  "log",
16252
17163
  "memory",
16253
- "github"
17164
+ "github",
17165
+ "human-input"
16254
17166
  ];
16255
17167
  validEventTriggers = [...VALID_EVENT_TRIGGERS];
16256
17168
  validOutputFormats = ["table", "json", "markdown", "sarif"];
@@ -16260,12 +17172,12 @@ var ConfigManager = class {
16260
17172
  */
16261
17173
  async loadConfig(configPath, options = {}) {
16262
17174
  const { validate = true, mergeDefaults = true, allowedRemotePatterns } = options;
16263
- const resolvedPath = path12.isAbsolute(configPath) ? configPath : path12.resolve(process.cwd(), configPath);
17175
+ const resolvedPath = path15.isAbsolute(configPath) ? configPath : path15.resolve(process.cwd(), configPath);
16264
17176
  try {
16265
- if (!fs11.existsSync(resolvedPath)) {
17177
+ if (!fs13.existsSync(resolvedPath)) {
16266
17178
  throw new Error(`Configuration file not found: ${resolvedPath}`);
16267
17179
  }
16268
- const configContent = fs11.readFileSync(resolvedPath, "utf8");
17180
+ const configContent = fs13.readFileSync(resolvedPath, "utf8");
16269
17181
  let parsedConfig;
16270
17182
  try {
16271
17183
  parsedConfig = yaml2.load(configContent);
@@ -16278,7 +17190,7 @@ var ConfigManager = class {
16278
17190
  }
16279
17191
  if (parsedConfig.extends) {
16280
17192
  const loaderOptions = {
16281
- baseDir: path12.dirname(resolvedPath),
17193
+ baseDir: path15.dirname(resolvedPath),
16282
17194
  allowRemote: this.isRemoteExtendsAllowed(),
16283
17195
  maxDepth: 10,
16284
17196
  allowedRemotePatterns
@@ -16296,6 +17208,7 @@ var ConfigManager = class {
16296
17208
  parsedConfig = merger.merge(mergedConfig, configWithoutExtends);
16297
17209
  parsedConfig = merger.removeDisabledChecks(parsedConfig);
16298
17210
  }
17211
+ parsedConfig = this.normalizeStepsAndChecks(parsedConfig);
16299
17212
  if (validate) {
16300
17213
  this.validateConfig(parsedConfig);
16301
17214
  }
@@ -16327,9 +17240,9 @@ var ConfigManager = class {
16327
17240
  const gitRoot = await this.findGitRepositoryRoot();
16328
17241
  const searchDirs = [gitRoot, process.cwd()].filter(Boolean);
16329
17242
  for (const baseDir of searchDirs) {
16330
- const possiblePaths = [path12.join(baseDir, ".visor.yaml"), path12.join(baseDir, ".visor.yml")];
17243
+ const possiblePaths = [path15.join(baseDir, ".visor.yaml"), path15.join(baseDir, ".visor.yml")];
16331
17244
  for (const configPath of possiblePaths) {
16332
- if (fs11.existsSync(configPath)) {
17245
+ if (fs13.existsSync(configPath)) {
16333
17246
  return this.loadConfig(configPath, options);
16334
17247
  }
16335
17248
  }
@@ -16362,7 +17275,9 @@ var ConfigManager = class {
16362
17275
  async getDefaultConfig() {
16363
17276
  return {
16364
17277
  version: "1.0",
17278
+ steps: {},
16365
17279
  checks: {},
17280
+ // Keep for backward compatibility
16366
17281
  max_parallelism: 3,
16367
17282
  output: {
16368
17283
  pr_comment: {
@@ -16381,34 +17296,35 @@ var ConfigManager = class {
16381
17296
  const possiblePaths = [];
16382
17297
  if (typeof __dirname !== "undefined") {
16383
17298
  possiblePaths.push(
16384
- path12.join(__dirname, "defaults", ".visor.yaml"),
16385
- path12.join(__dirname, "..", "defaults", ".visor.yaml")
17299
+ path15.join(__dirname, "defaults", ".visor.yaml"),
17300
+ path15.join(__dirname, "..", "defaults", ".visor.yaml")
16386
17301
  );
16387
17302
  }
16388
17303
  const pkgRoot = this.findPackageRoot();
16389
17304
  if (pkgRoot) {
16390
- possiblePaths.push(path12.join(pkgRoot, "defaults", ".visor.yaml"));
17305
+ possiblePaths.push(path15.join(pkgRoot, "defaults", ".visor.yaml"));
16391
17306
  }
16392
17307
  if (process.env.GITHUB_ACTION_PATH) {
16393
17308
  possiblePaths.push(
16394
- path12.join(process.env.GITHUB_ACTION_PATH, "defaults", ".visor.yaml"),
16395
- path12.join(process.env.GITHUB_ACTION_PATH, "dist", "defaults", ".visor.yaml")
17309
+ path15.join(process.env.GITHUB_ACTION_PATH, "defaults", ".visor.yaml"),
17310
+ path15.join(process.env.GITHUB_ACTION_PATH, "dist", "defaults", ".visor.yaml")
16396
17311
  );
16397
17312
  }
16398
17313
  let bundledConfigPath;
16399
17314
  for (const possiblePath of possiblePaths) {
16400
- if (fs11.existsSync(possiblePath)) {
17315
+ if (fs13.existsSync(possiblePath)) {
16401
17316
  bundledConfigPath = possiblePath;
16402
17317
  break;
16403
17318
  }
16404
17319
  }
16405
- if (bundledConfigPath && fs11.existsSync(bundledConfigPath)) {
17320
+ if (bundledConfigPath && fs13.existsSync(bundledConfigPath)) {
16406
17321
  console.error(`\u{1F4E6} Loading bundled default configuration from ${bundledConfigPath}`);
16407
- const configContent = fs11.readFileSync(bundledConfigPath, "utf8");
16408
- const parsedConfig = yaml2.load(configContent);
17322
+ const configContent = fs13.readFileSync(bundledConfigPath, "utf8");
17323
+ let parsedConfig = yaml2.load(configContent);
16409
17324
  if (!parsedConfig || typeof parsedConfig !== "object") {
16410
17325
  return null;
16411
17326
  }
17327
+ parsedConfig = this.normalizeStepsAndChecks(parsedConfig);
16412
17328
  this.validateConfig(parsedConfig);
16413
17329
  return this.mergeWithDefaults(parsedConfig);
16414
17330
  }
@@ -16425,21 +17341,35 @@ var ConfigManager = class {
16425
17341
  */
16426
17342
  findPackageRoot() {
16427
17343
  let currentDir = __dirname;
16428
- while (currentDir !== path12.dirname(currentDir)) {
16429
- const packageJsonPath = path12.join(currentDir, "package.json");
16430
- if (fs11.existsSync(packageJsonPath)) {
17344
+ while (currentDir !== path15.dirname(currentDir)) {
17345
+ const packageJsonPath = path15.join(currentDir, "package.json");
17346
+ if (fs13.existsSync(packageJsonPath)) {
16431
17347
  try {
16432
- const packageJson = JSON.parse(fs11.readFileSync(packageJsonPath, "utf8"));
17348
+ const packageJson = JSON.parse(fs13.readFileSync(packageJsonPath, "utf8"));
16433
17349
  if (packageJson.name === "@probelabs/visor") {
16434
17350
  return currentDir;
16435
17351
  }
16436
17352
  } catch {
16437
17353
  }
16438
17354
  }
16439
- currentDir = path12.dirname(currentDir);
17355
+ currentDir = path15.dirname(currentDir);
16440
17356
  }
16441
17357
  return null;
16442
17358
  }
17359
+ /**
17360
+ * Normalize 'checks' and 'steps' keys for backward compatibility
17361
+ * Ensures both keys are present and contain the same data
17362
+ */
17363
+ normalizeStepsAndChecks(config) {
17364
+ if (config.steps && config.checks) {
17365
+ config.checks = config.steps;
17366
+ } else if (config.steps && !config.checks) {
17367
+ config.checks = config.steps;
17368
+ } else if (config.checks && !config.steps) {
17369
+ config.steps = config.checks;
17370
+ }
17371
+ return config;
17372
+ }
16443
17373
  /**
16444
17374
  * Merge configuration with CLI options
16445
17375
  */
@@ -16495,13 +17425,15 @@ var ConfigManager = class {
16495
17425
  message: "Missing required field: version"
16496
17426
  });
16497
17427
  }
16498
- if (!config.checks) {
17428
+ if (!config.checks && !config.steps) {
16499
17429
  errors.push({
16500
- field: "checks",
16501
- message: "Missing required field: checks"
17430
+ field: "checks/steps",
17431
+ message: 'Missing required field: either "checks" or "steps" must be defined. "steps" is recommended for new configurations.'
16502
17432
  });
16503
- } else {
16504
- for (const [checkName, checkConfig] of Object.entries(config.checks)) {
17433
+ }
17434
+ const checksToValidate = config.checks || config.steps;
17435
+ if (checksToValidate) {
17436
+ for (const [checkName, checkConfig] of Object.entries(checksToValidate)) {
16505
17437
  if (!checkConfig.type) {
16506
17438
  checkConfig.type = "ai";
16507
17439
  }
@@ -16821,7 +17753,7 @@ var ConfigManager = class {
16821
17753
  try {
16822
17754
  if (!__ajvValidate) {
16823
17755
  try {
16824
- const jsonPath = path12.resolve(__dirname, "generated", "config-schema.json");
17756
+ const jsonPath = path15.resolve(__dirname, "generated", "config-schema.json");
16825
17757
  const jsonSchema = require(jsonPath);
16826
17758
  if (jsonSchema) {
16827
17759
  const ajv = new import_ajv.default({ allErrors: true, allowUnionTypes: true, strict: false });
@@ -17110,6 +18042,9 @@ async function runChecks(opts = {}) {
17110
18042
  }
17111
18043
  const checks = opts.checks && opts.checks.length > 0 ? resolveChecks(opts.checks, config) : Object.keys(config.checks || {});
17112
18044
  const engine = new CheckExecutionEngine(opts.cwd);
18045
+ if (opts.executionContext) {
18046
+ engine.setExecutionContext(opts.executionContext);
18047
+ }
17113
18048
  const result = await engine.executeChecks({
17114
18049
  checks,
17115
18050
  workingDirectory: opts.cwd,