@probelabs/visor 0.1.125 → 0.1.127

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 (82) hide show
  1. package/dist/ai-review-service.d.ts +1 -0
  2. package/dist/ai-review-service.d.ts.map +1 -1
  3. package/dist/config.d.ts.map +1 -1
  4. package/dist/docs/ai-configuration.md +23 -0
  5. package/dist/docs/slack-integration.md +6 -0
  6. package/dist/docs/workflows.md +30 -1
  7. package/dist/failure-condition-evaluator.d.ts.map +1 -1
  8. package/dist/frontends/slack-frontend.d.ts +2 -0
  9. package/dist/frontends/slack-frontend.d.ts.map +1 -1
  10. package/dist/generated/config-schema.d.ts +18 -6
  11. package/dist/generated/config-schema.d.ts.map +1 -1
  12. package/dist/generated/config-schema.json +18 -6
  13. package/dist/index.js +7138 -7865
  14. package/dist/liquid-extensions.d.ts.map +1 -1
  15. package/dist/output/traces/{run-2026-01-30T09-25-36-981Z.ndjson → run-2026-02-01T09-59-08-165Z.ndjson} +84 -84
  16. package/dist/output/traces/{run-2026-01-30T09-26-20-969Z.ndjson → run-2026-02-01T09-59-52-595Z.ndjson} +1015 -1015
  17. package/dist/providers/ai-check-provider.d.ts.map +1 -1
  18. package/dist/providers/command-check-provider.d.ts.map +1 -1
  19. package/dist/providers/custom-tool-executor.d.ts.map +1 -1
  20. package/dist/providers/http-client-provider.d.ts.map +1 -1
  21. package/dist/providers/mcp-check-provider.d.ts.map +1 -1
  22. package/dist/providers/memory-check-provider.d.ts.map +1 -1
  23. package/dist/providers/workflow-check-provider.d.ts.map +1 -1
  24. package/dist/sdk/{check-provider-registry-3KI5RKXT.mjs → check-provider-registry-CVUONJ5A.mjs} +8 -8
  25. package/dist/sdk/{chunk-7GUAFV6L.mjs → chunk-BHOKBQPB.mjs} +15 -10
  26. package/dist/sdk/chunk-BHOKBQPB.mjs.map +1 -0
  27. package/dist/sdk/{chunk-PXFIALUH.mjs → chunk-EORMDOZU.mjs} +19 -62
  28. package/dist/sdk/chunk-EORMDOZU.mjs.map +1 -0
  29. package/dist/sdk/{chunk-SWEEZ5D5.mjs → chunk-MPS4HVQI.mjs} +11 -77
  30. package/dist/sdk/chunk-MPS4HVQI.mjs.map +1 -0
  31. package/dist/sdk/{chunk-A4PGHURG.mjs → chunk-TS6BUNAI.mjs} +130 -75
  32. package/dist/sdk/chunk-TS6BUNAI.mjs.map +1 -0
  33. package/dist/sdk/{chunk-J6EVEXC2.mjs → chunk-VW2GBXQT.mjs} +56 -8
  34. package/dist/sdk/chunk-VW2GBXQT.mjs.map +1 -0
  35. package/dist/sdk/{chunk-RTKJXNZS.mjs → chunk-XWJPT5KQ.mjs} +26 -9
  36. package/dist/sdk/chunk-XWJPT5KQ.mjs.map +1 -0
  37. package/dist/sdk/{config-6CUVEH7H.mjs → config-DXX64GD3.mjs} +2 -2
  38. package/dist/sdk/{failure-condition-evaluator-HB35XRLZ.mjs → failure-condition-evaluator-G4HMJPXF.mjs} +3 -3
  39. package/dist/sdk/{github-frontend-BZ4N3BFZ.mjs → github-frontend-5PCKKHVC.mjs} +3 -3
  40. package/dist/sdk/{host-NYWXLIFC.mjs → host-H3AWNZ2F.mjs} +3 -3
  41. package/dist/sdk/{liquid-extensions-DFDEBMUI.mjs → liquid-extensions-I7O7KMHF.mjs} +3 -2
  42. package/dist/sdk/{routing-7FXPULTO.mjs → routing-QHTGDIXF.mjs} +4 -4
  43. package/dist/sdk/sdk.d.mts +6 -0
  44. package/dist/sdk/sdk.d.ts +6 -0
  45. package/dist/sdk/sdk.js +255 -214
  46. package/dist/sdk/sdk.js.map +1 -1
  47. package/dist/sdk/sdk.mjs +11 -11
  48. package/dist/sdk/{slack-frontend-J442FJWZ.mjs → slack-frontend-JUT3TYVC.mjs} +34 -6
  49. package/dist/sdk/slack-frontend-JUT3TYVC.mjs.map +1 -0
  50. package/dist/sdk/{workflow-check-provider-YUNNF4KC.mjs → workflow-check-provider-3IWBAZP7.mjs} +8 -8
  51. package/dist/sdk/{workflow-registry-6LZKCWHP.mjs → workflow-registry-KFWSDSLM.mjs} +2 -2
  52. package/dist/slack/socket-runner.d.ts +2 -0
  53. package/dist/slack/socket-runner.d.ts.map +1 -1
  54. package/dist/state-machine/dispatch/stats-manager.d.ts.map +1 -1
  55. package/dist/state-machine/states/routing.d.ts.map +1 -1
  56. package/dist/test-runner/index.d.ts.map +1 -1
  57. package/dist/test-runner/validator.d.ts.map +1 -1
  58. package/dist/traces/{run-2026-01-30T09-25-36-981Z.ndjson → run-2026-02-01T09-59-08-165Z.ndjson} +84 -84
  59. package/dist/traces/{run-2026-01-30T09-26-20-969Z.ndjson → run-2026-02-01T09-59-52-595Z.ndjson} +1015 -1015
  60. package/dist/types/config.d.ts +6 -0
  61. package/dist/types/config.d.ts.map +1 -1
  62. package/dist/utils/worktree-manager.d.ts.map +1 -1
  63. package/dist/workflow-executor.d.ts.map +1 -1
  64. package/dist/workflow-registry.d.ts +1 -0
  65. package/dist/workflow-registry.d.ts.map +1 -1
  66. package/package.json +5 -6
  67. package/dist/sdk/chunk-7GUAFV6L.mjs.map +0 -1
  68. package/dist/sdk/chunk-A4PGHURG.mjs.map +0 -1
  69. package/dist/sdk/chunk-J6EVEXC2.mjs.map +0 -1
  70. package/dist/sdk/chunk-PXFIALUH.mjs.map +0 -1
  71. package/dist/sdk/chunk-RTKJXNZS.mjs.map +0 -1
  72. package/dist/sdk/chunk-SWEEZ5D5.mjs.map +0 -1
  73. package/dist/sdk/slack-frontend-J442FJWZ.mjs.map +0 -1
  74. /package/dist/sdk/{check-provider-registry-3KI5RKXT.mjs.map → check-provider-registry-CVUONJ5A.mjs.map} +0 -0
  75. /package/dist/sdk/{config-6CUVEH7H.mjs.map → config-DXX64GD3.mjs.map} +0 -0
  76. /package/dist/sdk/{failure-condition-evaluator-HB35XRLZ.mjs.map → failure-condition-evaluator-G4HMJPXF.mjs.map} +0 -0
  77. /package/dist/sdk/{github-frontend-BZ4N3BFZ.mjs.map → github-frontend-5PCKKHVC.mjs.map} +0 -0
  78. /package/dist/sdk/{host-NYWXLIFC.mjs.map → host-H3AWNZ2F.mjs.map} +0 -0
  79. /package/dist/sdk/{liquid-extensions-DFDEBMUI.mjs.map → liquid-extensions-I7O7KMHF.mjs.map} +0 -0
  80. /package/dist/sdk/{routing-7FXPULTO.mjs.map → routing-QHTGDIXF.mjs.map} +0 -0
  81. /package/dist/sdk/{workflow-check-provider-YUNNF4KC.mjs.map → workflow-check-provider-3IWBAZP7.mjs.map} +0 -0
  82. /package/dist/sdk/{workflow-registry-6LZKCWHP.mjs.map → workflow-registry-KFWSDSLM.mjs.map} +0 -0
