@probelabs/visor 0.1.92 → 0.1.94

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 (39) hide show
  1. package/defaults/.visor.yaml +15 -16
  2. package/dist/check-execution-engine.d.ts +11 -0
  3. package/dist/check-execution-engine.d.ts.map +1 -1
  4. package/dist/config.d.ts +3 -1
  5. package/dist/config.d.ts.map +1 -1
  6. package/dist/defaults/.visor.yaml +15 -16
  7. package/dist/index.js +587 -646
  8. package/dist/liquid-extensions.d.ts +12 -0
  9. package/dist/liquid-extensions.d.ts.map +1 -1
  10. package/dist/output/issue-assistant/schema.json +14 -1
  11. package/dist/providers/command-check-provider.d.ts.map +1 -1
  12. package/dist/providers/log-check-provider.d.ts.map +1 -1
  13. package/dist/providers/memory-check-provider.d.ts.map +1 -1
  14. package/dist/reviewer.d.ts +8 -0
  15. package/dist/reviewer.d.ts.map +1 -1
  16. package/dist/sdk/{check-execution-engine-L73PFZQY.mjs → check-execution-engine-YBRPVUWD.mjs} +3 -3
  17. package/dist/sdk/{chunk-LJHRU3WQ.mjs → chunk-DQRFOQAP.mjs} +160 -41
  18. package/dist/sdk/chunk-DQRFOQAP.mjs.map +1 -0
  19. package/dist/sdk/{chunk-2U6BIWSY.mjs → chunk-I3GQJIR7.mjs} +14 -10
  20. package/dist/sdk/chunk-I3GQJIR7.mjs.map +1 -0
  21. package/dist/sdk/{liquid-extensions-AFKRYROF.mjs → liquid-extensions-GMEGEGC3.mjs} +6 -2
  22. package/dist/sdk/sdk.d.mts +11 -2
  23. package/dist/sdk/sdk.d.ts +11 -2
  24. package/dist/sdk/sdk.js +202 -50
  25. package/dist/sdk/sdk.js.map +1 -1
  26. package/dist/sdk/sdk.mjs +36 -7
  27. package/dist/sdk/sdk.mjs.map +1 -1
  28. package/dist/sdk.d.ts +11 -2
  29. package/dist/sdk.d.ts.map +1 -1
  30. package/dist/traces/{run-2025-10-15T07-21-47-696Z.ndjson → run-2025-10-16T11-33-32-682Z.ndjson} +19 -3
  31. package/dist/traces/{run-2025-10-15T07-21-58-106Z.ndjson → run-2025-10-16T11-33-43-618Z.ndjson} +19 -3
  32. package/dist/traces/{run-2025-10-15T07-21-58-693Z.ndjson → run-2025-10-16T11-33-44-157Z.ndjson} +19 -3
  33. package/dist/traces/{run-2025-10-15T07-21-59-167Z.ndjson → run-2025-10-16T11-33-44-647Z.ndjson} +19 -3
  34. package/dist/traces/{run-2025-10-15T07-21-59-629Z.ndjson → run-2025-10-16T11-33-45-128Z.ndjson} +4 -0
  35. package/package.json +2 -2
  36. package/dist/sdk/chunk-2U6BIWSY.mjs.map +0 -1
  37. package/dist/sdk/chunk-LJHRU3WQ.mjs.map +0 -1
  38. /package/dist/sdk/{check-execution-engine-L73PFZQY.mjs.map → check-execution-engine-YBRPVUWD.mjs.map} +0 -0
  39. /package/dist/sdk/{liquid-extensions-AFKRYROF.mjs.map → liquid-extensions-GMEGEGC3.mjs.map} +0 -0
package/dist/sdk/sdk.js CHANGED
@@ -2424,14 +2424,66 @@ var init_reviewer = __esm({
2424
2424
  "No configuration provided. Please create a .visor.yaml file with check definitions. Built-in prompts have been removed - all checks must be explicitly configured."
2425
2425
  );
2426
2426
  }
