@probelabs/visor 0.1.177-ee → 0.1.178-ee

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 (99) hide show
  1. package/defaults/code-talk.yaml +10 -5
  2. package/dist/defaults/code-talk.yaml +10 -5
  3. package/dist/docs/ai-custom-tools.md +49 -0
  4. package/dist/docs/http.md +23 -0
  5. package/dist/docs/testing/cookbook.md +48 -0
  6. package/dist/docs/testing/dsl-reference.md +4 -2
  7. package/dist/docs/testing/flows.md +33 -1
  8. package/dist/examples/http-integration-config.yaml +16 -0
  9. package/dist/generated/config-schema.d.ts +51 -6
  10. package/dist/generated/config-schema.d.ts.map +1 -1
  11. package/dist/generated/config-schema.json +61 -6
  12. package/dist/github-comments.d.ts +5 -1
  13. package/dist/github-comments.d.ts.map +1 -1
  14. package/dist/index.js +386 -72
  15. package/dist/providers/api-tool-executor.d.ts +2 -0
  16. package/dist/providers/api-tool-executor.d.ts.map +1 -1
  17. package/dist/providers/http-client-provider.d.ts.map +1 -1
  18. package/dist/providers/mcp-custom-sse-server.d.ts.map +1 -1
  19. package/dist/providers/workflow-check-provider.d.ts.map +1 -1
  20. package/dist/sdk/{a2a-frontend-BPWLYLCG.mjs → a2a-frontend-U3PTNCLR.mjs} +2 -2
  21. package/dist/sdk/{check-provider-registry-G64PWDCZ.mjs → check-provider-registry-SRASECAR.mjs} +6 -6
  22. package/dist/sdk/{check-provider-registry-HW4QPPSA.mjs → check-provider-registry-ZX76MY2L.mjs} +6 -6
  23. package/dist/sdk/{chunk-OPI632LK.mjs → chunk-4ECMTCOM.mjs} +2 -2
  24. package/dist/sdk/{chunk-65SHRIQF.mjs → chunk-6YGCACBF.mjs} +2 -2
  25. package/dist/sdk/{chunk-65SHRIQF.mjs.map → chunk-6YGCACBF.mjs.map} +1 -1
  26. package/dist/sdk/{chunk-Y6PVSFCS.mjs → chunk-B7XHSG3L.mjs} +237 -47
  27. package/dist/sdk/chunk-B7XHSG3L.mjs.map +1 -0
  28. package/dist/sdk/{chunk-MM3TGVQ4.mjs → chunk-BMXVAJ2M.mjs} +52 -7
  29. package/dist/sdk/chunk-BMXVAJ2M.mjs.map +1 -0
  30. package/dist/sdk/{chunk-OHOBWVPP.mjs → chunk-ENSZDV3O.mjs} +3 -3
  31. package/dist/sdk/{chunk-IYXOLUDJ.mjs → chunk-MGY5JAN2.mjs} +221 -36
  32. package/dist/sdk/chunk-MGY5JAN2.mjs.map +1 -0
  33. package/dist/sdk/{config-OOUMTCEA.mjs → config-DFOF7LP4.mjs} +2 -2
  34. package/dist/sdk/{failure-condition-evaluator-HL33X7MH.mjs → failure-condition-evaluator-P3MS5DRL.mjs} +3 -3
  35. package/dist/sdk/{github-frontend-F2YCPK6H.mjs → github-frontend-QTKOYB56.mjs} +11 -3
  36. package/dist/sdk/github-frontend-QTKOYB56.mjs.map +1 -0
  37. package/dist/sdk/{host-HFOJQIOF.mjs → host-I2TBBKD5.mjs} +3 -3
  38. package/dist/sdk/{host-6TBS44ER.mjs → host-THORKOEL.mjs} +4 -4
  39. package/dist/sdk/{routing-GF2CF3JT.mjs → routing-2X6QF5IW.mjs} +4 -4
  40. package/dist/sdk/{schedule-tool-45NAALKS.mjs → schedule-tool-M6Y4YTXR.mjs} +6 -6
  41. package/dist/sdk/{schedule-tool-7O7SWSJ4.mjs → schedule-tool-R6JJIDZ6.mjs} +6 -6
  42. package/dist/sdk/{schedule-tool-handler-6MPP5DXK.mjs → schedule-tool-handler-AOMZV3Q3.mjs} +6 -6
  43. package/dist/sdk/{schedule-tool-handler-KYDXJ2ZL.mjs → schedule-tool-handler-JYCVH377.mjs} +6 -6
  44. package/dist/sdk/sdk.d.mts +21 -0
  45. package/dist/sdk/sdk.d.ts +21 -0
  46. package/dist/sdk/sdk.js +264 -26
  47. package/dist/sdk/sdk.js.map +1 -1
  48. package/dist/sdk/sdk.mjs +5 -5
  49. package/dist/sdk/{trace-helpers-FKM2MEDW.mjs → trace-helpers-K47ZVJSU.mjs} +2 -2
  50. package/dist/sdk/{workflow-check-provider-JIXZJNV5.mjs → workflow-check-provider-A3YH2UZJ.mjs} +6 -6
  51. package/dist/sdk/{workflow-check-provider-OA33MESM.mjs → workflow-check-provider-EMFC7A5K.mjs} +6 -6
  52. package/dist/state-machine/context/build-engine-context.d.ts.map +1 -1
  53. package/dist/test-runner/conversation-sugar.d.ts +3 -0
  54. package/dist/test-runner/conversation-sugar.d.ts.map +1 -1
  55. package/dist/test-runner/validator.d.ts.map +1 -1
  56. package/dist/types/config.d.ts +21 -0
  57. package/dist/types/config.d.ts.map +1 -1
  58. package/dist/utils/rate-limiter.d.ts +61 -0
  59. package/dist/utils/rate-limiter.d.ts.map +1 -0
  60. package/package.json +2 -2
  61. package/dist/sdk/a2a-frontend-FUJRKHJB.mjs +0 -1658
  62. package/dist/sdk/a2a-frontend-FUJRKHJB.mjs.map +0 -1
  63. package/dist/sdk/chunk-EFNNJIMY.mjs +0 -739
  64. package/dist/sdk/chunk-GVTWESYN.mjs +0 -516
  65. package/dist/sdk/chunk-GVTWESYN.mjs.map +0 -1
  66. package/dist/sdk/chunk-IYXOLUDJ.mjs.map +0 -1
  67. package/dist/sdk/chunk-MM3TGVQ4.mjs.map +0 -1
  68. package/dist/sdk/chunk-OPI632LK.mjs.map +0 -1
  69. package/dist/sdk/chunk-WJIV7MKY.mjs +0 -1502
  70. package/dist/sdk/chunk-WJIV7MKY.mjs.map +0 -1
  71. package/dist/sdk/chunk-Y6PVSFCS.mjs.map +0 -1
  72. package/dist/sdk/failure-condition-evaluator-DL6H57NX.mjs +0 -18
  73. package/dist/sdk/github-frontend-F2YCPK6H.mjs.map +0 -1
  74. package/dist/sdk/github-frontend-U2U42CKV.mjs +0 -1386
  75. package/dist/sdk/github-frontend-U2U42CKV.mjs.map +0 -1
  76. package/dist/sdk/routing-SFP4D6O3.mjs +0 -26
  77. package/dist/sdk/slack-frontend-6SXPTQDI.mjs +0 -895
  78. package/dist/sdk/slack-frontend-6SXPTQDI.mjs.map +0 -1
  79. package/dist/sdk/trace-helpers-L3EOYW5P.mjs +0 -29
  80. package/dist/sdk/trace-helpers-L3EOYW5P.mjs.map +0 -1
  81. package/dist/sdk/workflow-check-provider-JIXZJNV5.mjs.map +0 -1
  82. package/dist/sdk/workflow-check-provider-OA33MESM.mjs.map +0 -1
  83. /package/dist/sdk/{a2a-frontend-BPWLYLCG.mjs.map → a2a-frontend-U3PTNCLR.mjs.map} +0 -0
  84. /package/dist/sdk/{check-provider-registry-G64PWDCZ.mjs.map → check-provider-registry-SRASECAR.mjs.map} +0 -0
  85. /package/dist/sdk/{check-provider-registry-HW4QPPSA.mjs.map → check-provider-registry-ZX76MY2L.mjs.map} +0 -0
  86. /package/dist/sdk/{chunk-EFNNJIMY.mjs.map → chunk-4ECMTCOM.mjs.map} +0 -0
  87. /package/dist/sdk/{chunk-OHOBWVPP.mjs.map → chunk-ENSZDV3O.mjs.map} +0 -0
  88. /package/dist/sdk/{config-OOUMTCEA.mjs.map → config-DFOF7LP4.mjs.map} +0 -0
  89. /package/dist/sdk/{failure-condition-evaluator-DL6H57NX.mjs.map → failure-condition-evaluator-P3MS5DRL.mjs.map} +0 -0
  90. /package/dist/sdk/{host-6TBS44ER.mjs.map → host-I2TBBKD5.mjs.map} +0 -0
  91. /package/dist/sdk/{host-HFOJQIOF.mjs.map → host-THORKOEL.mjs.map} +0 -0
  92. /package/dist/sdk/{failure-condition-evaluator-HL33X7MH.mjs.map → routing-2X6QF5IW.mjs.map} +0 -0
  93. /package/dist/sdk/{routing-GF2CF3JT.mjs.map → schedule-tool-M6Y4YTXR.mjs.map} +0 -0
  94. /package/dist/sdk/{routing-SFP4D6O3.mjs.map → schedule-tool-R6JJIDZ6.mjs.map} +0 -0
  95. /package/dist/sdk/{schedule-tool-45NAALKS.mjs.map → schedule-tool-handler-AOMZV3Q3.mjs.map} +0 -0
  96. /package/dist/sdk/{schedule-tool-7O7SWSJ4.mjs.map → schedule-tool-handler-JYCVH377.mjs.map} +0 -0
  97. /package/dist/sdk/{schedule-tool-handler-6MPP5DXK.mjs.map → trace-helpers-K47ZVJSU.mjs.map} +0 -0
  98. /package/dist/sdk/{schedule-tool-handler-KYDXJ2ZL.mjs.map → workflow-check-provider-A3YH2UZJ.mjs.map} +0 -0
  99. /package/dist/sdk/{trace-helpers-FKM2MEDW.mjs.map → workflow-check-provider-EMFC7A5K.mjs.map} +0 -0
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
- process.env.VISOR_VERSION = '0.1.177';
3
- process.env.PROBE_VERSION = '0.6.0-rc292';
4
- process.env.VISOR_COMMIT_SHA = '5ceed041c645b4c6f1ed2b2cf327a7f376437656';
5
- process.env.VISOR_COMMIT_SHORT = '5ceed04';
2
+ process.env.VISOR_VERSION = '0.1.178';
3
+ process.env.PROBE_VERSION = '0.6.0-rc293';
4
+ process.env.VISOR_COMMIT_SHA = '79fcc6344e1ab954501ff6a7f9614a0372019b2b';
5
+ process.env.VISOR_COMMIT_SHORT = '79fcc63';
6
6
  /******/ (() => { // webpackBootstrap
7
7
  /******/ var __webpack_modules__ = ({
8
8
 
@@ -303229,7 +303229,7 @@ steps:
303229
303229
  You are a senior software engineer. Use the provided tools to explore
303230
303230
  the codebase. Always verify your assumptions by reading actual code.
303231
303231
  Be concise and cite file paths in your response.
303232
- max_iterations: 20
303232
+ max_iterations: 50
303233
303233
  ai_custom_tools: [search-code, list-files, read-file]
303234
303234
  enable_bash: true # also allow direct shell commands
303235
303235
  tags: [agent]
@@ -316414,6 +316414,10 @@ exports.configSchema = {
316414
316414
  '^x-': {},
316415
316415
  },
316416
316416
  },
316417
+ rate_limit: {
316418
+ $ref: '#/definitions/RateLimitConfig',
316419
+ description: 'Rate limiting configuration for HTTP/API tools',
316420
+ },
316417
316421
  workflow: {
316418
316422
  type: 'string',
316419
316423
  description: "Workflow ID (registry lookup) or file path (for type: 'workflow')",
@@ -316450,6 +316454,43 @@ exports.configSchema = {
316450
316454
  type: 'string',
316451
316455
  },
316452
316456
  },
316457
+ RateLimitConfig: {
316458
+ type: 'object',
316459
+ properties: {
316460
+ key: {
316461
+ type: 'string',
316462
+ description: 'Shared bucket name; defaults to URL origin',
316463
+ },
316464
+ requests: {
316465
+ type: 'number',
316466
+ description: 'Max requests per window',
316467
+ },
316468
+ per: {
316469
+ type: 'string',
316470
+ enum: ['second', 'minute', 'hour'],
316471
+ description: 'Time window unit',
316472
+ },
316473
+ max_retries: {
316474
+ type: 'number',
316475
+ description: 'Max retries on 429 (default: 3)',
316476
+ },
316477
+ backoff: {
316478
+ type: 'string',
316479
+ enum: ['fixed', 'exponential'],
316480
+ description: 'Backoff strategy (default: exponential)',
316481
+ },
316482
+ initial_delay_ms: {
316483
+ type: 'number',
316484
+ description: 'Base delay for backoff in ms (default: 1000)',
316485
+ },
316486
+ },
316487
+ required: ['requests', 'per'],
316488
+ additionalProperties: false,
316489
+ description: 'Rate limit configuration for HTTP/API requests.',
316490
+ patternProperties: {
316491
+ '^x-': {},
316492
+ },
316493
+ },
316453
316494
  WorkflowInput: {
316454
316495
  type: 'object',
316455
316496
  properties: {
@@ -316552,6 +316593,10 @@ exports.configSchema = {
316552
316593
  $ref: '#/definitions/Record%3Cstring%2Cstring%3E',
316553
316594
  description: 'HTTP headers',
316554
316595
  },
316596
+ rate_limit: {
316597
+ $ref: '#/definitions/RateLimitConfig',
316598
+ description: 'Rate limiting configuration for http_client checks',
316599
+ },
316555
316600
  endpoint: {
316556
316601
  type: 'string',
316557
316602
  description: 'HTTP endpoint path - required for http_input checks',
@@ -316953,7 +316998,7 @@ exports.configSchema = {
316953
316998
  description: 'Arguments/inputs for the workflow',
316954
316999
  },
316955
317000
  overrides: {
316956
- $ref: '#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-14017-28611-src_types_config.ts-0-57090%3E%3E',
317001
+ $ref: '#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-14532-29218-src_types_config.ts-0-57785%3E%3E',
316957
317002
  description: 'Override specific step configurations in the workflow',
316958
317003
  },
316959
317004
  output_mapping: {
@@ -316969,7 +317014,7 @@ exports.configSchema = {
316969
317014
  description: 'Config file path - alternative to workflow ID (loads a Visor config file as workflow)',
316970
317015
  },
316971
317016
  workflow_overrides: {
316972
- $ref: '#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-14017-28611-src_types_config.ts-0-57090%3E%3E',
317017
+ $ref: '#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-14532-29218-src_types_config.ts-0-57785%3E%3E',
316973
317018
  description: 'Alias for overrides - workflow step overrides (backward compatibility)',
316974
317019
  },
316975
317020
  ref: {
@@ -317671,7 +317716,7 @@ exports.configSchema = {
317671
317716
  description: 'Custom output name (defaults to workflow name)',
317672
317717
  },
317673
317718
  overrides: {
317674
- $ref: '#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-14017-28611-src_types_config.ts-0-57090%3E%3E',
317719
+ $ref: '#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-14532-29218-src_types_config.ts-0-57785%3E%3E',
317675
317720
  description: 'Step overrides',
317676
317721
  },
317677
317722
  output_mapping: {
@@ -317686,13 +317731,13 @@ exports.configSchema = {
317686
317731
  '^x-': {},
317687
317732
  },
317688
317733
  },
317689
- 'Record<string,Partial<interface-src_types_config.ts-14017-28611-src_types_config.ts-0-57090>>': {
317734
+ 'Record<string,Partial<interface-src_types_config.ts-14532-29218-src_types_config.ts-0-57785>>': {
317690
317735
  type: 'object',
317691
317736
  additionalProperties: {
317692
- $ref: '#/definitions/Partial%3Cinterface-src_types_config.ts-14017-28611-src_types_config.ts-0-57090%3E',
317737
+ $ref: '#/definitions/Partial%3Cinterface-src_types_config.ts-14532-29218-src_types_config.ts-0-57785%3E',
317693
317738
  },
317694
317739
  },
317695
- 'Partial<interface-src_types_config.ts-14017-28611-src_types_config.ts-0-57090>': {
317740
+ 'Partial<interface-src_types_config.ts-14532-29218-src_types_config.ts-0-57785>': {
317696
317741
  type: 'object',
317697
317742
  additionalProperties: false,
317698
317743
  },
@@ -320856,11 +320901,16 @@ const human_id_1 = __nccwpck_require__(30920);
320856
320901
  const logger_1 = __nccwpck_require__(86999);
320857
320902
  const footer_1 = __nccwpck_require__(6924);
320858
320903
  /**
320859
- * Manages GitHub PR comments with dynamic updating capabilities
320904
+ * Manages GitHub PR comments with dynamic updating capabilities.
320905
+ * All write operations are serialized through an internal queue to prevent
320906
+ * concurrent GitHub API calls from racing against each other.
320860
320907
  */
320861
320908
  class CommentManager {
320862
320909
  octokit;
320863
320910
  retryConfig;
320911
+ // Serial write queue: chains all updateOrCreateComment calls so only one
320912
+ // GitHub comment write is in-flight at a time within a job.
320913
+ _writeQueue = Promise.resolve();
320864
320914
  constructor(octokit, retryConfig) {
320865
320915
  this.octokit = octokit;
320866
320916
  this.retryConfig = {
@@ -320901,6 +320951,16 @@ class CommentManager {
320901
320951
  * Update existing comment or create new one with collision detection
320902
320952
  */
320903
320953
  async updateOrCreateComment(owner, repo, prNumber, content, options = {}) {
320954
+ // Serialize all comment writes through a single queue so only one
320955
+ // GitHub API write is in-flight at a time, preventing races between
320956
+ // concurrent checks updating the same or different comments.
320957
+ return new Promise((resolve, reject) => {
320958
+ this._writeQueue = this._writeQueue
320959
+ .then(() => this._doUpdateOrCreate(owner, repo, prNumber, content, options))
320960
+ .then(resolve, reject);
320961
+ });
320962
+ }
320963
+ async _doUpdateOrCreate(owner, repo, prNumber, content, options = {}) {
320904
320964
  const { commentId = this.generateCommentId(), triggeredBy = 'unknown', allowConcurrentUpdates = false, commitSha, cachedGithubCommentId, } = options;
320905
320965
  return this.withRetry(async () => {
320906
320966
  // First try to find the comment via listComments API
@@ -328686,6 +328746,7 @@ const deepmerge_1 = __importDefault(__nccwpck_require__(2569));
328686
328746
  const jsonpath_plus_1 = __nccwpck_require__(55464);
328687
328747
  const minimatch_1 = __nccwpck_require__(46507);
328688
328748
  const logger_1 = __nccwpck_require__(86999);
328749
+ const rate_limiter_1 = __nccwpck_require__(56898);
328689
328750
  const HTTP_METHODS = new Set(['get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace']);
328690
328751
  function isHttpUrl(value) {
328691
328752
  return value.startsWith('http://') || value.startsWith('https://');
@@ -328951,6 +329012,7 @@ function getApiToolConfig(tool) {
328951
329012
  securitySchemeName: tool.securitySchemeName ?? tool.security_scheme_name,
328952
329013
  securityCredentials: tool.securityCredentials || tool.security_credentials || {},
328953
329014
  requestTimeoutMs: tool.requestTimeoutMs ?? tool.request_timeout_ms ?? tool.timeout ?? 30000,
329015
+ rateLimitConfig: tool.rate_limit,
328954
329016
  };
328955
329017
  }
328956
329018
  function buildOutputSchema(operation) {
@@ -329425,7 +329487,7 @@ async function executeMappedApiTool(mappedTool, args) {
329425
329487
  const controller = new AbortController();
329426
329488
  const timeout = setTimeout(() => controller.abort(), apiToolConfig.requestTimeoutMs);
329427
329489
  try {
329428
- const response = await fetch(endpoint.toString(), {
329490
+ const response = await (0, rate_limiter_1.rateLimitedFetch)(endpoint.toString(), {
329429
329491
  method,
329430
329492
  headers,
329431
329493
  body: requestBodyValue === undefined
@@ -329434,7 +329496,7 @@ async function executeMappedApiTool(mappedTool, args) {
329434
329496
  ? JSON.stringify(requestBodyValue)
329435
329497
  : String(requestBodyValue),
329436
329498
  signal: controller.signal,
329437
- });
329499
+ }, apiToolConfig.rateLimitConfig);
329438
329500
  const raw = await response.text();
329439
329501
  let body = raw;
329440
329502
  const contentType = response.headers.get('content-type') || '';
@@ -333414,6 +333476,7 @@ const sandbox_1 = __nccwpck_require__(12630);
333414
333476
  const template_context_1 = __nccwpck_require__(1581);
333415
333477
  const oauth2_token_cache_1 = __nccwpck_require__(34713);
333416
333478
  const logger_1 = __nccwpck_require__(86999);
333479
+ const rate_limiter_1 = __nccwpck_require__(56898);
333417
333480
  const fs = __importStar(__nccwpck_require__(79896));
333418
333481
  const path = __importStar(__nccwpck_require__(16928));
333419
333482
  /**
@@ -333591,12 +333654,13 @@ class HttpClientProvider extends check_provider_interface_1.CheckProvider {
333591
333654
  if (requestBody) {
333592
333655
  logger_1.logger.verbose(`[http_client] Body: ${requestBody.substring(0, 500)}${requestBody.length > 500 ? '...' : ''}`);
333593
333656
  }
333657
+ const rateLimitConfig = config.rate_limit;
333594
333658
  // If output_file is specified, download to file instead of returning data
333595
333659
  if (resolvedOutputFile) {
333596
- const fileResult = await this.downloadToFile(renderedUrl, method, resolvedHeaders, requestBody, timeout, resolvedOutputFile);
333660
+ const fileResult = await this.downloadToFile(renderedUrl, method, resolvedHeaders, requestBody, timeout, resolvedOutputFile, rateLimitConfig);
333597
333661
  return fileResult;
333598
333662
  }
333599
- const data = await this.fetchData(renderedUrl, method, resolvedHeaders, requestBody, timeout);
333663
+ const data = await this.fetchData(renderedUrl, method, resolvedHeaders, requestBody, timeout, rateLimitConfig);
333600
333664
  // Apply Liquid transformation if specified
333601
333665
  let processedData = data;
333602
333666
  if (transform) {
@@ -333687,7 +333751,7 @@ class HttpClientProvider extends check_provider_interface_1.CheckProvider {
333687
333751
  };
333688
333752
  }
333689
333753
  }
333690
- async fetchData(url, method, headers, body, timeout = 30000) {
333754
+ async fetchData(url, method, headers, body, timeout = 30000, rateLimitConfig) {
333691
333755
  // Check if fetch is available (Node 18+)
333692
333756
  if (typeof fetch === 'undefined') {
333693
333757
  throw new Error('HTTP client provider requires Node.js 18+ or node-fetch package');
@@ -333713,7 +333777,7 @@ class HttpClientProvider extends check_provider_interface_1.CheckProvider {
333713
333777
  };
333714
333778
  }
333715
333779
  }
333716
- const response = await fetch(url, requestOptions);
333780
+ const response = await (0, rate_limiter_1.rateLimitedFetch)(url, requestOptions, rateLimitConfig);
333717
333781
  clearTimeout(timeoutId);
333718
333782
  logger_1.logger.verbose(`[http_client] Response: ${response.status} ${response.statusText}`);
333719
333783
  if (!response.ok) {
@@ -333752,7 +333816,7 @@ class HttpClientProvider extends check_provider_interface_1.CheckProvider {
333752
333816
  throw error;
333753
333817
  }
333754
333818
  }
333755
- async downloadToFile(url, method, headers, body, timeout, outputFile) {
333819
+ async downloadToFile(url, method, headers, body, timeout, outputFile, rateLimitConfig) {
333756
333820
  // Check if fetch is available (Node 18+)
333757
333821
  if (typeof fetch === 'undefined') {
333758
333822
  throw new Error('HTTP client provider requires Node.js 18+ or node-fetch package');
@@ -333775,7 +333839,7 @@ class HttpClientProvider extends check_provider_interface_1.CheckProvider {
333775
333839
  };
333776
333840
  }
333777
333841
  }
333778
- const response = await fetch(url, requestOptions);
333842
+ const response = await (0, rate_limiter_1.rateLimitedFetch)(url, requestOptions, rateLimitConfig);
333779
333843
  clearTimeout(timeoutId);
333780
333844
  if (!response.ok) {
333781
333845
  return {
@@ -335716,6 +335780,7 @@ const schedule_tool_1 = __nccwpck_require__(13395);
335716
335780
  // Legacy Slack-specific imports for backwards compatibility
335717
335781
  const schedule_tool_handler_1 = __nccwpck_require__(28883);
335718
335782
  const env_resolver_1 = __nccwpck_require__(58749);
335783
+ const rate_limiter_1 = __nccwpck_require__(56898);
335719
335784
  /**
335720
335785
  * Check if a tool definition is an http_client tool
335721
335786
  */
@@ -336488,7 +336553,8 @@ class CustomToolsSSEServer {
336488
336553
  resolvedHeaders['Content-Type'] = 'application/json';
336489
336554
  }
336490
336555
  }
336491
- const response = await fetch(url, requestOptions);
336556
+ const rateLimitConfig = tool.rate_limit;
336557
+ const response = await (0, rate_limiter_1.rateLimitedFetch)(url, requestOptions, rateLimitConfig);
336492
336558
  clearTimeout(timeoutId);
336493
336559
  if (!response.ok) {
336494
336560
  let errorBody = '';
@@ -338008,6 +338074,17 @@ class WorkflowCheckProvider extends check_provider_interface_1.CheckProvider {
338008
338074
  validateWorkflowDepth(currentDepth, maxDepth, workflow.id);
338009
338075
  // Project workflow to dependency graph
338010
338076
  const { config: workflowConfig, checks: checksMetadata } = projectWorkflowToGraph(workflow, inputs, config.checkName || workflow.id);
338077
+ // Propagate parent check's timeout to nested workflow steps that don't define their own.
338078
+ // This ensures that a parent `timeout: 120000` caps nested AI steps instead of them
338079
+ // falling back to the 30-minute default.
338080
+ const parentTimeout = config.timeout || config.ai?.timeout;
338081
+ if (parentTimeout && workflowConfig.checks) {
338082
+ for (const stepCfg of Object.values(workflowConfig.checks)) {
338083
+ if (!stepCfg.timeout && !stepCfg.ai?.timeout) {
338084
+ stepCfg.timeout = parentTimeout;
338085
+ }
338086
+ }
338087
+ }
338011
338088
  // Build isolated child engine context (separate journal/memory to avoid state contamination)
338012
338089
  // Reuse parent's memory config if available, but never the instance
338013
338090
  const parentMemoryCfg = (parentContext?.memory &&
@@ -349636,7 +349713,12 @@ function buildEngineContextForRun(workingDirectory, config, prInfo, debug, maxPa
349636
349713
  async acquire(parentSessionId, _dbg, queueTimeout) {
349637
349714
  // Use visor session ID if probe didn't provide one
349638
349715
  const sid = parentSessionId || sessionId;
349639
- return fairLimiter.acquire(sid, _dbg, queueTimeout);
349716
+ // ProbeAgent calls acquire(null) without queueTimeout, which defaults
349717
+ // to 120s in FairConcurrencyLimiter — too short when AI checks take
349718
+ // 5-30+ min and slots are occupied. Override to 0 (disabled) so the
349719
+ // step/AI timeout governs cancellation instead.
349720
+ const effectiveQueueTimeout = queueTimeout ?? 0;
349721
+ return fairLimiter.acquire(sid, _dbg, effectiveQueueTimeout);
349640
349722
  },
349641
349723
  release(parentSessionId, _dbg) {
349642
349724
  const sid = parentSessionId || sessionId;
@@ -353504,11 +353586,14 @@ async function executeCheckWithForEachItems(checkId, forEachParent, forEachItems
353504
353586
  // Evaluate assume contract for this iteration (design-by-contract)
353505
353587
  {
353506
353588
  const assumeExpr = checkConfig?.assume;
353507
- if (assumeExpr) {
353589
+ if (assumeExpr !== undefined && assumeExpr !== null) {
353508
353590
  let ok = true;
353509
353591
  try {
353510
353592
  const evaluator = new failure_condition_evaluator_1.FailureConditionEvaluator();
353511
- const exprs = Array.isArray(assumeExpr) ? assumeExpr : [assumeExpr];
353593
+ const rawExprs = Array.isArray(assumeExpr) ? assumeExpr : [assumeExpr];
353594
+ // Coerce non-string values (e.g., YAML boolean `true`) to strings
353595
+ // so they can be safely evaluated as JavaScript expressions.
353596
+ const exprs = rawExprs.map((e) => (typeof e === 'string' ? e : String(e)));
353512
353597
  // Get conversation from execution context (TUI/CLI) or provider event context (Slack)
353513
353598
  const conversation = context.executionContext?.conversation ||
353514
353599
  providerConfig?.eventContext?.conversation;
@@ -354676,11 +354761,14 @@ async function executeSingleCheck(checkId, context, state, emitEvent, transition
354676
354761
  // Evaluate assume contract (design-by-contract) before executing
354677
354762
  {
354678
354763
  const assumeExpr = checkConfig?.assume;
354679
- if (assumeExpr) {
354764
+ if (assumeExpr !== undefined && assumeExpr !== null) {
354680
354765
  let ok = true;
354681
354766
  try {
354682
354767
  const evaluator = new failure_condition_evaluator_1.FailureConditionEvaluator();
354683
- const exprs = Array.isArray(assumeExpr) ? assumeExpr : [assumeExpr];
354768
+ const rawExprs = Array.isArray(assumeExpr) ? assumeExpr : [assumeExpr];
354769
+ // Coerce non-string values (e.g., YAML boolean `true`) to strings
354770
+ // so they can be safely evaluated as JavaScript expressions.
354771
+ const exprs = rawExprs.map((e) => (typeof e === 'string' ? e : String(e)));
354684
354772
  // Get conversation from execution context (TUI/CLI) or provider event context (Slack)
354685
354773
  const conversation = context.executionContext?.conversation ||
354686
354774
  providerConfig?.eventContext?.conversation;
@@ -361627,7 +361715,7 @@ function expandConversationToFlow(testCase) {
361627
361715
  transport,
361628
361716
  thread: { id: threadId },
361629
361717
  messages: [...currentMessages],
361630
- current: { role: 'user', text: turn.text },
361718
+ current: { role: 'user', text: turn.text, ...(turn.user ? { user: turn.user } : {}) },
361631
361719
  },
361632
361720
  },
361633
361721
  ...(turn.mocks ? { mocks: turn.mocks } : {}),
@@ -366093,6 +366181,7 @@ const schema = {
366093
366181
  properties: {
366094
366182
  role: { type: 'string', enum: ['user', 'assistant'] },
366095
366183
  text: { type: 'string' },
366184
+ user: { type: 'string' },
366096
366185
  mocks: {
366097
366186
  type: 'object',
366098
366187
  additionalProperties: {
@@ -372440,6 +372529,193 @@ class OAuth2TokenCache {
372440
372529
  exports.OAuth2TokenCache = OAuth2TokenCache;
372441
372530
 
372442
372531
 
372532
+ /***/ }),
372533
+
372534
+ /***/ 56898:
372535
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
372536
+
372537
+ "use strict";
372538
+
372539
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
372540
+ exports.RateLimiterRegistry = exports.TokenBucket = void 0;
372541
+ exports.resolveRateLimitKey = resolveRateLimitKey;
372542
+ exports.rateLimitedFetch = rateLimitedFetch;
372543
+ const logger_1 = __nccwpck_require__(86999);
372544
+ /**
372545
+ * Token bucket rate limiter with FIFO wait queue.
372546
+ */
372547
+ class TokenBucket {
372548
+ tokens;
372549
+ capacity;
372550
+ refillRate; // tokens per ms
372551
+ lastRefill;
372552
+ waitQueue = [];
372553
+ constructor(capacity, windowMs) {
372554
+ this.capacity = capacity;
372555
+ this.tokens = capacity;
372556
+ this.refillRate = capacity / windowMs;
372557
+ this.lastRefill = Date.now();
372558
+ }
372559
+ refill() {
372560
+ const now = Date.now();
372561
+ const elapsed = now - this.lastRefill;
372562
+ const newTokens = elapsed * this.refillRate;
372563
+ this.tokens = Math.min(this.capacity, this.tokens + newTokens);
372564
+ this.lastRefill = now;
372565
+ }
372566
+ /**
372567
+ * Non-blocking: try to consume one token.
372568
+ */
372569
+ tryConsume() {
372570
+ this.refill();
372571
+ if (this.tokens >= 1) {
372572
+ this.tokens -= 1;
372573
+ return true;
372574
+ }
372575
+ return false;
372576
+ }
372577
+ /**
372578
+ * Blocking: wait until a token is available, then consume it.
372579
+ * Requests are served FIFO.
372580
+ */
372581
+ async acquire() {
372582
+ if (this.tryConsume()) {
372583
+ return;
372584
+ }
372585
+ // Calculate wait time for next token
372586
+ const waitMs = Math.ceil((1 - this.tokens) / this.refillRate);
372587
+ return new Promise(resolve => {
372588
+ const entry = { resolve };
372589
+ this.waitQueue.push(entry);
372590
+ setTimeout(() => {
372591
+ // Remove from queue
372592
+ const idx = this.waitQueue.indexOf(entry);
372593
+ if (idx >= 0) {
372594
+ this.waitQueue.splice(idx, 1);
372595
+ }
372596
+ this.refill();
372597
+ if (this.tokens >= 1) {
372598
+ this.tokens -= 1;
372599
+ }
372600
+ resolve();
372601
+ }, waitMs);
372602
+ });
372603
+ }
372604
+ }
372605
+ exports.TokenBucket = TokenBucket;
372606
+ function windowToMs(per) {
372607
+ switch (per) {
372608
+ case 'second':
372609
+ return 1000;
372610
+ case 'minute':
372611
+ return 60_000;
372612
+ case 'hour':
372613
+ return 3_600_000;
372614
+ }
372615
+ }
372616
+ const REGISTRY_KEY = Symbol.for('visor.rateLimiterRegistry');
372617
+ /**
372618
+ * Global singleton registry of named token buckets.
372619
+ */
372620
+ class RateLimiterRegistry {
372621
+ buckets = new Map();
372622
+ static getInstance() {
372623
+ const g = globalThis;
372624
+ if (!g[REGISTRY_KEY]) {
372625
+ g[REGISTRY_KEY] = new RateLimiterRegistry();
372626
+ }
372627
+ return g[REGISTRY_KEY];
372628
+ }
372629
+ getOrCreate(key, config) {
372630
+ let bucket = this.buckets.get(key);
372631
+ if (!bucket) {
372632
+ const windowMs = windowToMs(config.per);
372633
+ bucket = new TokenBucket(config.requests, windowMs);
372634
+ this.buckets.set(key, bucket);
372635
+ logger_1.logger.verbose(`[rate-limiter] Created bucket "${key}": ${config.requests} req/${config.per}`);
372636
+ }
372637
+ return bucket;
372638
+ }
372639
+ cleanup() {
372640
+ this.buckets.clear();
372641
+ }
372642
+ }
372643
+ exports.RateLimiterRegistry = RateLimiterRegistry;
372644
+ /**
372645
+ * Resolve the rate limit key from config and optional fallback URL.
372646
+ */
372647
+ function resolveRateLimitKey(config, fallbackUrl) {
372648
+ if (config.key) {
372649
+ return config.key;
372650
+ }
372651
+ if (fallbackUrl) {
372652
+ try {
372653
+ const url = new URL(fallbackUrl);
372654
+ return url.origin;
372655
+ }
372656
+ catch {
372657
+ // not a valid URL, use as-is
372658
+ return fallbackUrl;
372659
+ }
372660
+ }
372661
+ return '__default__';
372662
+ }
372663
+ /**
372664
+ * Rate-limited fetch wrapper.
372665
+ *
372666
+ * If rateLimitConfig is provided, acquires a token before making the request
372667
+ * and retries on 429 responses with backoff.
372668
+ *
372669
+ * If no config is provided, behaves exactly like native fetch().
372670
+ */
372671
+ async function rateLimitedFetch(url, options, rateLimitConfig) {
372672
+ if (!rateLimitConfig) {
372673
+ return fetch(url, options);
372674
+ }
372675
+ const key = resolveRateLimitKey(rateLimitConfig, url);
372676
+ const registry = RateLimiterRegistry.getInstance();
372677
+ const bucket = registry.getOrCreate(key, rateLimitConfig);
372678
+ const maxRetries = rateLimitConfig.max_retries ?? 3;
372679
+ const backoff = rateLimitConfig.backoff ?? 'exponential';
372680
+ const initialDelay = rateLimitConfig.initial_delay_ms ?? 1000;
372681
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
372682
+ // Acquire a token (waits if bucket is empty)
372683
+ await bucket.acquire();
372684
+ const response = await fetch(url, options);
372685
+ if (response.status !== 429) {
372686
+ return response;
372687
+ }
372688
+ // 429 — rate limited by server
372689
+ if (attempt === maxRetries) {
372690
+ logger_1.logger.warn(`[rate-limiter] Exhausted ${maxRetries} retries for ${url} (bucket: ${key})`);
372691
+ return response;
372692
+ }
372693
+ // Calculate delay: respect Retry-After header if present
372694
+ let delayMs;
372695
+ const retryAfter = response.headers.get('retry-after');
372696
+ if (retryAfter) {
372697
+ const parsed = Number(retryAfter);
372698
+ if (!isNaN(parsed)) {
372699
+ // Retry-After in seconds
372700
+ delayMs = parsed * 1000;
372701
+ }
372702
+ else {
372703
+ // Retry-After as HTTP date
372704
+ const date = new Date(retryAfter).getTime();
372705
+ delayMs = Math.max(0, date - Date.now());
372706
+ }
372707
+ }
372708
+ else {
372709
+ delayMs = backoff === 'exponential' ? initialDelay * Math.pow(2, attempt) : initialDelay;
372710
+ }
372711
+ logger_1.logger.verbose(`[rate-limiter] 429 on ${url} (bucket: ${key}), retry ${attempt + 1}/${maxRetries} in ${delayMs}ms`);
372712
+ await new Promise(resolve => setTimeout(resolve, delayMs));
372713
+ }
372714
+ // Should not reach here, but satisfy TypeScript
372715
+ return fetch(url, options);
372716
+ }
372717
+
372718
+
372443
372719
  /***/ }),
372444
372720
 
372445
372721
  /***/ 12630:
@@ -477163,6 +477439,10 @@ var init_symbolEdit = __esm({
477163
477439
  });
477164
477440
 
477165
477441
  // src/tools/fileTracker.js
477442
+ function normalizePath(filePath) {
477443
+ if (!filePath) return filePath;
477444
+ return (0, import_path6.resolve)(filePath);
477445
+ }
477166
477446
  function computeContentHash(content) {
477167
477447
  const normalized = (content || "").split("\n").map((l) => l.trimEnd()).join("\n");
477168
477448
  return (0, import_crypto2.createHash)("sha256").update(normalized).digest("hex").slice(0, 16);
@@ -477225,10 +477505,11 @@ var init_fileTracker = __esm({
477225
477505
  * @param {string} resolvedPath - Absolute path to the file
477226
477506
  */
477227
477507
  markFileSeen(resolvedPath) {
477228
- this._seenFiles.add(resolvedPath);
477229
- this._textEditCounts.set(resolvedPath, 0);
477508
+ const normalized = normalizePath(resolvedPath);
477509
+ this._seenFiles.add(normalized);
477510
+ this._textEditCounts.set(normalized, 0);
477230
477511
  if (this.debug) {
477231
- console.error(`[FileTracker] Marked as seen: ${resolvedPath}`);
477512
+ console.error(`[FileTracker] Marked as seen: ${normalized}`);
477232
477513
  }
477233
477514
  }
477234
477515
  /**
@@ -477237,7 +477518,7 @@ var init_fileTracker = __esm({
477237
477518
  * @returns {boolean}
477238
477519
  */
477239
477520
  isFileSeen(resolvedPath) {
477240
- return this._seenFiles.has(resolvedPath);
477521
+ return this._seenFiles.has(normalizePath(resolvedPath));
477241
477522
  }
477242
477523
  /**
477243
477524
  * Store a content hash for a symbol in a file.
@@ -477249,7 +477530,7 @@ var init_fileTracker = __esm({
477249
477530
  * @param {string} [source='extract'] - How the content was obtained
477250
477531
  */
477251
477532
  trackSymbolContent(resolvedPath, symbolName, code, startLine, endLine, source = "extract") {
477252
- const key = `${resolvedPath}#${symbolName}`;
477533
+ const key = `${normalizePath(resolvedPath)}#${symbolName}`;
477253
477534
  const contentHash = computeContentHash(code);
477254
477535
  this._contentRecords.set(key, {
477255
477536
  contentHash,
@@ -477270,7 +477551,7 @@ var init_fileTracker = __esm({
477270
477551
  * @returns {Object|null} The stored record or null
477271
477552
  */
477272
477553
  getSymbolRecord(resolvedPath, symbolName) {
477273
- return this._contentRecords.get(`${resolvedPath}#${symbolName}`) || null;
477554
+ return this._contentRecords.get(`${normalizePath(resolvedPath)}#${symbolName}`) || null;
477274
477555
  }
477275
477556
  /**
477276
477557
  * Check if a symbol's current content matches what was stored.
@@ -477280,7 +477561,7 @@ var init_fileTracker = __esm({
477280
477561
  * @returns {{ok: boolean, reason?: string, message?: string}}
477281
477562
  */
477282
477563
  checkSymbolContent(resolvedPath, symbolName, currentCode) {
477283
- const key = `${resolvedPath}#${symbolName}`;
477564
+ const key = `${normalizePath(resolvedPath)}#${symbolName}`;
477284
477565
  const record2 = this._contentRecords.get(key);
477285
477566
  if (!record2) {
477286
477567
  return { ok: true };
@@ -477357,7 +477638,7 @@ var init_fileTracker = __esm({
477357
477638
  * @returns {{ok: boolean, reason?: string, message?: string}}
477358
477639
  */
477359
477640
  checkBeforeEdit(resolvedPath) {
477360
- if (!this._seenFiles.has(resolvedPath)) {
477641
+ if (!this._seenFiles.has(normalizePath(resolvedPath))) {
477361
477642
  return {
477362
477643
  ok: false,
477363
477644
  reason: "untracked",
@@ -477372,8 +477653,9 @@ var init_fileTracker = __esm({
477372
477653
  * @param {string} resolvedPath - Absolute path to the file
477373
477654
  */
477374
477655
  async trackFileAfterWrite(resolvedPath) {
477375
- this._seenFiles.add(resolvedPath);
477376
- this.invalidateFileRecords(resolvedPath);
477656
+ const normalized = normalizePath(resolvedPath);
477657
+ this._seenFiles.add(normalized);
477658
+ this.invalidateFileRecords(normalized);
477377
477659
  }
477378
477660
  /**
477379
477661
  * Record a text-mode edit (old_string/new_string) to a file.
@@ -477381,10 +477663,11 @@ var init_fileTracker = __esm({
477381
477663
  * @param {string} resolvedPath - Absolute path to the file
477382
477664
  */
477383
477665
  recordTextEdit(resolvedPath) {
477384
- const count = (this._textEditCounts.get(resolvedPath) || 0) + 1;
477385
- this._textEditCounts.set(resolvedPath, count);
477666
+ const normalized = normalizePath(resolvedPath);
477667
+ const count = (this._textEditCounts.get(normalized) || 0) + 1;
477668
+ this._textEditCounts.set(normalized, count);
477386
477669
  if (this.debug) {
477387
- console.error(`[FileTracker] Text edit #${count} for ${resolvedPath}`);
477670
+ console.error(`[FileTracker] Text edit #${count} for ${normalized}`);
477388
477671
  }
477389
477672
  }
477390
477673
  /**
@@ -477393,7 +477676,7 @@ var init_fileTracker = __esm({
477393
477676
  * @returns {{ok: boolean, editCount?: number, message?: string}}
477394
477677
  */
477395
477678
  checkTextEditStaleness(resolvedPath) {
477396
- const count = this._textEditCounts.get(resolvedPath) || 0;
477679
+ const count = this._textEditCounts.get(normalizePath(resolvedPath)) || 0;
477397
477680
  if (count >= this.maxConsecutiveTextEdits) {
477398
477681
  return {
477399
477682
  ok: false,
@@ -477422,7 +477705,7 @@ var init_fileTracker = __esm({
477422
477705
  * @param {string} resolvedPath - Absolute path to the file
477423
477706
  */
477424
477707
  invalidateFileRecords(resolvedPath) {
477425
- const prefix = resolvedPath + "#";
477708
+ const prefix = normalizePath(resolvedPath) + "#";
477426
477709
  for (const key of this._contentRecords.keys()) {
477427
477710
  if (key.startsWith(prefix)) {
477428
477711
  this._contentRecords.delete(key);
@@ -477438,7 +477721,7 @@ var init_fileTracker = __esm({
477438
477721
  * @returns {boolean}
477439
477722
  */
477440
477723
  isTracked(resolvedPath) {
477441
- return this.isFileSeen(resolvedPath);
477724
+ return this.isFileSeen(normalizePath(resolvedPath));
477442
477725
  }
477443
477726
  /**
477444
477727
  * Clear all tracking state.
@@ -481491,7 +481774,7 @@ var init_esm3 = __esm({
481491
481774
  });
481492
481775
 
481493
481776
  // node_modules/path-scurry/dist/esm/index.js
481494
- var import_node_path, import_node_url, import_fs4, actualFS, import_promises, realpathSync2, defaultFS, fsFromOption, uncDriveRegexp, uncToDrive, eitherSep, UNKNOWN, IFIFO, IFCHR, IFDIR, IFBLK, IFREG, IFLNK, IFSOCK, IFMT, IFMT_UNKNOWN, READDIR_CALLED, LSTAT_CALLED, ENOTDIR, ENOENT, ENOREADLINK, ENOREALPATH, ENOCHILD, TYPEMASK, entToType, normalizeCache, normalize, normalizeNocaseCache, normalizeNocase, ResolveCache, ChildrenCache, setAsCwd, PathBase, PathWin32, PathPosix, PathScurryBase, PathScurryWin32, PathScurryPosix, PathScurryDarwin, Path, PathScurry;
481777
+ var import_node_path, import_node_url, import_fs4, actualFS, import_promises, realpathSync2, defaultFS, fsFromOption, uncDriveRegexp, uncToDrive, eitherSep, UNKNOWN, IFIFO, IFCHR, IFDIR, IFBLK, IFREG, IFLNK, IFSOCK, IFMT, IFMT_UNKNOWN, READDIR_CALLED, LSTAT_CALLED, ENOTDIR, ENOENT, ENOREADLINK, ENOREALPATH, ENOCHILD, TYPEMASK, entToType, normalizeCache, normalize2, normalizeNocaseCache, normalizeNocase, ResolveCache, ChildrenCache, setAsCwd, PathBase, PathWin32, PathPosix, PathScurryBase, PathScurryWin32, PathScurryPosix, PathScurryDarwin, Path, PathScurry;
481495
481778
  var init_esm4 = __esm({
481496
481779
  "node_modules/path-scurry/dist/esm/index.js"() {
481497
481780
  init_esm2();
@@ -481546,7 +481829,7 @@ var init_esm4 = __esm({
481546
481829
  TYPEMASK = 1023;
481547
481830
  entToType = (s) => s.isFile() ? IFREG : s.isDirectory() ? IFDIR : s.isSymbolicLink() ? IFLNK : s.isCharacterDevice() ? IFCHR : s.isBlockDevice() ? IFBLK : s.isSocket() ? IFSOCK : s.isFIFO() ? IFIFO : UNKNOWN;
481548
481831
  normalizeCache = /* @__PURE__ */ new Map();
481549
- normalize = (s) => {
481832
+ normalize2 = (s) => {
481550
481833
  const c = normalizeCache.get(s);
481551
481834
  if (c)
481552
481835
  return c;
@@ -481559,7 +481842,7 @@ var init_esm4 = __esm({
481559
481842
  const c = normalizeNocaseCache.get(s);
481560
481843
  if (c)
481561
481844
  return c;
481562
- const n = normalize(s.toLowerCase());
481845
+ const n = normalize2(s.toLowerCase());
481563
481846
  normalizeNocaseCache.set(s, n);
481564
481847
  return n;
481565
481848
  };
@@ -481726,7 +482009,7 @@ var init_esm4 = __esm({
481726
482009
  */
481727
482010
  constructor(name15, type = UNKNOWN, root2, roots, nocase, children, opts) {
481728
482011
  this.name = name15;
481729
- this.#matchName = nocase ? normalizeNocase(name15) : normalize(name15);
482012
+ this.#matchName = nocase ? normalizeNocase(name15) : normalize2(name15);
481730
482013
  this.#type = type & TYPEMASK;
481731
482014
  this.nocase = nocase;
481732
482015
  this.roots = roots;
@@ -481819,7 +482102,7 @@ var init_esm4 = __esm({
481819
482102
  return this.parent || this;
481820
482103
  }
481821
482104
  const children = this.children();
481822
- const name15 = this.nocase ? normalizeNocase(pathPart) : normalize(pathPart);
482105
+ const name15 = this.nocase ? normalizeNocase(pathPart) : normalize2(pathPart);
481823
482106
  for (const p of children) {
481824
482107
  if (p.#matchName === name15) {
481825
482108
  return p;
@@ -482064,7 +482347,7 @@ var init_esm4 = __esm({
482064
482347
  * directly.
482065
482348
  */
482066
482349
  isNamed(n) {
482067
- return !this.nocase ? this.#matchName === normalize(n) : this.#matchName === normalizeNocase(n);
482350
+ return !this.nocase ? this.#matchName === normalize2(n) : this.#matchName === normalizeNocase(n);
482068
482351
  }
482069
482352
  /**
482070
482353
  * Return the Path object corresponding to the target of a symbolic link.
@@ -482203,7 +482486,7 @@ var init_esm4 = __esm({
482203
482486
  #readdirMaybePromoteChild(e, c) {
482204
482487
  for (let p = c.provisional; p < c.length; p++) {
482205
482488
  const pchild = c[p];
482206
- const name15 = this.nocase ? normalizeNocase(e.name) : normalize(e.name);
482489
+ const name15 = this.nocase ? normalizeNocase(e.name) : normalize2(e.name);
482207
482490
  if (name15 !== pchild.#matchName) {
482208
482491
  continue;
482209
482492
  }
@@ -503520,7 +503803,7 @@ var init_graph_builder = __esm({
503520
503803
  applyLinkStyles() {
503521
503804
  if (!this.pendingLinkStyles.length || !this.edges.length)
503522
503805
  return;
503523
- const normalize3 = (s) => {
503806
+ const normalize4 = (s) => {
503524
503807
  const out = {};
503525
503808
  for (const [kRaw, vRaw] of Object.entries(s)) {
503526
503809
  const k = kRaw.trim().toLowerCase();
@@ -503541,7 +503824,7 @@ var init_graph_builder = __esm({
503541
503824
  return out;
503542
503825
  };
503543
503826
  for (const cmd of this.pendingLinkStyles) {
503544
- const style = normalize3(cmd.props);
503827
+ const style = normalize4(cmd.props);
503545
503828
  for (const idx of cmd.indices) {
503546
503829
  if (idx >= 0 && idx < this.edges.length) {
503547
503830
  const e = this.edges[idx];
@@ -510808,7 +511091,7 @@ var require_layout = __commonJS({
510808
511091
  "use strict";
510809
511092
  var _ = require_lodash2();
510810
511093
  var acyclic = require_acyclic();
510811
- var normalize3 = require_normalize();
511094
+ var normalize4 = require_normalize();
510812
511095
  var rank = require_rank();
510813
511096
  var normalizeRanks = require_util().normalizeRanks;
510814
511097
  var parentDummyChains = require_parent_dummy_chains();
@@ -510870,7 +511153,7 @@ var require_layout = __commonJS({
510870
511153
  removeEdgeLabelProxies(g);
510871
511154
  });
510872
511155
  time3(" normalize.run", function() {
510873
- normalize3.run(g);
511156
+ normalize4.run(g);
510874
511157
  });
510875
511158
  time3(" parentDummyChains", function() {
510876
511159
  parentDummyChains(g);
@@ -510897,7 +511180,7 @@ var require_layout = __commonJS({
510897
511180
  removeBorderNodes(g);
510898
511181
  });
510899
511182
  time3(" normalize.undo", function() {
510900
- normalize3.undo(g);
511183
+ normalize4.undo(g);
510901
511184
  });
510902
511185
  time3(" fixupEdgeLabelCoords", function() {
510903
511186
  fixupEdgeLabelCoords(g);
@@ -517268,8 +517551,8 @@ var require_resolve = __commonJS({
517268
517551
  }
517269
517552
  return count;
517270
517553
  }
517271
- function getFullPath(resolver, id = "", normalize3) {
517272
- if (normalize3 !== false)
517554
+ function getFullPath(resolver, id = "", normalize4) {
517555
+ if (normalize4 !== false)
517273
517556
  id = normalizeId(id);
517274
517557
  const p = resolver.parse(id);
517275
517558
  return _getFullPath(resolver, p);
@@ -518609,7 +518892,7 @@ var require_fast_uri = __commonJS({
518609
518892
  "use strict";
518610
518893
  var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizeComponentEncoding, isIPv4, nonSimpleDomain } = require_utils();
518611
518894
  var { SCHEMES, getSchemeHandler } = require_schemes();
518612
- function normalize3(uri, options) {
518895
+ function normalize4(uri, options) {
518613
518896
  if (typeof uri === "string") {
518614
518897
  uri = /** @type {T} */
518615
518898
  serialize(parse11(uri, options), options);
@@ -518845,7 +519128,7 @@ var require_fast_uri = __commonJS({
518845
519128
  }
518846
519129
  var fastUri = {
518847
519130
  SCHEMES,
518848
- normalize: normalize3,
519131
+ normalize: normalize4,
518849
519132
  resolve: resolve9,
518850
519133
  resolveComponent,
518851
519134
  equal,
@@ -523052,9 +523335,9 @@ If the solution is clear, you can jump to implementation right away. If not, ask
523052
523335
  - Do not add code comments unless the logic is genuinely complex and non-obvious.
523053
523336
 
523054
523337
  # Before Implementation
523055
- - Focus on high-level design patterns and system organization
523056
- - Identify architectural patterns and component relationships
523057
- - Evaluate system structure and suggest architectural improvements
523338
+ - Read tests first \u2014 find existing test files for the module you're changing. They reveal expected behavior, edge cases, and the project's testing patterns.
523339
+ - Read neighboring files \u2014 understand naming conventions, error handling patterns, import style, and existing utilities before creating new ones.
523340
+ - Trace the call chain \u2014 follow how the code you're changing is called and what depends on it. Check interfaces, types, and consumers.
523058
523341
  - Focus on backward compatibility
523059
523342
  - Consider scalability, maintainability, and extensibility in your analysis
523060
523343
 
@@ -523079,6 +523362,20 @@ Before building or testing, determine the project's toolchain:
523079
523362
  - Read README for build/test instructions if the above are unclear
523080
523363
  - Common patterns: \`make build\`/\`make test\`, \`npm run build\`/\`npm test\`, \`cargo build\`/\`cargo test\`, \`go build ./...\`/\`go test ./...\`, \`python -m pytest\`
523081
523364
 
523365
+ # File Editing Rules
523366
+ You have access to the \`edit\`, \`create\`, and \`multi_edit\` tools for modifying files. You MUST use these tools for ALL code changes. They are purpose-built, atomic, and safe.
523367
+
523368
+ DO NOT use sed, awk, echo/cat redirection, or heredocs to modify source code. These commands cause real damage in practice: truncated lines, duplicate code blocks, broken syntax. Every bad edit wastes iterations on fix-up commits.
523369
+
523370
+ Use the right tool:
523371
+ 1. To MODIFY existing code \u2192 \`edit\` tool (old_string \u2192 new_string, or start_line/end_line)
523372
+ 2. To CREATE a new file \u2192 \`create\` tool
523373
+ 3. To CHANGE multiple files at once \u2192 \`multi_edit\` tool
523374
+ 4. To READ code \u2192 \`extract\` or \`search\` tools
523375
+ 5. If \`edit\` fails with "file has not been read yet" \u2192 use \`extract\` with the EXACT same file path you will pass to \`edit\`. Relative vs absolute path mismatch causes this error. Use the same path format consistently. If it still fails, use bash \`cat\` to read the file, then use \`create\` to write the entire modified file. Do NOT fall back to sed.
523376
+
523377
+ Bash is fine for: formatters (gofmt, prettier, black), build/test/lint commands, git operations, and read-only file inspection (cat, head, tail). sed/awk should ONLY be used for trivial non-code tasks (e.g., config file tweaks) where the replacement is a simple literal string swap.
523378
+
523082
523379
  # During Implementation
523083
523380
  - Always create a new branch before making changes to the codebase.
523084
523381
  - Fix problems at the root cause, not with surface-level patches. Prefer general solutions over special cases.
@@ -523105,6 +523402,22 @@ Before committing or creating a PR, run through this checklist:
523105
523402
 
523106
523403
  Do NOT skip verification. Do NOT proceed to PR creation with a broken build or failing tests.
523107
523404
 
523405
+ # Output Integrity
523406
+ Your final output MUST accurately reflect what ACTUALLY happened. Do NOT fabricate, hallucinate, or report aspirational results.
523407
+
523408
+ - Only report PR URLs you actually created or updated with \`gh pr create\` or \`git push\`. If you checked out an existing PR but did NOT push changes to it, do NOT claim you updated it.
523409
+ - Describe what you ACTUALLY DID, not what you planned or intended to do. If you ran out of iterations, say so. If tests failed, say so.
523410
+ - Only list files you actually modified AND committed.
523411
+ - If you could not complete the task \u2014 ran out of iterations, tests failed, build broken, push rejected \u2014 report the real reason honestly.
523412
+
523413
+ NEVER claim success when:
523414
+ - You did not run \`git push\` successfully
523415
+ - Tests failed and you did not fix them
523416
+ - You hit the iteration limit before completing the work
523417
+ - You only analyzed/investigated but did not implement changes
523418
+
523419
+ A false success report is WORSE than an honest failure \u2014 it misleads the user into thinking work is done when it is not.
523420
+
523108
523421
  # GitHub Integration
523109
523422
  - Use the \`gh\` CLI for all GitHub operations: issues, pull requests, checks, releases.
523110
523423
  - To view issues or PRs: \`gh issue view <number>\`, \`gh pr view <number>\`.
@@ -551307,7 +551620,7 @@ var init_vercel = __esm({
551307
551620
  name: "search",
551308
551621
  description: searchDelegate ? searchDelegateDescription : searchDescription,
551309
551622
  inputSchema: searchSchema,
551310
- execute: async ({ query: searchQuery, path: path9, allow_tests, exact, maxTokens: paramMaxTokens, language, session, nextPage }) => {
551623
+ execute: async ({ query: searchQuery, path: path9, allow_tests, exact, maxTokens: paramMaxTokens, language, session, nextPage, workingDirectory }) => {
551311
551624
  if (!exact && searchQuery) {
551312
551625
  const originalQuery = searchQuery;
551313
551626
  searchQuery = autoQuoteSearchTerms(searchQuery);
@@ -551316,18 +551629,19 @@ var init_vercel = __esm({
551316
551629
  }
551317
551630
  }
551318
551631
  const effectiveMaxTokens = paramMaxTokens || maxTokens;
551632
+ const effectiveSearchCwd = workingDirectory || options.cwd || ".";
551319
551633
  let searchPaths;
551320
551634
  if (path9) {
551321
- searchPaths = parseAndResolvePaths(path9, options.cwd);
551635
+ searchPaths = parseAndResolvePaths(path9, effectiveSearchCwd);
551322
551636
  }
551323
551637
  if (!searchPaths || searchPaths.length === 0) {
551324
- searchPaths = [options.cwd || "."];
551638
+ searchPaths = [effectiveSearchCwd];
551325
551639
  }
551326
551640
  const searchPath = searchPaths.join(" ");
551327
551641
  const searchOptions = {
551328
551642
  query: searchQuery,
551329
551643
  path: searchPath,
551330
- cwd: options.cwd,
551644
+ cwd: effectiveSearchCwd,
551331
551645
  // Working directory for resolving relative paths
551332
551646
  allowTests: allow_tests ?? true,
551333
551647
  exact,
@@ -551378,7 +551692,7 @@ var init_vercel = __esm({
551378
551692
  try {
551379
551693
  const result = maybeAnnotate(await runRawSearch());
551380
551694
  if (options.fileTracker && typeof result === "string") {
551381
- options.fileTracker.trackFilesFromOutput(result, options.cwd || ".").catch(() => {
551695
+ options.fileTracker.trackFilesFromOutput(result, effectiveSearchCwd).catch(() => {
551382
551696
  });
551383
551697
  }
551384
551698
  return result;
@@ -551431,7 +551745,7 @@ var init_vercel = __esm({
551431
551745
  }
551432
551746
  const fallbackResult = maybeAnnotate(await runRawSearch());
551433
551747
  if (options.fileTracker && typeof fallbackResult === "string") {
551434
- options.fileTracker.trackFilesFromOutput(fallbackResult, options.cwd || ".").catch(() => {
551748
+ options.fileTracker.trackFilesFromOutput(fallbackResult, effectiveSearchCwd).catch(() => {
551435
551749
  });
551436
551750
  }
551437
551751
  return fallbackResult;
@@ -551494,7 +551808,7 @@ var init_vercel = __esm({
551494
551808
  try {
551495
551809
  const fallbackResult2 = maybeAnnotate(await runRawSearch());
551496
551810
  if (options.fileTracker && typeof fallbackResult2 === "string") {
551497
- options.fileTracker.trackFilesFromOutput(fallbackResult2, options.cwd || ".").catch(() => {
551811
+ options.fileTracker.trackFilesFromOutput(fallbackResult2, effectiveSearchCwd).catch(() => {
551498
551812
  });
551499
551813
  }
551500
551814
  return fallbackResult2;
@@ -551548,9 +551862,9 @@ var init_vercel = __esm({
551548
551862
  name: "extract",
551549
551863
  description: extractDescription,
551550
551864
  inputSchema: extractSchema,
551551
- execute: async ({ targets, input_content, line, end_line, allow_tests, context_lines, format }) => {
551865
+ execute: async ({ targets, input_content, line, end_line, allow_tests, context_lines, format, workingDirectory }) => {
551552
551866
  try {
551553
- const effectiveCwd = options.cwd || ".";
551867
+ const effectiveCwd = workingDirectory || options.cwd || ".";
551554
551868
  if (debug) {
551555
551869
  if (targets) {
551556
551870
  console.error(`Executing extract with targets: "${targets}", cwd: "${effectiveCwd}", context lines: ${context_lines || 10}`);
@@ -610680,7 +610994,7 @@ module.exports = /*#__PURE__*/JSON.parse('["aaa","aarp","abb","abbott","abbvie",
610680
610994
  /***/ ((module) => {
610681
610995
 
610682
610996
  "use strict";
610683
- module.exports = /*#__PURE__*/JSON.parse('{"name":"@probelabs/visor","version":"0.1.42","main":"dist/index.js","bin":{"visor":"./dist/index.js"},"exports":{".":{"require":"./dist/index.js","import":"./dist/index.js"},"./sdk":{"types":"./dist/sdk/sdk.d.ts","import":"./dist/sdk/sdk.mjs","require":"./dist/sdk/sdk.js"},"./cli":{"require":"./dist/index.js"}},"files":["dist/","defaults/","action.yml","README.md","LICENSE"],"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"scripts":{"build:cli":"ncc build src/index.ts -o dist && cp -r defaults dist/ && cp -r output dist/ && cp -r docs dist/ && cp -r examples dist/ && cp -r src/debug-visualizer/ui dist/debug-visualizer/ && node scripts/inject-version.js && echo \'#!/usr/bin/env node\' | cat - dist/index.js > temp && mv temp dist/index.js && chmod +x dist/index.js","build:sdk":"tsup src/sdk.ts --dts --sourcemap --format esm,cjs --out-dir dist/sdk","build":"./scripts/build-oss.sh","build:ee":"npm run build:cli && npm run build:sdk","test":"jest && npm run test:yaml","test:unit":"jest","prepublishOnly":"npm run build","test:watch":"jest --watch","test:coverage":"jest --coverage","test:ee":"jest --testPathPatterns=\'tests/ee\' --testPathIgnorePatterns=\'/node_modules/\' --no-coverage","test:manual:bash":"RUN_MANUAL_TESTS=true jest tests/manual/bash-config-manual.test.ts","lint":"eslint src tests --ext .ts","lint:fix":"eslint src tests --ext .ts --fix","format":"prettier --write src tests","format:check":"prettier --check src tests","clean":"","clean:traces":"node scripts/clean-traces.js","prebuild":"npm run clean && node scripts/generate-config-schema.js","pretest":"npm run clean:traces && node scripts/generate-config-schema.js && npm run build:cli","pretest:unit":"npm run clean:traces && node scripts/generate-config-schema.js && npm run build:cli","test:with-build":"npm run build:cli && jest","test:yaml":"node dist/index.js test --progress compact","test:yaml:parallel":"node dist/index.js test --progress compact --max-parallel 4","prepare":"husky","pre-commit":"lint-staged","deploy:site":"cd site && npx wrangler pages deploy . --project-name=visor-site --commit-dirty=true","deploy:worker":"npx wrangler deploy","deploy":"npm run deploy:site && npm run deploy:worker","publish:ee":"./scripts/publish-ee.sh","release":"./scripts/release.sh","release:patch":"./scripts/release.sh patch","release:minor":"./scripts/release.sh minor","release:major":"./scripts/release.sh major","release:prerelease":"./scripts/release.sh prerelease","docs:validate":"node scripts/validate-readme-links.js","workshop:setup":"npm install -D reveal-md@6.1.2","workshop:serve":"cd workshop && reveal-md slides.md -w","workshop:export":"reveal-md workshop/slides.md --static workshop/build","workshop:pdf":"reveal-md workshop/slides.md --print workshop/Visor-Workshop.pdf --print-size letter","workshop:pdf:ci":"reveal-md workshop/slides.md --print workshop/Visor-Workshop.pdf --print-size letter --puppeteer-launch-args=\\"--no-sandbox --disable-dev-shm-usage\\"","workshop:pdf:a4":"reveal-md workshop/slides.md --print workshop/Visor-Workshop-A4.pdf --print-size A4","workshop:build":"npm run workshop:export && npm run workshop:pdf","simulate:issue":"TS_NODE_TRANSPILE_ONLY=1 ts-node scripts/simulate-gh-run.ts --event issues --action opened --debug","simulate:comment":"TS_NODE_TRANSPILE_ONLY=1 ts-node scripts/simulate-gh-run.ts --event issue_comment --action created --debug"},"keywords":["code-review","ai","github-action","cli","pr-review","visor"],"author":"Probe Labs","license":"MIT","description":"AI workflow engine for code review, assistants, and automation — orchestrate checks, MCP tools, and AI providers with YAML-driven pipelines","repository":{"type":"git","url":"git+https://github.com/probelabs/visor.git"},"bugs":{"url":"https://github.com/probelabs/visor/issues"},"homepage":"https://github.com/probelabs/visor#readme","dependencies":{"@actions/core":"^1.11.1","@apidevtools/swagger-parser":"^12.1.0","@grammyjs/runner":"^2.0.3","@modelcontextprotocol/sdk":"^1.25.3","@nyariv/sandboxjs":"github:probelabs/SandboxJS#23c4bb611f7d05f3cb8c523917b5f57103e48108","@octokit/action":"^8.0.2","@octokit/auth-app":"^8.1.0","@octokit/core":"^7.0.3","@octokit/rest":"^22.0.0","@opentelemetry/api":"^1.9.0","@opentelemetry/api-logs":"^0.203.0","@opentelemetry/core":"^1.30.1","@opentelemetry/exporter-logs-otlp-http":"^0.203.0","@opentelemetry/exporter-metrics-otlp-http":"^0.203.0","@opentelemetry/exporter-trace-otlp-grpc":"^0.203.0","@opentelemetry/exporter-trace-otlp-http":"^0.203.0","@opentelemetry/instrumentation":"^0.203.0","@opentelemetry/resources":"^1.30.1","@opentelemetry/sdk-logs":"^0.203.0","@opentelemetry/sdk-metrics":"^1.30.1","@opentelemetry/sdk-node":"^0.203.0","@opentelemetry/sdk-trace-base":"^1.30.1","@opentelemetry/semantic-conventions":"^1.30.1","@probelabs/probe":"^0.6.0-rc292","@types/commander":"^2.12.0","@types/uuid":"^10.0.0","acorn":"^8.16.0","acorn-walk":"^8.3.5","ajv":"^8.17.1","ajv-formats":"^3.0.1","better-sqlite3":"^11.0.0","blessed":"^0.1.81","botbuilder":"^4.23.3","botframework-connector":"^4.23.3","cli-table3":"^0.6.5","commander":"^14.0.0","deepmerge":"^4.3.1","dotenv":"^17.2.3","grammy":"^1.41.1","ignore":"^7.0.5","imapflow":"^1.2.12","js-yaml":"^4.1.0","jsonpath-plus":"^10.4.0","liquidjs":"^10.21.1","mailparser":"^3.9.3","minimatch":"^10.2.2","node-cron":"^3.0.3","nodemailer":"^8.0.1","open":"^9.1.0","resend":"^6.9.3","simple-git":"^3.28.0","uuid":"^11.1.0","ws":"^8.18.3"},"optionalDependencies":{"@anthropic/claude-code-sdk":"npm:null@*","@open-policy-agent/opa-wasm":"^1.10.0","knex":"^3.1.0","mysql2":"^3.11.0","pg":"^8.13.0","tedious":"^19.0.0"},"devDependencies":{"@eslint/js":"^9.34.0","@kie/act-js":"^2.6.2","@kie/mock-github":"^2.0.1","@swc/core":"^1.13.2","@swc/jest":"^0.2.37","@types/better-sqlite3":"^7.6.0","@types/blessed":"^0.1.27","@types/jest":"^30.0.0","@types/js-yaml":"^4.0.9","@types/mailparser":"^3.4.6","@types/node":"^24.3.0","@types/node-cron":"^3.0.11","@types/nodemailer":"^7.0.11","@types/ws":"^8.18.1","@typescript-eslint/eslint-plugin":"^8.42.0","@typescript-eslint/parser":"^8.42.0","@vercel/ncc":"^0.38.4","eslint":"^9.34.0","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.4","husky":"^9.1.7","jest":"^30.1.3","lint-staged":"^16.1.6","prettier":"^3.6.2","reveal-md":"^6.1.2","ts-json-schema-generator":"^1.5.1","ts-node":"^10.9.2","tsup":"^8.5.0","typescript":"^5.9.2","wrangler":"^3.0.0"},"peerDependenciesMeta":{"@anthropic/claude-code-sdk":{"optional":true}},"directories":{"test":"tests"},"lint-staged":{"src/**/*.{ts,js}":["eslint --fix","prettier --write"],"tests/**/*.{ts,js}":["eslint --fix","prettier --write"],"*.{json,md,yml,yaml}":["prettier --write"]}}');
610997
+ module.exports = /*#__PURE__*/JSON.parse('{"name":"@probelabs/visor","version":"0.1.42","main":"dist/index.js","bin":{"visor":"./dist/index.js"},"exports":{".":{"require":"./dist/index.js","import":"./dist/index.js"},"./sdk":{"types":"./dist/sdk/sdk.d.ts","import":"./dist/sdk/sdk.mjs","require":"./dist/sdk/sdk.js"},"./cli":{"require":"./dist/index.js"}},"files":["dist/","defaults/","action.yml","README.md","LICENSE"],"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"scripts":{"build:cli":"ncc build src/index.ts -o dist && cp -r defaults dist/ && cp -r output dist/ && cp -r docs dist/ && cp -r examples dist/ && cp -r src/debug-visualizer/ui dist/debug-visualizer/ && node scripts/inject-version.js && echo \'#!/usr/bin/env node\' | cat - dist/index.js > temp && mv temp dist/index.js && chmod +x dist/index.js","build:sdk":"tsup src/sdk.ts --dts --sourcemap --format esm,cjs --out-dir dist/sdk","build":"./scripts/build-oss.sh","build:ee":"npm run build:cli && npm run build:sdk","test":"jest && npm run test:yaml","test:unit":"jest","prepublishOnly":"npm run build","test:watch":"jest --watch","test:coverage":"jest --coverage","test:ee":"jest --testPathPatterns=\'tests/ee\' --testPathIgnorePatterns=\'/node_modules/\' --no-coverage","test:manual:bash":"RUN_MANUAL_TESTS=true jest tests/manual/bash-config-manual.test.ts","lint":"eslint src tests --ext .ts","lint:fix":"eslint src tests --ext .ts --fix","format":"prettier --write src tests","format:check":"prettier --check src tests","clean":"","clean:traces":"node scripts/clean-traces.js","prebuild":"npm run clean && node scripts/generate-config-schema.js","pretest":"npm run clean:traces && node scripts/generate-config-schema.js && npm run build:cli","pretest:unit":"npm run clean:traces && node scripts/generate-config-schema.js && npm run build:cli","test:with-build":"npm run build:cli && jest","test:yaml":"node dist/index.js test --progress compact","test:yaml:parallel":"node dist/index.js test --progress compact --max-parallel 4","prepare":"husky","pre-commit":"lint-staged","deploy:site":"cd site && npx wrangler pages deploy . --project-name=visor-site --commit-dirty=true","deploy:worker":"npx wrangler deploy","deploy":"npm run deploy:site && npm run deploy:worker","publish:ee":"./scripts/publish-ee.sh","release":"./scripts/release.sh","release:patch":"./scripts/release.sh patch","release:minor":"./scripts/release.sh minor","release:major":"./scripts/release.sh major","release:prerelease":"./scripts/release.sh prerelease","docs:validate":"node scripts/validate-readme-links.js","workshop:setup":"npm install -D reveal-md@6.1.2","workshop:serve":"cd workshop && reveal-md slides.md -w","workshop:export":"reveal-md workshop/slides.md --static workshop/build","workshop:pdf":"reveal-md workshop/slides.md --print workshop/Visor-Workshop.pdf --print-size letter","workshop:pdf:ci":"reveal-md workshop/slides.md --print workshop/Visor-Workshop.pdf --print-size letter --puppeteer-launch-args=\\"--no-sandbox --disable-dev-shm-usage\\"","workshop:pdf:a4":"reveal-md workshop/slides.md --print workshop/Visor-Workshop-A4.pdf --print-size A4","workshop:build":"npm run workshop:export && npm run workshop:pdf","simulate:issue":"TS_NODE_TRANSPILE_ONLY=1 ts-node scripts/simulate-gh-run.ts --event issues --action opened --debug","simulate:comment":"TS_NODE_TRANSPILE_ONLY=1 ts-node scripts/simulate-gh-run.ts --event issue_comment --action created --debug"},"keywords":["code-review","ai","github-action","cli","pr-review","visor"],"author":"Probe Labs","license":"MIT","description":"AI workflow engine for code review, assistants, and automation — orchestrate checks, MCP tools, and AI providers with YAML-driven pipelines","repository":{"type":"git","url":"git+https://github.com/probelabs/visor.git"},"bugs":{"url":"https://github.com/probelabs/visor/issues"},"homepage":"https://github.com/probelabs/visor#readme","dependencies":{"@actions/core":"^1.11.1","@apidevtools/swagger-parser":"^12.1.0","@grammyjs/runner":"^2.0.3","@modelcontextprotocol/sdk":"^1.25.3","@nyariv/sandboxjs":"github:probelabs/SandboxJS#23c4bb611f7d05f3cb8c523917b5f57103e48108","@octokit/action":"^8.0.2","@octokit/auth-app":"^8.1.0","@octokit/core":"^7.0.3","@octokit/rest":"^22.0.0","@opentelemetry/api":"^1.9.0","@opentelemetry/api-logs":"^0.203.0","@opentelemetry/core":"^1.30.1","@opentelemetry/exporter-logs-otlp-http":"^0.203.0","@opentelemetry/exporter-metrics-otlp-http":"^0.203.0","@opentelemetry/exporter-trace-otlp-grpc":"^0.203.0","@opentelemetry/exporter-trace-otlp-http":"^0.203.0","@opentelemetry/instrumentation":"^0.203.0","@opentelemetry/resources":"^1.30.1","@opentelemetry/sdk-logs":"^0.203.0","@opentelemetry/sdk-metrics":"^1.30.1","@opentelemetry/sdk-node":"^0.203.0","@opentelemetry/sdk-trace-base":"^1.30.1","@opentelemetry/semantic-conventions":"^1.30.1","@probelabs/probe":"^0.6.0-rc293","@types/commander":"^2.12.0","@types/uuid":"^10.0.0","acorn":"^8.16.0","acorn-walk":"^8.3.5","ajv":"^8.17.1","ajv-formats":"^3.0.1","better-sqlite3":"^11.0.0","blessed":"^0.1.81","botbuilder":"^4.23.3","botframework-connector":"^4.23.3","cli-table3":"^0.6.5","commander":"^14.0.0","deepmerge":"^4.3.1","dotenv":"^17.2.3","grammy":"^1.41.1","ignore":"^7.0.5","imapflow":"^1.2.12","js-yaml":"^4.1.0","jsonpath-plus":"^10.4.0","liquidjs":"^10.21.1","mailparser":"^3.9.3","minimatch":"^10.2.2","node-cron":"^3.0.3","nodemailer":"^8.0.1","open":"^9.1.0","resend":"^6.9.3","simple-git":"^3.28.0","uuid":"^11.1.0","ws":"^8.18.3"},"optionalDependencies":{"@anthropic/claude-code-sdk":"npm:null@*","@open-policy-agent/opa-wasm":"^1.10.0","knex":"^3.1.0","mysql2":"^3.11.0","pg":"^8.13.0","tedious":"^19.0.0"},"devDependencies":{"@eslint/js":"^9.34.0","@kie/act-js":"^2.6.2","@kie/mock-github":"^2.0.1","@swc/core":"^1.13.2","@swc/jest":"^0.2.37","@types/better-sqlite3":"^7.6.0","@types/blessed":"^0.1.27","@types/jest":"^30.0.0","@types/js-yaml":"^4.0.9","@types/mailparser":"^3.4.6","@types/node":"^24.3.0","@types/node-cron":"^3.0.11","@types/nodemailer":"^7.0.11","@types/ws":"^8.18.1","@typescript-eslint/eslint-plugin":"^8.42.0","@typescript-eslint/parser":"^8.42.0","@vercel/ncc":"^0.38.4","eslint":"^9.34.0","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.4","husky":"^9.1.7","jest":"^30.1.3","lint-staged":"^16.1.6","prettier":"^3.6.2","reveal-md":"^6.1.2","ts-json-schema-generator":"^1.5.1","ts-node":"^10.9.2","tsup":"^8.5.0","typescript":"^5.9.2","wrangler":"^3.0.0"},"peerDependenciesMeta":{"@anthropic/claude-code-sdk":{"optional":true}},"directories":{"test":"tests"},"lint-staged":{"src/**/*.{ts,js}":["eslint --fix","prettier --write"],"tests/**/*.{ts,js}":["eslint --fix","prettier --write"],"*.{json,md,yml,yaml}":["prettier --write"]}}');
610684
610998
 
610685
610999
  /***/ })
610686
611000