package/dist/sdk/sdk.js CHANGED
@@ -2986,79 +2986,14 @@ var init_failure_condition_evaluator = __esm({
2986
2986
  if (!this.sandbox) {
2987
2987
  this.sandbox = this.createSecureSandbox();
2988
2988
  }
2989
- let result;
2989
+ let exec2;
2990
2990
  try {
2991
- let exec2;
2992
- try {
2993
- exec2 = this.sandbox.compile(`return (${raw});`);
2994
- } catch {
2995
- const normalizedExpr = normalize3(condition);
2996
- exec2 = this.sandbox.compile(`return (${normalizedExpr});`);
2997
- }
2998
- result = exec2(scope).run();
2999
- } catch (_primaryErr) {
3000
- try {
3001
- const vm = require("vm");
3002
- const ctx = {
3003
- // Scope vars
3004
- output,
3005
- outputs,
3006
- debug: debugData,
3007
- memory: memoryAccessor,
3008
- issues,
3009
- metadata,
3010
- criticalIssues,
3011
- errorIssues,
3012
- totalIssues,
3013
- warningIssues,
3014
- infoIssues,
3015
- checkName,
3016
- schema,
3017
- group,
3018
- branch,
3019
- baseBranch,
3020
- filesChanged,
3021
- filesCount,
3022
- event,
3023
- env,
3024
- inputs,
3025
- // Helpers
3026
- contains,
3027
- startsWith,
3028
- endsWith,
3029
- length,
3030
- always,
3031
- success,
3032
- failure,
3033
- log: log2,
3034
- hasIssue,
3035
- countIssues,
3036
- hasFileMatching,
3037
- hasIssueWith,
3038
- hasFileWith,
3039
- hasMinPermission: hasMinPermission2,
3040
- isOwner: isOwner2,
3041
- isMember: isMember2,
3042
- isCollaborator: isCollaborator2,
3043
- isContributor: isContributor2,
3044
- isFirstTimer: isFirstTimer2,
3045
- Math,
3046
- JSON
3047
- };
3048
- const context3 = vm.createContext(ctx);
3049
- let code = `(${raw})`;
3050
- try {
3051
- result = new vm.Script(code).runInContext(context3, { timeout: 50 });
3052
- } catch {
3053
- const normalizedExpr = normalize3(condition);
3054
- code = `(${normalizedExpr})`;
3055
- result = new vm.Script(code).runInContext(context3, { timeout: 50 });
3056
- }
3057
- } catch (vmErr) {
3058
- console.error("\u274C Failed to evaluate expression:", condition, vmErr);
3059
- throw vmErr;
3060
- }
2991
+ exec2 = this.sandbox.compile(`return (${raw});`);
2992
+ } catch {
2993
+ const normalizedExpr = normalize3(condition);
2994
+ exec2 = this.sandbox.compile(`return (${normalizedExpr});`);
3061
2995
  }
2996
+ const result = exec2(scope).run();
3062
2997
  try {
3063
2998
  (init_logger(), __toCommonJS(logger_exports)).logger.debug(` fail_if: result=${Boolean(result)}`);
3064
2999
  } catch {
@@ -3447,9 +3382,10 @@ function classifyFailure(result) {
3447
3382
  for (const iss of issues) {
3448
3383
  const id = String(iss.ruleId || "");
3449
3384
  const msg = String(iss.message || "");
3385
+ const msgLower = msg.toLowerCase();
3450
3386
  if (id.endsWith("_fail_if") || id.includes("contract/guarantee_failed") || id.includes("contract/schema_validation_failed"))
3451
3387
  hasLogical = true;
3452
- if (id.includes("/execution_error") || msg.includes("Command execution failed"))
3388
+ if (id.endsWith("/error") || id.includes("/execution_error") || id.includes("timeout") || msgLower.includes("timed out") || msg.includes("Command execution failed"))
3453
3389
  hasExecution = true;
3454
3390
  if (id.includes("forEach/execution_error") || msg.includes("sandbox_runner_error"))
3455
3391
  hasExecution = true;
@@ -4465,18 +4401,10 @@ async function evaluateRunJs(runJs, checkId, checkConfig, result, context2, _sta
4465
4401
  { injectLog: false, wrapFunction: false }
4466
4402
  );
4467
4403
  return Array.isArray(evalResult) ? evalResult.filter(Boolean) : [];
4468
- } catch (_e) {
4469
- try {
4470
- const vm = require("vm");
4471
- const context3 = vm.createContext({ scope: scopeObj, console: { log: () => {
4472
- } } });
4473
- const src = `(() => { ${runJs}
4474
- })()`;
4475
- const val = new vm.Script(src).runInContext(context3, { timeout: 100 });
4476
- return Array.isArray(val) ? val.filter((x) => typeof x === "string" && x) : [];
4477
- } catch (_vmErr) {
4478
- return [];
4479
- }
4404
+ } catch (error) {
4405
+ const msg = error instanceof Error ? error.message : String(error);
4406
+ logger.error(`[Routing] Error in run_js sandbox evaluation: ${msg}`);
4407
+ return [];
4480
4408
  }
4481
4409
  } catch (error) {
4482
4410
  const msg = error instanceof Error ? error.message : String(error);
@@ -4582,25 +4510,9 @@ async function evaluateGoto(gotoJs, gotoStatic, checkId, checkConfig, result, co
4582
4510
  if (typeof evalResult === "string" && evalResult) {
4583
4511
  return evalResult;
4584
4512
  }
4585
- } catch (_e) {
4586
- try {
4587
- const vm = require("vm");
4588
- const contextObj = {
4589
- step: scopeObj.step,
4590
- outputs: scopeObj.outputs,
4591
- outputs_history: scopeObj.outputs_history,
4592
- output: scopeObj.output,
4593
- memory: scopeObj.memory,
4594
- event: scopeObj.event,
4595
- forEach: scopeObj.forEach
4596
- };
4597
- const vmctx = vm.createContext(contextObj);
4598
- const src = `(() => { ${gotoJs}
4599
- })()`;
4600
- const res = new vm.Script(src).runInContext(vmctx, { timeout: 100 });
4601
- if (typeof res === "string" && res) return res;
4602
- } catch (_vmErr) {
4603
- }
4513
+ } catch (error) {
4514
+ const msg = error instanceof Error ? error.message : String(error);
4515
+ logger.error(`[Routing] Error in goto_js sandbox evaluation: ${msg}`);
4604
4516
  }
4605
4517
  } catch (error) {
4606
4518
  const msg = error instanceof Error ? error.message : String(error);
@@ -4675,29 +4587,10 @@ async function evaluateTransitions(transitions, checkId, checkConfig, result, co
4675
4587
  { scope: scopeObj },
4676
4588
  { injectLog: false, wrapFunction: false }
4677
4589
  );
4678
- } catch (_e) {
4679
- try {
4680
- const vm = require("vm");
4681
- const helpersFns = {
4682
- any: (arr, pred) => Array.isArray(arr) && arr.some(pred),
4683
- all: (arr, pred) => Array.isArray(arr) && arr.every(pred),
4684
- none: (arr, pred) => Array.isArray(arr) && !arr.some(pred),
4685
- count: (arr, pred) => Array.isArray(arr) ? arr.filter(pred).length : 0
4686
- };
4687
- const context3 = vm.createContext({
4688
- step: scopeObj.step,
4689
- outputs: scopeObj.outputs,
4690
- outputs_history: scopeObj.outputs_history,
4691
- output: scopeObj.output,
4692
- memory: scopeObj.memory,
4693
- event: scopeObj.event,
4694
- ...helpersFns
4695
- });
4696
- const res = new vm.Script(`(${rule.when})`).runInContext(context3, { timeout: 50 });
4697
- matched = !!res;
4698
- } catch (_vmErr) {
4699
- matched = false;
4700
- }
4590
+ } catch (error) {
4591
+ const msg = error instanceof Error ? error.message : String(error);
4592
+ logger.warn(`[Routing] Error evaluating transition 'when' clause: ${msg}`);
4593
+ matched = false;
4701
4594
  }
4702
4595
  if (matched) {
4703
4596
  if (rule.to === null) return null;
@@ -5141,22 +5034,21 @@ function configureLiquidWithExtensions(liquid) {
5141
5034
  const name = typeof varName === "string" && varName.trim() ? varName.trim() : "i";
5142
5035
  const body = String(expr || "");
5143
5036
  try {
5144
- const fn = new Function(
5145
- name,
5146
- "idx",
5147
- "arr",
5148
- `try { return (${body}); } catch { return false; }`
5149
- );
5037
+ const sandbox = createSecureSandbox();
5150
5038
  const out = [];
5151
5039
  for (let idx = 0; idx < arr.length; idx++) {
5152
- const i = arr[idx];
5040
+ const item = arr[idx];
5153
5041
  let ok = false;
5154
5042
  try {
5155
- ok = !!fn(i, idx, arr);
5043
+ const scope = { [name]: item, idx, arr };
5044
+ ok = !!compileAndRun(sandbox, body, scope, {
5045
+ injectLog: false,
5046
+ wrapFunction: true
5047
+ });
5156
5048
  } catch {
5157
5049
  ok = false;
5158
5050
  }
5159
- if (ok) out.push(i);
5051
+ if (ok) out.push(item);
5160
5052
  }
5161
5053
  return out;
5162
5054
  } catch {
@@ -5351,6 +5243,7 @@ var init_liquid_extensions = __esm({
5351
5243
  import_path2 = __toESM(require("path"));
5352
5244
  init_author_permissions();
5353
5245
  init_memory_store();
5246
+ init_sandbox();
5354
5247
  ReadFileTag = class extends import_liquidjs.Tag {
5355
5248
  filepath;
5356
5249
  constructor(token, remainTokens, liquid) {
@@ -5462,7 +5355,7 @@ function hasFatalIssues(result) {
5462
5355
  if (!result.issues) return false;
5463
5356
  return result.issues.some((issue) => {
5464
5357
  const ruleId = issue.ruleId || "";
5465
- return ruleId.endsWith("/error") || ruleId.includes("/execution_error") || ruleId.endsWith("_fail_if");
5358
+ return ruleId.endsWith("/error") || ruleId.includes("/execution_error") || ruleId.includes("timeout") || ruleId.endsWith("_fail_if");
5466
5359
  });
5467
5360
  }
5468
5361
  function updateStats(results, state, isForEachIteration = false) {
@@ -7244,6 +7137,9 @@ ${"=".repeat(60)}
7244
7137
  // Use systemPrompt (native in rc168+) with fallback to customPrompt for backward compat
7245
7138
  systemPrompt: systemPrompt || this.config.systemPrompt || this.config.customPrompt
7246
7139
  };
7140
+ if (this.config.maxIterations !== void 0) {
7141
+ options.maxIterations = this.config.maxIterations;
7142
+ }
7247
7143
  let traceFilePath = "";
7248
7144
  let telemetryConfig = null;
7249
7145
  let probeFileTracer = null;
@@ -7733,9 +7629,12 @@ ${"=".repeat(60)}
7733
7629
  const errMsg = parseErr instanceof Error ? parseErr.message : String(parseErr);
7734
7630
  log(`\u{1F50D} Direct JSON parsing failed: ${errMsg}`);
7735
7631
  if (response.toLowerCase().includes("i cannot") || response.toLowerCase().includes("unable to")) {
7736
- console.error("\u{1F6AB} AI refused to analyze - returning empty result");
7632
+ console.error("\u{1F6AB} AI refused to analyze - returning refusal as output");
7633
+ const trimmed2 = response.trim();
7737
7634
  return {
7738
- issues: []
7635
+ issues: [],
7636
+ output: trimmed2 ? { text: trimmed2 } : {},
7637
+ debug: debugInfo
7739
7638
  };
7740
7639
  }
7741
7640
  log("\u{1F527} Treating response as plain text (no JSON extraction)");
@@ -8685,9 +8584,7 @@ var init_custom_tool_executor = __esm({
8685
8584
  * Apply JavaScript transform to output
8686
8585
  */
8687
8586
  async applyJavaScriptTransform(transformJs, output, context2) {
8688
- if (!this.sandbox) {
8689
- this.sandbox = createSecureSandbox();
8690
- }
8587
+ this.sandbox = createSecureSandbox();
8691
8588
  const code = `
8692
8589
  const output = ${JSON.stringify(output)};
8693
8590
  const context = ${JSON.stringify(context2)};
@@ -8833,12 +8730,45 @@ var init_workflow_registry = __esm({
8833
8730
  * Import workflows from a file or URL
8834
8731
  */
8835
8732
  async import(source, options) {
8733
+ return this.importInternal(source, options, /* @__PURE__ */ new Set());
8734
+ }
8735
+ async importInternal(source, options, visited) {
8836
8736
  const results = [];
8837
8737
  try {
8838
- const content = await this.loadWorkflowContent(source, options?.basePath);
8839
- const data = this.parseWorkflowContent(content, source);
8738
+ const { content, resolvedSource, importBasePath } = await this.loadWorkflowContent(
8739
+ source,
8740
+ options?.basePath
8741
+ );
8742
+ const visitKey = resolvedSource || source;
8743
+ if (visited.has(visitKey)) {
8744
+ return results;
8745
+ }
8746
+ visited.add(visitKey);
8747
+ const data = this.parseWorkflowContent(content, resolvedSource || source);
8748
+ const topImports = !Array.isArray(data) ? data?.imports : void 0;
8749
+ if (Array.isArray(topImports)) {
8750
+ for (const childSource of topImports) {
8751
+ const childResults = await this.importInternal(
8752
+ childSource,
8753
+ { ...options, basePath: importBasePath },
8754
+ visited
8755
+ );
8756
+ results.push(...childResults);
8757
+ }
8758
+ }
8840
8759
  const workflows = Array.isArray(data) ? data : [data];
8841
8760
  for (const workflow of workflows) {
8761
+ const workflowImports = workflow?.imports;
8762
+ if (Array.isArray(workflowImports)) {
8763
+ for (const childSource of workflowImports) {
8764
+ const childResults = await this.importInternal(
8765
+ childSource,
8766
+ { ...options, basePath: importBasePath },
8767
+ visited
8768
+ );
8769
+ results.push(...childResults);
8770
+ }
8771
+ }
8842
8772
  if (options?.validate !== false) {
8843
8773
  const validation = this.validateWorkflow(workflow);
8844
8774
  if (!validation.valid) {
@@ -8855,9 +8785,12 @@ var init_workflow_registry = __esm({
8855
8785
  }
8856
8786
  }
8857
8787
  }
8858
- const workflowWithoutTests = { ...workflow };
8859
- delete workflowWithoutTests.tests;
8860
- const result = this.register(workflowWithoutTests, source, { override: options?.override });
8788
+ const workflowWithoutExtras = { ...workflow };
8789
+ delete workflowWithoutExtras.tests;
8790
+ delete workflowWithoutExtras.imports;
8791
+ const result = this.register(workflowWithoutExtras, source, {
8792
+ override: options?.override
8793
+ });
8861
8794
  results.push(result);
8862
8795
  }
8863
8796
  } catch (error) {
@@ -9015,15 +8948,27 @@ var init_workflow_registry = __esm({
9015
8948
  * Load workflow content from file or URL
9016
8949
  */
9017
8950
  async loadWorkflowContent(source, basePath) {
8951
+ const baseIsUrl = basePath?.startsWith("http://") || basePath?.startsWith("https://");
9018
8952
  if (source.startsWith("http://") || source.startsWith("https://")) {
9019
8953
  const response = await fetch(source);
9020
8954
  if (!response.ok) {
9021
8955
  throw new Error(`Failed to fetch workflow from ${source}: ${response.statusText}`);
9022
8956
  }
9023
- return await response.text();
8957
+ const importBasePath = new URL(".", source).toString();
8958
+ return { content: await response.text(), resolvedSource: source, importBasePath };
8959
+ }
8960
+ if (baseIsUrl) {
8961
+ const resolvedUrl = new URL(source, basePath).toString();
8962
+ const response = await fetch(resolvedUrl);
8963
+ if (!response.ok) {
8964
+ throw new Error(`Failed to fetch workflow from ${resolvedUrl}: ${response.statusText}`);
8965
+ }
8966
+ const importBasePath = new URL(".", resolvedUrl).toString();
8967
+ return { content: await response.text(), resolvedSource: resolvedUrl, importBasePath };
9024
8968
  }
9025
8969
  const filePath = path8.isAbsolute(source) ? source : path8.resolve(basePath || process.cwd(), source);
9026
- return await import_fs.promises.readFile(filePath, "utf-8");
8970
+ const content = await import_fs.promises.readFile(filePath, "utf-8");
8971
+ return { content, resolvedSource: filePath, importBasePath: path8.dirname(filePath) };
9027
8972
  }
9028
8973
  /**
9029
8974
  * Parse workflow content (YAML or JSON)
@@ -9081,7 +9026,7 @@ var init_workflow_registry = __esm({
9081
9026
  });
9082
9027
 
9083
9028
  // src/workflow-executor.ts
9084
- var import_liquidjs2, WorkflowExecutor;
9029
+ var WorkflowExecutor;
9085
9030
  var init_workflow_executor = __esm({
9086
9031
  "src/workflow-executor.ts"() {
9087
9032
  "use strict";
@@ -9089,12 +9034,12 @@ var init_workflow_executor = __esm({
9089
9034
  init_dependency_resolver();
9090
9035
  init_logger();
9091
9036
  init_sandbox();
9092
- import_liquidjs2 = require("liquidjs");
9037
+ init_liquid_extensions();
9093
9038
  WorkflowExecutor = class {
9094
9039
  providerRegistry = null;
9095
9040
  liquid;
9096
9041
  constructor() {
9097
- this.liquid = new import_liquidjs2.Liquid();
9042
+ this.liquid = createExtendedLiquid();
9098
9043
  }
9099
9044
  /**
9100
9045
  * Lazy-load the provider registry to avoid circular dependency during initialization
@@ -10594,6 +10539,10 @@ var init_config_schema = __esm({
10594
10539
  type: "string",
10595
10540
  description: "Probe promptType for this check (underscore style)"
10596
10541
  },
10542
+ ai_max_iterations: {
10543
+ type: "number",
10544
+ description: "Maximum tool iterations for ProbeAgent (underscore style)"
10545
+ },
10597
10546
  ai_system_prompt: {
10598
10547
  type: "string",
10599
10548
  description: "System prompt for this check (underscore style)"
@@ -10877,7 +10826,7 @@ var init_config_schema = __esm({
10877
10826
  description: "Arguments/inputs for the workflow"
10878
10827
  },
10879
10828
  overrides: {
10880
- $ref: "#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-11359-23582-src_types_config.ts-0-41182%3E%3E",
10829
+ $ref: "#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-11434-23754-src_types_config.ts-0-41453%3E%3E",
10881
10830
  description: "Override specific step configurations in the workflow"
10882
10831
  },
10883
10832
  output_mapping: {
@@ -10893,7 +10842,7 @@ var init_config_schema = __esm({
10893
10842
  description: "Config file path - alternative to workflow ID (loads a Visor config file as workflow)"
10894
10843
  },
10895
10844
  workflow_overrides: {
10896
- $ref: "#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-11359-23582-src_types_config.ts-0-41182%3E%3E",
10845
+ $ref: "#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-11434-23754-src_types_config.ts-0-41453%3E%3E",
10897
10846
  description: "Alias for overrides - workflow step overrides (backward compatibility)"
10898
10847
  },
10899
10848
  ref: {
@@ -11025,6 +10974,10 @@ var init_config_schema = __esm({
11025
10974
  type: "number",
11026
10975
  description: "Request timeout in milliseconds"
11027
10976
  },
10977
+ max_iterations: {
10978
+ type: "number",
10979
+ description: "Maximum tool iterations for ProbeAgent"
10980
+ },
11028
10981
  debug: {
11029
10982
  type: "boolean",
11030
10983
  description: "Enable debug mode"
@@ -11523,7 +11476,7 @@ var init_config_schema = __esm({
11523
11476
  description: "Custom output name (defaults to workflow name)"
11524
11477
  },
11525
11478
  overrides: {
11526
- $ref: "#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-11359-23582-src_types_config.ts-0-41182%3E%3E",
11479
+ $ref: "#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-11434-23754-src_types_config.ts-0-41453%3E%3E",
11527
11480
  description: "Step overrides"
11528
11481
  },
11529
11482
  output_mapping: {
@@ -11538,13 +11491,13 @@ var init_config_schema = __esm({
11538
11491
  "^x-": {}
11539
11492
  }
11540
11493
  },
11541
- "Record<string,Partial<interface-src_types_config.ts-11359-23582-src_types_config.ts-0-41182>>": {
11494
+ "Record<string,Partial<interface-src_types_config.ts-11434-23754-src_types_config.ts-0-41453>>": {
11542
11495
  type: "object",
11543
11496
  additionalProperties: {
11544
- $ref: "#/definitions/Partial%3Cinterface-src_types_config.ts-11359-23582-src_types_config.ts-0-41182%3E"
11497
+ $ref: "#/definitions/Partial%3Cinterface-src_types_config.ts-11434-23754-src_types_config.ts-0-41453%3E"
11545
11498
  }
11546
11499
  },
11547
- "Partial<interface-src_types_config.ts-11359-23582-src_types_config.ts-0-41182>": {
11500
+ "Partial<interface-src_types_config.ts-11434-23754-src_types_config.ts-0-41453>": {
11548
11501
  type: "object",
11549
11502
  additionalProperties: false
11550
11503
  },
@@ -12153,6 +12106,10 @@ var init_config_schema = __esm({
12153
12106
  type: "string",
12154
12107
  description: "Thread handling: 'required', 'optional', etc."
12155
12108
  },
12109
+ allow_bot_messages: {
12110
+ type: "boolean",
12111
+ description: "Allow bot_message events to trigger runs (default: false)"
12112
+ },
12156
12113
  show_raw_output: {
12157
12114
  type: "boolean",
12158
12115
  description: "Show raw output in Slack responses"
@@ -12602,6 +12559,11 @@ ${errors}`);
12602
12559
  const results = await registry.import(source, { basePath, validate: true });
12603
12560
  for (const result of results) {
12604
12561
  if (!result.valid && result.errors) {
12562
+ const isAlreadyExists = result.errors.every((e) => e.message.includes("already exists"));
12563
+ if (isAlreadyExists) {
12564
+ logger.debug(`Workflow from '${source}' already imported, skipping`);
12565
+ continue;
12566
+ }
12605
12567
  const errors = result.errors.map((e) => ` ${e.path}: ${e.message}`).join("\n");
12606
12568
  throw new Error(`Failed to import workflow from '${source}':
12607
12569
  ${errors}`);
@@ -13349,7 +13311,7 @@ var workflow_check_provider_exports = {};
13349
13311
  __export(workflow_check_provider_exports, {
13350
13312
  WorkflowCheckProvider: () => WorkflowCheckProvider
13351
13313
  });
13352
- var import_liquidjs3, WorkflowCheckProvider;
13314
+ var WorkflowCheckProvider;
13353
13315
  var init_workflow_check_provider = __esm({
13354
13316
  "src/providers/workflow-check-provider.ts"() {
13355
13317
  "use strict";
@@ -13359,7 +13321,7 @@ var init_workflow_check_provider = __esm({
13359
13321
  init_logger();
13360
13322
  init_sandbox();
13361
13323
  init_human_id();
13362
- import_liquidjs3 = require("liquidjs");
13324
+ init_liquid_extensions();
13363
13325
  WorkflowCheckProvider = class extends CheckProvider {
13364
13326
  registry;
13365
13327
  executor;
@@ -13368,7 +13330,7 @@ var init_workflow_check_provider = __esm({
13368
13330
  super();
13369
13331
  this.registry = WorkflowRegistry.getInstance();
13370
13332
  this.executor = new WorkflowExecutor();
13371
- this.liquid = new import_liquidjs3.Liquid();
13333
+ this.liquid = createExtendedLiquid();
13372
13334
  }
13373
13335
  getName() {
13374
13336
  return "workflow";
@@ -13793,7 +13755,6 @@ var init_workflow_check_provider = __esm({
13793
13755
  if (!workflow.outputs) {
13794
13756
  return outputs;
13795
13757
  }
13796
- const sandbox = createSecureSandbox();
13797
13758
  const flat = {};
13798
13759
  try {
13799
13760
  for (const arr of Object.values(groupedResults || {})) {
@@ -13812,6 +13773,7 @@ var init_workflow_check_provider = __esm({
13812
13773
  );
13813
13774
  for (const output of workflow.outputs) {
13814
13775
  if (output.value_js) {
13776
+ const sandbox = createSecureSandbox();
13815
13777
  outputs[output.name] = compileAndRun(
13816
13778
  sandbox,
13817
13779
  output.value_js,
@@ -13909,26 +13871,25 @@ var init_workflow_check_provider = __esm({
13909
13871
  if (rawData.imports && Array.isArray(rawData.imports)) {
13910
13872
  const configDir = path22.dirname(resolved);
13911
13873
  for (const source of rawData.imports) {
13912
- try {
13913
- const results = await this.registry.import(source, {
13914
- basePath: configDir,
13915
- validate: true
13916
- });
13917
- for (const result of results) {
13918
- if (!result.valid && result.errors) {
13919
- const errors = result.errors.map((e) => ` ${e.path}: ${e.message}`).join("\n");
13920
- throw new Error(`Failed to import workflow from '${source}':
13921
- ${errors}`);
13874
+ const results = await this.registry.import(source, {
13875
+ basePath: configDir,
13876
+ validate: true
13877
+ });
13878
+ for (const result of results) {
13879
+ if (!result.valid && result.errors) {
13880
+ const isAlreadyExists = result.errors.every(
13881
+ (e) => e.message.includes("already exists")
13882
+ );
13883
+ if (isAlreadyExists) {
13884
+ logger.debug(`Workflow from '${source}' already imported, skipping`);
13885
+ continue;
13922
13886
  }
13923
- }
13924
- logger.info(`Imported workflows from: ${source}`);
13925
- } catch (err) {
13926
- if (err.message?.includes("already exists")) {
13927
- logger.debug(`Workflow from '${source}' already imported, skipping`);
13928
- } else {
13929
- throw err;
13887
+ const errors = result.errors.map((e) => ` ${e.path}: ${e.message}`).join("\n");
13888
+ throw new Error(`Failed to import workflow from '${source}':
13889
+ ${errors}`);
13930
13890
  }
13931
13891
  }
13892
+ logger.info(`Imported workflows from: ${source}`);
13932
13893
  }
13933
13894
  }
13934
13895
  const { ConfigManager: ConfigManager2 } = (init_config(), __toCommonJS(config_exports));
@@ -15173,6 +15134,10 @@ ${preview}`);
15173
15134
  if (aiAny2.timeout !== void 0) {
15174
15135
  aiConfig.timeout = aiAny2.timeout;
15175
15136
  }
15137
+ if (aiAny2.max_iterations !== void 0 || aiAny2.maxIterations !== void 0) {
15138
+ const raw = aiAny2.max_iterations ?? aiAny2.maxIterations;
15139
+ aiConfig.maxIterations = Number(raw);
15140
+ }
15176
15141
  if (aiAny2.provider !== void 0) {
15177
15142
  aiConfig.provider = aiAny2.provider;
15178
15143
  }
@@ -15307,6 +15272,9 @@ ${preview}`);
15307
15272
  if (config.ai_provider !== void 0) {
15308
15273
  aiConfig.provider = config.ai_provider;
15309
15274
  }
15275
+ if (config.ai_max_iterations !== void 0 && aiConfig.maxIterations === void 0) {
15276
+ aiConfig.maxIterations = config.ai_max_iterations;
15277
+ }
15310
15278
  const customPrompt = config.prompt;
15311
15279
  if (!customPrompt) {
15312
15280
  throw new Error(
@@ -15709,6 +15677,7 @@ ${processedPrompt}` : processedPrompt;
15709
15677
  "ai.model",
15710
15678
  "ai.apiKey",
15711
15679
  "ai.timeout",
15680
+ "ai.max_iterations",
15712
15681
  "ai.mcpServers",
15713
15682
  "ai.enableDelegate",
15714
15683
  // legacy persona/prompt keys supported in config
@@ -15716,6 +15685,7 @@ ${processedPrompt}` : processedPrompt;
15716
15685
  "ai_prompt_type",
15717
15686
  "ai_custom_prompt",
15718
15687
  "ai_system_prompt",
15688
+ "ai_max_iterations",
15719
15689
  // new provider resilience and tools toggles
15720
15690
  "ai.retry",
15721
15691
  "ai.fallback",
@@ -16402,9 +16372,7 @@ var init_http_client_provider = __esm({
16402
16372
  }
16403
16373
  if (transformJs) {
16404
16374
  try {
16405
- if (!this.sandbox) {
16406
- this.sandbox = this.createSecureSandbox();
16407
- }
16375
+ this.sandbox = this.createSecureSandbox();
16408
16376
  const jsScope = {
16409
16377
  output: data,
16410
16378
  pr: templateContext.pr,
@@ -18255,23 +18223,6 @@ ${bodyWithReturn}
18255
18223
  { injectLog: false, wrapFunction: false }
18256
18224
  );
18257
18225
  }
18258
- try {
18259
- if (finalOutput && typeof finalOutput === "object" && !Array.isArray(finalOutput) && (finalOutput.error === void 0 || finalOutput.issues === void 0)) {
18260
- const vm = await import("vm");
18261
- const vmContext = vm.createContext({ scope: jsContext });
18262
- const vmCode = `
18263
- (function(){
18264
- const output = scope.output; const pr = scope.pr; const files = scope.files; const outputs = scope.outputs; const env = scope.env; const log = ()=>{};
18265
- ${bodyWithReturn}
18266
- })()
18267
- `;
18268
- const vmResult = vm.runInContext(vmCode, vmContext, { timeout: 1e3 });
18269
- if (vmResult && typeof vmResult === "object") {
18270
- finalOutput = vmResult;
18271
- }
18272
- }
18273
- } catch {
18274
- }
18275
18226
  let finalSnapshot = null;
18276
18227
  try {
18277
18228
  if (finalOutput && typeof finalOutput === "object" && !Array.isArray(finalOutput)) {
@@ -18758,6 +18709,11 @@ ${bodyWithReturn}
18758
18709
  isTimeout = true;
18759
18710
  }
18760
18711
  }
18712
+ if (!isTimeout && error instanceof Error) {
18713
+ if (/timed out/i.test(error.message)) {
18714
+ isTimeout = true;
18715
+ }
18716
+ }
18761
18717
  let stderrOutput = "";
18762
18718
  if (error && typeof error === "object") {
18763
18719
  const execError = error;
@@ -19519,9 +19475,7 @@ var init_memory_check_provider = __esm({
19519
19475
  * Evaluate JavaScript expression in context using SandboxJS for secure execution
19520
19476
  */
19521
19477
  evaluateJavaScript(expression, context2) {
19522
- if (!this.sandbox) {
19523
- this.sandbox = this.createSecureSandbox();
19524
- }
19478
+ this.sandbox = this.createSecureSandbox();
19525
19479
  try {
19526
19480
  const scope = { ...context2 };
19527
19481
  return compileAndRun(this.sandbox, `return (${expression});`, scope, {
@@ -19754,9 +19708,7 @@ var init_mcp_check_provider = __esm({
19754
19708
  }
19755
19709
  if (cfg.transform_js) {
19756
19710
  try {
19757
- if (!this.sandbox) {
19758
- this.sandbox = this.createSecureSandbox();
19759
- }
19711
+ this.sandbox = this.createSecureSandbox();
19760
19712
  const scope = {
19761
19713
  output: finalOutput,
19762
19714
  pr: templateContext.pr,
@@ -21498,6 +21450,53 @@ var init_worktree_manager = __esm({
21498
21450
  const metadata2 = await this.loadMetadata(worktreePath);
21499
21451
  if (metadata2) {
21500
21452
  if (metadata2.ref === ref) {
21453
+ try {
21454
+ const bareRepoPath2 = metadata2.bare_repo_path || await this.getOrCreateBareRepo(
21455
+ repository,
21456
+ repoUrl,
21457
+ options.token,
21458
+ options.fetchDepth,
21459
+ options.cloneTimeoutMs
21460
+ );
21461
+ const fetched2 = await this.fetchRef(bareRepoPath2, ref);
21462
+ if (fetched2) {
21463
+ const latestCommit = await this.getCommitShaForRef(bareRepoPath2, ref);
21464
+ if (latestCommit && latestCommit !== metadata2.commit) {
21465
+ logger.info(
21466
+ `Worktree ref ${ref} advanced (${metadata2.commit} -> ${latestCommit}), updating...`
21467
+ );
21468
+ const checkoutCmd = `git -C ${this.escapeShellArg(worktreePath)} checkout --detach ${this.escapeShellArg(latestCommit)}`;
21469
+ const checkoutResult = await this.executeGitCommand(checkoutCmd, {
21470
+ timeout: 6e4
21471
+ });
21472
+ if (checkoutResult.exitCode !== 0) {
21473
+ throw new Error(`Failed to checkout updated ref: ${checkoutResult.stderr}`);
21474
+ }
21475
+ const updatedMetadata = {
21476
+ ...metadata2,
21477
+ commit: latestCommit,
21478
+ created_at: (/* @__PURE__ */ new Date()).toISOString()
21479
+ };
21480
+ await this.saveMetadata(worktreePath, updatedMetadata);
21481
+ if (options.clean) {
21482
+ logger.debug(`Cleaning updated worktree`);
21483
+ await this.cleanWorktree(worktreePath);
21484
+ }
21485
+ this.activeWorktrees.set(worktreeId, updatedMetadata);
21486
+ return {
21487
+ id: worktreeId,
21488
+ path: worktreePath,
21489
+ ref: updatedMetadata.ref,
21490
+ commit: updatedMetadata.commit,
21491
+ metadata: updatedMetadata,
21492
+ locked: false
21493
+ };
21494
+ }
21495
+ }
21496
+ } catch (error) {
21497
+ const errorMessage = error instanceof Error ? error.message : String(error);
21498
+ logger.warn(`Failed to refresh worktree, will reuse existing: ${errorMessage}`);
21499
+ }
21501
21500
  if (options.clean) {
21502
21501
  logger.debug(`Cleaning existing worktree`);
21503
21502
  await this.cleanWorktree(worktreePath);
@@ -21523,8 +21522,11 @@ var init_worktree_manager = __esm({
21523
21522
  options.fetchDepth,
21524
21523
  options.cloneTimeoutMs
21525
21524
  );
21526
- await this.fetchRef(bareRepoPath2, ref);
21525
+ const fetched2 = await this.fetchRef(bareRepoPath2, ref);
21527
21526
  const newCommit = await this.getCommitShaForRef(bareRepoPath2, ref);
21527
+ if (!fetched2) {
21528
+ logger.warn(`Using cached ref ${ref} for update; fetch failed`);
21529
+ }
21528
21530
  const checkoutCmd = `git -C ${this.escapeShellArg(worktreePath)} checkout --detach ${this.escapeShellArg(newCommit)}`;
21529
21531
  const checkoutResult = await this.executeGitCommand(checkoutCmd, { timeout: 6e4 });
21530
21532
  if (checkoutResult.exitCode !== 0) {
@@ -21562,8 +21564,11 @@ var init_worktree_manager = __esm({
21562
21564
  await fsp.rm(worktreePath, { recursive: true, force: true });
21563
21565
  }
21564
21566
  }
21565
- await this.fetchRef(bareRepoPath, ref);
21567
+ const fetched = await this.fetchRef(bareRepoPath, ref);
21566
21568
  const commit = await this.getCommitShaForRef(bareRepoPath, ref);
21569
+ if (!fetched) {
21570
+ logger.warn(`Using cached ref ${ref}; fetch failed`);
21571
+ }
21567
21572
  await this.pruneWorktrees(bareRepoPath);
21568
21573
  logger.info(`Creating worktree for ${repository}@${ref} (${commit})`);
21569
21574
  const createCmd = `git -C ${this.escapeShellArg(
@@ -21617,8 +21622,13 @@ var init_worktree_manager = __esm({
21617
21622
  async fetchRef(bareRepoPath, ref) {
21618
21623
  this.validateRef(ref);
21619
21624
  logger.debug(`Fetching ref: ${ref}`);
21620
- const fetchCmd = `git -C ${this.escapeShellArg(bareRepoPath)} fetch origin ${this.escapeShellArg(ref + ":" + ref)} 2>&1 || true`;
21621
- await this.executeGitCommand(fetchCmd, { timeout: 6e4 });
21625
+ const fetchCmd = `git -C ${this.escapeShellArg(bareRepoPath)} fetch origin ${this.escapeShellArg(ref + ":" + ref)} 2>&1`;
21626
+ const result = await this.executeGitCommand(fetchCmd, { timeout: 6e4 });
21627
+ if (result.exitCode !== 0) {
21628
+ logger.warn(`Failed to fetch ref ${ref}: ${result.stderr}`);
21629
+ return false;
21630
+ }
21631
+ return true;
21622
21632
  }
21623
21633
  /**
21624
21634
  * Clean worktree (reset and remove untracked files)
@@ -24502,6 +24512,7 @@ async function executeCheckWithForEachItems2(checkId, forEachParent, forEachItem
24502
24512
  const ruleId = issue.ruleId || "";
24503
24513
  return ruleId.endsWith("/error") || // System errors
24504
24514
  ruleId.includes("/execution_error") || // Command failures
24515
+ ruleId.includes("timeout") || // Timeouts
24505
24516
  ruleId.endsWith("_fail_if");
24506
24517
  });
24507
24518
  if (iterationHasFatalIssues && output !== void 0 && output !== null && typeof output === "object") {
@@ -24617,7 +24628,7 @@ async function executeCheckWithForEachItems2(checkId, forEachParent, forEachItem
24617
24628
  allIssues.push(failIssue);
24618
24629
  const nowHasFatalIssues = enrichedResult.issues.some((issue) => {
24619
24630
  const ruleId = issue.ruleId || "";
24620
- return ruleId.endsWith("/error") || ruleId.includes("/execution_error") || ruleId.endsWith("_fail_if");
24631
+ return ruleId.endsWith("/error") || ruleId.includes("/execution_error") || ruleId.includes("timeout") || ruleId.endsWith("_fail_if");
24621
24632
  });
24622
24633
  if (nowHasFatalIssues && output !== void 0 && output !== null && typeof output === "object" && !output.__failed) {
24623
24634
  output = { ...output, __failed: true };
@@ -25994,6 +26005,7 @@ function hasFatalIssues2(result) {
25994
26005
  const ruleId = issue.ruleId || "";
25995
26006
  return ruleId.endsWith("/error") || // System errors
25996
26007
  ruleId.includes("/execution_error") || // Command failures
26008
+ ruleId.includes("timeout") || // Timeouts
25997
26009
  ruleId.endsWith("_fail_if") && ruleId !== "global_fail_if";
25998
26010
  });
25999
26011
  }
@@ -26037,6 +26049,7 @@ function updateStats2(results, state, isForEachIteration = false) {
26037
26049
  const ruleId = issue.ruleId || "";
26038
26050
  return ruleId.endsWith("/error") || // System errors, exceptions
26039
26051
  ruleId.includes("/execution_error") || // Command failures
26052
+ ruleId.includes("timeout") || // Timeouts
26040
26053
  ruleId.endsWith("_fail_if") && ruleId !== "global_fail_if";
26041
26054
  });
26042
26055
  if (error) {
@@ -29515,6 +29528,8 @@ var init_slack_frontend = __esm({
29515
29528
  const ev = env && env.payload || env;
29516
29529
  await this.maybePostDirectReply(ctx, ev.checkId, ev.result).catch(() => {
29517
29530
  });
29531
+ await this.maybePostExecutionFailure(ctx, ev.checkId, ev.result).catch(() => {
29532
+ });
29518
29533
  })
29519
29534
  );
29520
29535
  this.subs.push(
@@ -29664,7 +29679,6 @@ var init_slack_frontend = __esm({
29664
29679
  }
29665
29680
  async maybePostError(ctx, title, message, checkId) {
29666
29681
  if (this.errorNotified) return;
29667
- if (!this.isTelemetryEnabled(ctx)) return;
29668
29682
  const slack = this.getSlack(ctx);
29669
29683
  if (!slack) return;
29670
29684
  const payload = this.getInboundSlackPayload(ctx);
@@ -29677,11 +29691,13 @@ var init_slack_frontend = __esm({
29677
29691
  Check: ${checkId}`;
29678
29692
  if (message) text += `
29679
29693
  ${message}`;
29680
- const traceInfo = this.getTraceInfo();
29681
- if (traceInfo?.traceId) {
29682
- text += `
29694
+ if (this.isTelemetryEnabled(ctx)) {
29695
+ const traceInfo = this.getTraceInfo();
29696
+ if (traceInfo?.traceId) {
29697
+ text += `
29683
29698
 
29684
29699
  \`trace_id: ${traceInfo.traceId}\``;
29700
+ }
29685
29701
  }
29686
29702
  const formattedText = formatSlackText(text);
29687
29703
  await slack.chat.postMessage({ channel, text: formattedText, thread_ts: threadTs });
@@ -29693,6 +29709,32 @@ ${message}`;
29693
29709
  }
29694
29710
  this.errorNotified = true;
29695
29711
  }
29712
+ isExecutionFailureIssue(issue) {
29713
+ const ruleId = String(issue?.ruleId || "");
29714
+ const msg = String(issue?.message || "");
29715
+ const msgLower = msg.toLowerCase();
29716
+ return ruleId.endsWith("/error") || ruleId.includes("/execution_error") || ruleId.includes("timeout") || ruleId.includes("sandbox_runner_error") || msgLower.includes("timed out") || msg.includes("Command execution failed");
29717
+ }
29718
+ async maybePostExecutionFailure(ctx, checkId, result) {
29719
+ try {
29720
+ if (this.errorNotified) return;
29721
+ const cfg = ctx.config || {};
29722
+ const checkCfg = cfg.checks?.[checkId];
29723
+ if (!checkCfg) return;
29724
+ if (checkCfg.type === "human-input") return;
29725
+ if (checkCfg.criticality === "internal") return;
29726
+ const issues = result?.issues;
29727
+ if (!Array.isArray(issues) || issues.length === 0) return;
29728
+ const failureIssue = issues.find((issue) => this.isExecutionFailureIssue(issue));
29729
+ if (!failureIssue) return;
29730
+ if (typeof failureIssue.message === "string" && failureIssue.message.toLowerCase().includes("awaiting human input")) {
29731
+ return;
29732
+ }
29733
+ const msg = typeof failureIssue.message === "string" && failureIssue.message.trim().length > 0 ? failureIssue.message.trim() : `Execution failed (${String(failureIssue.ruleId || "unknown")})`;
29734
+ await this.maybePostError(ctx, "Check failed", msg, checkId);
29735
+ } catch {
29736
+ }
29737
+ }
29696
29738
  async ensureAcknowledgement(ctx) {
29697
29739
  if (this.acked) return;
29698
29740
  const ref = this.getInboundSlackEvent(ctx);
@@ -29702,7 +29744,6 @@ ${message}`;
29702
29744
  try {
29703
29745
  const payload = this.getInboundSlackPayload(ctx);
29704
29746
  const ev = payload?.event;
29705
- if (ev?.subtype === "bot_message") return;
29706
29747
  try {
29707
29748
  const botId = await slack.getBotUserId?.();
29708
29749
  if (botId && ev?.user && String(ev.user) === String(botId)) return;