2427
+ /**
2428
+ * Helper to check if a schema definition has a "text" field in its properties
2429
+ */
2430
+ async schemaHasTextField(schema) {
2431
+ try {
2432
+ let schemaObj;
2433
+ if (typeof schema === "object") {
2434
+ schemaObj = schema;
2435
+ } else {
2436
+ const fs12 = require("fs").promises;
2437
+ const path12 = require("path");
2438
+ const sanitizedSchemaName = schema.replace(/[^a-zA-Z0-9-]/g, "");
2439
+ if (!sanitizedSchemaName || sanitizedSchemaName !== schema) {
2440
+ return false;
2441
+ }
2442
+ const schemaPath = path12.join(process.cwd(), "output", sanitizedSchemaName, "schema.json");
2443
+ try {
2444
+ const schemaContent = await fs12.readFile(schemaPath, "utf-8");
2445
+ schemaObj = JSON.parse(schemaContent);
2446
+ } catch {
2447
+ return false;
2448
+ }
2449
+ }
2450
+ const properties = schemaObj.properties;
2451
+ return !!(properties && "text" in properties);
2452
+ } catch {
2453
+ return false;
2454
+ }
2455
+ }
2456
+ /**
2457
+ * Filter check results to only include those that should post GitHub comments
2458
+ */
2459
+ async filterCommentGeneratingChecks(checkResults, config) {
2460
+ const filtered = [];
2461
+ for (const r of checkResults) {
2462
+ const cfg = config.checks?.[r.checkName];
2463
+ const type = cfg?.type || "ai";
2464
+ const schema = cfg?.schema;
2465
+ let shouldPostComment = false;
2466
+ const isAICheck = type === "ai" || type === "claude-code";
2467
+ if (!schema || schema === "") {
2468
+ shouldPostComment = isAICheck;
2469
+ } else if (typeof schema === "string") {
2470
+ if (schema === "text" || schema === "plain") {
2471
+ shouldPostComment = true;
2472
+ } else {
2473
+ shouldPostComment = await this.schemaHasTextField(schema);
2474
+ }
2475
+ } else if (typeof schema === "object") {
2476
+ shouldPostComment = await this.schemaHasTextField(schema);
2477
+ }
2478
+ if (shouldPostComment) {
2479
+ filtered.push(r);
2480
+ }
2481
+ }
2482
+ return filtered;
2483
+ }
2427
2484
  async postReviewComment(owner, repo, prNumber, groupedResults, options = {}) {
2428
2485
  for (const [groupName, checkResults] of Object.entries(groupedResults)) {
2429
- const filteredResults = options.config ? checkResults.filter((r) => {
2430
- const cfg = options.config.checks?.[r.checkName];
2431
- const t = cfg?.type || "";
2432
- const isGitHubOps = t === "github" || r.group === "github" || t === "noop" || t === "command";
2433
- return !isGitHubOps;
2434
- }) : checkResults;
2486
+ const filteredResults = options.config ? await this.filterCommentGeneratingChecks(checkResults, options.config) : checkResults;
2435
2487
  if (!filteredResults || filteredResults.length === 0) {
2436
2488
  continue;
2437
2489
  }
@@ -3923,8 +3975,19 @@ __export(liquid_extensions_exports, {
3923
3975
  ReadFileTag: () => ReadFileTag,
3924
3976
  configureLiquidWithExtensions: () => configureLiquidWithExtensions,
3925
3977
  createExtendedLiquid: () => createExtendedLiquid,
3978
+ sanitizeLabel: () => sanitizeLabel,
3979
+ sanitizeLabelList: () => sanitizeLabelList,
3926
3980
  withPermissionsContext: () => withPermissionsContext
3927
3981
  });
3982
+ function sanitizeLabel(value) {
3983
+ if (value == null) return "";
3984
+ const s = String(value);
3985
+ return s.replace(/[^A-Za-z0-9:\/]/g, "").replace(/\/{2,}/g, "/");
3986
+ }
3987
+ function sanitizeLabelList(labels) {
3988
+ if (!Array.isArray(labels)) return [];
3989
+ return labels.map((v) => sanitizeLabel(v)).filter((s) => s.length > 0);
3990
+ }
3928
3991
  async function withPermissionsContext(ctx, fn) {
3929
3992
  return await permissionsALS.run(ctx, fn);
3930
3993
  }
@@ -3947,15 +4010,8 @@ function configureLiquidWithExtensions(liquid) {
3947
4010
  return "[Error: Unable to serialize to JSON]";
3948
4011
  }
3949
4012
  });
