@probelabs/visor 0.1.98 → 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 (107) hide show
  1. package/README.md +2 -1
  2. package/action.yml +7 -2
  3. package/defaults/.visor.yaml +4 -4
  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.map +1 -1
  12. package/dist/debug-visualizer/debug-span-exporter.d.ts +47 -0
  13. package/dist/debug-visualizer/debug-span-exporter.d.ts.map +1 -0
  14. package/dist/debug-visualizer/trace-reader.d.ts +117 -0
  15. package/dist/debug-visualizer/trace-reader.d.ts.map +1 -0
  16. package/dist/debug-visualizer/ui/index.html +2568 -0
  17. package/dist/debug-visualizer/ws-server.d.ts +99 -0
  18. package/dist/debug-visualizer/ws-server.d.ts.map +1 -0
  19. package/dist/defaults/.visor.yaml +4 -4
  20. package/dist/failure-condition-evaluator.d.ts.map +1 -1
  21. package/dist/generated/config-schema.d.ts +1 -1
  22. package/dist/generated/config-schema.d.ts.map +1 -1
  23. package/dist/generated/config-schema.json +2 -1
  24. package/dist/git-repository-analyzer.d.ts +1 -7
  25. package/dist/git-repository-analyzer.d.ts.map +1 -1
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +17601 -1749
  28. package/dist/liquid-extensions.d.ts +1 -1
  29. package/dist/liquid-extensions.d.ts.map +1 -1
  30. package/dist/output/code-review/schema.json +2 -2
  31. package/dist/output/traces/run-2025-10-22T10-40-34-055Z.ndjson +218 -0
  32. package/dist/pr-analyzer.d.ts +2 -1
  33. package/dist/pr-analyzer.d.ts.map +1 -1
  34. package/dist/providers/ai-check-provider.d.ts.map +1 -1
  35. package/dist/providers/check-provider-registry.d.ts.map +1 -1
  36. package/dist/providers/check-provider.interface.d.ts +17 -6
  37. package/dist/providers/check-provider.interface.d.ts.map +1 -1
  38. package/dist/providers/command-check-provider.d.ts.map +1 -1
  39. package/dist/providers/github-ops-provider.d.ts.map +1 -1
  40. package/dist/providers/http-check-provider.d.ts.map +1 -1
  41. package/dist/providers/human-input-check-provider.d.ts +78 -0
  42. package/dist/providers/human-input-check-provider.d.ts.map +1 -0
  43. package/dist/providers/index.d.ts +2 -1
  44. package/dist/providers/index.d.ts.map +1 -1
  45. package/dist/providers/mcp-check-provider.d.ts.map +1 -1
  46. package/dist/providers/memory-check-provider.d.ts.map +1 -1
  47. package/dist/sdk/check-execution-engine-F3662LY7.mjs +11 -0
  48. package/dist/sdk/{chunk-I3GQJIR7.mjs → chunk-B5QBV2QJ.mjs} +2 -2
  49. package/dist/sdk/chunk-B5QBV2QJ.mjs.map +1 -0
  50. package/dist/sdk/{chunk-IG3BFIIN.mjs → chunk-FVS5CJ5S.mjs} +30 -1
  51. package/dist/sdk/chunk-FVS5CJ5S.mjs.map +1 -0
  52. package/dist/sdk/{chunk-YXOWIDEF.mjs → chunk-TUTOLSFV.mjs} +15 -3
  53. package/dist/sdk/chunk-TUTOLSFV.mjs.map +1 -0
  54. package/dist/sdk/{chunk-POYXI3MQ.mjs → chunk-X2JKUOE5.mjs} +1375 -570
  55. package/dist/sdk/chunk-X2JKUOE5.mjs.map +1 -0
  56. package/dist/sdk/{liquid-extensions-GMEGEGC3.mjs → liquid-extensions-KVL4MKRH.mjs} +2 -2
  57. package/dist/sdk/{mermaid-telemetry-4DUEYCLE.mjs → mermaid-telemetry-FBF6D35S.mjs} +2 -2
  58. package/dist/sdk/sdk.d.mts +58 -2
  59. package/dist/sdk/sdk.d.ts +58 -2
  60. package/dist/sdk/sdk.js +1629 -733
  61. package/dist/sdk/sdk.js.map +1 -1
  62. package/dist/sdk/sdk.mjs +12 -6
  63. package/dist/sdk/sdk.mjs.map +1 -1
  64. package/dist/sdk/{tracer-init-RJGAIOBP.mjs → tracer-init-WC75N5NW.mjs} +2 -2
  65. package/dist/sdk.d.ts +5 -2
  66. package/dist/sdk.d.ts.map +1 -1
  67. package/dist/telemetry/file-span-exporter.d.ts.map +1 -1
  68. package/dist/telemetry/opentelemetry.d.ts +2 -0
  69. package/dist/telemetry/opentelemetry.d.ts.map +1 -1
  70. package/dist/telemetry/state-capture.d.ts +53 -0
  71. package/dist/telemetry/state-capture.d.ts.map +1 -0
  72. package/dist/telemetry/trace-helpers.d.ts.map +1 -1
  73. package/dist/traces/run-2025-10-22T10-40-34-055Z.ndjson +218 -0
  74. package/dist/types/cli.d.ts +6 -0
  75. package/dist/types/cli.d.ts.map +1 -1
  76. package/dist/types/config.d.ts +40 -1
  77. package/dist/types/config.d.ts.map +1 -1
  78. package/dist/utils/file-exclusion.d.ts +50 -0
  79. package/dist/utils/file-exclusion.d.ts.map +1 -0
  80. package/dist/utils/interactive-prompt.d.ts +26 -0
  81. package/dist/utils/interactive-prompt.d.ts.map +1 -0
  82. package/dist/utils/sandbox.d.ts +26 -0
  83. package/dist/utils/sandbox.d.ts.map +1 -0
  84. package/dist/utils/stdin-reader.d.ts +22 -0
  85. package/dist/utils/stdin-reader.d.ts.map +1 -0
  86. package/dist/utils/tracer-init.d.ts +0 -5
  87. package/dist/utils/tracer-init.d.ts.map +1 -1
  88. package/package.json +8 -4
  89. package/dist/output/traces/run-2025-10-19T19-05-29-328Z.ndjson +0 -40
  90. package/dist/output/traces/run-2025-10-19T19-05-42-253Z.ndjson +0 -40
  91. package/dist/output/traces/run-2025-10-19T19-05-42-805Z.ndjson +0 -40
  92. package/dist/output/traces/run-2025-10-19T19-05-43-323Z.ndjson +0 -40
  93. package/dist/output/traces/run-2025-10-19T19-05-43-841Z.ndjson +0 -12
  94. package/dist/sdk/check-execution-engine-W5FLIWZL.mjs +0 -11
  95. package/dist/sdk/chunk-I3GQJIR7.mjs.map +0 -1
  96. package/dist/sdk/chunk-IG3BFIIN.mjs.map +0 -1
  97. package/dist/sdk/chunk-POYXI3MQ.mjs.map +0 -1
  98. package/dist/sdk/chunk-YXOWIDEF.mjs.map +0 -1
  99. package/dist/traces/run-2025-10-19T19-05-29-328Z.ndjson +0 -40
  100. package/dist/traces/run-2025-10-19T19-05-42-253Z.ndjson +0 -40
  101. package/dist/traces/run-2025-10-19T19-05-42-805Z.ndjson +0 -40
  102. package/dist/traces/run-2025-10-19T19-05-43-323Z.ndjson +0 -40
  103. package/dist/traces/run-2025-10-19T19-05-43-841Z.ndjson +0 -12
  104. /package/dist/sdk/{check-execution-engine-W5FLIWZL.mjs.map → check-execution-engine-F3662LY7.mjs.map} +0 -0
  105. /package/dist/sdk/{liquid-extensions-GMEGEGC3.mjs.map → liquid-extensions-KVL4MKRH.mjs.map} +0 -0
  106. /package/dist/sdk/{mermaid-telemetry-4DUEYCLE.mjs.map → mermaid-telemetry-FBF6D35S.mjs.map} +0 -0
  107. /package/dist/sdk/{tracer-init-RJGAIOBP.mjs.map → tracer-init-WC75N5NW.mjs.map} +0 -0
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  init_tracer_init,
3
3
  initializeTracer
