@probelabs/visor 0.1.86 → 0.1.88

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 (33) hide show
  1. package/README.md +19 -2
  2. package/dist/action-cli-bridge.d.ts.map +1 -1
  3. package/dist/ai-review-service.d.ts +2 -1
  4. package/dist/ai-review-service.d.ts.map +1 -1
  5. package/dist/check-execution-engine.d.ts.map +1 -1
  6. package/dist/config.d.ts.map +1 -1
  7. package/dist/failure-condition-evaluator.d.ts.map +1 -1
  8. package/dist/generated/config-schema.d.ts +5 -0
  9. package/dist/generated/config-schema.d.ts.map +1 -1
  10. package/dist/generated/config-schema.json +8 -0
  11. package/dist/github-check-service.d.ts.map +1 -1
  12. package/dist/index.js +384 -80
  13. package/dist/output-formatters.d.ts +8 -0
  14. package/dist/output-formatters.d.ts.map +1 -1
  15. package/dist/providers/ai-check-provider.d.ts.map +1 -1
  16. package/dist/reviewer.d.ts +2 -0
  17. package/dist/reviewer.d.ts.map +1 -1
  18. package/dist/sdk/{check-execution-engine-WI7LJL2G.mjs → check-execution-engine-D6FPIIKR.mjs} +2 -2
  19. package/dist/sdk/{chunk-P4XLP7NN.mjs → chunk-N34GS4A5.mjs} +160 -32
  20. package/dist/sdk/chunk-N34GS4A5.mjs.map +1 -0
  21. package/dist/sdk/sdk.d.mts +4 -0
  22. package/dist/sdk/sdk.d.ts +4 -0
  23. package/dist/sdk/sdk.js +179 -30
  24. package/dist/sdk/sdk.js.map +1 -1
  25. package/dist/sdk/sdk.mjs +22 -1
  26. package/dist/sdk/sdk.mjs.map +1 -1
  27. package/dist/session-registry.d.ts +5 -0
  28. package/dist/session-registry.d.ts.map +1 -1
  29. package/dist/types/config.d.ts +2 -0
  30. package/dist/types/config.d.ts.map +1 -1
  31. package/package.json +1 -1
  32. package/dist/sdk/chunk-P4XLP7NN.mjs.map +0 -1
  33. /package/dist/sdk/{check-execution-engine-WI7LJL2G.mjs.map → check-execution-engine-D6FPIIKR.mjs.map} +0 -0