3950
- liquid.registerFilter("safe_label", (value) => {
3951
- if (value == null) return "";
3952
- const s = String(value);
3953
- return s.replace(/[^A-Za-z0-9:\/]/g, "").replace(/\/{2,}/g, "/");
3954
- });
3955
- liquid.registerFilter("safe_label_list", (value) => {
3956
- if (!Array.isArray(value)) return [];
3957
- return value.map((v) => v == null ? "" : String(v)).map((s) => s.replace(/[^A-Za-z0-9:\/]/g, "").replace(/\/{2,}/g, "/")).filter((s) => s.length > 0);
3958
- });
4013
+ liquid.registerFilter("safe_label", (value) => sanitizeLabel(value));
4014
+ liquid.registerFilter("safe_label_list", (value) => sanitizeLabelList(value));
3959
4015
  liquid.registerFilter("unescape_newlines", (value) => {
3960
4016
  if (value == null) return "";
3961
4017
  const s = String(value);
@@ -5123,7 +5179,8 @@ var init_log_check_provider = __esm({
5123
5179
  dependencyResults,
5124
5180
  includePrContext,
5125
5181
  includeDependencies,
5126
- includeMetadata
5182
+ includeMetadata,
5183
+ config.__outputHistory
5127
5184
  );
5128
5185
  const renderedMessage = await this.liquid.parseAndRender(message, templateContext);
5129
5186
  const logOutput = this.formatLogOutput(
@@ -5144,7 +5201,7 @@ var init_log_check_provider = __esm({
5144
5201
  logOutput
5145
5202
  };
5146
5203
  }
5147
- buildTemplateContext(prInfo, dependencyResults, _includePrContext = true, _includeDependencies = true, includeMetadata = true) {
5204
+ buildTemplateContext(prInfo, dependencyResults, _includePrContext = true, _includeDependencies = true, includeMetadata = true, outputHistory) {
5148
5205
  const context2 = {};
5149
5206
  context2.pr = {
5150
5207
  number: prInfo.number,
@@ -5168,6 +5225,7 @@ var init_log_check_provider = __esm({
5168
5225
  if (dependencyResults) {
5169
5226
  const dependencies = {};
5170
5227
  const outputs = {};
5228
+ const history = {};
5171
5229
  context2.dependencyCount = dependencyResults.size;
5172
5230
  for (const [checkName, result] of dependencyResults.entries()) {
5173
5231
  dependencies[checkName] = {
@@ -5178,6 +5236,12 @@ var init_log_check_provider = __esm({
5178
5236
  const summary = result;
5179
5237
  outputs[checkName] = summary.output !== void 0 ? summary.output : summary;
5180
5238
  }
5239
+ if (outputHistory) {
5240
+ for (const [checkName, historyArray] of outputHistory) {
5241
+ history[checkName] = historyArray;
5242
+ }
5243
+ }
5244
+ outputs.history = history;
5181
5245
  context2.dependencies = dependencies;
5182
5246
  context2.outputs = outputs;
5183
5247
  }
@@ -6080,7 +6144,10 @@ var init_command_check_provider = __esm({
6080
6144
  },
6081
6145
  files: prInfo.files,
6082
6146
  fileCount: prInfo.files.length,
6083
- outputs: this.buildOutputContext(dependencyResults),
6147
+ outputs: this.buildOutputContext(
6148
+ dependencyResults,
6149
+ config.__outputHistory
6150
+ ),
6084
6151
  env: this.getSafeEnvironmentVariables()
6085
6152
  };
6086
6153
  logger.debug(
@@ -6806,16 +6873,23 @@ ${stderrOutput}` : `Command execution failed: ${errorMessage}`;
6806
6873
  };
6807
6874
  }
6808
6875
  }
6809
- buildOutputContext(dependencyResults) {
6876
+ buildOutputContext(dependencyResults, outputHistory) {
6810
6877
  if (!dependencyResults) {
6811
6878
  return {};
6812
6879
  }
6813
6880
  const outputs = {};
6881
+ const history = {};
6814
6882
  for (const [checkName, result] of dependencyResults) {
6815
6883
  const summary = result;
6816
6884
  const value = summary.output !== void 0 ? summary.output : summary;
6817
6885
  outputs[checkName] = this.makeJsonSmart(value);
6818
6886
  }
6887
+ if (outputHistory) {
6888
+ for (const [checkName, historyArray] of outputHistory) {
6889
+ history[checkName] = historyArray.map((val) => this.makeJsonSmart(val));
6890
+ }
6891
+ }
6892
+ outputs.history = history;
6819
6893
  return outputs;
6820
6894
  }
6821
6895
  /**
@@ -7373,7 +7447,12 @@ var init_memory_check_provider = __esm({
7373
7447
  const key = config.key;
7374
7448
  const namespace = config.namespace;
7375
7449
  const memoryStore = MemoryStore.getInstance();
7376
- const templateContext = this.buildTemplateContext(prInfo, dependencyResults, memoryStore);
7450
+ const templateContext = this.buildTemplateContext(
7451
+ prInfo,
7452
+ dependencyResults,
7453
+ memoryStore,
7454
+ config.__outputHistory
7455
+ );
7377
7456
  let result;
7378
7457
  try {
7379
7458
  switch (operation) {
@@ -7656,7 +7735,7 @@ var init_memory_check_provider = __esm({
7656
7735
  /**
7657
7736
  * Build template context for Liquid and JS evaluation
7658
7737
  */
7659
- buildTemplateContext(prInfo, dependencyResults, memoryStore) {
7738
+ buildTemplateContext(prInfo, dependencyResults, memoryStore, outputHistory) {
7660
7739
  const context2 = {};
7661
7740
  context2.pr = {
7662
7741
  number: prInfo.number,
@@ -7675,14 +7754,21 @@ var init_memory_check_provider = __esm({
7675
7754
  changes: f.changes
7676
7755
  }))
7677
7756
  };
7757
+ const outputs = {};
7758
+ const history = {};
7678
7759
  if (dependencyResults) {
7679
- const outputs = {};
7680
7760
  for (const [checkName, result] of dependencyResults.entries()) {
7681
7761
  const summary = result;
7682
7762
  outputs[checkName] = summary.output !== void 0 ? summary.output : summary;
7683
7763
  }
7684
- context2.outputs = outputs;
7685
7764
  }
7765
+ if (outputHistory) {
7766
+ for (const [checkName, historyArray] of outputHistory) {
7767
+ history[checkName] = historyArray;
7768
+ }
7769
+ }
7770
+ outputs.history = history;
7771
+ context2.outputs = outputs;
7686
7772
  if (memoryStore) {
7687
7773
  context2.memory = {
7688
7774
  get: (key, ns) => memoryStore.get(key, ns),
@@ -10677,6 +10763,8 @@ var init_check_execution_engine = __esm({
10677
10763
  webhookContext;
10678
10764
  routingSandbox;
10679
10765
  executionStats = /* @__PURE__ */ new Map();
10766
+ // Track history of all outputs for each check (useful for loops and goto)
10767
+ outputHistory = /* @__PURE__ */ new Map();
10680
10768
  // Event override to simulate alternate event (used during routing goto)
10681
10769
  routingEventOverride;
10682
10770
  // Cached GitHub context for context elevation when running in Actions
@@ -10696,6 +10784,18 @@ var init_check_execution_engine = __esm({
10696
10784
  this.mockOctokit = this.createMockOctokit();
10697
10785
  this.reviewer = new PRReviewer(this.mockOctokit);
10698
10786
  }
10787
+ /**
10788
+ * Enrich event context with authenticated octokit instance
10789
+ * @param eventContext - The event context to enrich
10790
+ * @returns Enriched event context with octokit if available
10791
+ */
10792
+ enrichEventContext(eventContext) {
10793
+ const baseContext = eventContext || {};
10794
+ if (this.actionContext?.octokit) {
10795
+ return { ...baseContext, octokit: this.actionContext.octokit };
10796
+ }
10797
+ return baseContext;
10798
+ }
10699
10799
  /**
10700
10800
  * Lazily create a secure sandbox for routing JS (goto_js, run_js)
10701
10801
  */
@@ -10895,10 +10995,6 @@ ${expr}
10895
10995
  const providerType = targetCfg.type || "ai";
10896
10996
  const prov = this.providerRegistry.getProviderOrThrow(providerType);
10897
10997
  this.setProviderWebhookContext(prov);
10898
- const enrichedEventContext = {
10899
- ...prInfo.eventContext,
10900
- ...this.actionContext?.octokit ? { octokit: this.actionContext.octokit } : {}
10901
- };
10902
10998
  const provCfg = {
10903
10999
  type: providerType,
10904
11000
  prompt: targetCfg.prompt,
@@ -10907,11 +11003,13 @@ ${expr}
10907
11003
  schema: targetCfg.schema,
10908
11004
  group: targetCfg.group,
10909
11005
  checkName: target,
10910
- eventContext: enrichedEventContext,
11006
+ eventContext: this.enrichEventContext(prInfo.eventContext),
10911
11007
  transform: targetCfg.transform,
10912
11008
  transform_js: targetCfg.transform_js,
10913
11009
  env: targetCfg.env,
10914
11010
  forEach: targetCfg.forEach,
11011
+ // Pass output history for loop/goto scenarios
11012
+ __outputHistory: this.outputHistory,
10915
11013
  // Include provider-specific keys (e.g., op/values for github)
10916
11014
  ...targetCfg,
10917
11015
  ai: {
@@ -10979,6 +11077,10 @@ ${expr}
10979
11077
  timestamp: Date.now()
10980
11078
  }));
10981
11079
  const enriched = { ...r, issues: enrichedIssues };
11080
+ const enrichedWithOutput = enriched;
11081
+ if (enrichedWithOutput.output !== void 0) {
11082
+ this.trackOutputHistory(target, enrichedWithOutput.output);
11083
+ }
10982
11084
  resultsMap?.set(target, enriched);
10983
11085
  if (debug) log2(`\u{1F527} Debug: inline executed '${target}', issues: ${enrichedIssues.length}`);
10984
11086
  return enriched;
@@ -11548,8 +11650,7 @@ ${expr}
11548
11650
  const providerConfig = {
11549
11651
  type: checks[0],
11550
11652
  prompt: "all",
11551
- eventContext: prInfo.eventContext,
11552
- // Pass event context for templates
11653
+ eventContext: this.enrichEventContext(prInfo.eventContext),
11553
11654
  ai: timeout ? { timeout } : void 0
11554
11655
  };
11555
11656
  const result = await provider.execute(prInfo, providerConfig);
@@ -11583,8 +11684,7 @@ ${expr}
11583
11684
  type: "ai",
11584
11685
  prompt: focus2,
11585
11686
  focus: focus2,
11586
- eventContext: prInfo.eventContext,
11587
- // Pass event context for templates
11687
+ eventContext: this.enrichEventContext(prInfo.eventContext),
11588
11688
  ai: timeout ? { timeout } : void 0,
11589
11689
  // Inherit global AI provider and model settings if config is available
11590
11690
  ai_provider: config?.ai_provider,
@@ -11739,8 +11839,7 @@ ${expr}
11739
11839
  focus: checkConfig.focus || this.mapCheckNameToFocus(checkName),
11740
11840
  schema: checkConfig.schema,
11741
11841
  group: checkConfig.group,
11742
- eventContext: prInfo.eventContext,
11743
- // Pass event context for templates
11842
+ eventContext: this.enrichEventContext(prInfo.eventContext),
11744
11843
  ai: {
11745
11844
  timeout: timeout || 6e5,
11746
11845
  debug,
@@ -11750,6 +11849,8 @@ ${expr}
11750
11849
  ai_model: checkConfig.ai_model || config.ai_model,
11751
11850
  // Pass claude_code config if present
11752
11851
  claude_code: checkConfig.claude_code,
11852
+ // Pass output history for loop/goto scenarios
11853
+ __outputHistory: this.outputHistory,
11753
11854
  // Pass any provider-specific config
11754
11855
  ...checkConfig
11755
11856
  };
@@ -11786,10 +11887,14 @@ ${expr}
11786
11887
  }
11787
11888
  }
11788
11889
  const content = await this.renderCheckContent(checkName, result, checkConfig, prInfo);
11890
+ let group = checkConfig.group || "default";
11891
+ if (config?.output?.pr_comment?.group_by === "check" && !checkConfig.group) {
11892
+ group = checkName;
11893
+ }
11789
11894
  return {
11790
11895
  checkName,
11791
11896
  content,
11792
- group: checkConfig.group || "default",
11897
+ group,
11793
11898
  output: result.output,
11794
11899
  debug: result.debug,
11795
11900
  issues: result.issues
@@ -11928,16 +12033,19 @@ ${expr}
11928
12033
  }
11929
12034
  ];
11930
12035
  }
12036
+ let group = checkConfig.group || "default";
12037
+ if (config?.output?.pr_comment?.group_by === "check" && !checkConfig.group) {
12038
+ group = checkName;
12039
+ }
11931
12040
  const checkResult = {
11932
12041
  checkName,
11933
12042
  content,
11934
- group: checkConfig.group || "default",
12043
+ group,
11935
12044
  output: checkSummary.output,
11936
12045
  debug: reviewSummary.debug,
11937
12046
  issues: issuesForCheck
11938
12047
  // Include structured issues + rendering error if any
11939
12048
  };
11940
- const group = checkResult.group;
11941
12049
  if (!groupedResults[group]) {
11942
12050
  groupedResults[group] = [];
11943
12051
  }
@@ -12344,8 +12452,7 @@ ${expr}
12344
12452
  group: checkConfig.group,
12345
12453
  checkName,
12346
12454
  // Add checkName for sessionID
12347
- eventContext: prInfo.eventContext,
12348
- // Pass event context for templates
12455
+ eventContext: this.enrichEventContext(prInfo.eventContext),
12349
12456
  transform: checkConfig.transform,
12350
12457
  transform_js: checkConfig.transform_js,
12351
12458
  // Important: pass through provider-level timeout from check config
@@ -12532,7 +12639,7 @@ ${expr}
12532
12639
  schema: childCfg.schema,
12533
12640
  group: childCfg.group,
12534
12641
  checkName: childName,
12535
- eventContext: prInfo.eventContext,
12642
+ eventContext: this.enrichEventContext(prInfo.eventContext),
12536
12643
  transform: childCfg.transform,
12537
12644
  transform_js: childCfg.transform_js,
12538
12645
  env: childCfg.env,
@@ -12839,6 +12946,10 @@ ${expr}
12839
12946
  itemResult.issues || [],
12840
12947
  itemResult.output
12841
12948
  );
12949
+ const itemOutput = itemResult.output;
12950
+ if (itemOutput !== void 0) {
12951
+ this.trackOutputHistory(checkName, itemOutput);
12952
+ }
12842
12953
  const descendantSet = (() => {
12843
12954
  const visited = /* @__PURE__ */ new Set();
12844
12955
  const stack = [checkName];
@@ -12926,7 +13037,7 @@ ${expr}
12926
13037
  schema: nodeCfg.schema,
12927
13038
  group: nodeCfg.group,
12928
13039
  checkName: node,
12929
- eventContext: prInfo.eventContext,
13040
+ eventContext: this.enrichEventContext(prInfo.eventContext),
12930
13041
  transform: nodeCfg.transform,
12931
13042
  transform_js: nodeCfg.transform_js,
12932
13043
  env: nodeCfg.env,
@@ -13469,6 +13580,10 @@ ${error.stack || ""}` : String(error);
13469
13580
  ]);
13470
13581
  } catch {
13471
13582
  }
13583
+ const reviewResultWithOutput = reviewResult;
13584
+ if (reviewResultWithOutput.output !== void 0) {
13585
+ this.trackOutputHistory(checkName, reviewResultWithOutput.output);
13586
+ }
13472
13587
  results.set(checkName, reviewResult);
13473
13588
  } else {
13474
13589
  const errorSummary = {
@@ -13622,8 +13737,7 @@ ${error.stack || ""}` : String(error);
13622
13737
  focus: checkConfig.focus || this.mapCheckNameToFocus(checkName),
13623
13738
  schema: checkConfig.schema,
13624
13739
  group: checkConfig.group,
13625
- eventContext: prInfo.eventContext,
13626
- // Pass event context for templates
13740
+ eventContext: this.enrichEventContext(prInfo.eventContext),
13627
13741
  ai: {
13628
13742
  timeout: timeout || 6e5,
13629
13743
  debug,
@@ -13699,8 +13813,7 @@ ${error.stack || ""}` : String(error);
13699
13813
  focus: checkConfig.focus || this.mapCheckNameToFocus(checkName),
13700
13814
  schema: checkConfig.schema,
13701
13815
  group: checkConfig.group,
13702
- eventContext: prInfo.eventContext,
13703
- // Pass event context for templates
13816
+ eventContext: this.enrichEventContext(prInfo.eventContext),
13704
13817
  ai: {
13705
13818
  timeout: timeout || 6e5,
13706
13819
  ...checkConfig.ai || {}
@@ -14714,6 +14827,16 @@ ${result.value.result.debug.rawResponse}`;
14714
14827
  stats.outputsProduced = (stats.outputsProduced || 0) + 1;
14715
14828
  }
14716
14829
  }
14830
+ /**
14831
+ * Track output in history for loop/goto scenarios
14832
+ */
14833
+ trackOutputHistory(checkName, output) {
14834
+ if (output === void 0) return;
14835
+ if (!this.outputHistory.has(checkName)) {
14836
+ this.outputHistory.set(checkName, []);
14837
+ }
14838
+ this.outputHistory.get(checkName).push(output);
14839
+ }
14717
14840
  /**
14718
14841
  * Record that a check was skipped
14719
14842
  */
@@ -16817,8 +16940,10 @@ var ConfigManager = class {
16817
16940
  }
16818
16941
  /**
16819
16942
  * Validate configuration against schema
16943
+ * @param config The config to validate
16944
+ * @param strict If true, treat warnings as errors (default: false)
16820
16945
  */
16821
- validateConfig(config) {
16946
+ validateConfig(config, strict = false) {
16822
16947
  const errors = [];
16823
16948
  const warnings = [];
16824
16949
  this.validateWithAjvSchema(config, errors, warnings);
@@ -16926,10 +17051,13 @@ var ConfigManager = class {
16926
17051
  if (config.tag_filter) {
16927
17052
  this.validateTagFilter(config.tag_filter, errors);
16928
17053
  }
17054
+ if (strict && warnings.length > 0) {
17055
+ errors.push(...warnings);
17056
+ }
16929
17057
  if (errors.length > 0) {
16930
17058
  throw new Error(errors[0].message);
16931
17059
  }
16932
- if (warnings.length > 0) {
17060
+ if (!strict && warnings.length > 0) {
16933
17061
  for (const w of warnings) {
16934
17062
  logger.warn(`\u26A0\uFE0F Config warning [${w.field}]: ${w.message}`);
16935
17063
  }
@@ -17385,9 +17513,25 @@ var __ajvValidate = null;
17385
17513
  var __ajvErrors = null;
17386
17514
 
17387
17515
  // src/sdk.ts
17388
- async function loadConfig(configPath) {
17516
+ async function loadConfig(configOrPath, options) {
17389
17517
  const cm = new ConfigManager();
17390
- if (configPath) return cm.loadConfig(configPath);
17518
+ if (typeof configOrPath === "object" && configOrPath !== null) {
17519
+ cm.validateConfig(configOrPath, options?.strict ?? false);
17520
+ const defaultConfig = {
17521
+ version: "1.0",
17522
+ checks: {},
17523
+ max_parallelism: 3,
17524
+ fail_fast: false
17525
+ };
17526
+ return {
17527
+ ...defaultConfig,
17528
+ ...configOrPath,
17529
+ checks: configOrPath.checks || {}
17530
+ };
17531
+ }
17532
+ if (typeof configOrPath === "string") {
17533
+ return cm.loadConfig(configOrPath);
17534
+ }
17391
17535
  return cm.findAndLoadConfig();
17392
17536
  }
17393
17537
  function resolveChecks(checkIds, config) {
@@ -17413,7 +17557,15 @@ function resolveChecks(checkIds, config) {
17413
17557
  }
17414
17558
  async function runChecks(opts = {}) {
17415
17559
  const cm = new ConfigManager();
17416
- const config = opts.config ? opts.config : opts.configPath ? await cm.loadConfig(opts.configPath) : await cm.findAndLoadConfig();
17560
+ let config;
17561
+ if (opts.config) {
17562
+ cm.validateConfig(opts.config, opts.strictValidation ?? false);
17563
+ config = opts.config;
17564
+ } else if (opts.configPath) {
17565
+ config = await cm.loadConfig(opts.configPath);
17566
+ } else {
17567
+ config = await cm.findAndLoadConfig();
17568
+ }
17417
17569
  const checks = opts.checks && opts.checks.length > 0 ? resolveChecks(opts.checks, config) : Object.keys(config.checks || {});
17418
17570
  const engine = new CheckExecutionEngine(opts.cwd);
17419
17571
  const result = await engine.executeChecks({