4
- } from "./chunk-YXOWIDEF.mjs";
4
+ } from "./chunk-TUTOLSFV.mjs";
5
5
  import {
6
6
  MemoryStore,
7
7
  createExtendedLiquid,
@@ -11,15 +11,16 @@ import {
11
11
  logger,
12
12
  logger_exports,
13
13
  resolveAssociationFromEvent
14
- } from "./chunk-I3GQJIR7.mjs";
14
+ } from "./chunk-B5QBV2QJ.mjs";
15
15
  import {
16
16
  addEvent,
17
17
  addFailIfTriggered,
18
18
  emitNdjsonFallback,
19
19
  emitNdjsonSpanWithEvents,
20
20
  fallback_ndjson_exports,
21
- init_fallback_ndjson
22
- } from "./chunk-IG3BFIIN.mjs";
21
+ init_fallback_ndjson,
22
+ withActiveSpan
23
+ } from "./chunk-FVS5CJ5S.mjs";
23
24
  import {
24
25
  __esm,
25
26
  __export,
@@ -139,7 +140,7 @@ var init_session_registry = __esm({
139
140
  });
140
141
  if (sourceAgent.debug && checkName) {
141
142
  try {
142
- const { initializeTracer: initializeTracer2 } = await import("./tracer-init-RJGAIOBP.mjs");
143
+ const { initializeTracer: initializeTracer2 } = await import("./tracer-init-WC75N5NW.mjs");
143
144
  const tracerResult = await initializeTracer2(newSessionId, checkName);
144
145
  if (tracerResult) {
145
146
  clonedAgent.tracer = tracerResult.tracer;
@@ -476,7 +477,7 @@ ${content}
476
477
  * Sleep utility
477
478
  */
478
479
  sleep(ms) {
479
- return new Promise((resolve) => setTimeout(resolve, ms));
480
+ return new Promise((resolve4) => setTimeout(resolve4, ms));
480
481
  }
481
482
  /**
482
483
  * Group results by specified criteria
@@ -544,7 +545,7 @@ async function processDiffWithOutline(diffContent) {
544
545
  }
545
546
  try {
546
547
  const originalProbePath = process.env.PROBE_PATH;
547
- const fs5 = __require("fs");
548
+ const fs7 = __require("fs");
548
549
  const possiblePaths = [
549
550
  // Relative to current working directory (most common in production)
550
551
  path.join(process.cwd(), "node_modules/@probelabs/probe/bin/probe-binary"),
@@ -555,7 +556,7 @@ async function processDiffWithOutline(diffContent) {
555
556
  ];
556
557
  let probeBinaryPath;
557
558
  for (const candidatePath of possiblePaths) {
558
- if (fs5.existsSync(candidatePath)) {
559
+ if (fs7.existsSync(candidatePath)) {
559
560
  probeBinaryPath = candidatePath;
560
561
  break;
561
562
  }
@@ -630,6 +631,7 @@ var AIReviewService = class {
630
631
  this.config.model = process.env.MODEL_NAME;
631
632
  }
632
633
  }
634
+ // NOTE: per request, no additional redaction/encryption helpers are used.
633
635
  /**
634
636
  * Execute AI review using probe agent
635
637
  */
@@ -946,14 +948,12 @@ ${prContext}
946
948
  const isIssue = prContextInfo.isIssue === true;
947
949
  const isPRContext = prContextInfo.isPRContext === true;
948
950
  const includeCodeContext = isPRContext || prContextInfo.includeCodeContext !== false;
949
- const log2 = this.config.debug ? console.error : () => {
950
- };
951
951
  if (isPRContext) {
952
- log2("\u{1F50D} Including full code diffs in AI context (PR mode)");
952
+ log("\u{1F50D} Including full code diffs in AI context (PR mode)");
953
953
  } else if (!includeCodeContext) {
954
- log2("\u{1F4CA} Including only file summary in AI context (no diffs)");
954
+ log("\u{1F4CA} Including only file summary in AI context (no diffs)");
955
955
  } else {
956
- log2("\u{1F50D} Including code diffs in AI context");
956
+ log("\u{1F50D} Including code diffs in AI context");
957
957
  }
958
958
  if (isIssue) {
959
959
  let context2 = `<issue>
@@ -1201,8 +1201,8 @@ ${schemaString}`);
1201
1201
  }
1202
1202
  if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
1203
1203
  try {
1204
- const fs5 = __require("fs");
1205
- const path6 = __require("path");
1204
+ const fs7 = __require("fs");
1205
+ const path9 = __require("path");
1206
1206
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
1207
1207
  const provider = this.config.provider || "auto";
1208
1208
  const model = this.config.model || "default";
@@ -1316,20 +1316,20 @@ ${"=".repeat(60)}
1316
1316
  `;
1317
1317
  readableVersion += `${"=".repeat(60)}
1318
1318
  `;
1319
- const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path6.join(process.cwd(), "debug-artifacts");
1320
- if (!fs5.existsSync(debugArtifactsDir)) {
1321
- fs5.mkdirSync(debugArtifactsDir, { recursive: true });
1319
+ const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path9.join(process.cwd(), "debug-artifacts");
1320
+ if (!fs7.existsSync(debugArtifactsDir)) {
1321
+ fs7.mkdirSync(debugArtifactsDir, { recursive: true });
1322
1322
  }
1323
- const debugFile = path6.join(
1323
+ const debugFile = path9.join(
1324
1324
  debugArtifactsDir,
1325
1325
  `prompt-${_checkName || "unknown"}-${timestamp}.json`
1326
1326
  );
1327
- fs5.writeFileSync(debugFile, debugJson, "utf-8");
1328
- const readableFile = path6.join(
1327
+ fs7.writeFileSync(debugFile, debugJson, "utf-8");
1328
+ const readableFile = path9.join(
1329
1329
  debugArtifactsDir,
1330
1330
  `prompt-${_checkName || "unknown"}-${timestamp}.txt`
1331
1331
  );
1332
- fs5.writeFileSync(readableFile, readableVersion, "utf-8");
1332
+ fs7.writeFileSync(readableFile, readableVersion, "utf-8");
1333
1333
  log(`
1334
1334
  \u{1F4BE} Full debug info saved to:`);
1335
1335
  log(` JSON: ${debugFile}`);
@@ -1361,8 +1361,8 @@ ${"=".repeat(60)}
1361
1361
  log(`\u{1F4E4} Response length: ${response.length} characters`);
1362
1362
  if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
1363
1363
  try {
1364
- const fs5 = __require("fs");
1365
- const path6 = __require("path");
1364
+ const fs7 = __require("fs");
1365
+ const path9 = __require("path");
1366
1366
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
1367
1367
  const agentAny2 = agent;
1368
1368
  let fullHistory = [];
@@ -1373,10 +1373,10 @@ ${"=".repeat(60)}
1373
1373
  } else if (agentAny2._messages) {
1374
1374
  fullHistory = agentAny2._messages;
1375
1375
  }
1376
- const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path6.join(process.cwd(), "debug-artifacts");
1377
- const sessionFile = path6.join(
1376
+ const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path9.join(process.cwd(), "debug-artifacts");
1377
+ const sessionBase = path9.join(
1378
1378
  debugArtifactsDir,
1379
- `session-${_checkName || "unknown"}-${timestamp}.json`
1379
+ `session-${_checkName || "unknown"}-${timestamp}`
1380
1380
  );
1381
1381
  const sessionData = {
1382
1382
  timestamp,
@@ -1384,15 +1384,9 @@ ${"=".repeat(60)}
1384
1384
  provider: this.config.provider || "auto",
1385
1385
  model: this.config.model || "default",
1386
1386
  schema: effectiveSchema,
1387
- fullConversationHistory: fullHistory,
1388
- totalMessages: fullHistory.length,
1389
- latestResponse: response
1387
+ totalMessages: fullHistory.length
1390
1388
  };
1391
- fs5.writeFileSync(sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
1392
- const sessionTxtFile = path6.join(
1393
- debugArtifactsDir,
1394
- `session-${_checkName || "unknown"}-${timestamp}.txt`
1395
- );
1389
+ fs7.writeFileSync(sessionBase + ".json", JSON.stringify(sessionData, null, 2), "utf-8");
1396
1390
  let readable = `=============================================================
1397
1391
  `;
1398
1392
  readable += `COMPLETE AI SESSION HISTORY (AFTER RESPONSE)
@@ -1409,22 +1403,18 @@ ${"=".repeat(60)}
1409
1403
 
1410
1404
  `;
1411
1405
  fullHistory.forEach((msg, idx) => {
1406
+ const role = msg.role || "unknown";
1407
+ const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content, null, 2);
1412
1408
  readable += `
1413
1409
  ${"=".repeat(60)}
1410
+ MESSAGE ${idx + 1}/${fullHistory.length}
1411
+ Role: ${role}
1412
+ ${"=".repeat(60)}
1414
1413
  `;
1415
- readable += `MESSAGE ${idx + 1}/${fullHistory.length}
1416
- `;
1417
- readable += `Role: ${msg.role || "unknown"}
1418
- `;
1419
- readable += `${"=".repeat(60)}
1420
- `;
1421
- const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content, null, 2);
1422
1414
  readable += content + "\n";
1423
1415
  });
1424
- fs5.writeFileSync(sessionTxtFile, readable, "utf-8");
1416
+ fs7.writeFileSync(sessionBase + ".summary.txt", readable, "utf-8");
1425
1417
  log(`\u{1F4BE} Complete session history saved:`);
1426
- log(` JSON: ${sessionFile}`);
1427
- log(` TXT: ${sessionTxtFile}`);
1428
1418
  log(` - Contains ALL ${fullHistory.length} messages (prompts + responses)`);
1429
1419
  } catch (error) {
1430
1420
  log(`\u26A0\uFE0F Could not save complete session history: ${error}`);
@@ -1432,11 +1422,11 @@ ${"=".repeat(60)}
1432
1422
  }
1433
1423
  if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
1434
1424
  try {
1435
- const fs5 = __require("fs");
1436
- const path6 = __require("path");
1425
+ const fs7 = __require("fs");
1426
+ const path9 = __require("path");
1437
1427
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
1438
- const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path6.join(process.cwd(), "debug-artifacts");
1439
- const responseFile = path6.join(
1428
+ const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path9.join(process.cwd(), "debug-artifacts");
1429
+ const responseFile = path9.join(
1440
1430
  debugArtifactsDir,
1441
1431
  `response-${_checkName || "unknown"}-${timestamp}.txt`
1442
1432
  );
@@ -1469,7 +1459,7 @@ ${"=".repeat(60)}
1469
1459
  `;
1470
1460
  responseContent += `${"=".repeat(60)}
1471
1461
  `;
1472
- fs5.writeFileSync(responseFile, responseContent, "utf-8");
1462
+ fs7.writeFileSync(responseFile, responseContent, "utf-8");
1473
1463
  log(`\u{1F4BE} Response saved to: ${responseFile}`);
1474
1464
  } catch (error) {
1475
1465
  log(`\u26A0\uFE0F Could not save response file: ${error}`);
@@ -1485,9 +1475,9 @@ ${"=".repeat(60)}
1485
1475
  await agentAny._telemetryConfig.shutdown();
1486
1476
  log(`\u{1F4CA} OpenTelemetry trace saved to: ${agentAny._traceFilePath}`);
1487
1477
  if (process.env.GITHUB_ACTIONS) {
1488
- const fs5 = __require("fs");
1489
- if (fs5.existsSync(agentAny._traceFilePath)) {
1490
- const stats = fs5.statSync(agentAny._traceFilePath);
1478
+ const fs7 = __require("fs");
1479
+ if (fs7.existsSync(agentAny._traceFilePath)) {
1480
+ const stats = fs7.statSync(agentAny._traceFilePath);
1491
1481
  console.log(
1492
1482
  `::notice title=AI Trace Saved::${agentAny._traceFilePath} (${stats.size} bytes)`
1493
1483
  );
@@ -1498,12 +1488,14 @@ ${"=".repeat(60)}
1498
1488
  log(`\u{1F4CA} Trace saved to: ${agentAny._traceFilePath}`);
1499
1489
  }
1500
1490
  } catch (exportError) {
1501
- console.error("\u26A0\uFE0F Warning: Failed to export trace for cloned session:", exportError);
1491
+ logger.warn(`\u26A0\uFE0F Warning: Failed to export trace for cloned session: ${exportError}`);
1502
1492
  }
1503
1493
  }
1504
1494
  return { response, effectiveSchema };
1505
1495
  } catch (error) {
1506
- console.error("\u274C ProbeAgent session reuse failed:", error);
1496
+ logger.error(
1497
+ `\u274C ProbeAgent session reuse failed: ${error instanceof Error ? error.message : "Unknown error"}`
1498
+ );
1507
1499
  throw new Error(
1508
1500
  `ProbeAgent session reuse failed: ${error instanceof Error ? error.message : "Unknown error"}`
1509
1501
  );
@@ -1607,8 +1599,8 @@ ${schemaString}`);
1607
1599
  const model = this.config.model || "default";
1608
1600
  if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
1609
1601
  try {
1610
- const fs5 = __require("fs");
1611
- const path6 = __require("path");
1602
+ const fs7 = __require("fs");
1603
+ const path9 = __require("path");
1612
1604
  const os = __require("os");
1613
1605
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
1614
1606
  const debugData = {
@@ -1682,30 +1674,20 @@ ${"=".repeat(60)}
1682
1674
  readableVersion += `${"=".repeat(60)}
1683
1675
  `;
1684
1676
  const tempDir = os.tmpdir();
1685
- const promptFile = path6.join(tempDir, `visor-prompt-${timestamp}.txt`);
1686
- fs5.writeFileSync(promptFile, prompt, "utf-8");
1677
+ const promptFile = path9.join(tempDir, `visor-prompt-${timestamp}.txt`);
1678
+ fs7.writeFileSync(promptFile, prompt, "utf-8");
1687
1679
  log(`
1688
1680
  \u{1F4BE} Prompt saved to: ${promptFile}`);
1689
- const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path6.join(process.cwd(), "debug-artifacts");
1681
+ const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path9.join(process.cwd(), "debug-artifacts");
1690
1682
  try {
1691
- if (!fs5.existsSync(debugArtifactsDir)) {
1692
- fs5.mkdirSync(debugArtifactsDir, { recursive: true });
1693
- }
1694
- const debugFile = path6.join(
1695
- debugArtifactsDir,
1696
- `prompt-${_checkName || "unknown"}-${timestamp}.json`
1697
- );
1698
- fs5.writeFileSync(debugFile, debugJson, "utf-8");
1699
- const readableFile = path6.join(
1683
+ const base = path9.join(
1700
1684
  debugArtifactsDir,
1701
- `prompt-${_checkName || "unknown"}-${timestamp}.txt`
1685
+ `prompt-${_checkName || "unknown"}-${timestamp}`
1702
1686
  );
1703
- fs5.writeFileSync(readableFile, readableVersion, "utf-8");
1687
+ fs7.writeFileSync(base + ".json", debugJson, "utf-8");
1688
+ fs7.writeFileSync(base + ".summary.txt", readableVersion, "utf-8");
1704
1689
  log(`
1705
- \u{1F4BE} Full debug info saved to:`);
1706
- log(` JSON: ${debugFile}`);
1707
- log(` TXT: ${readableFile}`);
1708
- log(` - Includes: prompt, schema, provider, model, and schema options`);
1690
+ \u{1F4BE} Full debug info saved to directory: ${debugArtifactsDir}`);
1709
1691
  } catch {
1710
1692
  }
1711
1693
  log(`
@@ -1748,8 +1730,8 @@ $ ${cliCommand}
1748
1730
  log(`\u{1F4E4} Response length: ${response.length} characters`);
1749
1731
  if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
1750
1732
  try {
1751
- const fs5 = __require("fs");
1752
- const path6 = __require("path");
1733
+ const fs7 = __require("fs");
1734
+ const path9 = __require("path");
1753
1735
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
1754
1736
  const agentAny = agent;
1755
1737
  let fullHistory = [];
@@ -1760,10 +1742,10 @@ $ ${cliCommand}
1760
1742
  } else if (agentAny._messages) {
1761
1743
  fullHistory = agentAny._messages;
1762
1744
  }
1763
- const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path6.join(process.cwd(), "debug-artifacts");
1764
- const sessionFile = path6.join(
1745
+ const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path9.join(process.cwd(), "debug-artifacts");
1746
+ const sessionBase = path9.join(
1765
1747
  debugArtifactsDir,
1766
- `session-${_checkName || "unknown"}-${timestamp}.json`
1748
+ `session-${_checkName || "unknown"}-${timestamp}`
1767
1749
  );
1768
1750
  const sessionData = {
1769
1751
  timestamp,
@@ -1771,15 +1753,9 @@ $ ${cliCommand}
1771
1753
  provider: this.config.provider || "auto",
1772
1754
  model: this.config.model || "default",
1773
1755
  schema: effectiveSchema,
1774
- fullConversationHistory: fullHistory,
1775
- totalMessages: fullHistory.length,
1776
- latestResponse: response
1756
+ totalMessages: fullHistory.length
1777
1757
  };
1778
- fs5.writeFileSync(sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
1779
- const sessionTxtFile = path6.join(
1780
- debugArtifactsDir,
1781
- `session-${_checkName || "unknown"}-${timestamp}.txt`
1782
- );
1758
+ fs7.writeFileSync(sessionBase + ".json", JSON.stringify(sessionData, null, 2), "utf-8");
1783
1759
  let readable = `=============================================================
1784
1760
  `;
1785
1761
  readable += `COMPLETE AI SESSION HISTORY (AFTER RESPONSE)
@@ -1796,22 +1772,18 @@ $ ${cliCommand}
1796
1772
 
1797
1773
  `;
1798
1774
  fullHistory.forEach((msg, idx) => {
1775
+ const role = msg.role || "unknown";
1776
+ const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content, null, 2);
1799
1777
  readable += `
1800
1778
  ${"=".repeat(60)}
1779
+ MESSAGE ${idx + 1}/${fullHistory.length}
1780
+ Role: ${role}
1781
+ ${"=".repeat(60)}
1801
1782
  `;
1802
- readable += `MESSAGE ${idx + 1}/${fullHistory.length}
1803
- `;
1804
- readable += `Role: ${msg.role || "unknown"}
1805
- `;
1806
- readable += `${"=".repeat(60)}
1807
- `;
1808
- const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content, null, 2);
1809
1783
  readable += content + "\n";
1810
1784
  });
1811
- fs5.writeFileSync(sessionTxtFile, readable, "utf-8");
1785
+ fs7.writeFileSync(sessionBase + ".summary.txt", readable, "utf-8");
1812
1786
  log(`\u{1F4BE} Complete session history saved:`);
1813
- log(` JSON: ${sessionFile}`);
1814
- log(` TXT: ${sessionTxtFile}`);
1815
1787
  log(` - Contains ALL ${fullHistory.length} messages (prompts + responses)`);
1816
1788
  } catch (error) {
1817
1789
  log(`\u26A0\uFE0F Could not save complete session history: ${error}`);
@@ -1819,11 +1791,11 @@ ${"=".repeat(60)}
1819
1791
  }
1820
1792
  if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
1821
1793
  try {
1822
- const fs5 = __require("fs");
1823
- const path6 = __require("path");
1794
+ const fs7 = __require("fs");
1795
+ const path9 = __require("path");
1824
1796
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
1825
- const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path6.join(process.cwd(), "debug-artifacts");
1826
- const responseFile = path6.join(
1797
+ const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path9.join(process.cwd(), "debug-artifacts");
1798
+ const responseFile = path9.join(
1827
1799
  debugArtifactsDir,
1828
1800
  `response-${_checkName || "unknown"}-${timestamp}.txt`
1829
1801
  );
@@ -1856,7 +1828,7 @@ ${"=".repeat(60)}
1856
1828
  `;
1857
1829
  responseContent += `${"=".repeat(60)}
1858
1830
  `;
1859
- fs5.writeFileSync(responseFile, responseContent, "utf-8");
1831
+ fs7.writeFileSync(responseFile, responseContent, "utf-8");
1860
1832
  log(`\u{1F4BE} Response saved to: ${responseFile}`);
1861
1833
  } catch (error) {
1862
1834
  log(`\u26A0\uFE0F Could not save response file: ${error}`);
@@ -1874,9 +1846,9 @@ ${"=".repeat(60)}
1874
1846
  await telemetry.shutdown();
1875
1847
  log(`\u{1F4CA} OpenTelemetry trace saved to: ${traceFilePath}`);
1876
1848
  if (process.env.GITHUB_ACTIONS) {
1877
- const fs5 = __require("fs");
1878
- if (fs5.existsSync(traceFilePath)) {
1879
- const stats = fs5.statSync(traceFilePath);
1849
+ const fs7 = __require("fs");
1850
+ if (fs7.existsSync(traceFilePath)) {
1851
+ const stats = fs7.statSync(traceFilePath);
1880
1852
  console.log(
1881
1853
  `::notice title=AI Trace Saved::OpenTelemetry trace file size: ${stats.size} bytes`
1882
1854
  );
@@ -1887,7 +1859,7 @@ ${"=".repeat(60)}
1887
1859
  log(`\u{1F4CA} Trace saved to: ${traceFilePath}`);
1888
1860
  }
1889
1861
  } catch (exportError) {
1890
- console.error("\u26A0\uFE0F Warning: Failed to export trace:", exportError);
1862
+ logger.warn(`\u26A0\uFE0F Warning: Failed to export trace: ${exportError}`);
1891
1863
  }
1892
1864
  }
1893
1865
  if (_checkName) {
@@ -1914,8 +1886,8 @@ ${"=".repeat(60)}
1914
1886
  * Load schema content from schema files or inline definitions
1915
1887
  */
1916
1888
  async loadSchemaContent(schema) {
1917
- const fs5 = __require("fs").promises;
1918
- const path6 = __require("path");
1889
+ const fs7 = __require("fs").promises;
1890
+ const path9 = __require("path");
1919
1891
  if (typeof schema === "object" && schema !== null) {
1920
1892
  log("\u{1F4CB} Using inline schema object from configuration");
1921
1893
  return JSON.stringify(schema);
@@ -1928,14 +1900,14 @@ ${"=".repeat(60)}
1928
1900
  }
1929
1901
  } catch {
1930
1902
  }
1931
- if ((schema.startsWith("./") || schema.includes(".json")) && !path6.isAbsolute(schema)) {
1903
+ if ((schema.startsWith("./") || schema.includes(".json")) && !path9.isAbsolute(schema)) {
1932
1904
  if (schema.includes("..") || schema.includes("\0")) {
1933
1905
  throw new Error("Invalid schema path: path traversal not allowed");
1934
1906
  }
1935
1907
  try {
1936
- const schemaPath2 = path6.resolve(process.cwd(), schema);
1908
+ const schemaPath2 = path9.resolve(process.cwd(), schema);
1937
1909
  log(`\u{1F4CB} Loading custom schema from file: ${schemaPath2}`);
1938
- const schemaContent = await fs5.readFile(schemaPath2, "utf-8");
1910
+ const schemaContent = await fs7.readFile(schemaPath2, "utf-8");
1939
1911
  return schemaContent.trim();
1940
1912
  } catch (error) {
1941
1913
  throw new Error(
@@ -1947,9 +1919,9 @@ ${"=".repeat(60)}
1947
1919
  if (!sanitizedSchemaName || sanitizedSchemaName !== schema) {
1948
1920
  throw new Error("Invalid schema name");
1949
1921
  }
1950
- const schemaPath = path6.join(process.cwd(), "output", sanitizedSchemaName, "schema.json");
1922
+ const schemaPath = path9.join(process.cwd(), "output", sanitizedSchemaName, "schema.json");
1951
1923
  try {
1952
- const schemaContent = await fs5.readFile(schemaPath, "utf-8");
1924
+ const schemaContent = await fs7.readFile(schemaPath, "utf-8");
1953
1925
  return schemaContent.trim();
1954
1926
  } catch (error) {
1955
1927
  throw new Error(
@@ -2059,6 +2031,25 @@ ${"=".repeat(60)}
2059
2031
  }
2060
2032
  }
2061
2033
  const isCustomSchema = _schema === "custom" || _schema && (_schema.startsWith("./") || _schema.endsWith(".json")) || _schema && _schema !== "code-review" && !_schema.includes("output/");
2034
+ const _debugSchemaLogging = this.config.debug === true || process.env.VISOR_DEBUG_AI_SESSIONS === "true";
2035
+ if (_debugSchemaLogging) {
2036
+ const details = {
2037
+ schema: _schema,
2038
+ isCustomSchema,
2039
+ isCustomLiteral: _schema === "custom",
2040
+ startsWithDotSlash: typeof _schema === "string" ? _schema.startsWith("./") : false,
2041
+ endsWithJson: typeof _schema === "string" ? _schema.endsWith(".json") : false,
2042
+ notCodeReview: _schema !== "code-review",
2043
+ noOutputPrefix: typeof _schema === "string" ? !_schema.includes("output/") : false
2044
+ };
2045
+ try {
2046
+ log(`\u{1F50D} Schema detection: ${JSON.stringify(details)}`);
2047
+ } catch {
2048
+ log(
2049
+ `\u{1F50D} Schema detection: _schema="${String(_schema)}", isCustomSchema=${isCustomSchema}`
2050
+ );
2051
+ }
2052
+ }
2062
2053
  if (isCustomSchema) {
2063
2054
  log("\u{1F4CB} Custom schema detected - preserving all fields from parsed JSON");
2064
2055
  log(`\u{1F4CA} Schema: ${_schema}`);
@@ -2104,33 +2095,39 @@ ${"=".repeat(60)}
2104
2095
  log("\u2705 Successfully created ReviewSummary");
2105
2096
  return result;
2106
2097
  } catch (error) {
2107
- console.error("\u274C Failed to parse AI response:", error);
2108
- console.error("\u{1F4C4} FULL RAW RESPONSE:");
2109
- console.error("=".repeat(80));
2110
- console.error(response);
2111
- console.error("=".repeat(80));
2112
- console.error(`\u{1F4CF} Response length: ${response.length} characters`);
2113
- if (error instanceof SyntaxError) {
2114
- console.error("\u{1F50D} JSON parsing error - the response may not be valid JSON");
2115
- console.error("\u{1F50D} Error details:", error.message);
2116
- const errorMatch = error.message.match(/position (\d+)/);
2117
- if (errorMatch) {
2118
- const position = parseInt(errorMatch[1]);
2119
- console.error(`\u{1F50D} Error at position ${position}:`);
2120
- const start = Math.max(0, position - 50);
2121
- const end = Math.min(response.length, position + 50);
2122
- console.error(`\u{1F50D} Context: "${response.substring(start, end)}"`);
2123
- console.error(`\u{1F50D} Response beginning: "${response.substring(0, 100)}"`);
2124
- }
2125
- if (response.includes("I cannot")) {
2126
- console.error("\u{1F50D} Response appears to be a refusal/explanation rather than JSON");
2127
- }
2128
- if (response.includes("```")) {
2129
- console.error("\u{1F50D} Response appears to contain markdown code blocks");
2130
- }
2131
- if (response.startsWith("<")) {
2132
- console.error("\u{1F50D} Response appears to start with XML/HTML");
2098
+ const detailed = this.config.debug === true || process.env.VISOR_DEBUG_AI_SESSIONS === "true";
2099
+ const message = error instanceof Error ? error.message : String(error);
2100
+ if (detailed) {
2101
+ logger.debug(`\u274C Failed to parse AI response: ${message}`);
2102
+ logger.debug("\u{1F4C4} FULL RAW RESPONSE:");
2103
+ logger.debug("=".repeat(80));
2104
+ logger.debug(response);
2105
+ logger.debug("=".repeat(80));
2106
+ logger.debug(`\u{1F4CF} Response length: ${response.length} characters`);
2107
+ if (error instanceof SyntaxError) {
2108
+ logger.debug("\u{1F50D} JSON parsing error - the response may not be valid JSON");
2109
+ logger.debug(`\u{1F50D} Error details: ${error.message}`);
2110
+ const errorMatch = error.message.match(/position (\d+)/);
2111
+ if (errorMatch) {
2112
+ const position = parseInt(errorMatch[1]);
2113
+ logger.debug(`\u{1F50D} Error at position ${position}:`);
2114
+ const start = Math.max(0, position - 50);
2115
+ const end = Math.min(response.length, position + 50);
2116
+ logger.debug(`\u{1F50D} Context: "${response.substring(start, end)}"`);
2117
+ logger.debug(`\u{1F50D} Response beginning: "${response.substring(0, 100)}"`);
2118
+ }
2119
+ if (response.includes("I cannot")) {
2120
+ logger.debug("\u{1F50D} Response appears to be a refusal/explanation rather than JSON");
2121
+ }
2122
+ if (response.includes("```")) {
2123
+ logger.debug("\u{1F50D} Response appears to contain markdown code blocks");
2124
+ }
2125
+ if (response.startsWith("<")) {
2126
+ logger.debug("\u{1F50D} Response appears to start with XML/HTML");
2127
+ }
2133
2128
  }
2129
+ } else {
2130
+ logger.error(`\u274C Failed to parse AI response: ${message}`);
2134
2131
  }
2135
2132
  throw new Error(
2136
2133
  `Invalid AI response format: ${error instanceof Error ? error.message : "Unknown error"}`
@@ -2195,7 +2192,7 @@ ${"=".repeat(60)}
2195
2192
  * Generate mock response for testing
2196
2193
  */
2197
2194
  async generateMockResponse(_prompt) {
2198
- await new Promise((resolve) => setTimeout(resolve, 500));
2195
+ await new Promise((resolve4) => setTimeout(resolve4, 500));
2199
2196
  const mockResponse = {
2200
2197
  content: JSON.stringify({
2201
2198
  issues: [
@@ -2277,7 +2274,7 @@ var PRReviewer = class {
2277
2274
  async reviewPR(owner, repo, prNumber, prInfo, options = {}) {
2278
2275
  const { debug = false, config, checks } = options;
2279
2276
  if (config && checks && checks.length > 0) {
2280
- const { CheckExecutionEngine: CheckExecutionEngine2 } = await import("./check-execution-engine-W5FLIWZL.mjs");
2277
+ const { CheckExecutionEngine: CheckExecutionEngine2 } = await import("./check-execution-engine-F3662LY7.mjs");
2281
2278
  const engine = new CheckExecutionEngine2();
2282
2279
  const { results } = await engine.executeGroupedChecks(
2283
2280
  prInfo,
@@ -2308,15 +2305,15 @@ var PRReviewer = class {
2308
2305
  if (["code-review", "overview", "plain", "text"].includes(schema)) {
2309
2306
  return true;
2310
2307
  }
2311
- const fs5 = __require("fs").promises;
2312
- const path6 = __require("path");
2308
+ const fs7 = __require("fs").promises;
2309
+ const path9 = __require("path");
2313
2310
  const sanitizedSchemaName = schema.replace(/[^a-zA-Z0-9-]/g, "");
2314
2311
  if (!sanitizedSchemaName || sanitizedSchemaName !== schema) {
2315
2312
  return false;
2316
2313
  }
2317
- const schemaPath = path6.join(process.cwd(), "output", sanitizedSchemaName, "schema.json");
2314
+ const schemaPath = path9.join(process.cwd(), "output", sanitizedSchemaName, "schema.json");
2318
2315
  try {
2319
- const schemaContent = await fs5.readFile(schemaPath, "utf-8");
2316
+ const schemaContent = await fs7.readFile(schemaPath, "utf-8");
2320
2317
  const schemaObj = JSON.parse(schemaContent);
2321
2318
  const properties = schemaObj.properties;
2322
2319
  return !!(properties && "text" in properties);
@@ -2382,16 +2379,16 @@ var PRReviewer = class {
2382
2379
  }
2383
2380
  }
2384
2381
  async formatGroupComment(checkResults, _options, _githubContext) {
2385
- const normalize = (s) => s.replace(/\\n/g, "\n");
2382
+ const normalize2 = (s) => s.replace(/\\n/g, "\n");
2386
2383
  const checkContents = checkResults.map((result) => {
2387
2384
  const trimmed = result.content?.trim();
2388
- if (trimmed) return normalize(trimmed);
2385
+ if (trimmed) return normalize2(trimmed);
2389
2386
  const out = result.output;
2390
2387
  if (out) {
2391
- if (typeof out === "string" && out.trim()) return normalize(out.trim());
2388
+ if (typeof out === "string" && out.trim()) return normalize2(out.trim());
2392
2389
  if (typeof out === "object") {
2393
2390
  const txt = out.text || out.response || out.message;
2394
- if (typeof txt === "string" && txt.trim()) return normalize(txt.trim());
2391
+ if (typeof txt === "string" && txt.trim()) return normalize2(txt.trim());
2395
2392
  }
2396
2393
  }
2397
2394
  return "";
@@ -2487,15 +2484,15 @@ var PRReviewer = class {
2487
2484
  }
2488
2485
  saveDebugArtifact(debug) {
2489
2486
  try {
2490
- const fs5 = __require("fs");
2491
- const path6 = __require("path");
2492
- const debugDir = path6.join(process.cwd(), "debug-artifacts");
2493
- if (!fs5.existsSync(debugDir)) {
2494
- fs5.mkdirSync(debugDir, { recursive: true });
2487
+ const fs7 = __require("fs");
2488
+ const path9 = __require("path");
2489
+ const debugDir = path9.join(process.cwd(), "debug-artifacts");
2490
+ if (!fs7.existsSync(debugDir)) {
2491
+ fs7.mkdirSync(debugDir, { recursive: true });
2495
2492
  }
2496
2493
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
2497
2494
  const filename = `visor-debug-${timestamp}.md`;
2498
- const filepath = path6.join(debugDir, filename);
2495
+ const filepath = path9.join(debugDir, filename);
2499
2496
  const content = [
2500
2497
  `# Visor Debug Information`,
2501
2498
  ``,
@@ -2516,7 +2513,7 @@ var PRReviewer = class {
2516
2513
  debug.rawResponse,
2517
2514
  "```"
2518
2515
  ].join("\n");
2519
- fs5.writeFileSync(filepath, content, "utf8");
2516
+ fs7.writeFileSync(filepath, content, "utf8");
2520
2517
  return filename;
2521
2518
  } catch (error) {
2522
2519
  console.error("Failed to save debug artifact:", error);
@@ -2527,15 +2524,90 @@ var PRReviewer = class {
2527
2524
 
2528
2525
  // src/git-repository-analyzer.ts
2529
2526
  import { simpleGit } from "simple-git";
2530
- import * as path2 from "path";
2527
+ import * as path3 from "path";
2528
+ import * as fs2 from "fs";
2529
+
2530
+ // src/utils/file-exclusion.ts
2531
+ import ignore from "ignore";
2531
2532
  import * as fs from "fs";
2533
+ import * as path2 from "path";
2534
+ var DEFAULT_EXCLUSION_PATTERNS = [
2535
+ "dist/",
2536
+ "build/",
2537
+ ".next/",
2538
+ "out/",
2539
+ "node_modules/",
2540
+ "coverage/",
2541
+ ".turbo/",
2542
+ "bundled/"
2543
+ ];
2544
+ var FileExclusionHelper = class {
2545
+ gitignore = null;
2546
+ workingDirectory;
2547
+ /**
2548
+ * @param workingDirectory - Directory to search for .gitignore
2549
+ * @param additionalPatterns - Additional patterns to include (optional, defaults to common build artifacts)
2550
+ */
2551
+ constructor(workingDirectory = process.cwd(), additionalPatterns = DEFAULT_EXCLUSION_PATTERNS) {
2552
+ const normalizedPath = path2.resolve(workingDirectory);
2553
+ if (normalizedPath.includes("\0")) {
2554
+ throw new Error("Invalid workingDirectory: contains null bytes");
2555
+ }
2556
+ this.workingDirectory = normalizedPath;
2557
+ this.loadGitignore(additionalPatterns);
2558
+ }
2559
+ /**
2560
+ * Load .gitignore patterns from the working directory (called once in constructor)
2561
+ * @param additionalPatterns - Additional patterns to add to gitignore rules
2562
+ */
2563
+ loadGitignore(additionalPatterns) {
2564
+ const gitignorePath = path2.resolve(this.workingDirectory, ".gitignore");
2565
+ const resolvedWorkingDir = path2.resolve(this.workingDirectory);
2566
+ try {
2567
+ const relativePath = path2.relative(resolvedWorkingDir, gitignorePath);
2568
+ if (relativePath.startsWith("..") || path2.isAbsolute(relativePath)) {
2569
+ throw new Error("Invalid gitignore path: path traversal detected");
2570
+ }
2571
+ if (relativePath !== ".gitignore") {
2572
+ throw new Error("Invalid gitignore path: must be .gitignore in working directory");
2573
+ }
2574
+ this.gitignore = ignore();
2575
+ if (additionalPatterns && additionalPatterns.length > 0) {
2576
+ this.gitignore.add(additionalPatterns);
2577
+ }
2578
+ if (fs.existsSync(gitignorePath)) {
2579
+ const rawContent = fs.readFileSync(gitignorePath, "utf8");
2580
+ 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();
2581
+ this.gitignore.add(gitignoreContent);
2582
+ console.error("\u2705 Loaded .gitignore patterns for file filtering");
2583
+ } else if (additionalPatterns && additionalPatterns.length > 0) {
2584
+ console.error("\u26A0\uFE0F No .gitignore found, using default exclusion patterns");
2585
+ }
2586
+ } catch (error) {
2587
+ console.warn("\u26A0\uFE0F Failed to load .gitignore:", error instanceof Error ? error.message : error);
2588
+ }
2589
+ }
2590
+ /**
2591
+ * Check if a file should be excluded based on .gitignore patterns
2592
+ */
2593
+ shouldExcludeFile(filename) {
2594
+ if (this.gitignore) {
2595
+ return this.gitignore.ignores(filename);
2596
+ }
2597
+ return false;
2598
+ }
2599
+ };
2600
+
2601
+ // src/git-repository-analyzer.ts
2532
2602
  var MAX_PATCH_SIZE = 50 * 1024;
2533
2603
  var GitRepositoryAnalyzer = class {
2534
2604
  git;
2535
2605
  cwd;
2606
+ fileExclusionHelper;
2536
2607
  constructor(workingDirectory = process.cwd()) {
2537
2608
  this.cwd = workingDirectory;
2538
2609
  this.git = simpleGit(workingDirectory);
2610
+ this.fileExclusionHelper = new FileExclusionHelper(workingDirectory);
2539
2611
  }
2540
2612
  /**
2541
2613
  * Analyze the current git repository state and return data compatible with PRInfo interface
@@ -2670,34 +2742,6 @@ ${file.patch}`).join("\n\n");
2670
2742
  return "main";
2671
2743
  }
2672
2744
  }
2673
- /**
2674
- * Check if a file should be excluded from analysis
2675
- * This includes:
2676
- * - Files in .gitignore (even if force-added)
2677
- * - Built/generated files (dist/, build/, .next/, etc.)
2678
- */
2679
- async shouldExcludeFile(filename) {
2680
- const excludePatterns = [
2681
- /^dist\//,
2682
- /^build\//,
2683
- /^\.next\//,
2684
- /^out\//,
2685
- /^node_modules\//,
2686
- /^coverage\//,
2687
- /^\.turbo\//
2688
- ];
2689
- for (const pattern of excludePatterns) {
2690
- if (pattern.test(filename)) {
2691
- return true;
2692
- }
2693
- }
2694
- try {
2695
- const result = await this.git.raw(["check-ignore", filename]);
2696
- return result.trim().length > 0;
2697
- } catch {
2698
- return false;
2699
- }
2700
- }
2701
2745
  /**
2702
2746
  * Truncate a patch if it exceeds MAX_PATCH_SIZE
2703
2747
  */
@@ -2738,11 +2782,11 @@ ${file.patch}`).join("\n\n");
2738
2782
  }))
2739
2783
  ];
2740
2784
  for (const { file, status: status2 } of fileChanges) {
2741
- if (await this.shouldExcludeFile(file)) {
2785
+ if (this.fileExclusionHelper.shouldExcludeFile(file)) {
2742
2786
  console.error(`\u23ED\uFE0F Skipping excluded file: ${file}`);
2743
2787
  continue;
2744
2788
  }
2745
- const filePath = path2.join(this.cwd, file);
2789
+ const filePath = path3.join(this.cwd, file);
2746
2790
  const fileChange = await this.analyzeFileChange(file, status2, filePath, includeContext);
2747
2791
  changes.push(fileChange);
2748
2792
  }
@@ -2763,7 +2807,7 @@ ${file.patch}`).join("\n\n");
2763
2807
  return [];
2764
2808
  }
2765
2809
  for (const file of diffSummary.files) {
2766
- if (await this.shouldExcludeFile(file.file)) {
2810
+ if (this.fileExclusionHelper.shouldExcludeFile(file.file)) {
2767
2811
  console.error(`\u23ED\uFE0F Skipping excluded file: ${file.file}`);
2768
2812
  continue;
2769
2813
  }
@@ -2818,7 +2862,7 @@ ${file.patch}`).join("\n\n");
2818
2862
  let content;
2819
2863
  let truncated = false;
2820
2864
  try {
2821
- if (includeContext && status !== "added" && fs.existsSync(filePath)) {
2865
+ if (includeContext && status !== "added" && fs2.existsSync(filePath)) {
2822
2866
  const diff = await this.git.diff(["--", filename]).catch(() => "");
2823
2867
  if (diff) {
2824
2868
  const result = this.truncatePatch(diff, filename);
@@ -2828,7 +2872,7 @@ ${file.patch}`).join("\n\n");
2828
2872
  additions = lines.filter((line) => line.startsWith("+")).length;
2829
2873
  deletions = lines.filter((line) => line.startsWith("-")).length;
2830
2874
  }
2831
- } else if (status !== "added" && fs.existsSync(filePath)) {
2875
+ } else if (status !== "added" && fs2.existsSync(filePath)) {
2832
2876
  const diff = await this.git.diff(["--", filename]).catch(() => "");
2833
2877
  if (diff) {
2834
2878
  const lines = diff.split("\n");
@@ -2836,17 +2880,17 @@ ${file.patch}`).join("\n\n");
2836
2880
  deletions = lines.filter((line) => line.startsWith("-")).length;
2837
2881
  }
2838
2882
  }
2839
- if (status === "added" && fs.existsSync(filePath)) {
2883
+ if (status === "added" && fs2.existsSync(filePath)) {
2840
2884
  try {
2841
- const stats = fs.statSync(filePath);
2885
+ const stats = fs2.statSync(filePath);
2842
2886
  if (stats.isFile() && stats.size < 1024 * 1024) {
2843
2887
  if (includeContext) {
2844
- content = fs.readFileSync(filePath, "utf8");
2888
+ content = fs2.readFileSync(filePath, "utf8");
2845
2889
  const result = this.truncatePatch(content, filename);
2846
2890
  patch = result.patch;
2847
2891
  truncated = result.truncated;
2848
2892
  }
2849
- const fileContent = includeContext ? content : fs.readFileSync(filePath, "utf8");
2893
+ const fileContent = includeContext ? content : fs2.readFileSync(filePath, "utf8");
2850
2894
  additions = fileContent.split("\n").length;
2851
2895
  }
2852
2896
  } catch {
@@ -2929,11 +2973,14 @@ ${file.patch}`).join("\n\n");
2929
2973
  };
2930
2974
 
2931
2975
  // src/pr-analyzer.ts
2976
+ import * as path4 from "path";
2932
2977
  var PRAnalyzer = class {
2933
- constructor(octokit, maxRetries = 3) {
2978
+ constructor(octokit, maxRetries = 3, workingDirectory = path4.resolve(process.cwd())) {
2934
2979
  this.octokit = octokit;
2935
2980
  this.maxRetries = maxRetries;
2981
+ this.fileExclusionHelper = new FileExclusionHelper(workingDirectory);
2936
2982
  }
2983
+ fileExclusionHelper;
2937
2984
  /**
2938
2985
  * Fetch commit diff for incremental analysis
2939
2986
  */
@@ -2989,14 +3036,25 @@ ${file.patch}`).join("\n\n");
2989
3036
  const authorAssociation = pr.author_association && typeof pr.author_association === "string" ? pr.author_association : void 0;
2990
3037
  const base = pr.base && typeof pr.base === "object" && pr.base.ref ? typeof pr.base.ref === "string" ? pr.base.ref : String(pr.base.ref) : "main";
2991
3038
  const head = pr.head && typeof pr.head === "object" && pr.head.ref ? typeof pr.head.ref === "string" ? pr.head.ref : String(pr.head.ref) : "feature";
2992
- const validFiles = files ? files.filter((file) => file && typeof file === "object" && file.filename).map((file) => ({
3039
+ let skippedCount = 0;
3040
+ const validFiles = files ? files.filter((file) => file && typeof file === "object" && file.filename).filter((file) => {
3041
+ const filename = typeof file.filename === "string" ? file.filename : String(file.filename || "unknown");
3042
+ if (!filename || this.fileExclusionHelper.shouldExcludeFile(filename)) {
3043
+ skippedCount++;
3044
+ return false;
3045
+ }
3046
+ return true;
3047
+ }).map((file) => ({
2993
3048
  filename: typeof file.filename === "string" ? file.filename : String(file.filename || "unknown"),
2994
3049
  additions: typeof file.additions === "number" ? Math.max(0, file.additions) : 0,
2995
3050
  deletions: typeof file.deletions === "number" ? Math.max(0, file.deletions) : 0,
2996
3051
  changes: typeof file.changes === "number" ? Math.max(0, file.changes) : 0,
2997
3052
  patch: typeof file.patch === "string" ? file.patch : void 0,
2998
3053
  status: ["added", "removed", "modified", "renamed"].includes(file.status) ? file.status : "modified"
2999
- })).filter((file) => file.filename.length > 0) : [];
3054
+ })) : [];
3055
+ if (skippedCount > 0) {
3056
+ console.log(`\u23ED\uFE0F Skipped ${skippedCount} excluded file(s)`);
3057
+ }
3000
3058
  const prInfo = {
3001
3059
  number: typeof pr.number === "number" ? pr.number : parseInt(String(pr.number || 1), 10),
3002
3060
  title,
@@ -3075,7 +3133,7 @@ ${file.patch}`).join("\n\n");
3075
3133
  }
3076
3134
  if (this.isRetryableError(error)) {
3077
3135
  const delay = Math.min(1e3 * Math.pow(2, attempt), 5e3);
3078
- await new Promise((resolve) => setTimeout(resolve, delay));
3136
+ await new Promise((resolve4) => setTimeout(resolve4, delay));
3079
3137
  } else {
3080
3138
  throw error;
3081
3139
  }
@@ -3202,8 +3260,8 @@ var EnvironmentResolver = class {
3202
3260
  };
3203
3261
 
3204
3262
  // src/issue-filter.ts
3205
- import * as fs2 from "fs";
3206
- import * as path3 from "path";
3263
+ import * as fs3 from "fs";
3264
+ import * as path5 from "path";
3207
3265
  var IssueFilter = class {
3208
3266
  fileCache = /* @__PURE__ */ new Map();
3209
3267
  suppressionEnabled;
@@ -3271,17 +3329,17 @@ var IssueFilter = class {
3271
3329
  return this.fileCache.get(filePath);
3272
3330
  }
3273
3331
  try {
3274
- const resolvedPath = path3.isAbsolute(filePath) ? filePath : path3.join(workingDir, filePath);
3275
- if (!fs2.existsSync(resolvedPath)) {
3276
- if (fs2.existsSync(filePath)) {
3277
- const content2 = fs2.readFileSync(filePath, "utf8");
3332
+ const resolvedPath = path5.isAbsolute(filePath) ? filePath : path5.join(workingDir, filePath);
3333
+ if (!fs3.existsSync(resolvedPath)) {
3334
+ if (fs3.existsSync(filePath)) {
3335
+ const content2 = fs3.readFileSync(filePath, "utf8");
3278
3336
  const lines2 = content2.split("\n");
3279
3337
  this.fileCache.set(filePath, lines2);
3280
3338
  return lines2;
3281
3339
  }
3282
3340
  return null;
3283
3341
  }
3284
- const content = fs2.readFileSync(resolvedPath, "utf8");
3342
+ const content = fs3.readFileSync(resolvedPath, "utf8");
3285
3343
  const lines = content.split("\n");
3286
3344
  this.fileCache.set(filePath, lines);
3287
3345
  return lines;
@@ -3298,8 +3356,135 @@ var IssueFilter = class {
3298
3356
  };
3299
3357
 
3300
3358
  // src/providers/ai-check-provider.ts
3301
- import fs3 from "fs/promises";
3302
- import path4 from "path";
3359
+ import fs4 from "fs/promises";
3360
+ import path6 from "path";
3361
+ import { trace, context as otContext } from "@opentelemetry/api";
3362
+
3363
+ // src/telemetry/state-capture.ts
3364
+ var MAX_ATTRIBUTE_LENGTH = 1e4;
3365
+ var MAX_ARRAY_ITEMS = 100;
3366
+ function safeSerialize(value, maxLength = MAX_ATTRIBUTE_LENGTH) {
3367
+ try {
3368
+ if (value === void 0 || value === null) return String(value);
3369
+ const seen = /* @__PURE__ */ new WeakSet();
3370
+ const json = JSON.stringify(value, (key, val) => {
3371
+ if (typeof val === "object" && val !== null) {
3372
+ if (seen.has(val)) return "[Circular]";
3373
+ seen.add(val);
3374
+ }
3375
+ if (typeof val === "string" && val.length > maxLength) {
3376
+ return val.substring(0, maxLength) + "...[truncated]";
3377
+ }
3378
+ return val;
3379
+ });
3380
+ if (json.length > maxLength) {
3381
+ return json.substring(0, maxLength) + "...[truncated]";
3382
+ }
3383
+ return json;
3384
+ } catch (err) {
3385
+ return `[Error serializing: ${err instanceof Error ? err.message : String(err)}]`;
3386
+ }
3387
+ }
3388
+ function captureCheckInputContext(span, context) {
3389
+ try {
3390
+ const keys = Object.keys(context);
3391
+ span.setAttribute("visor.check.input.keys", keys.join(","));
3392
+ span.setAttribute("visor.check.input.count", keys.length);
3393
+ span.setAttribute("visor.check.input.context", safeSerialize(context));
3394
+ if (context.pr) {
3395
+ span.setAttribute("visor.check.input.pr", safeSerialize(context.pr, 1e3));
3396
+ }
3397
+ if (context.outputs) {
3398
+ span.setAttribute("visor.check.input.outputs", safeSerialize(context.outputs, 5e3));
3399
+ }
3400
+ if (context.env) {
3401
+ span.setAttribute("visor.check.input.env_keys", Object.keys(context.env).join(","));
3402
+ }
3403
+ } catch (err) {
3404
+ try {
3405
+ span.setAttribute("visor.check.input.error", String(err));
3406
+ } catch {
3407
+ }
3408
+ }
3409
+ }
3410
+ function captureCheckOutput(span, output) {
3411
+ try {
3412
+ span.setAttribute("visor.check.output.type", typeof output);
3413
+ if (Array.isArray(output)) {
3414
+ span.setAttribute("visor.check.output.length", output.length);
3415
+ const preview = output.slice(0, 10);
3416
+ span.setAttribute("visor.check.output.preview", safeSerialize(preview, 2e3));
3417
+ }
3418
+ span.setAttribute("visor.check.output", safeSerialize(output));
3419
+ } catch (err) {
3420
+ try {
3421
+ span.setAttribute("visor.check.output.error", String(err));
3422
+ } catch {
3423
+ }
3424
+ }
3425
+ }
3426
+ function captureForEachState(span, items, index, currentItem) {
3427
+ try {
3428
+ span.setAttribute("visor.foreach.total", items.length);
3429
+ span.setAttribute("visor.foreach.index", index);
3430
+ span.setAttribute("visor.foreach.current_item", safeSerialize(currentItem, 500));
3431
+ if (items.length <= MAX_ARRAY_ITEMS) {
3432
+ span.setAttribute("visor.foreach.items", safeSerialize(items));
3433
+ } else {
3434
+ span.setAttribute(
3435
+ "visor.foreach.items.preview",
3436
+ safeSerialize(items.slice(0, MAX_ARRAY_ITEMS))
3437
+ );
3438
+ span.setAttribute("visor.foreach.items.truncated", true);
3439
+ }
3440
+ } catch (err) {
3441
+ span.setAttribute("visor.foreach.error", String(err));
3442
+ }
3443
+ }
3444
+ function captureTransformJS(span, code, input, output) {
3445
+ try {
3446
+ const codePreview = code.length > 2e3 ? code.substring(0, 2e3) + "...[truncated]" : code;
3447
+ span.setAttribute("visor.transform.code", codePreview);
3448
+ span.setAttribute("visor.transform.code.length", code.length);
3449
+ span.setAttribute("visor.transform.input", safeSerialize(input, 2e3));
3450
+ span.setAttribute("visor.transform.output", safeSerialize(output, 2e3));
3451
+ } catch (err) {
3452
+ span.setAttribute("visor.transform.error", String(err));
3453
+ }
3454
+ }
3455
+ function captureProviderCall(span, providerType, request, response) {
3456
+ try {
3457
+ span.setAttribute("visor.provider.type", providerType);
3458
+ if (request.model) span.setAttribute("visor.provider.request.model", String(request.model));
3459
+ if (request.prompt) {
3460
+ span.setAttribute("visor.provider.request.prompt.length", request.prompt.length);
3461
+ span.setAttribute("visor.provider.request.prompt.preview", request.prompt.substring(0, 500));
3462
+ }
3463
+ if (response.content) {
3464
+ span.setAttribute("visor.provider.response.length", response.content.length);
3465
+ span.setAttribute("visor.provider.response.preview", response.content.substring(0, 500));
3466
+ }
3467
+ if (response.tokens) {
3468
+ span.setAttribute("visor.provider.response.tokens", response.tokens);
3469
+ }
3470
+ } catch (err) {
3471
+ span.setAttribute("visor.provider.error", String(err));
3472
+ }
3473
+ }
3474
+ function captureStateSnapshot(span, checkId, outputs, memory) {
3475
+ try {
3476
+ span.addEvent("state.snapshot", {
3477
+ "visor.snapshot.check_id": checkId,
3478
+ "visor.snapshot.outputs": safeSerialize(outputs, 5e3),
3479
+ "visor.snapshot.memory": safeSerialize(memory, 5e3),
3480
+ "visor.snapshot.timestamp": (/* @__PURE__ */ new Date()).toISOString()
3481
+ });
3482
+ } catch (err) {
3483
+ span.setAttribute("visor.snapshot.error", String(err));
3484
+ }
3485
+ }
3486
+
3487
+ // src/providers/ai-check-provider.ts
3303
3488
  var AICheckProvider = class extends CheckProvider {
3304
3489
  aiReviewService;
3305
3490
  liquidEngine;
@@ -3416,7 +3601,7 @@ var AICheckProvider = class extends CheckProvider {
3416
3601
  const hasFileExtension = /\.[a-zA-Z0-9]{1,10}$/i.test(str);
3417
3602
  const hasPathSeparators = /[\/\\]/.test(str);
3418
3603
  const isRelativePath = /^\.{1,2}\//.test(str);
3419
- const isAbsolutePath = path4.isAbsolute(str);
3604
+ const isAbsolutePath = path6.isAbsolute(str);
3420
3605
  const hasTypicalFileChars = /^[a-zA-Z0-9._\-\/\\:~]+$/.test(str);
3421
3606
  if (!(hasFileExtension || isRelativePath || isAbsolutePath || hasPathSeparators)) {
3422
3607
  return false;
@@ -3426,14 +3611,14 @@ var AICheckProvider = class extends CheckProvider {
3426
3611
  }
3427
3612
  try {
3428
3613
  let resolvedPath;
3429
- if (path4.isAbsolute(str)) {
3430
- resolvedPath = path4.normalize(str);
3614
+ if (path6.isAbsolute(str)) {
3615
+ resolvedPath = path6.normalize(str);
3431
3616
  } else {
3432
- resolvedPath = path4.resolve(process.cwd(), str);
3617
+ resolvedPath = path6.resolve(process.cwd(), str);
3433
3618
  }
3434
- const fs5 = __require("fs").promises;
3619
+ const fs7 = __require("fs").promises;
3435
3620
  try {
3436
- const stat = await fs5.stat(resolvedPath);
3621
+ const stat = await fs7.stat(resolvedPath);
3437
3622
  return stat.isFile();
3438
3623
  } catch {
3439
3624
  return hasFileExtension && (isRelativePath || isAbsolutePath || hasPathSeparators);
@@ -3450,14 +3635,14 @@ var AICheckProvider = class extends CheckProvider {
3450
3635
  throw new Error("Prompt file must have .liquid extension");
3451
3636
  }
3452
3637
  let resolvedPath;
3453
- if (path4.isAbsolute(promptPath)) {
3638
+ if (path6.isAbsolute(promptPath)) {
3454
3639
  resolvedPath = promptPath;
3455
3640
  } else {
3456
- resolvedPath = path4.resolve(process.cwd(), promptPath);
3641
+ resolvedPath = path6.resolve(process.cwd(), promptPath);
3457
3642
  }
3458
- if (!path4.isAbsolute(promptPath)) {
3459
- const normalizedPath = path4.normalize(resolvedPath);
3460
- const currentDir = path4.resolve(process.cwd());
3643
+ if (!path6.isAbsolute(promptPath)) {
3644
+ const normalizedPath = path6.normalize(resolvedPath);
3645
+ const currentDir = path6.resolve(process.cwd());
3461
3646
  if (!normalizedPath.startsWith(currentDir)) {
3462
3647
  throw new Error("Invalid prompt file path: path traversal detected");
3463
3648
  }
@@ -3466,7 +3651,7 @@ var AICheckProvider = class extends CheckProvider {
3466
3651
  throw new Error("Invalid prompt file path: path traversal detected");
3467
3652
  }
3468
3653
  try {
3469
- const promptContent = await fs3.readFile(resolvedPath, "utf-8");
3654
+ const promptContent = await fs4.readFile(resolvedPath, "utf-8");
3470
3655
  return promptContent;
3471
3656
  } catch (error) {
3472
3657
  throw new Error(
@@ -3639,6 +3824,40 @@ var AICheckProvider = class extends CheckProvider {
3639
3824
  );
3640
3825
  }
3641
3826
  }
3827
+ const templateContext = {
3828
+ pr: {
3829
+ number: prInfo.number,
3830
+ title: prInfo.title,
3831
+ author: prInfo.author,
3832
+ branch: prInfo.head,
3833
+ base: prInfo.base
3834
+ },
3835
+ files: prInfo.files,
3836
+ outputs: _dependencyResults ? Object.fromEntries(
3837
+ Array.from(_dependencyResults.entries()).map(([checkName, result]) => [
3838
+ checkName,
3839
+ result.output !== void 0 ? result.output : result
3840
+ ])
3841
+ ) : {}
3842
+ };
3843
+ try {
3844
+ const span = trace.getSpan(otContext.active());
3845
+ if (span) {
3846
+ captureCheckInputContext(span, templateContext);
3847
+ }
3848
+ } catch {
3849
+ }
3850
+ try {
3851
+ const checkId = config.checkName || config.id || "unknown";
3852
+ const ctxJson = JSON.stringify(templateContext);
3853
+ const { emitNdjsonSpanWithEvents: emitNdjsonSpanWithEvents2 } = (init_fallback_ndjson(), __toCommonJS(fallback_ndjson_exports));
3854
+ emitNdjsonSpanWithEvents2(
3855
+ "visor.check",
3856
+ { "visor.check.id": checkId, "visor.check.input.context": ctxJson },
3857
+ []
3858
+ );
3859
+ } catch {
3860
+ }
3642
3861
  const processedPrompt = await this.processPrompt(
3643
3862
  customPrompt,
3644
3863
  prInfo,
@@ -3691,10 +3910,43 @@ var AICheckProvider = class extends CheckProvider {
3691
3910
  const suppressionEnabled = config.suppressionEnabled !== false;
3692
3911
  const issueFilter = new IssueFilter(suppressionEnabled);
3693
3912
  const filteredIssues = issueFilter.filterIssues(result.issues || [], process.cwd());
3694
- return {
3913
+ const finalResult = {
3695
3914
  ...result,
3696
3915
  issues: filteredIssues
3697
3916
  };
3917
+ try {
3918
+ const span = trace.getSpan(otContext.active());
3919
+ if (span) {
3920
+ captureProviderCall(
3921
+ span,
3922
+ "ai",
3923
+ {
3924
+ prompt: processedPrompt.substring(0, 500),
3925
+ // Preview only
3926
+ model: aiConfig.model
3927
+ },
3928
+ {
3929
+ content: JSON.stringify(finalResult).substring(0, 500),
3930
+ tokens: result.usage?.totalTokens
3931
+ }
3932
+ );
3933
+ const outputForSpan = finalResult.output ?? finalResult;
3934
+ captureCheckOutput(span, outputForSpan);
3935
+ }
3936
+ } catch {
3937
+ }
3938
+ try {
3939
+ const checkId = config.checkName || config.id || "unknown";
3940
+ const outJson = JSON.stringify(finalResult.output ?? finalResult);
3941
+ const { emitNdjsonSpanWithEvents: emitNdjsonSpanWithEvents2 } = (init_fallback_ndjson(), __toCommonJS(fallback_ndjson_exports));
3942
+ emitNdjsonSpanWithEvents2(
3943
+ "visor.check",
3944
+ { "visor.check.id": checkId, "visor.check.output": outJson },
3945
+ []
3946
+ );
3947
+ } catch {
3948
+ }
3949
+ return finalResult;
3698
3950
  } catch (error) {
3699
3951
  const errorMessage = error instanceof Error ? error.message : String(error);
3700
3952
  console.error(`\u274C AI Check Provider Error for check: ${errorMessage}`);
@@ -3739,6 +3991,7 @@ var AICheckProvider = class extends CheckProvider {
3739
3991
  };
3740
3992
 
3741
3993
  // src/providers/http-check-provider.ts
3994
+ import { trace as trace2, context as otContext2 } from "@opentelemetry/api";
3742
3995
  var HttpCheckProvider = class extends CheckProvider {
3743
3996
  liquid;
3744
3997
  constructor() {
@@ -3800,6 +4053,13 @@ var HttpCheckProvider = class extends CheckProvider {
3800
4053
  outputs: dependencyResults ? Object.fromEntries(dependencyResults) : {},
3801
4054
  metadata: config.metadata || {}
3802
4055
  };
4056
+ try {
4057
+ const span = trace2.getSpan(otContext2.active());
4058
+ if (span) {
4059
+ captureCheckInputContext(span, templateContext);
4060
+ }
4061
+ } catch {
4062
+ }
3803
4063
  let payload;
3804
4064
  try {
3805
4065
  const renderedBody = await this.liquid.parseAndRender(bodyTemplate, templateContext);
@@ -3822,10 +4082,31 @@ var HttpCheckProvider = class extends CheckProvider {
3822
4082
  const suppressionEnabled = config.suppressionEnabled !== false;
3823
4083
  const issueFilter = new IssueFilter(suppressionEnabled);
3824
4084
  const filteredIssues = issueFilter.filterIssues(result.issues || [], process.cwd());
3825
- return {
4085
+ const finalResult = {
3826
4086
  ...result,
3827
4087
  issues: filteredIssues
3828
4088
  };
4089
+ try {
4090
+ const span = trace2.getSpan(otContext2.active());
4091
+ if (span) {
4092
+ captureProviderCall(
4093
+ span,
4094
+ "http",
4095
+ {
4096
+ url,
4097
+ method,
4098
+ body: JSON.stringify(payload).substring(0, 500)
4099
+ },
4100
+ {
4101
+ content: JSON.stringify(response).substring(0, 500)
4102
+ }
4103
+ );
4104
+ const outputForSpan = finalResult.output ?? finalResult;
4105
+ captureCheckOutput(span, outputForSpan);
4106
+ }
4107
+ } catch {
4108
+ }
4109
+ return finalResult;
3829
4110
  } catch (error) {
3830
4111
  return this.createErrorResult(url, error);
3831
4112
  }
@@ -4474,8 +4755,111 @@ var LogCheckProvider = class extends CheckProvider {
4474
4755
  }
4475
4756
  };
4476
4757
 
4477
- // src/providers/github-ops-provider.ts
4758
+ // src/utils/sandbox.ts
4478
4759
  import Sandbox from "@nyariv/sandboxjs";
4760
+ function createSecureSandbox() {
4761
+ const globals = {
4762
+ ...Sandbox.SAFE_GLOBALS,
4763
+ Math,
4764
+ JSON,
4765
+ // Provide console with limited surface. Calls are harmless in CI logs and
4766
+ // help with debugging value_js / transform_js expressions.
4767
+ console: {
4768
+ log: console.log,
4769
+ warn: console.warn,
4770
+ error: console.error
4771
+ }
4772
+ };
4773
+ const prototypeWhitelist = new Map(Sandbox.SAFE_PROTOTYPES);
4774
+ const arrayMethods = /* @__PURE__ */ new Set([
4775
+ "some",
4776
+ "every",
4777
+ "filter",
4778
+ "map",
4779
+ "reduce",
4780
+ "find",
4781
+ "includes",
4782
+ "indexOf",
4783
+ "length",
4784
+ "slice",
4785
+ "concat",
4786
+ "join",
4787
+ "push",
4788
+ "pop",
4789
+ "shift",
4790
+ "unshift",
4791
+ "sort",
4792
+ "reverse",
4793
+ "flat",
4794
+ "flatMap"
4795
+ ]);
4796
+ prototypeWhitelist.set(Array.prototype, arrayMethods);
4797
+ const stringMethods = /* @__PURE__ */ new Set([
4798
+ "toLowerCase",
4799
+ "toUpperCase",
4800
+ "includes",
4801
+ "indexOf",
4802
+ "startsWith",
4803
+ "endsWith",
4804
+ "slice",
4805
+ "substring",
4806
+ "length",
4807
+ "trim",
4808
+ "split",
4809
+ "replace",
4810
+ "match",
4811
+ "padStart",
4812
+ "padEnd"
4813
+ ]);
4814
+ prototypeWhitelist.set(String.prototype, stringMethods);
4815
+ const objectMethods = /* @__PURE__ */ new Set([
4816
+ "hasOwnProperty",
4817
+ "toString",
4818
+ "valueOf",
4819
+ "keys",
4820
+ "values"
4821
+ ]);
4822
+ prototypeWhitelist.set(Object.prototype, objectMethods);
4823
+ return new Sandbox({ globals, prototypeWhitelist });
4824
+ }
4825
+ function compileAndRun(sandbox, userCode, scope, opts = { injectLog: true, wrapFunction: true, logPrefix: "[sandbox]" }) {
4826
+ const inject = opts?.injectLog === true;
4827
+ let safePrefix = String(opts?.logPrefix ?? "[sandbox]");
4828
+ safePrefix = safePrefix.replace(/[\r\n\t\0]/g, "").replace(/[`$\\]/g, "").replace(/\$\{/g, "").slice(0, 64);
4829
+ const header = inject ? `const __lp = ${JSON.stringify(safePrefix)}; const log = (...a) => { try { console.log(__lp, ...a); } catch {} };
4830
+ ` : "";
4831
+ const body = opts.wrapFunction ? `const __fn = () => {
4832
+ ${userCode}
4833
+ };
4834
+ return __fn();
4835
+ ` : `${userCode}`;
4836
+ const code = `${header}${body}`;
4837
+ let exec;
4838
+ try {
4839
+ exec = sandbox.compile(code);
4840
+ } catch (e) {
4841
+ const msg = e instanceof Error ? e.message : String(e);
4842
+ throw new Error(`sandbox_compile_error: ${msg}`);
4843
+ }
4844
+ let out;
4845
+ try {
4846
+ out = exec(scope);
4847
+ } catch (e) {
4848
+ const msg = e instanceof Error ? e.message : String(e);
4849
+ throw new Error(`sandbox_execution_error: ${msg}`);
4850
+ }
4851
+ if (out && typeof out.run === "function") {
4852
+ try {
4853
+ return out.run();
4854
+ } catch (e) {
4855
+ const msg = e instanceof Error ? e.message : String(e);
4856
+ throw new Error(`sandbox_runner_error: ${msg}`);
4857
+ }
4858
+ }
4859
+ return out;
4860
+ }
4861
+
4862
+ // src/providers/github-ops-provider.ts
4479
4863
  var GitHubOpsProvider = class extends CheckProvider {
4480
4864
  sandbox;
4481
4865
  getName() {
@@ -4502,25 +4886,20 @@ var GitHubOpsProvider = class extends CheckProvider {
4502
4886
  }
4503
4887
  async execute(prInfo, config, dependencyResults) {
4504
4888
  const cfg = config;
4505
- let octokit = config.eventContext?.octokit;
4889
+ const octokit = config.eventContext?.octokit;
4506
4890
  if (!octokit) {
4507
- const token = process.env["INPUT_GITHUB-TOKEN"] || process.env["GITHUB_TOKEN"];
4508
- if (!token) {
4509
- return {
4510
- issues: [
4511
- {
4512
- file: "system",
4513
- line: 0,
4514
- ruleId: "github/missing_token",
4515
- message: "No GitHub token available; set GITHUB_TOKEN or pass github-token input for native GitHub operations",
4516
- severity: "error",
4517
- category: "logic"
4518
- }
4519
- ]
4520
- };
4521
- }
4522
- const { Octokit } = await import("@octokit/rest");
4523
- octokit = new Octokit({ auth: token });
4891
+ return {
4892
+ issues: [
4893
+ {
4894
+ file: "system",
4895
+ line: 0,
4896
+ ruleId: "github/missing_octokit",
4897
+ message: "No authenticated Octokit instance available in event context. GitHub operations require proper authentication context.",
4898
+ severity: "error",
4899
+ category: "logic"
4900
+ }
4901
+ ]
4902
+ };
4524
4903
  }
4525
4904
  const repoEnv = process.env.GITHUB_REPOSITORY || "";
4526
4905
  const [owner, repo] = repoEnv.split("/");
@@ -4598,14 +4977,19 @@ var GitHubOpsProvider = class extends CheckProvider {
4598
4977
  if (cfg.value_js && cfg.value_js.trim()) {
4599
4978
  try {
4600
4979
  const sandbox = this.getSecureSandbox();
4601
- const code = `
4602
- const __fn = () => {
4603
- ${cfg.value_js}
4604
- };
4605
- return __fn();
4606
- `;
4607
- const exec = sandbox.compile(code);
4608
- const res = exec({ pr: prInfo, values });
4980
+ const depOutputs = {};
4981
+ if (dependencyResults) {
4982
+ for (const [name, result] of dependencyResults.entries()) {
4983
+ const summary = result;
4984
+ depOutputs[name] = summary.output !== void 0 ? summary.output : summary;
4985
+ }
4986
+ }
4987
+ const res = compileAndRun(
4988
+ sandbox,
4989
+ cfg.value_js,
4990
+ { pr: prInfo, values, outputs: depOutputs },
4991
+ { injectLog: true, wrapFunction: true, logPrefix: "[github:value_js]" }
4992
+ );
4609
4993
  if (typeof res === "string") values = [res];
4610
4994
  else if (Array.isArray(res)) values = res.map((v) => String(v));
4611
4995
  } catch (e) {
@@ -4696,51 +5080,14 @@ ${cfg.value_js}
4696
5080
  */
4697
5081
  getSecureSandbox() {
4698
5082
  if (this.sandbox) return this.sandbox;
4699
- const globals = {
4700
- ...Sandbox.SAFE_GLOBALS,
4701
- Math
4702
- };
4703
- const prototypeWhitelist = new Map(Sandbox.SAFE_PROTOTYPES);
4704
- const arrayMethods = /* @__PURE__ */ new Set([
4705
- "some",
4706
- "every",
4707
- "filter",
4708
- "map",
4709
- "reduce",
4710
- "find",
4711
- "includes",
4712
- "indexOf",
4713
- "length",
4714
- "slice",
4715
- "concat",
4716
- "join"
4717
- ]);
4718
- prototypeWhitelist.set(Array.prototype, arrayMethods);
4719
- const stringMethods = /* @__PURE__ */ new Set([
4720
- "toLowerCase",
4721
- "toUpperCase",
4722
- "includes",
4723
- "indexOf",
4724
- "startsWith",
4725
- "endsWith",
4726
- "slice",
4727
- "substring",
4728
- "length",
4729
- "trim",
4730
- "split",
4731
- "replace"
4732
- ]);
4733
- prototypeWhitelist.set(String.prototype, stringMethods);
4734
- const objectMethods = /* @__PURE__ */ new Set(["hasOwnProperty", "toString", "valueOf"]);
4735
- prototypeWhitelist.set(Object.prototype, objectMethods);
4736
- this.sandbox = new Sandbox({ globals, prototypeWhitelist });
5083
+ this.sandbox = createSecureSandbox();
4737
5084
  return this.sandbox;
4738
5085
  }
4739
5086
  };
4740
5087
 
4741
5088
  // src/providers/claude-code-check-provider.ts
4742
- import fs4 from "fs/promises";
4743
- import path5 from "path";
5089
+ import fs5 from "fs/promises";
5090
+ import path7 from "path";
4744
5091
 
4745
5092
  // src/providers/claude-code-types.ts
4746
5093
  async function safeImport(moduleName) {
@@ -4901,7 +5248,7 @@ var ClaudeCodeCheckProvider = class extends CheckProvider {
4901
5248
  const hasFileExtension = /\.[a-zA-Z0-9]{1,10}$/i.test(str);
4902
5249
  const hasPathSeparators = /[\/\\]/.test(str);
4903
5250
  const isRelativePath = /^\.{1,2}\//.test(str);
4904
- const isAbsolutePath = path5.isAbsolute(str);
5251
+ const isAbsolutePath = path7.isAbsolute(str);
4905
5252
  const hasTypicalFileChars = /^[a-zA-Z0-9._\-\/\\:~]+$/.test(str);
4906
5253
  if (!(hasFileExtension || isRelativePath || isAbsolutePath || hasPathSeparators)) {
4907
5254
  return false;
@@ -4911,13 +5258,13 @@ var ClaudeCodeCheckProvider = class extends CheckProvider {
4911
5258
  }
4912
5259
  try {
4913
5260
  let resolvedPath;
4914
- if (path5.isAbsolute(str)) {
4915
- resolvedPath = path5.normalize(str);
5261
+ if (path7.isAbsolute(str)) {
5262
+ resolvedPath = path7.normalize(str);
4916
5263
  } else {
4917
- resolvedPath = path5.resolve(process.cwd(), str);
5264
+ resolvedPath = path7.resolve(process.cwd(), str);
4918
5265
  }
4919
5266
  try {
4920
- const stat = await fs4.stat(resolvedPath);
5267
+ const stat = await fs5.stat(resolvedPath);
4921
5268
  return stat.isFile();
4922
5269
  } catch {
4923
5270
  return hasFileExtension && (isRelativePath || isAbsolutePath || hasPathSeparators);
@@ -4934,14 +5281,14 @@ var ClaudeCodeCheckProvider = class extends CheckProvider {
4934
5281
  throw new Error("Prompt file must have .liquid extension");
4935
5282
  }
4936
5283
  let resolvedPath;
4937
- if (path5.isAbsolute(promptPath)) {
5284
+ if (path7.isAbsolute(promptPath)) {
4938
5285
  resolvedPath = promptPath;
4939
5286
  } else {
4940
- resolvedPath = path5.resolve(process.cwd(), promptPath);
5287
+ resolvedPath = path7.resolve(process.cwd(), promptPath);
4941
5288
  }
4942
- if (!path5.isAbsolute(promptPath)) {
4943
- const normalizedPath = path5.normalize(resolvedPath);
4944
- const currentDir = path5.resolve(process.cwd());
5289
+ if (!path7.isAbsolute(promptPath)) {
5290
+ const normalizedPath = path7.normalize(resolvedPath);
5291
+ const currentDir = path7.resolve(process.cwd());
4945
5292
  if (!normalizedPath.startsWith(currentDir)) {
4946
5293
  throw new Error("Invalid prompt file path: path traversal detected");
4947
5294
  }
@@ -4950,7 +5297,7 @@ var ClaudeCodeCheckProvider = class extends CheckProvider {
4950
5297
  throw new Error("Invalid prompt file path: path traversal detected");
4951
5298
  }
4952
5299
  try {
4953
- const promptContent = await fs4.readFile(resolvedPath, "utf-8");
5300
+ const promptContent = await fs5.readFile(resolvedPath, "utf-8");
4954
5301
  return promptContent;
4955
5302
  } catch (error) {
4956
5303
  throw new Error(
@@ -5192,8 +5539,8 @@ var ClaudeCodeCheckProvider = class extends CheckProvider {
5192
5539
  };
5193
5540
 
5194
5541
  // src/providers/command-check-provider.ts
5195
- import Sandbox2 from "@nyariv/sandboxjs";
5196
5542
  init_logger();
5543
+ import { trace as trace3, context as otContext3 } from "@opentelemetry/api";
5197
5544
  var CommandCheckProvider = class extends CheckProvider {
5198
5545
  liquid;
5199
5546
  sandbox;
@@ -5206,13 +5553,7 @@ var CommandCheckProvider = class extends CheckProvider {
5206
5553
  });
5207
5554
  }
5208
5555
  createSecureSandbox() {
5209
- const globals = {
5210
- ...Sandbox2.SAFE_GLOBALS,
5211
- console,
5212
- JSON
5213
- };
5214
- const prototypeWhitelist = new Map(Sandbox2.SAFE_PROTOTYPES);
5215
- return new Sandbox2({ globals, prototypeWhitelist });
5556
+ return createSecureSandbox();
5216
5557
  }
5217
5558
  getName() {
5218
5559
  return "command";
@@ -5261,6 +5602,24 @@ var CommandCheckProvider = class extends CheckProvider {
5261
5602
  logger.debug(
5262
5603
  `\u{1F527} Debug: Template outputs keys: ${Object.keys(templateContext.outputs || {}).join(", ")}`
5263
5604
  );
5605
+ try {
5606
+ const span = trace3.getSpan(otContext3.active());
5607
+ if (span) {
5608
+ captureCheckInputContext(span, templateContext);
5609
+ }
5610
+ } catch {
5611
+ }
5612
+ try {
5613
+ const checkId = config.checkName || config.id || "unknown";
5614
+ const ctxJson = JSON.stringify(templateContext);
5615
+ const { emitNdjsonSpanWithEvents: emitNdjsonSpanWithEvents2 } = (init_fallback_ndjson(), __toCommonJS(fallback_ndjson_exports));
5616
+ emitNdjsonSpanWithEvents2(
5617
+ "visor.check",
5618
+ { "visor.check.id": checkId, "visor.check.input.context": ctxJson },
5619
+ [{ name: "check.started" }, { name: "check.completed" }]
5620
+ );
5621
+ } catch {
5622
+ }
5264
5623
  try {
5265
5624
  let renderedCommand = command;
5266
5625
  if (command.includes("{{") || command.includes("{%")) {
@@ -5455,8 +5814,12 @@ ${bodyWithReturn}
5455
5814
  if (parsedFromSandboxJson !== void 0) {
5456
5815
  finalOutput = parsedFromSandboxJson;
5457
5816
  } else {
5458
- const exec2 = this.sandbox.compile(code);
5459
- finalOutput = exec2({ scope: jsContext }).run();
5817
+ finalOutput = compileAndRun(
5818
+ this.sandbox,
5819
+ code,
5820
+ { scope: jsContext },
5821
+ { injectLog: false, wrapFunction: false }
5822
+ );
5460
5823
  }
5461
5824
  try {
5462
5825
  if (finalOutput && typeof finalOutput === "object" && !Array.isArray(finalOutput) && (finalOutput.error === void 0 || finalOutput.issues === void 0)) {
@@ -5867,6 +6230,27 @@ ${bodyWithReturn}
5867
6230
  ...content ? { content } : {},
5868
6231
  ...promoted
5869
6232
  };
6233
+ try {
6234
+ const span = trace3.getSpan(otContext3.active());
6235
+ if (span) {
6236
+ captureCheckOutput(span, outputForDependents);
6237
+ if (transformJs && output !== finalOutput) {
6238
+ captureTransformJS(span, transformJs, output, finalOutput);
6239
+ }
6240
+ }
6241
+ } catch {
6242
+ }
6243
+ try {
6244
+ const checkId = config.checkName || config.id || "unknown";
6245
+ const outJson = JSON.stringify(result.output ?? result);
6246
+ const { emitNdjsonSpanWithEvents: emitNdjsonSpanWithEvents2 } = (init_fallback_ndjson(), __toCommonJS(fallback_ndjson_exports));
6247
+ emitNdjsonSpanWithEvents2(
6248
+ "visor.check",
6249
+ { "visor.check.id": checkId, "visor.check.output": outJson },
6250
+ [{ name: "check.started" }, { name: "check.completed" }]
6251
+ );
6252
+ } catch {
6253
+ }
5870
6254
  try {
5871
6255
  if (transformJs) {
5872
6256
  const rawObj = snapshotForExtraction || finalOutput;
@@ -6431,7 +6815,6 @@ ${stderrOutput}` : `Command execution failed: ${errorMessage}`;
6431
6815
 
6432
6816
  // src/providers/memory-check-provider.ts
6433
6817
  init_logger();
6434
- import Sandbox3 from "@nyariv/sandboxjs";
6435
6818
  var MemoryCheckProvider = class extends CheckProvider {
6436
6819
  liquid;
6437
6820
  sandbox;
@@ -6446,61 +6829,7 @@ var MemoryCheckProvider = class extends CheckProvider {
6446
6829
  * Create a secure sandbox for JavaScript execution
6447
6830
  */
6448
6831
  createSecureSandbox() {
6449
- const globals = {
6450
- ...Sandbox3.SAFE_GLOBALS,
6451
- Math,
6452
- console: {
6453
- log: console.log,
6454
- warn: console.warn,
6455
- error: console.error
6456
- }
6457
- };
6458
- const prototypeWhitelist = new Map(Sandbox3.SAFE_PROTOTYPES);
6459
- const arrayMethods = /* @__PURE__ */ new Set([
6460
- "some",
6461
- "every",
6462
- "filter",
6463
- "map",
6464
- "reduce",
6465
- "find",
6466
- "includes",
6467
- "indexOf",
6468
- "length",
6469
- "slice",
6470
- "concat",
6471
- "join",
6472
- "push",
6473
- "pop",
6474
- "shift",
6475
- "unshift",
6476
- "sort",
6477
- "reverse"
6478
- ]);
6479
- prototypeWhitelist.set(Array.prototype, arrayMethods);
6480
- const stringMethods = /* @__PURE__ */ new Set([
6481
- "toLowerCase",
6482
- "toUpperCase",
6483
- "includes",
6484
- "indexOf",
6485
- "startsWith",
6486
- "endsWith",
6487
- "slice",
6488
- "substring",
6489
- "length",
6490
- "trim",
6491
- "split",
6492
- "replace",
6493
- "match",
6494
- "padStart",
6495
- "padEnd"
6496
- ]);
6497
- prototypeWhitelist.set(String.prototype, stringMethods);
6498
- const objectMethods = /* @__PURE__ */ new Set(["hasOwnProperty", "toString", "valueOf"]);
6499
- prototypeWhitelist.set(Object.prototype, objectMethods);
6500
- return new Sandbox3({
6501
- globals,
6502
- prototypeWhitelist
6503
- });
6832
+ return createSecureSandbox();
6504
6833
  }
6505
6834
  getName() {
6506
6835
  return "memory";
@@ -6792,15 +7121,12 @@ var MemoryCheckProvider = class extends CheckProvider {
6792
7121
  this.sandbox = this.createSecureSandbox();
6793
7122
  }
6794
7123
  try {
6795
- const log2 = (...args) => {
6796
- logger.info(`\u{1F50D} [memory-js] ${args.map((a) => JSON.stringify(a)).join(" ")}`);
6797
- };
6798
- const scope = {
6799
- ...context,
6800
- log: log2
6801
- };
6802
- const exec = this.sandbox.compile(`return (${expression});`);
6803
- return exec(scope).run();
7124
+ const scope = { ...context };
7125
+ return compileAndRun(this.sandbox, `return (${expression});`, scope, {
7126
+ injectLog: true,
7127
+ wrapFunction: false,
7128
+ logPrefix: "[memory:value_js]"
7129
+ });
6804
7130
  } catch (error) {
6805
7131
  const errorMsg = error instanceof Error ? error.message : "Unknown error";
6806
7132
  throw new Error(`Failed to evaluate value_js: ${errorMsg}`);
@@ -6811,20 +7137,16 @@ var MemoryCheckProvider = class extends CheckProvider {
6811
7137
  * Unlike evaluateJavaScript, this supports full scripts with statements, not just expressions
6812
7138
  */
6813
7139
  evaluateJavaScriptBlock(script, context) {
6814
- if (!this.sandbox) {
6815
- this.sandbox = this.createSecureSandbox();
6816
- }
6817
- try {
6818
- const log2 = (...args) => {
6819
- logger.info(`\u{1F50D} [memory-js] ${args.map((a) => JSON.stringify(a)).join(" ")}`);
6820
- };
6821
- const scope = {
6822
- ...context,
6823
- log: log2
6824
- };
6825
- const exec = this.sandbox.compile(script);
6826
- const result = exec(scope).run();
6827
- return result;
7140
+ if (!this.sandbox) {
7141
+ this.sandbox = this.createSecureSandbox();
7142
+ }
7143
+ try {
7144
+ const scope = { ...context };
7145
+ return compileAndRun(this.sandbox, script, scope, {
7146
+ injectLog: true,
7147
+ wrapFunction: false,
7148
+ logPrefix: "[memory:exec_js]"
7149
+ });
6828
7150
  } catch (error) {
6829
7151
  const errorMsg = error instanceof Error ? error.message : "Unknown error";
6830
7152
  logger.error(`[memory-js] Script execution error: ${errorMsg}`);
@@ -6916,7 +7238,6 @@ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
6916
7238
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
6917
7239
  import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
6918
7240
  import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
6919
- import Sandbox4 from "@nyariv/sandboxjs";
6920
7241
  var McpCheckProvider = class extends CheckProvider {
6921
7242
  liquid;
6922
7243
  sandbox;
@@ -6935,67 +7256,7 @@ var McpCheckProvider = class extends CheckProvider {
6935
7256
  * - No access to filesystem, network, or system resources
6936
7257
  */
6937
7258
  createSecureSandbox() {
6938
- const log2 = (...args) => {
6939
- logger.debug(`[transform_js] ${args.map((a) => JSON.stringify(a)).join(" ")}`);
6940
- };
6941
- const globals = {
6942
- ...Sandbox4.SAFE_GLOBALS,
6943
- // Excludes Function, eval, require, process, etc.
6944
- Math,
6945
- JSON,
6946
- console: {
6947
- log: log2
6948
- }
6949
- };
6950
- const prototypeWhitelist = new Map(Sandbox4.SAFE_PROTOTYPES);
6951
- const arrayMethods = /* @__PURE__ */ new Set([
6952
- "some",
6953
- "every",
6954
- "filter",
6955
- "map",
6956
- "reduce",
6957
- "find",
6958
- "includes",
6959
- "indexOf",
6960
- "length",
6961
- "slice",
6962
- "concat",
6963
- "join",
6964
- "push",
6965
- "pop",
6966
- "shift",
6967
- "unshift",
6968
- "sort",
6969
- "reverse",
6970
- "flat",
6971
- "flatMap"
6972
- ]);
6973
- prototypeWhitelist.set(Array.prototype, arrayMethods);
6974
- const stringMethods = /* @__PURE__ */ new Set([
6975
- "toLowerCase",
6976
- "toUpperCase",
6977
- "includes",
6978
- "indexOf",
6979
- "startsWith",
6980
- "endsWith",
6981
- "slice",
6982
- "substring",
6983
- "length",
6984
- "trim",
6985
- "split",
6986
- "replace",
6987
- // String.prototype.replace for text manipulation (e.g., "hello".replace("h", "H"))
6988
- "match",
6989
- "padStart",
6990
- "padEnd"
6991
- ]);
6992
- prototypeWhitelist.set(String.prototype, stringMethods);
6993
- const objectMethods = /* @__PURE__ */ new Set(["hasOwnProperty", "toString", "valueOf", "keys", "values"]);
6994
- prototypeWhitelist.set(Object.prototype, objectMethods);
6995
- return new Sandbox4({
6996
- globals,
6997
- prototypeWhitelist
6998
- });
7259
+ return createSecureSandbox();
6999
7260
  }
7000
7261
  getName() {
7001
7262
  return "mcp";
@@ -7124,8 +7385,12 @@ var McpCheckProvider = class extends CheckProvider {
7124
7385
  outputs: templateContext.outputs,
7125
7386
  env: templateContext.env
7126
7387
  };
7127
- const exec = this.sandbox.compile(`return (${cfg.transform_js});`);
7128
- finalOutput = exec(scope).run();
7388
+ finalOutput = compileAndRun(
7389
+ this.sandbox,
7390
+ `return (${cfg.transform_js});`,
7391
+ scope,
7392
+ { injectLog: true, wrapFunction: false, logPrefix: "[mcp:transform_js]" }
7393
+ );
7129
7394
  } catch (error) {
7130
7395
  logger.error(`Failed to apply JavaScript transform: ${error}`);
7131
7396
  return {
@@ -7490,6 +7755,484 @@ var McpCheckProvider = class extends CheckProvider {
7490
7755
  }
7491
7756
  };
7492
7757
 
7758
+ // src/utils/interactive-prompt.ts
7759
+ import * as readline from "readline";
7760
+ var colors = {
7761
+ reset: "\x1B[0m",
7762
+ dim: "\x1B[2m",
7763
+ bold: "\x1B[1m",
7764
+ cyan: "\x1B[36m",
7765
+ green: "\x1B[32m",
7766
+ yellow: "\x1B[33m",
7767
+ gray: "\x1B[90m"
7768
+ };
7769
+ var supportsUnicode = process.env.LANG?.includes("UTF-8") || process.platform === "darwin";
7770
+ var box = supportsUnicode ? {
7771
+ topLeft: "\u250C",
7772
+ topRight: "\u2510",
7773
+ bottomLeft: "\u2514",
7774
+ bottomRight: "\u2518",
7775
+ horizontal: "\u2500",
7776
+ vertical: "\u2502",
7777
+ leftT: "\u251C",
7778
+ rightT: "\u2524"
7779
+ } : {
7780
+ topLeft: "+",
7781
+ topRight: "+",
7782
+ bottomLeft: "+",
7783
+ bottomRight: "+",
7784
+ horizontal: "-",
7785
+ vertical: "|",
7786
+ leftT: "+",
7787
+ rightT: "+"
7788
+ };
7789
+ function formatTime(ms) {
7790
+ const seconds = Math.ceil(ms / 1e3);
7791
+ const mins = Math.floor(seconds / 60);
7792
+ const secs = seconds % 60;
7793
+ return `${mins}:${secs.toString().padStart(2, "0")}`;
7794
+ }
7795
+ function drawLine(char, width) {
7796
+ return char.repeat(width);
7797
+ }
7798
+ function wrapText(text, width) {
7799
+ const words = text.split(" ");
7800
+ const lines = [];
7801
+ let currentLine = "";
7802
+ for (const word of words) {
7803
+ if (currentLine.length + word.length + 1 <= width) {
7804
+ currentLine += (currentLine ? " " : "") + word;
7805
+ } else {
7806
+ if (currentLine) lines.push(currentLine);
7807
+ currentLine = word;
7808
+ }
7809
+ }
7810
+ if (currentLine) lines.push(currentLine);
7811
+ return lines;
7812
+ }
7813
+ function displayPromptUI(options, remainingMs) {
7814
+ const width = Math.min(process.stdout.columns || 80, 80) - 4;
7815
+ const icon = supportsUnicode ? "\u{1F4AC}" : ">";
7816
+ console.log("\n");
7817
+ console.log(`${box.topLeft}${drawLine(box.horizontal, width + 2)}${box.topRight}`);
7818
+ console.log(
7819
+ `${box.vertical} ${colors.bold}${icon} Human Input Required${colors.reset}${" ".repeat(
7820
+ width - 22
7821
+ )} ${box.vertical}`
7822
+ );
7823
+ console.log(`${box.leftT}${drawLine(box.horizontal, width + 2)}${box.rightT}`);
7824
+ console.log(`${box.vertical} ${" ".repeat(width)} ${box.vertical}`);
7825
+ const promptLines = wrapText(options.prompt, width - 2);
7826
+ for (const line of promptLines) {
7827
+ console.log(
7828
+ `${box.vertical} ${colors.cyan}${line}${colors.reset}${" ".repeat(
7829
+ width - line.length
7830
+ )} ${box.vertical}`
7831
+ );
7832
+ }
7833
+ console.log(`${box.vertical} ${" ".repeat(width)} ${box.vertical}`);
7834
+ const instruction = options.multiline ? "(Type your response, press Ctrl+D when done)" : "(Type your response and press Enter)";
7835
+ console.log(
7836
+ `${box.vertical} ${colors.dim}${instruction}${colors.reset}${" ".repeat(
7837
+ width - instruction.length
7838
+ )} ${box.vertical}`
7839
+ );
7840
+ if (options.placeholder && !options.multiline) {
7841
+ console.log(
7842
+ `${box.vertical} ${colors.dim}${options.placeholder}${colors.reset}${" ".repeat(
7843
+ width - options.placeholder.length
7844
+ )} ${box.vertical}`
7845
+ );
7846
+ }
7847
+ console.log(`${box.vertical} ${" ".repeat(width)} ${box.vertical}`);
7848
+ if (remainingMs !== void 0 && options.timeout) {
7849
+ const timeIcon = supportsUnicode ? "\u23F1 " : "Time: ";
7850
+ const timeStr = `${timeIcon} ${formatTime(remainingMs)} remaining`;
7851
+ console.log(
7852
+ `${box.vertical} ${colors.yellow}${timeStr}${colors.reset}${" ".repeat(
7853
+ width - timeStr.length
7854
+ )} ${box.vertical}`
7855
+ );
7856
+ }
7857
+ console.log(`${box.bottomLeft}${drawLine(box.horizontal, width + 2)}${box.bottomRight}`);
7858
+ console.log("");
7859
+ process.stdout.write(`${colors.green}>${colors.reset} `);
7860
+ }
7861
+ async function interactivePrompt(options) {
7862
+ return new Promise((resolve4, reject) => {
7863
+ let input = "";
7864
+ let timeoutId;
7865
+ let countdownInterval;
7866
+ let remainingMs = options.timeout;
7867
+ const rl = readline.createInterface({
7868
+ input: process.stdin,
7869
+ output: process.stdout,
7870
+ terminal: true
7871
+ });
7872
+ displayPromptUI(options, remainingMs);
7873
+ const cleanup = () => {
7874
+ if (timeoutId) clearTimeout(timeoutId);
7875
+ if (countdownInterval) clearInterval(countdownInterval);
7876
+ rl.close();
7877
+ };
7878
+ const finish = (value) => {
7879
+ cleanup();
7880
+ console.log("");
7881
+ resolve4(value);
7882
+ };
7883
+ if (options.timeout) {
7884
+ timeoutId = setTimeout(() => {
7885
+ cleanup();
7886
+ console.log(`
7887
+ ${colors.yellow}\u23F1 Timeout reached${colors.reset}`);
7888
+ if (options.defaultValue !== void 0) {
7889
+ console.log(
7890
+ `${colors.gray}Using default value: ${options.defaultValue}${colors.reset}
7891
+ `
7892
+ );
7893
+ resolve4(options.defaultValue);
7894
+ } else {
7895
+ reject(new Error("Input timeout"));
7896
+ }
7897
+ }, options.timeout);
7898
+ if (remainingMs) {
7899
+ countdownInterval = setInterval(() => {
7900
+ remainingMs = remainingMs - 1e3;
7901
+ if (remainingMs <= 0) {
7902
+ if (countdownInterval) clearInterval(countdownInterval);
7903
+ }
7904
+ }, 1e3);
7905
+ }
7906
+ }
7907
+ if (options.multiline) {
7908
+ rl.on("line", (line) => {
7909
+ input += (input ? "\n" : "") + line;
7910
+ });
7911
+ rl.on("close", () => {
7912
+ cleanup();
7913
+ const trimmed = input.trim();
7914
+ if (!trimmed && !options.allowEmpty) {
7915
+ console.log(`${colors.yellow}\u26A0 Empty input not allowed${colors.reset}`);
7916
+ reject(new Error("Empty input not allowed"));
7917
+ } else {
7918
+ finish(trimmed);
7919
+ }
7920
+ });
7921
+ } else {
7922
+ rl.question("", (answer) => {
7923
+ const trimmed = answer.trim();
7924
+ if (!trimmed && !options.allowEmpty && !options.defaultValue) {
7925
+ cleanup();
7926
+ console.log(`${colors.yellow}\u26A0 Empty input not allowed${colors.reset}`);
7927
+ reject(new Error("Empty input not allowed"));
7928
+ } else {
7929
+ finish(trimmed || options.defaultValue || "");
7930
+ }
7931
+ });
7932
+ }
7933
+ rl.on("SIGINT", () => {
7934
+ cleanup();
7935
+ console.log("\n\n" + colors.yellow + "\u26A0 Cancelled by user" + colors.reset);
7936
+ reject(new Error("Cancelled by user"));
7937
+ });
7938
+ });
7939
+ }
7940
+ async function simplePrompt(prompt) {
7941
+ return new Promise((resolve4) => {
7942
+ const rl = readline.createInterface({
7943
+ input: process.stdin,
7944
+ output: process.stdout
7945
+ });
7946
+ rl.question(`${prompt}
7947
+ > `, (answer) => {
7948
+ rl.close();
7949
+ resolve4(answer.trim());
7950
+ });
7951
+ });
7952
+ }
7953
+
7954
+ // src/utils/stdin-reader.ts
7955
+ function isStdinAvailable() {
7956
+ return !process.stdin.isTTY;
7957
+ }
7958
+ async function readStdin(timeout, maxSize = 1024 * 1024) {
7959
+ return new Promise((resolve4, reject) => {
7960
+ let data = "";
7961
+ let timeoutId;
7962
+ if (timeout) {
7963
+ timeoutId = setTimeout(() => {
7964
+ cleanup();
7965
+ reject(new Error(`Stdin read timeout after ${timeout}ms`));
7966
+ }, timeout);
7967
+ }
7968
+ const cleanup = () => {
7969
+ if (timeoutId) {
7970
+ clearTimeout(timeoutId);
7971
+ }
7972
+ process.stdin.removeListener("data", onData);
7973
+ process.stdin.removeListener("end", onEnd);
7974
+ process.stdin.removeListener("error", onError);
7975
+ process.stdin.pause();
7976
+ };
7977
+ const onData = (chunk) => {
7978
+ data += chunk.toString();
7979
+ if (data.length > maxSize) {
7980
+ cleanup();
7981
+ reject(new Error(`Input exceeds maximum size of ${maxSize} bytes`));
7982
+ }
7983
+ };
7984
+ const onEnd = () => {
7985
+ cleanup();
7986
+ resolve4(data.trim());
7987
+ };
7988
+ const onError = (err) => {
7989
+ cleanup();
7990
+ reject(err);
7991
+ };
7992
+ process.stdin.setEncoding("utf8");
7993
+ process.stdin.on("data", onData);
7994
+ process.stdin.on("end", onEnd);
7995
+ process.stdin.on("error", onError);
7996
+ process.stdin.resume();
7997
+ });
7998
+ }
7999
+ async function tryReadStdin(timeout, maxSize = 1024 * 1024) {
8000
+ if (!isStdinAvailable()) {
8001
+ return null;
8002
+ }
8003
+ try {
8004
+ return await readStdin(timeout, maxSize);
8005
+ } catch {
8006
+ return null;
8007
+ }
8008
+ }
8009
+
8010
+ // src/providers/human-input-check-provider.ts
8011
+ import * as fs6 from "fs";
8012
+ import * as path8 from "path";
8013
+ var HumanInputCheckProvider = class _HumanInputCheckProvider extends CheckProvider {
8014
+ /**
8015
+ * @deprecated Use ExecutionContext.cliMessage instead
8016
+ * Kept for backward compatibility
8017
+ */
8018
+ static cliMessage;
8019
+ /**
8020
+ * @deprecated Use ExecutionContext.hooks instead
8021
+ * Kept for backward compatibility
8022
+ */
8023
+ static hooks = {};
8024
+ /**
8025
+ * Set the CLI message value (from --message argument)
8026
+ * @deprecated Use ExecutionContext.cliMessage instead
8027
+ */
8028
+ static setCLIMessage(message) {
8029
+ _HumanInputCheckProvider.cliMessage = message;
8030
+ }
8031
+ /**
8032
+ * Get the current CLI message value
8033
+ * @deprecated Use ExecutionContext.cliMessage instead
8034
+ */
8035
+ static getCLIMessage() {
8036
+ return _HumanInputCheckProvider.cliMessage;
8037
+ }
8038
+ /**
8039
+ * Set hooks for SDK mode
8040
+ * @deprecated Use ExecutionContext.hooks instead
8041
+ */
8042
+ static setHooks(hooks) {
8043
+ _HumanInputCheckProvider.hooks = hooks;
8044
+ }
8045
+ getName() {
8046
+ return "human-input";
8047
+ }
8048
+ getDescription() {
8049
+ return "Prompts for human input during workflow execution (CLI interactive or SDK hook)";
8050
+ }
8051
+ async validateConfig(config) {
8052
+ if (!config || typeof config !== "object") {
8053
+ return false;
8054
+ }
8055
+ const cfg = config;
8056
+ if (cfg.type !== "human-input") {
8057
+ return false;
8058
+ }
8059
+ if (!cfg.prompt || typeof cfg.prompt !== "string") {
8060
+ console.error('human-input check requires a "prompt" field');
8061
+ return false;
8062
+ }
8063
+ return true;
8064
+ }
8065
+ /**
8066
+ * Check if a string looks like a file path
8067
+ */
8068
+ looksLikePath(str) {
8069
+ return str.includes("/") || str.includes("\\");
8070
+ }
8071
+ /**
8072
+ * Sanitize user input to prevent injection attacks in dependent checks
8073
+ * Removes potentially dangerous characters while preserving useful input
8074
+ */
8075
+ sanitizeInput(input) {
8076
+ let sanitized = input.replace(/\0/g, "");
8077
+ sanitized = sanitized.replace(/[\x00-\x08\x0B-\x0C\x0E-\x1F\x7F]/g, "");
8078
+ const maxLength = 100 * 1024;
8079
+ if (sanitized.length > maxLength) {
8080
+ sanitized = sanitized.substring(0, maxLength);
8081
+ }
8082
+ return sanitized;
8083
+ }
8084
+ /**
8085
+ * Try to read message from file if it exists
8086
+ * Validates path to prevent directory traversal attacks
8087
+ */
8088
+ async tryReadFile(filePath) {
8089
+ try {
8090
+ const absolutePath = path8.isAbsolute(filePath) ? filePath : path8.resolve(process.cwd(), filePath);
8091
+ const normalizedPath = path8.normalize(absolutePath);
8092
+ const cwd = process.cwd();
8093
+ if (!normalizedPath.startsWith(cwd + path8.sep) && normalizedPath !== cwd) {
8094
+ return null;
8095
+ }
8096
+ try {
8097
+ await fs6.promises.access(normalizedPath, fs6.constants.R_OK);
8098
+ const stats = await fs6.promises.stat(normalizedPath);
8099
+ if (!stats.isFile()) {
8100
+ return null;
8101
+ }
8102
+ const content = await fs6.promises.readFile(normalizedPath, "utf-8");
8103
+ return content.trim();
8104
+ } catch {
8105
+ return null;
8106
+ }
8107
+ } catch {
8108
+ }
8109
+ return null;
8110
+ }
8111
+ /**
8112
+ * Get user input through various methods
8113
+ */
8114
+ async getUserInput(checkName, config, context) {
8115
+ const prompt = config.prompt || "Please provide input:";
8116
+ const placeholder = config.placeholder || "Enter your response...";
8117
+ const allowEmpty = config.allow_empty ?? false;
8118
+ const multiline = config.multiline ?? false;
8119
+ const timeout = config.timeout ? config.timeout * 1e3 : void 0;
8120
+ const defaultValue = config.default;
8121
+ const cliMessage = context?.cliMessage ?? _HumanInputCheckProvider.cliMessage;
8122
+ if (cliMessage !== void 0) {
8123
+ const message = cliMessage;
8124
+ if (this.looksLikePath(message)) {
8125
+ const fileContent = await this.tryReadFile(message);
8126
+ if (fileContent !== null) {
8127
+ return fileContent;
8128
+ }
8129
+ }
8130
+ return message;
8131
+ }
8132
+ const stdinInput = await tryReadStdin(timeout);
8133
+ if (stdinInput !== null && stdinInput.length > 0) {
8134
+ return stdinInput;
8135
+ }
8136
+ const hooks = context?.hooks ?? _HumanInputCheckProvider.hooks;
8137
+ if (hooks?.onHumanInput) {
8138
+ const request = {
8139
+ checkId: checkName,
8140
+ prompt,
8141
+ placeholder,
8142
+ allowEmpty,
8143
+ multiline,
8144
+ timeout,
8145
+ default: defaultValue
8146
+ };
8147
+ try {
8148
+ const result = await hooks.onHumanInput(request);
8149
+ return result;
8150
+ } catch (error) {
8151
+ throw new Error(
8152
+ `Hook onHumanInput failed: ${error instanceof Error ? error.message : String(error)}`
8153
+ );
8154
+ }
8155
+ }
8156
+ if (process.stdin.isTTY) {
8157
+ try {
8158
+ const result = await interactivePrompt({
8159
+ prompt,
8160
+ placeholder,
8161
+ multiline,
8162
+ timeout,
8163
+ defaultValue,
8164
+ allowEmpty
8165
+ });
8166
+ return result;
8167
+ } catch (error) {
8168
+ throw new Error(
8169
+ `Interactive prompt failed: ${error instanceof Error ? error.message : String(error)}`
8170
+ );
8171
+ }
8172
+ }
8173
+ try {
8174
+ const result = await simplePrompt(prompt);
8175
+ if (!result && !allowEmpty && !defaultValue) {
8176
+ throw new Error("Empty input not allowed");
8177
+ }
8178
+ return result || defaultValue || "";
8179
+ } catch (error) {
8180
+ throw new Error(
8181
+ `Simple prompt failed: ${error instanceof Error ? error.message : String(error)}`
8182
+ );
8183
+ }
8184
+ }
8185
+ async execute(_prInfo, config, _dependencyResults, context) {
8186
+ const checkName = config.checkName || "human-input";
8187
+ try {
8188
+ const userInput = await this.getUserInput(checkName, config, context);
8189
+ const sanitizedInput = this.sanitizeInput(userInput);
8190
+ return {
8191
+ issues: [],
8192
+ output: sanitizedInput
8193
+ };
8194
+ } catch (error) {
8195
+ return {
8196
+ issues: [
8197
+ {
8198
+ file: "",
8199
+ line: 0,
8200
+ ruleId: "human-input-error",
8201
+ message: `Failed to get user input: ${error instanceof Error ? error.message : String(error)}`,
8202
+ severity: "error",
8203
+ category: "logic"
8204
+ }
8205
+ ]
8206
+ };
8207
+ }
8208
+ }
8209
+ getSupportedConfigKeys() {
8210
+ return [
8211
+ "type",
8212
+ "prompt",
8213
+ "placeholder",
8214
+ "allow_empty",
8215
+ "multiline",
8216
+ "timeout",
8217
+ "default",
8218
+ "depends_on",
8219
+ "on",
8220
+ "if",
8221
+ "group"
8222
+ ];
8223
+ }
8224
+ async isAvailable() {
8225
+ return true;
8226
+ }
8227
+ getRequirements() {
8228
+ return [
8229
+ "No external dependencies required",
8230
+ "Works in CLI mode with --message argument, piped stdin, or interactive prompts",
8231
+ "SDK mode requires onHumanInput hook to be configured"
8232
+ ];
8233
+ }
8234
+ };
8235
+
7493
8236
  // src/providers/check-provider-registry.ts
7494
8237
  var CheckProviderRegistry = class _CheckProviderRegistry {
7495
8238
  providers = /* @__PURE__ */ new Map();
@@ -7519,6 +8262,7 @@ var CheckProviderRegistry = class _CheckProviderRegistry {
7519
8262
  this.register(new LogCheckProvider());
7520
8263
  this.register(new MemoryCheckProvider());
7521
8264
  this.register(new GitHubOpsProvider());
8265
+ this.register(new HumanInputCheckProvider());
7522
8266
  try {
7523
8267
  this.register(new ClaudeCodeCheckProvider());
7524
8268
  } catch (error) {
@@ -7823,7 +8567,6 @@ var DependencyResolver = class {
7823
8567
  };
7824
8568
 
7825
8569
  // src/failure-condition-evaluator.ts
7826
- import Sandbox5 from "@nyariv/sandboxjs";
7827
8570
  var FailureConditionEvaluator = class _FailureConditionEvaluator {
7828
8571
  sandbox;
7829
8572
  constructor() {
@@ -7832,54 +8575,7 @@ var FailureConditionEvaluator = class _FailureConditionEvaluator {
7832
8575
  * Create a secure sandbox with whitelisted functions and globals
7833
8576
  */
7834
8577
  createSecureSandbox() {
7835
- const globals = {
7836
- ...Sandbox5.SAFE_GLOBALS,
7837
- // Allow Math for calculations
7838
- Math,
7839
- // Allow console for debugging (in controlled environment)
7840
- console: {
7841
- log: console.log,
7842
- warn: console.warn,
7843
- error: console.error
7844
- }
7845
- };
7846
- const prototypeWhitelist = new Map(Sandbox5.SAFE_PROTOTYPES);
7847
- const arrayMethods = /* @__PURE__ */ new Set([
7848
- "some",
7849
- "every",
7850
- "filter",
7851
- "map",
7852
- "reduce",
7853
- "find",
7854
- "includes",
7855
- "indexOf",
7856
- "length",
7857
- "slice",
7858
- "concat",
7859
- "join"
7860
- ]);
7861
- prototypeWhitelist.set(Array.prototype, arrayMethods);
7862
- const stringMethods = /* @__PURE__ */ new Set([
7863
- "toLowerCase",
7864
- "toUpperCase",
7865
- "includes",
7866
- "indexOf",
7867
- "startsWith",
7868
- "endsWith",
7869
- "slice",
7870
- "substring",
7871
- "length",
7872
- "trim",
7873
- "split",
7874
- "replace"
7875
- ]);
7876
- prototypeWhitelist.set(String.prototype, stringMethods);
7877
- const objectMethods = /* @__PURE__ */ new Set(["hasOwnProperty", "toString", "valueOf"]);
7878
- prototypeWhitelist.set(Object.prototype, objectMethods);
7879
- return new Sandbox5({
7880
- globals,
7881
- prototypeWhitelist
7882
- });
8578
+ return createSecureSandbox();
7883
8579
  }
7884
8580
  /**
7885
8581
  * Evaluate simple fail_if condition
@@ -8152,7 +8848,7 @@ var FailureConditionEvaluator = class _FailureConditionEvaluator {
8152
8848
  */
8153
8849
  evaluateExpression(condition, context) {
8154
8850
  try {
8155
- const normalize = (expr) => {
8851
+ const normalize2 = (expr) => {
8156
8852
  const trimmed = expr.trim();
8157
8853
  if (!/[\n;]/.test(trimmed)) return trimmed;
8158
8854
  const parts = trimmed.split(/[\n;]+/).map((s) => s.trim()).filter((s) => s.length > 0 && !s.startsWith("//"));
@@ -8295,7 +8991,7 @@ var FailureConditionEvaluator = class _FailureConditionEvaluator {
8295
8991
  try {
8296
8992
  exec = this.sandbox.compile(`return (${raw});`);
8297
8993
  } catch {
8298
- const normalizedExpr = normalize(condition);
8994
+ const normalizedExpr = normalize2(condition);
8299
8995
  exec = this.sandbox.compile(`return (${normalizedExpr});`);
8300
8996
  }
8301
8997
  const result = exec(scope).run();
@@ -8981,8 +9677,8 @@ Please check your configuration and try again.`
8981
9677
 
8982
9678
  // src/check-execution-engine.ts
8983
9679
  init_logger();
8984
- import Sandbox6 from "@nyariv/sandboxjs";
8985
9680
  init_fallback_ndjson();
9681
+ import { trace as trace4, context as otContext4 } from "@opentelemetry/api";
8986
9682
  function getSafeEnvironmentVariables() {
8987
9683
  const safeEnvVars = [
8988
9684
  "CI",
@@ -9024,6 +9720,8 @@ var CheckExecutionEngine = class _CheckExecutionEngine {
9024
9720
  outputHistory = /* @__PURE__ */ new Map();
9025
9721
  // Event override to simulate alternate event (used during routing goto)
9026
9722
  routingEventOverride;
9723
+ // Execution context for providers (CLI message, hooks, etc.)
9724
+ executionContext;
9027
9725
  // Cached GitHub context for context elevation when running in Actions
9028
9726
  actionContext;
9029
9727
  constructor(workingDirectory, octokit) {
@@ -9053,19 +9751,19 @@ var CheckExecutionEngine = class _CheckExecutionEngine {
9053
9751
  }
9054
9752
  return baseContext;
9055
9753
  }
9754
+ /**
9755
+ * Set execution context for providers (CLI message, hooks, etc.)
9756
+ * This allows passing state without using static properties
9757
+ */
9758
+ setExecutionContext(context) {
9759
+ this.executionContext = context;
9760
+ }
9056
9761
  /**
9057
9762
  * Lazily create a secure sandbox for routing JS (goto_js, run_js)
9058
9763
  */
9059
9764
  getRoutingSandbox() {
9060
9765
  if (this.routingSandbox) return this.routingSandbox;
9061
- const globals = {
9062
- ...Sandbox6.SAFE_GLOBALS,
9063
- Math,
9064
- JSON,
9065
- console: { log: console.log }
9066
- };
9067
- const prototypeWhitelist = new Map(Sandbox6.SAFE_PROTOTYPES);
9068
- this.routingSandbox = new Sandbox6({ globals, prototypeWhitelist });
9766
+ this.routingSandbox = createSecureSandbox();
9069
9767
  return this.routingSandbox;
9070
9768
  }
9071
9769
  redact(str, limit = 200) {
@@ -9077,7 +9775,7 @@ var CheckExecutionEngine = class _CheckExecutionEngine {
9077
9775
  }
9078
9776
  }
9079
9777
  async sleep(ms) {
9080
- return new Promise((resolve) => setTimeout(resolve, ms));
9778
+ return new Promise((resolve4) => setTimeout(resolve4, ms));
9081
9779
  }
9082
9780
  deterministicJitter(baseMs, seedStr) {
9083
9781
  let h = 2166136261;
@@ -9138,16 +9836,16 @@ var CheckExecutionEngine = class _CheckExecutionEngine {
9138
9836
  ),
9139
9837
  event: eventObj
9140
9838
  };
9141
- const code = `
9142
- 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;
9143
- const __fn = () => {
9144
- ${expr}
9145
- };
9146
- const __res = __fn();
9147
- return Array.isArray(__res) ? __res : (__res ? [__res] : []);
9148
- `;
9149
- const exec = sandbox.compile(code);
9150
- const res = exec({ scope }).run();
9839
+ 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;`;
9840
+ const code = `${prelude}
9841
+ ${expr}`;
9842
+ const result = compileAndRun(
9843
+ sandbox,
9844
+ code,
9845
+ { scope },
9846
+ { injectLog: false, wrapFunction: true }
9847
+ );
9848
+ const res = Array.isArray(result) ? result : result ? [result] : [];
9151
9849
  if (debug) {
9152
9850
  log2(`\u{1F527} Debug: run_js evaluated \u2192 [${this.redact(res)}]`);
9153
9851
  }
@@ -9191,16 +9889,15 @@ ${expr}
9191
9889
  ),
9192
9890
  event: eventObj
9193
9891
  };
9194
- const code = `
9195
- 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;
9196
- const __fn = () => {
9197
- ${expr}
9198
- };
9199
- const __res = __fn();
9200
- return (typeof __res === 'string' && __res) ? __res : null;
9201
- `;
9202
- const exec = sandbox.compile(code);
9203
- const res = exec({ scope }).run();
9892
+ 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;`;
9893
+ const code2 = `${prelude2}
9894
+ ${expr}`;
9895
+ const res = compileAndRun(
9896
+ sandbox,
9897
+ code2,
9898
+ { scope },
9899
+ { injectLog: false, wrapFunction: true }
9900
+ );
9204
9901
  if (debug) {
9205
9902
  log2(`\u{1F527} Debug: goto_js evaluated \u2192 ${this.redact(res)}`);
9206
9903
  }
@@ -9320,7 +10017,11 @@ ${expr}
9320
10017
  }
9321
10018
  let r;
9322
10019
  try {
9323
- r = await prov.execute(prInfoForInline, provCfg, depResults, sessionInfo);
10020
+ const inlineContext = {
10021
+ ...sessionInfo,
10022
+ ...this.executionContext
10023
+ };
10024
+ r = await prov.execute(prInfoForInline, provCfg, depResults, inlineContext);
9324
10025
  } finally {
9325
10026
  this.routingEventOverride = prevEventOverride;
9326
10027
  }
@@ -9351,7 +10052,31 @@ ${expr}
9351
10052
  });
9352
10053
  } catch {
9353
10054
  }
9354
- const res = await provider.execute(prInfo, providerConfig, dependencyResults, sessionInfo);
10055
+ const context = {
10056
+ ...sessionInfo,
10057
+ ...this.executionContext
10058
+ };
10059
+ const res = await withActiveSpan(
10060
+ `visor.check.${checkName}`,
10061
+ {
10062
+ "visor.check.id": checkName,
10063
+ "visor.check.type": providerConfig.type || "ai",
10064
+ "visor.check.attempt": attempt
10065
+ },
10066
+ async () => {
10067
+ try {
10068
+ return await provider.execute(prInfo, providerConfig, dependencyResults, context);
10069
+ } finally {
10070
+ try {
10071
+ emitNdjsonSpanWithEvents("visor.check", { "visor.check.id": checkName }, [
10072
+ { name: "check.started" },
10073
+ { name: "check.completed" }
10074
+ ]);
10075
+ } catch {
10076
+ }
10077
+ }
10078
+ }
10079
+ );
9355
10080
  try {
9356
10081
  currentRouteOutput = res?.output;
9357
10082
  } catch {
@@ -9979,7 +10704,7 @@ ${expr}
9979
10704
  /**
9980
10705
  * Execute review checks and return grouped results with statistics for new architecture
9981
10706
  */
9982
- async executeGroupedChecks(prInfo, checks, timeout, config, outputFormat, debug, maxParallelism, failFast, tagFilter) {
10707
+ async executeGroupedChecks(prInfo, checks, timeout, config, outputFormat, debug, maxParallelism, failFast, tagFilter, _pauseGate) {
9983
10708
  const logFn = outputFormat === "json" || outputFormat === "sarif" ? debug ? console.error : () => {
9984
10709
  } : console.log;
9985
10710
  if (debug) {
@@ -10052,7 +10777,8 @@ ${expr}
10052
10777
  logFn,
10053
10778
  debug,
10054
10779
  maxParallelism,
10055
- failFast
10780
+ failFast,
10781
+ _pauseGate
10056
10782
  );
10057
10783
  }
10058
10784
  if (checks.length === 1) {
@@ -10065,7 +10791,8 @@ ${expr}
10065
10791
  timeout,
10066
10792
  config,
10067
10793
  logFn,
10068
- debug
10794
+ debug,
10795
+ _pauseGate
10069
10796
  );
10070
10797
  const groupedResults = {};
10071
10798
  groupedResults[checkResult.group] = [checkResult];
@@ -10082,7 +10809,7 @@ ${expr}
10082
10809
  /**
10083
10810
  * Execute single check and return grouped result
10084
10811
  */
10085
- async executeSingleGroupedCheck(prInfo, checkName, timeout, config, logFn, debug) {
10812
+ async executeSingleGroupedCheck(prInfo, checkName, timeout, config, logFn, debug, _pauseGate) {
10086
10813
  if (!config?.checks?.[checkName]) {
10087
10814
  throw new Error(`No configuration found for check: ${checkName}`);
10088
10815
  }
@@ -10096,6 +10823,8 @@ ${expr}
10096
10823
  focus: checkConfig.focus || this.mapCheckNameToFocus(checkName),
10097
10824
  schema: checkConfig.schema,
10098
10825
  group: checkConfig.group,
10826
+ checkName,
10827
+ // propagate for fallback NDJSON attribution
10099
10828
  eventContext: this.enrichEventContext(prInfo.eventContext),
10100
10829
  ai: {
10101
10830
  timeout: timeout || 6e5,
@@ -10210,7 +10939,7 @@ ${expr}
10210
10939
  /**
10211
10940
  * Execute multiple checks with dependency awareness - return grouped results with statistics
10212
10941
  */
10213
- async executeGroupedDependencyAwareChecks(prInfo, checks, timeout, config, logFn, debug, maxParallelism, failFast) {
10942
+ async executeGroupedDependencyAwareChecks(prInfo, checks, timeout, config, logFn, debug, maxParallelism, failFast, pauseGate) {
10214
10943
  const reviewSummary = await this.executeDependencyAwareChecks(
10215
10944
  prInfo,
10216
10945
  checks,
@@ -10219,7 +10948,8 @@ ${expr}
10219
10948
  logFn,
10220
10949
  debug,
10221
10950
  maxParallelism,
10222
- failFast
10951
+ failFast,
10952
+ pauseGate
10223
10953
  );
10224
10954
  const executionStatistics = this.buildExecutionStatistics();
10225
10955
  const groupedResults = await this.convertReviewSummaryToGroupedResults(
@@ -10320,7 +11050,7 @@ ${expr}
10320
11050
  * - Enforcing .liquid file extension
10321
11051
  */
10322
11052
  async validateTemplatePath(templatePath) {
10323
- const path6 = await import("path");
11053
+ const path9 = await import("path");
10324
11054
  if (!templatePath || typeof templatePath !== "string" || templatePath.trim() === "") {
10325
11055
  throw new Error("Template path must be a non-empty string");
10326
11056
  }
@@ -10330,7 +11060,7 @@ ${expr}
10330
11060
  if (!templatePath.endsWith(".liquid")) {
10331
11061
  throw new Error("Template file must have .liquid extension");
10332
11062
  }
10333
- if (path6.isAbsolute(templatePath)) {
11063
+ if (path9.isAbsolute(templatePath)) {
10334
11064
  throw new Error("Template path must be relative to project directory");
10335
11065
  }
10336
11066
  if (templatePath.includes("..")) {
@@ -10344,14 +11074,14 @@ ${expr}
10344
11074
  if (!projectRoot || typeof projectRoot !== "string") {
10345
11075
  throw new Error("Unable to determine project root directory");
10346
11076
  }
10347
- const resolvedPath = path6.resolve(projectRoot, templatePath);
10348
- const resolvedProjectRoot = path6.resolve(projectRoot);
11077
+ const resolvedPath = path9.resolve(projectRoot, templatePath);
11078
+ const resolvedProjectRoot = path9.resolve(projectRoot);
10349
11079
  if (!resolvedPath || !resolvedProjectRoot || resolvedPath === "" || resolvedProjectRoot === "") {
10350
11080
  throw new Error(
10351
11081
  `Unable to resolve template path: projectRoot="${projectRoot}", templatePath="${templatePath}", resolvedPath="${resolvedPath}", resolvedProjectRoot="${resolvedProjectRoot}"`
10352
11082
  );
10353
11083
  }
10354
- if (!resolvedPath.startsWith(resolvedProjectRoot + path6.sep) && resolvedPath !== resolvedProjectRoot) {
11084
+ if (!resolvedPath.startsWith(resolvedProjectRoot + path9.sep) && resolvedPath !== resolvedProjectRoot) {
10355
11085
  throw new Error("Template path escapes project directory");
10356
11086
  }
10357
11087
  return resolvedPath;
@@ -10394,9 +11124,9 @@ ${expr}
10394
11124
  if (typeof directContent === "string" && directContent.trim()) {
10395
11125
  return directContent.trim();
10396
11126
  }
10397
- const { createExtendedLiquid: createExtendedLiquid2 } = await import("./liquid-extensions-GMEGEGC3.mjs");
10398
- const fs5 = await import("fs/promises");
10399
- const path6 = await import("path");
11127
+ const { createExtendedLiquid: createExtendedLiquid2 } = await import("./liquid-extensions-KVL4MKRH.mjs");
11128
+ const fs7 = await import("fs/promises");
11129
+ const path9 = await import("path");
10400
11130
  const liquid = createExtendedLiquid2({
10401
11131
  trimTagLeft: false,
10402
11132
  trimTagRight: false,
@@ -10419,7 +11149,7 @@ ${expr}
10419
11149
  templateContent = checkConfig.template.content;
10420
11150
  } else if (checkConfig.template.file) {
10421
11151
  const validatedPath = await this.validateTemplatePath(checkConfig.template.file);
10422
- templateContent = await fs5.readFile(validatedPath, "utf-8");
11152
+ templateContent = await fs7.readFile(validatedPath, "utf-8");
10423
11153
  } else {
10424
11154
  throw new Error('Custom template must specify either "file" or "content"');
10425
11155
  }
@@ -10430,8 +11160,8 @@ ${expr}
10430
11160
  if (!sanitizedSchema) {
10431
11161
  throw new Error("Invalid schema name");
10432
11162
  }
10433
- const templatePath = path6.join(__dirname, `output/${sanitizedSchema}/template.liquid`);
10434
- templateContent = await fs5.readFile(templatePath, "utf-8");
11163
+ const templatePath = path9.join(__dirname, `output/${sanitizedSchema}/template.liquid`);
11164
+ templateContent = await fs7.readFile(templatePath, "utf-8");
10435
11165
  if (sanitizedSchema === "issue-assistant") {
10436
11166
  enrichAssistantContext = true;
10437
11167
  }
@@ -10463,7 +11193,7 @@ ${expr}
10463
11193
  templateData.authorAssociation = authorAssociation;
10464
11194
  templateData.event = { name: eventName, action: eventAction };
10465
11195
  }
10466
- const { withPermissionsContext } = await import("./liquid-extensions-GMEGEGC3.mjs");
11196
+ const { withPermissionsContext } = await import("./liquid-extensions-KVL4MKRH.mjs");
10467
11197
  let authorAssociationForFilters;
10468
11198
  try {
10469
11199
  const anyInfo = _prInfo;
@@ -10487,7 +11217,7 @@ ${expr}
10487
11217
  }
10488
11218
  const finalRendered = rendered.trim();
10489
11219
  try {
10490
- const { emitMermaidFromMarkdown } = await import("./mermaid-telemetry-4DUEYCLE.mjs");
11220
+ const { emitMermaidFromMarkdown } = await import("./mermaid-telemetry-FBF6D35S.mjs");
10491
11221
  emitMermaidFromMarkdown(checkName, finalRendered, "content");
10492
11222
  } catch {
10493
11223
  }
@@ -10539,7 +11269,7 @@ ${expr}
10539
11269
  /**
10540
11270
  * Execute multiple checks with dependency awareness - intelligently parallel and sequential
10541
11271
  */
10542
- async executeDependencyAwareChecks(prInfo, checks, timeout, config, logFn, debug, maxParallelism, failFast) {
11272
+ async executeDependencyAwareChecks(prInfo, checks, timeout, config, logFn, debug, maxParallelism, failFast, pauseGate) {
10543
11273
  const log2 = logFn || console.error;
10544
11274
  if (debug) {
10545
11275
  log2(`\u{1F527} Debug: Starting dependency-aware execution of ${checks.length} checks`);
@@ -10674,6 +11404,13 @@ ${expr}
10674
11404
  }
10675
11405
  const levelChecks = executionGroup.parallel.filter((name) => !results.has(name));
10676
11406
  const levelTaskFunctions = levelChecks.map((checkName) => async () => {
11407
+ if (pauseGate) {
11408
+ try {
11409
+ await pauseGate();
11410
+ } catch {
11411
+ return { checkName, error: "__STOP__", result: null, skipped: true };
11412
+ }
11413
+ }
10677
11414
  if (results.has(checkName)) {
10678
11415
  if (debug) log2(`\u{1F527} Debug: Skipping ${checkName} (already satisfied earlier)`);
10679
11416
  return { checkName, error: null, result: results.get(checkName) };
@@ -10762,7 +11499,7 @@ ${expr}
10762
11499
  });
10763
11500
  if (!hasFatalFailure && config && (config.fail_if || config.checks[depId]?.fail_if)) {
10764
11501
  try {
10765
- hasFatalFailure = await this.failIfTriggered(depId, depRes, config);
11502
+ hasFatalFailure = await this.failIfTriggered(depId, depRes, config, results);
10766
11503
  } catch {
10767
11504
  }
10768
11505
  }
@@ -10995,7 +11732,9 @@ ${expr}
10995
11732
  const fRes = await this.evaluateFailureConditions(
10996
11733
  childName,
10997
11734
  childItemRes,
10998
- config
11735
+ config,
11736
+ prInfo,
11737
+ results
10999
11738
  );
11000
11739
  if (fRes.length > 0) {
11001
11740
  const fIssues = fRes.filter((f) => f.failed).map((f) => ({
@@ -11038,6 +11777,13 @@ ${expr}
11038
11777
  }
11039
11778
  };
11040
11779
  const itemTasks = forEachItems.map((item, itemIndex) => async () => {
11780
+ if (pauseGate) {
11781
+ try {
11782
+ await pauseGate();
11783
+ } catch {
11784
+ throw new Error("__STOP__");
11785
+ }
11786
+ }
11041
11787
  try {
11042
11788
  emitNdjsonSpanWithEvents(
11043
11789
  "visor.foreach.item",
@@ -11050,6 +11796,13 @@ ${expr}
11050
11796
  );
11051
11797
  } catch {
11052
11798
  }
11799
+ try {
11800
+ const span = trace4.getSpan(otContext4.active());
11801
+ if (span) {
11802
+ captureForEachState(span, forEachItems, itemIndex, item);
11803
+ }
11804
+ } catch {
11805
+ }
11053
11806
  const forEachDependencyResults = /* @__PURE__ */ new Map();
11054
11807
  for (const [depName, depResult] of dependencyResults) {
11055
11808
  if (forEachParents.includes(depName)) {
@@ -11100,7 +11853,9 @@ ${expr}
11100
11853
  const depFailures = await this.evaluateFailureConditions(
11101
11854
  depId,
11102
11855
  depItemRes,
11103
- config
11856
+ config,
11857
+ prInfo,
11858
+ results
11104
11859
  );
11105
11860
  hasFatalDepFailure = depFailures.some((f) => f.failed);
11106
11861
  } catch {
@@ -11176,7 +11931,9 @@ ${expr}
11176
11931
  const itemFailures = await this.evaluateFailureConditions(
11177
11932
  checkName,
11178
11933
  itemResult,
11179
- config
11934
+ config,
11935
+ prInfo,
11936
+ results
11180
11937
  );
11181
11938
  if (itemFailures.length > 0) {
11182
11939
  const failureIssues = itemFailures.filter((f) => f.failed).map((f) => ({
@@ -11346,7 +12103,13 @@ ${expr}
11346
12103
  { index: itemIndex, total: forEachItems.length, parent: forEachParentName }
11347
12104
  );
11348
12105
  if (config && (config.fail_if || nodeCfg.fail_if)) {
11349
- const fRes = await this.evaluateFailureConditions(node, nodeItemRes, config);
12106
+ const fRes = await this.evaluateFailureConditions(
12107
+ node,
12108
+ nodeItemRes,
12109
+ config,
12110
+ prInfo,
12111
+ results
12112
+ );
11350
12113
  if (fRes.length > 0) {
11351
12114
  const fIssues = fRes.filter((f) => f.failed).map((f) => ({
11352
12115
  file: "system",
@@ -11441,7 +12204,13 @@ ${expr}
11441
12204
  rForEval = { ...r, output: parsed };
11442
12205
  }
11443
12206
  }
11444
- const failures = await this.evaluateFailureConditions(parent, rForEval, config);
12207
+ const failures = await this.evaluateFailureConditions(
12208
+ parent,
12209
+ rForEval,
12210
+ config,
12211
+ prInfo,
12212
+ results
12213
+ );
11445
12214
  if (failures.some((f) => f.failed)) {
11446
12215
  }
11447
12216
  if (failures.some((f) => f.failed)) return true;
@@ -11560,7 +12329,9 @@ ${expr}
11560
12329
  const failures = await this.evaluateFailureConditions(
11561
12330
  checkName,
11562
12331
  r,
11563
- config
12332
+ config,
12333
+ prInfo,
12334
+ results
11564
12335
  );
11565
12336
  hadFatal = failures.some((f) => f.failed);
11566
12337
  } catch {
@@ -11667,7 +12438,9 @@ ${expr}
11667
12438
  const failureResults = await this.evaluateFailureConditions(
11668
12439
  checkName,
11669
12440
  finalResult,
11670
- config
12441
+ config,
12442
+ prInfo,
12443
+ results
11671
12444
  );
11672
12445
  if (failureResults.length > 0) {
11673
12446
  const failureIssues = failureResults.filter((f) => f.failed).map((f) => ({
@@ -11782,6 +12555,14 @@ ${error.stack || ""}` : String(error);
11782
12555
  const checkName = levelChecksList[i];
11783
12556
  const result = levelResults[i];
11784
12557
  const checkConfig = config.checks[checkName];
12558
+ if (result.status === "fulfilled" && result.value?.error === "__STOP__") {
12559
+ shouldStopExecution = true;
12560
+ break;
12561
+ }
12562
+ if (result.status === "rejected" && result.reason instanceof Error && result.reason.message === "__STOP__") {
12563
+ shouldStopExecution = true;
12564
+ break;
12565
+ }
11785
12566
  if (result.status === "fulfilled" && result.value.result && !result.value.error) {
11786
12567
  if (result.value.skipped) {
11787
12568
  if (debug) {
@@ -11842,6 +12623,21 @@ ${error.stack || ""}` : String(error);
11842
12623
  this.trackOutputHistory(checkName, reviewResultWithOutput.output);
11843
12624
  }
11844
12625
  results.set(checkName, reviewResult);
12626
+ try {
12627
+ const span = trace4.getSpan(otContext4.active());
12628
+ if (span) {
12629
+ const allOutputs = {};
12630
+ results.forEach((result2, name) => {
12631
+ if (result2.output !== void 0) {
12632
+ allOutputs[name] = result2.output;
12633
+ }
12634
+ });
12635
+ const memoryStore = MemoryStore.getInstance();
12636
+ const memoryData = await memoryStore.getAll();
12637
+ captureStateSnapshot(span, checkName, allOutputs, memoryData);
12638
+ }
12639
+ } catch {
12640
+ }
11845
12641
  } else {
11846
12642
  const errorSummary = {
11847
12643
  issues: [
@@ -12560,13 +13356,14 @@ ${result.value.result.debug.rawResponse}`;
12560
13356
  /**
12561
13357
  * Evaluate failure conditions for a check result
12562
13358
  */
12563
- async evaluateFailureConditions(checkName, reviewSummary, config, prInfo) {
13359
+ async evaluateFailureConditions(checkName, reviewSummary, config, prInfo, previousOutputs) {
12564
13360
  if (!config) {
12565
13361
  return [];
12566
13362
  }
12567
13363
  const checkConfig = config.checks[checkName];
12568
13364
  const checkSchema = typeof checkConfig?.schema === "object" ? "custom" : checkConfig?.schema || "";
12569
13365
  const checkGroup = checkConfig?.group || "";
13366
+ const outputsRecord = previousOutputs ? previousOutputs instanceof Map ? Object.fromEntries(previousOutputs.entries()) : previousOutputs : void 0;
12570
13367
  const globalFailIf = config.fail_if;
12571
13368
  const checkFailIf = checkConfig?.fail_if;
12572
13369
  if (globalFailIf || checkFailIf) {
@@ -12577,7 +13374,8 @@ ${result.value.result.debug.rawResponse}`;
12577
13374
  checkSchema,
12578
13375
  checkGroup,
12579
13376
  reviewSummary,
12580
- globalFailIf
13377
+ globalFailIf,
13378
+ outputsRecord
12581
13379
  );
12582
13380
  try {
12583
13381
  addEvent("fail_if.evaluated", {
@@ -12642,7 +13440,8 @@ ${result.value.result.debug.rawResponse}`;
12642
13440
  checkSchema,
12643
13441
  checkGroup,
12644
13442
  reviewSummary,
12645
- checkFailIf
13443
+ checkFailIf,
13444
+ outputsRecord
12646
13445
  );
12647
13446
  try {
12648
13447
  addEvent("fail_if.evaluated", {
@@ -13171,9 +13970,15 @@ ${result.value.result.debug.rawResponse}`;
13171
13970
  if (!issues || issues.length === 0) return false;
13172
13971
  return issues.some((i) => this.isFatalRule(i.ruleId || "", i.severity));
13173
13972
  }
13174
- async failIfTriggered(checkName, result, config) {
13973
+ async failIfTriggered(checkName, result, config, previousOutputs) {
13175
13974
  if (!config) return false;
13176
- const failures = await this.evaluateFailureConditions(checkName, result, config);
13975
+ const failures = await this.evaluateFailureConditions(
13976
+ checkName,
13977
+ result,
13978
+ config,
13979
+ void 0,
13980
+ previousOutputs
13981
+ );
13177
13982
  return failures.some((f) => f.failed);
13178
13983
  }
13179
13984
  /**
@@ -13286,4 +14091,4 @@ ${result.value.result.debug.rawResponse}`;
13286
14091
  export {
13287
14092
  CheckExecutionEngine
13288
14093
  };
13289
- //# sourceMappingURL=chunk-POYXI3MQ.mjs.map
14094
+ //# sourceMappingURL=chunk-X2JKUOE5.mjs.map