@@ -59,6 +59,8 @@ interface ReviewIssue {
59
59
  interface ReviewSummary {
60
60
  issues?: ReviewIssue[];
61
61
  debug?: AIDebugInfo;
62
+ /** Session ID created for this check (for cleanup tracking) */
63
+ sessionId?: string;
62
64
  }
63
65
 
64
66
  /**
@@ -262,6 +264,8 @@ interface CheckConfig {
262
264
  if?: string;
263
265
  /** Check name to reuse AI session from, or true to use first dependency (only works with depends_on) */
264
266
  reuse_ai_session?: string | boolean;
267
+ /** How to reuse AI session: 'clone' (default, copy history) or 'append' (share history) */
268
+ session_mode?: 'clone' | 'append';
265
269
  /** Simple fail condition - fails check if expression evaluates to true */
266
270
  fail_if?: string;
267
271
  /** Check-specific failure conditions - optional (deprecated, use fail_if) */
package/dist/sdk/sdk.d.ts CHANGED
@@ -59,6 +59,8 @@ interface ReviewIssue {
59
59
  interface ReviewSummary {
60
60
  issues?: ReviewIssue[];
61
61
  debug?: AIDebugInfo;
62
+ /** Session ID created for this check (for cleanup tracking) */
63
+ sessionId?: string;
62
64
  }
63
65
 
64
66
  /**
@@ -262,6 +264,8 @@ interface CheckConfig {
262
264
  if?: string;
263
265
  /** Check name to reuse AI session from, or true to use first dependency (only works with depends_on) */
264
266
  reuse_ai_session?: string | boolean;
267
+ /** How to reuse AI session: 'clone' (default, copy history) or 'append' (share history) */
268
+ session_mode?: 'clone' | 'append';
265
269
  /** Simple fail condition - fails check if expression evaluates to true */
266
270
  fail_if?: string;
267
271
  /** Check-specific failure conditions - optional (deprecated, use fail_if) */
package/dist/sdk/sdk.js CHANGED
@@ -400,6 +400,47 @@ var init_session_registry = __esm({
400
400
  hasSession(sessionId) {
401
401
  return this.sessions.has(sessionId);
402
402
  }
403
+ /**
404
+ * Clone a session with a new session ID
405
+ * Creates a new ProbeAgent with a copy of the conversation history
406
+ */
407
+ async cloneSession(sourceSessionId, newSessionId) {
408
+ const sourceAgent = this.sessions.get(sourceSessionId);
409
+ if (!sourceAgent) {
410
+ console.error(`\u26A0\uFE0F Cannot clone session: ${sourceSessionId} not found`);
411
+ return void 0;
412
+ }
413
+ try {
414
+ const sourceHistory = sourceAgent.conversationHistory || [];
415
+ const sourceOptions = sourceAgent.options || {};
416
+ const { ProbeAgent: ProbeAgentClass } = await import("@probelabs/probe");
417
+ const clonedAgent = new ProbeAgentClass({
418
+ ...sourceOptions,
419
+ sessionId: newSessionId
420
+ });
421
+ if (sourceHistory.length > 0) {
422
+ try {
423
+ const deepClonedHistory = JSON.parse(JSON.stringify(sourceHistory));
424
+ clonedAgent.conversationHistory = deepClonedHistory;
425
+ console.error(
426
+ `\u{1F4CB} Cloned session ${sourceSessionId} \u2192 ${newSessionId} (${sourceHistory.length} messages, deep copy)`
427
+ );
428
+ } catch (cloneError) {
429
+ console.error(
430
+ `\u26A0\uFE0F Warning: Deep clone failed for session ${sourceSessionId}, using shallow copy: ${cloneError}`
431
+ );
432
+ clonedAgent.conversationHistory = [...sourceHistory];
433
+ }
434
+ } else {
435
+ console.error(`\u{1F4CB} Cloned session ${sourceSessionId} \u2192 ${newSessionId} (no history)`);
436
+ }
437
+ this.registerSession(newSessionId, clonedAgent);
438
+ return clonedAgent;
439
+ } catch (error) {
440
+ console.error(`\u26A0\uFE0F Failed to clone session ${sourceSessionId}: ${error}`);
441
+ return void 0;
442
+ }
443
+ }
403
444
  /**
404
445
  * Register process exit handlers to cleanup sessions on exit
405
446
  */
@@ -671,8 +712,9 @@ var init_ai_review_service = __esm({
671
712
  }
672
713
  /**
673
714
  * Execute AI review using session reuse - reuses an existing ProbeAgent session
715
+ * @param sessionMode - 'clone' (default) clones history, 'append' shares history
674
716
  */
675
- async executeReviewWithSessionReuse(prInfo, customPrompt, parentSessionId, schema, checkName) {
717
+ async executeReviewWithSessionReuse(prInfo, customPrompt, parentSessionId, schema, checkName, sessionMode = "clone") {
676
718
  const startTime = Date.now();
677
719
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
678
720
  const existingAgent = this.sessionRegistry.getSession(parentSessionId);
@@ -682,7 +724,24 @@ var init_ai_review_service = __esm({
682
724
  );
683
725
  }
684
726
  const prompt = await this.buildCustomPrompt(prInfo, customPrompt, schema);
685
- log(`\u{1F504} Reusing AI session ${parentSessionId} for review...`);
727
+ let agentToUse;
728
+ let currentSessionId;
729
+ if (sessionMode === "clone") {
730
+ currentSessionId = `${parentSessionId}-clone-${Date.now()}`;
731
+ log(`\u{1F4CB} Cloning AI session ${parentSessionId} \u2192 ${currentSessionId}...`);
732
+ const clonedAgent = await this.sessionRegistry.cloneSession(
733
+ parentSessionId,
734
+ currentSessionId
735
+ );
736
+ if (!clonedAgent) {
737
+ throw new Error(`Failed to clone session ${parentSessionId}. Falling back to append mode.`);
738
+ }
739
+ agentToUse = clonedAgent;
740
+ } else {
741
+ log(`\u{1F504} Appending to AI session ${parentSessionId} (shared history)...`);
742
+ agentToUse = existingAgent;
743
+ currentSessionId = parentSessionId;
744
+ }
686
745
  log(`\u{1F527} Debug: Raw schema parameter: ${JSON.stringify(schema)} (type: ${typeof schema})`);
687
746
  log(`Schema type: ${schema || "none (no schema)"}`);
688
747
  let debugInfo;
@@ -706,7 +765,7 @@ var init_ai_review_service = __esm({
706
765
  }
707
766
  try {
708
767
  const { response, effectiveSchema } = await this.callProbeAgentWithExistingSession(
709
- existingAgent,
768
+ agentToUse,
710
769
  prompt,
711
770
  schema,
712
771
  debugInfo,
@@ -722,6 +781,9 @@ var init_ai_review_service = __esm({
722
781
  if (debugInfo) {
723
782
  result.debug = debugInfo;
724
783
  }
784
+ if (sessionMode === "clone" && currentSessionId !== parentSessionId) {
785
+ result.sessionId = currentSessionId;
786
+ }
725
787
  return result;
726
788
  } catch (error) {
727
789
  if (debugInfo) {
@@ -810,7 +872,8 @@ ${prContext}
810
872
  <rule>For INCREMENTAL analysis, ONLY review changes in commit_diff section</rule>
811
873
  <rule>For FULL analysis, review all changes in full_diff section</rule>
812
874
  <rule>Reference specific XML elements like files_summary, metadata when providing context</rule>
813
- <rule>SEVERITY ASSIGNMENT: All severity levels (critical, error, warning, info) should be based on the NEW code being introduced, not on what it fixes. When code INTRODUCES new problems (bugs, vulnerabilities, performance issues, breaking changes), assign appropriate severity (critical/error/warning). When code FIXES or IMPROVES existing issues, use lower severity (info/warning) to acknowledge the improvement. This applies to all issue types: security, performance, style, logic, documentation. The schema provides detailed severity level definitions.</rule>
875
+ <rule>STRICT OUTPUT POLICY: Report only actual problems, risks, or deficiencies. Do not write praise, congratulations, or celebratory text. Do not create issues that merely restate improvements or say "no action needed".</rule>
876
+ <rule>SEVERITY ASSIGNMENT: Assign severity ONLY to problems introduced or left unresolved by this change (critical/error/warning/info as appropriate). Do NOT create issue entries solely to acknowledge improvements; if no problems exist, return zero issues.</rule>
814
877
  </rules>
815
878
  </review_request>`;
816
879
  }
@@ -2713,9 +2776,10 @@ var init_ai_check_provider = __esm({
2713
2776
  }
2714
2777
  let result;
2715
2778
  if (sessionInfo?.reuseSession && sessionInfo.parentSessionId) {
2779
+ const sessionMode = config.session_mode || "clone";
2716
2780
  if (aiConfig.debug) {
2717
2781
  console.error(
2718
- `\u{1F504} Debug: Using session reuse with parent session: ${sessionInfo.parentSessionId}`
2782
+ `\u{1F504} Debug: Using session reuse with parent session: ${sessionInfo.parentSessionId} (mode: ${sessionMode})`
2719
2783
  );
2720
2784
  }
2721
2785
  result = await service.executeReviewWithSessionReuse(
@@ -2723,7 +2787,8 @@ var init_ai_check_provider = __esm({
2723
2787
  processedPrompt,
2724
2788
  sessionInfo.parentSessionId,
2725
2789
  schema,
2726
- config.checkName
2790
+ config.checkName,
2791
+ sessionMode
2727
2792
  );
2728
2793
  } else {
2729
2794
  if (aiConfig.debug) {
@@ -5460,26 +5525,36 @@ var init_failure_condition_evaluator = __esm({
5460
5525
  // eslint-disable-line @typescript-eslint/no-unused-vars
5461
5526
  ...otherFields
5462
5527
  } = reviewSummaryWithOutput;
5528
+ const aggregatedOutput = {
5529
+ issues: (issues || []).map((issue) => ({
5530
+ file: issue.file,
5531
+ line: issue.line,
5532
+ endLine: issue.endLine,
5533
+ ruleId: issue.ruleId,
5534
+ message: issue.message,
5535
+ severity: issue.severity,
5536
+ category: issue.category,
5537
+ group: issue.group,
5538
+ schema: issue.schema,
5539
+ suggestion: issue.suggestion,
5540
+ replacement: issue.replacement
5541
+ })),
5542
+ // Include additional schema-specific data from reviewSummary
5543
+ ...otherFields
5544
+ };
5545
+ if (Array.isArray(extractedOutput)) {
5546
+ aggregatedOutput.items = extractedOutput;
5547
+ const anyError = extractedOutput.find(
5548
+ (it) => it && typeof it === "object" && it.error
5549
+ );
5550
+ if (anyError && anyError.error !== void 0) {
5551
+ aggregatedOutput.error = anyError.error;
5552
+ }
5553
+ } else if (extractedOutput && typeof extractedOutput === "object") {
5554
+ Object.assign(aggregatedOutput, extractedOutput);
5555
+ }
5463
5556
  const context = {
5464
- output: {
5465
- issues: (issues || []).map((issue) => ({
5466
- file: issue.file,
5467
- line: issue.line,
5468
- endLine: issue.endLine,
5469
- ruleId: issue.ruleId,
5470
- message: issue.message,
5471
- severity: issue.severity,
5472
- category: issue.category,
5473
- group: issue.group,
5474
- schema: issue.schema,
5475
- suggestion: issue.suggestion,
5476
- replacement: issue.replacement
5477
- })),
5478
- // Include additional schema-specific data from reviewSummary
5479
- ...otherFields,
5480
- // Spread the extracted output directly (avoid output.output nesting)
5481
- ...extractedOutput && typeof extractedOutput === "object" ? extractedOutput : {}
5482
- },
5557
+ output: aggregatedOutput,
5483
5558
  outputs: (() => {
5484
5559
  if (!previousOutputs) return {};
5485
5560
  const outputs = {};
@@ -5645,9 +5720,11 @@ var init_github_check_service = __esm({
5645
5720
  reviewIssues,
5646
5721
  executionError
5647
5722
  );
5648
- let filteredIssues = reviewIssues;
5723
+ let filteredIssues = reviewIssues.filter(
5724
+ (issue) => !(issue.file === "system" && issue.line === 0)
5725
+ );
5649
5726
  if (filesChangedInCommit && filesChangedInCommit.length > 0) {
5650
- filteredIssues = reviewIssues.filter(
5727
+ filteredIssues = filteredIssues.filter(
5651
5728
  (issue) => filesChangedInCommit.some((changedFile) => issue.file === changedFile)
5652
5729
  );
5653
5730
  }
@@ -7387,10 +7464,19 @@ ${expr}
7387
7464
  const id = issue.ruleId || "";
7388
7465
  return id.endsWith("/__skipped");
7389
7466
  });
7390
- const hasFatalFailure = (depRes.issues || []).some((issue) => {
7467
+ let hasFatalFailure = (depRes.issues || []).some((issue) => {
7391
7468
  const id = issue.ruleId || "";
7392
- return id === "command/execution_error" || id.endsWith("/command/execution_error") || id === "command/transform_js_error" || id.endsWith("/command/transform_js_error") || id === "command/transform_error" || id.endsWith("/command/transform_error") || id.endsWith("/forEach/iteration_error") || id === "forEach/undefined_output" || id.endsWith("/forEach/undefined_output") || id.endsWith("_fail_if") || id.endsWith("/global_fail_if");
7469
+ return id === "command/execution_error" || id.endsWith("/command/execution_error") || id === "command/timeout" || id.endsWith("/command/timeout") || id === "command/transform_js_error" || id.endsWith("/command/transform_js_error") || id === "command/transform_error" || id.endsWith("/command/transform_error") || id.endsWith("/forEach/iteration_error") || id === "forEach/undefined_output" || id.endsWith("/forEach/undefined_output") || id.endsWith("_fail_if") || id.endsWith("/global_fail_if");
7393
7470
  });
7471
+ if (!hasFatalFailure && config && (config.fail_if || config.checks[depId]?.fail_if)) {
7472
+ const failIfResults = await this.evaluateFailureConditions(depId, depRes, config);
7473
+ hasFatalFailure = failIfResults.some((r) => r.failed);
7474
+ }
7475
+ if (debug) {
7476
+ log2(
7477
+ `\u{1F527} Debug: gating check '${checkName}' against dep '${depId}': wasSkipped=${wasSkipped} hasFatalFailure=${hasFatalFailure}`
7478
+ );
7479
+ }
7394
7480
  if (wasSkipped || hasFatalFailure) failedDeps.push(depId);
7395
7481
  }
7396
7482
  if (failedDeps.length > 0) {
@@ -7622,6 +7708,24 @@ ${expr}
7622
7708
  issues: allIssues,
7623
7709
  ...finalOutput !== void 0 ? { output: finalOutput } : {}
7624
7710
  };
7711
+ if (config && (config.fail_if || checkConfig.fail_if)) {
7712
+ const failureResults = await this.evaluateFailureConditions(
7713
+ checkName,
7714
+ finalResult,
7715
+ config
7716
+ );
7717
+ if (failureResults.length > 0) {
7718
+ const failureIssues = failureResults.filter((f) => f.failed).map((f) => ({
7719
+ file: "system",
7720
+ line: 0,
7721
+ ruleId: f.conditionName,
7722
+ message: f.message || `Failure condition met: ${f.expression}`,
7723
+ severity: f.severity || "error",
7724
+ category: "logic"
7725
+ }));
7726
+ finalResult.issues = [...finalResult.issues || [], ...failureIssues];
7727
+ }
7728
+ }
7625
7729
  if (allOutputs.length > 0) {
7626
7730
  finalResult.isForEach = true;
7627
7731
  finalResult.forEachItems = allOutputs;
@@ -7668,9 +7772,27 @@ ${expr}
7668
7772
  debug,
7669
7773
  results
7670
7774
  );
7775
+ if (config && (config.fail_if || checkConfig.fail_if)) {
7776
+ const failureResults = await this.evaluateFailureConditions(
7777
+ checkName,
7778
+ finalResult,
7779
+ config
7780
+ );
7781
+ if (failureResults.length > 0) {
7782
+ const failureIssues = failureResults.filter((f) => f.failed).map((f) => ({
7783
+ file: "system",
7784
+ line: 0,
7785
+ ruleId: f.conditionName,
7786
+ message: f.message || `Failure condition met: ${f.expression}`,
7787
+ severity: f.severity || "error",
7788
+ category: "logic"
7789
+ }));
7790
+ finalResult.issues = [...finalResult.issues || [], ...failureIssues];
7791
+ }
7792
+ }
7671
7793
  const hadFatalError = (finalResult.issues || []).some((issue) => {
7672
7794
  const id = issue.ruleId || "";
7673
- return id === "command/execution_error" || id.endsWith("/command/execution_error") || id === "command/transform_js_error" || id.endsWith("/command/transform_js_error") || id === "command/transform_error" || id.endsWith("/command/transform_error") || id === "forEach/undefined_output" || id.endsWith("/forEach/undefined_output");
7795
+ return id === "command/execution_error" || id.endsWith("/command/execution_error") || id === "command/timeout" || id.endsWith("/command/timeout") || id === "command/transform_js_error" || id.endsWith("/command/transform_js_error") || id === "command/transform_error" || id.endsWith("/command/transform_error") || id === "forEach/undefined_output" || id.endsWith("/forEach/undefined_output");
7674
7796
  });
7675
7797
  this.recordIterationComplete(
7676
7798
  checkName,
@@ -7693,6 +7815,12 @@ ${expr}
7693
7815
  `\u{1F527} Debug: Completed check: ${checkName}, issues found: ${(finalResult.issues || []).length}`
7694
7816
  );
7695
7817
  }
7818
+ if (finalResult.sessionId) {
7819
+ sessionIds.set(checkName, finalResult.sessionId);
7820
+ if (debug) {
7821
+ log2(`\u{1F527} Debug: Tracked cloned session for cleanup: ${finalResult.sessionId}`);
7822
+ }
7823
+ }
7696
7824
  }
7697
7825
  const enrichedIssues = (finalResult.issues || []).map((issue) => ({
7698
7826
  ...issue,
@@ -9530,6 +9658,11 @@ var init_config_schema = __esm({
9530
9658
  type: ["string", "boolean"],
9531
9659
  description: "Check name to reuse AI session from, or true to use first dependency (only works with depends_on)"
9532
9660
  },
9661
+ session_mode: {
9662
+ type: "string",
9663
+ enum: ["clone", "append"],
9664
+ description: "How to reuse AI session: 'clone' (default, copy history) or 'append' (share history)"
9665
+ },
9533
9666
  fail_if: {
9534
9667
  type: "string",
9535
9668
  description: "Simple fail condition - fails check if expression evaluates to true"
@@ -11073,6 +11206,22 @@ var ConfigManager = class {
11073
11206
  }
11074
11207
  }
11075
11208
  }
11209
+ if (checkConfig.session_mode !== void 0) {
11210
+ if (checkConfig.session_mode !== "clone" && checkConfig.session_mode !== "append") {
11211
+ errors.push({
11212
+ field: `checks.${checkName}.session_mode`,
11213
+ message: `Invalid session_mode value for "${checkName}": must be 'clone' or 'append'`,
11214
+ value: checkConfig.session_mode
11215
+ });
11216
+ }
11217
+ if (!checkConfig.reuse_ai_session) {
11218
+ errors.push({
11219
+ field: `checks.${checkName}.session_mode`,
11220
+ message: `Check "${checkName}" has session_mode but no reuse_ai_session. session_mode requires reuse_ai_session to be set.`,
11221
+ value: checkConfig.session_mode
11222
+ });
11223
+ }
11224
+ }
11076
11225
  if (checkConfig.tags !== void 0) {
11077
11226
  if (!Array.isArray(checkConfig.tags)) {
11078
11227
  errors.push({