@probelabs/visor 0.1.113 → 0.1.122
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.
- package/defaults/workflow-builder.tests.yaml +363 -0
- package/defaults/workflow-builder.yaml +720 -0
- package/dist/ai-review-service.d.ts.map +1 -1
- package/dist/cli-main.d.ts.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/defaults/workflow-builder.tests.yaml +363 -0
- package/dist/defaults/workflow-builder.yaml +720 -0
- package/dist/docs/workflow-creation-guide.md +1274 -0
- package/dist/frontends/slack-frontend.d.ts +3 -0
- package/dist/frontends/slack-frontend.d.ts.map +1 -1
- package/dist/generated/config-schema.d.ts +14 -6
- package/dist/generated/config-schema.d.ts.map +1 -1
- package/dist/generated/config-schema.json +14 -6
- package/dist/index.js +36879 -13895
- package/dist/logger.d.ts +9 -0
- package/dist/logger.d.ts.map +1 -1
- package/dist/mcp-server.d.ts +4 -4
- package/dist/output/traces/{run-2026-01-21T12-31-10-108Z.ndjson → run-2026-01-28T13-56-32-263Z.ndjson} +84 -84
- package/dist/output/traces/run-2026-01-28T13-57-13-140Z.ndjson +1357 -0
- package/dist/providers/ai-check-provider.d.ts.map +1 -1
- package/dist/providers/check-provider.interface.d.ts +15 -1
- package/dist/providers/check-provider.interface.d.ts.map +1 -1
- package/dist/providers/command-check-provider.d.ts.map +1 -1
- package/dist/providers/git-checkout-provider.d.ts.map +1 -1
- package/dist/sdk/{check-provider-registry-534KL5HT.mjs → check-provider-registry-JMNLGIMJ.mjs} +11 -11
- package/dist/sdk/{chunk-AIVFBIS4.mjs → chunk-35NT3725.mjs} +30 -10
- package/dist/sdk/chunk-35NT3725.mjs.map +1 -0
- package/dist/sdk/{chunk-AGIZJ4UZ.mjs → chunk-3NMLT3YS.mjs} +42 -8
- package/dist/sdk/chunk-3NMLT3YS.mjs.map +1 -0
- package/dist/sdk/{chunk-AK6BVWIT.mjs → chunk-7GUAFV6L.mjs} +2 -2
- package/dist/sdk/{chunk-QY2XYPEV.mjs → chunk-CUNPH6TR.mjs} +18 -10
- package/dist/sdk/chunk-CUNPH6TR.mjs.map +1 -0
- package/dist/sdk/{chunk-HTOKWMPO.mjs → chunk-HQL734ZI.mjs} +2 -2
- package/dist/sdk/{chunk-7UK3NIIT.mjs → chunk-IHZOSIF4.mjs} +2 -2
- package/dist/sdk/{chunk-AUT26LHW.mjs → chunk-J2QWVDXK.mjs} +2 -2
- package/dist/sdk/{chunk-QR7MOMJH.mjs → chunk-J6EVEXC2.mjs} +2 -2
- package/dist/sdk/{chunk-SIWNBRTK.mjs → chunk-SWEEZ5D5.mjs} +3 -3
- package/dist/sdk/{chunk-23L3QRYX.mjs → chunk-VPEQOQ7G.mjs} +279 -70
- package/dist/sdk/chunk-VPEQOQ7G.mjs.map +1 -0
- package/dist/sdk/{command-executor-TYUV6HUS.mjs → command-executor-Q7MHJKZJ.mjs} +3 -3
- package/dist/sdk/{config-YNC2EOOT.mjs → config-MK4XTU45.mjs} +3 -3
- package/dist/sdk/{failure-condition-evaluator-YGTF2GHG.mjs → failure-condition-evaluator-HB35XRLZ.mjs} +4 -4
- package/dist/sdk/{github-frontend-SIAEOCON.mjs → github-frontend-6Q4BISZX.mjs} +4 -4
- package/dist/sdk/{host-DXUYTNMU.mjs → host-P5NQICP7.mjs} +3 -3
- package/dist/sdk/{liquid-extensions-PKWCKK7E.mjs → liquid-extensions-DFDEBMUI.mjs} +4 -4
- package/dist/sdk/{memory-store-XGBB7LX7.mjs → memory-store-RW5N2NGJ.mjs} +3 -3
- package/dist/sdk/{prompt-state-YRJY6QAL.mjs → prompt-state-EZYOUG75.mjs} +3 -3
- package/dist/sdk/{renderer-schema-LPKN5UJS.mjs → renderer-schema-CKFB5NDB.mjs} +2 -2
- package/dist/sdk/{routing-6N45MJ4F.mjs → routing-KZ345OFG.mjs} +5 -5
- package/dist/sdk/sdk.d.mts +19 -1
- package/dist/sdk/sdk.d.ts +19 -1
- package/dist/sdk/sdk.js +421 -75
- package/dist/sdk/sdk.js.map +1 -1
- package/dist/sdk/sdk.mjs +35 -18
- package/dist/sdk/sdk.mjs.map +1 -1
- package/dist/sdk/{slack-frontend-BVKW3GD5.mjs → slack-frontend-J442FJWZ.mjs} +61 -3
- package/dist/sdk/slack-frontend-J442FJWZ.mjs.map +1 -0
- package/dist/sdk/{workflow-registry-R6KSACFR.mjs → workflow-registry-6LZKCWHP.mjs} +3 -3
- package/dist/state-machine/context/build-engine-context.d.ts.map +1 -1
- package/dist/state-machine/dispatch/history-snapshot.d.ts.map +1 -1
- package/dist/state-machine/runner.d.ts.map +1 -1
- package/dist/state-machine/states/level-dispatch.d.ts.map +1 -1
- package/dist/state-machine/states/routing.d.ts.map +1 -1
- package/dist/state-machine/states/wave-planning.d.ts.map +1 -1
- package/dist/ter-u14b.json +17826 -0
- package/dist/ter-u14n.json +17826 -0
- package/dist/test-runner/index.d.ts.map +1 -1
- package/dist/traces/{run-2026-01-21T12-31-10-108Z.ndjson → run-2026-01-28T13-56-32-263Z.ndjson} +84 -84
- package/dist/traces/run-2026-01-28T13-57-13-140Z.ndjson +1357 -0
- package/dist/tui.d.ts +51 -0
- package/dist/tui.d.ts.map +1 -0
- package/dist/types/cli.d.ts +10 -0
- package/dist/types/cli.d.ts.map +1 -1
- package/dist/types/config.d.ts +4 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/engine.d.ts +3 -0
- package/dist/types/engine.d.ts.map +1 -1
- package/dist/usr/fonts/AUTHORS +1 -0
- package/dist/usr/fonts/LICENSE +94 -0
- package/dist/usr/fonts/README +340 -0
- package/dist/usr/fonts/ter-u14b.json +17826 -0
- package/dist/usr/fonts/ter-u14n.json +17826 -0
- package/dist/usr/linux +0 -0
- package/dist/usr/windows-ansi +0 -0
- package/dist/usr/xterm +0 -0
- package/dist/usr/xterm-256color +0 -0
- package/dist/usr/xterm.termcap +243 -0
- package/dist/usr/xterm.terminfo +1977 -0
- package/dist/utils/workspace-manager.d.ts +2 -0
- package/dist/utils/workspace-manager.d.ts.map +1 -1
- package/dist/utils/worktree-manager.d.ts +5 -0
- package/dist/utils/worktree-manager.d.ts.map +1 -1
- package/dist/xterm +0 -0
- package/dist/xterm.termcap +243 -0
- package/package.json +9 -7
- package/dist/output/traces/run-2026-01-21T12-32-04-510Z.ndjson +0 -1067
- package/dist/sdk/chunk-23L3QRYX.mjs.map +0 -1
- package/dist/sdk/chunk-AGIZJ4UZ.mjs.map +0 -1
- package/dist/sdk/chunk-AIVFBIS4.mjs.map +0 -1
- package/dist/sdk/chunk-QY2XYPEV.mjs.map +0 -1
- package/dist/sdk/slack-frontend-BVKW3GD5.mjs.map +0 -1
- package/dist/traces/run-2026-01-21T12-32-04-510Z.ndjson +0 -1067
- /package/dist/sdk/{check-provider-registry-534KL5HT.mjs.map → check-provider-registry-JMNLGIMJ.mjs.map} +0 -0
- /package/dist/sdk/{chunk-AK6BVWIT.mjs.map → chunk-7GUAFV6L.mjs.map} +0 -0
- /package/dist/sdk/{chunk-HTOKWMPO.mjs.map → chunk-HQL734ZI.mjs.map} +0 -0
- /package/dist/sdk/{chunk-7UK3NIIT.mjs.map → chunk-IHZOSIF4.mjs.map} +0 -0
- /package/dist/sdk/{chunk-AUT26LHW.mjs.map → chunk-J2QWVDXK.mjs.map} +0 -0
- /package/dist/sdk/{chunk-QR7MOMJH.mjs.map → chunk-J6EVEXC2.mjs.map} +0 -0
- /package/dist/sdk/{chunk-SIWNBRTK.mjs.map → chunk-SWEEZ5D5.mjs.map} +0 -0
- /package/dist/sdk/{command-executor-TYUV6HUS.mjs.map → command-executor-Q7MHJKZJ.mjs.map} +0 -0
- /package/dist/sdk/{config-YNC2EOOT.mjs.map → config-MK4XTU45.mjs.map} +0 -0
- /package/dist/sdk/{failure-condition-evaluator-YGTF2GHG.mjs.map → failure-condition-evaluator-HB35XRLZ.mjs.map} +0 -0
- /package/dist/sdk/{github-frontend-SIAEOCON.mjs.map → github-frontend-6Q4BISZX.mjs.map} +0 -0
- /package/dist/sdk/{host-DXUYTNMU.mjs.map → host-P5NQICP7.mjs.map} +0 -0
- /package/dist/sdk/{liquid-extensions-PKWCKK7E.mjs.map → liquid-extensions-DFDEBMUI.mjs.map} +0 -0
- /package/dist/sdk/{memory-store-XGBB7LX7.mjs.map → memory-store-RW5N2NGJ.mjs.map} +0 -0
- /package/dist/sdk/{prompt-state-YRJY6QAL.mjs.map → prompt-state-EZYOUG75.mjs.map} +0 -0
- /package/dist/sdk/{renderer-schema-LPKN5UJS.mjs.map → renderer-schema-CKFB5NDB.mjs.map} +0 -0
- /package/dist/sdk/{routing-6N45MJ4F.mjs.map → routing-KZ345OFG.mjs.map} +0 -0
- /package/dist/sdk/{workflow-registry-R6KSACFR.mjs.map → workflow-registry-6LZKCWHP.mjs.map} +0 -0
package/dist/sdk/sdk.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/workspace-manager.ts","../../src/state-machine/context/build-engine-context.ts","../../src/state-machine/execution/summary.ts","../../src/state-machine-execution-engine.ts","../../src/sdk.ts"],"sourcesContent":["/**\n * Workspace Manager\n *\n * Provides full isolation between parallel visor runs with human-readable project names.\n * Each run gets its own workspace in /tmp containing worktrees for all projects.\n */\n\nimport * as fsp from 'fs/promises';\nimport * as path from 'path';\nimport { commandExecutor } from './command-executor';\nimport { logger } from '../logger';\n\n/**\n * Escape a string for safe use in shell commands.\n * Uses single quotes and escapes any embedded single quotes.\n */\nfunction shellEscape(str: string): string {\n // Replace single quotes with '\\'' (end quote, escaped quote, start quote)\n // Then wrap the whole thing in single quotes\n return \"'\" + str.replace(/'/g, \"'\\\\''\") + \"'\";\n}\n\n/**\n * Sanitize a path component to prevent path traversal attacks.\n * Removes directory separators and parent directory references.\n */\nfunction sanitizePathComponent(name: string): string {\n return (\n name\n .replace(/\\.\\./g, '') // Remove parent directory references\n .replace(/[\\/\\\\]/g, '-') // Replace path separators with dashes\n .replace(/^\\.+/, '') // Remove leading dots\n .trim() || 'unnamed'\n ); // Ensure non-empty result\n}\n\nexport interface WorkspaceConfig {\n enabled: boolean;\n basePath: string;\n cleanupOnExit: boolean;\n}\n\nexport interface WorkspaceInfo {\n sessionId: string;\n workspacePath: string;\n mainProjectPath: string;\n mainProjectName: string;\n originalPath: string;\n}\n\nexport interface ProjectInfo {\n name: string;\n path: string;\n worktreePath: string;\n repository: string;\n}\n\n/**\n * WorkspaceManager creates isolated workspaces for parallel visor runs.\n * Each run gets a unique workspace directory containing worktrees for all projects.\n */\nexport class WorkspaceManager {\n private static instances: Map<string, WorkspaceManager> = new Map();\n\n private sessionId: string;\n private basePath: string;\n private workspacePath: string;\n private originalPath: string;\n private config: WorkspaceConfig;\n private initialized: boolean = false;\n private mainProjectInfo: WorkspaceInfo | null = null;\n private projects: Map<string, ProjectInfo> = new Map();\n private cleanupHandlersRegistered: boolean = false;\n private usedNames: Set<string> = new Set();\n\n private constructor(sessionId: string, originalPath: string, config?: Partial<WorkspaceConfig>) {\n this.sessionId = sessionId;\n this.originalPath = originalPath;\n\n // Default configuration\n this.config = {\n enabled: true,\n basePath: process.env.VISOR_WORKSPACE_PATH || '/tmp/visor-workspaces',\n cleanupOnExit: true,\n ...config,\n };\n\n this.basePath = this.config.basePath;\n this.workspacePath = path.join(this.basePath, sanitizePathComponent(this.sessionId));\n }\n\n /**\n * Get or create a WorkspaceManager instance for a session\n */\n static getInstance(\n sessionId: string,\n originalPath: string,\n config?: Partial<WorkspaceConfig>\n ): WorkspaceManager {\n if (!WorkspaceManager.instances.has(sessionId)) {\n WorkspaceManager.instances.set(\n sessionId,\n new WorkspaceManager(sessionId, originalPath, config)\n );\n }\n return WorkspaceManager.instances.get(sessionId)!;\n }\n\n /**\n * Clear all instances (for testing)\n */\n static clearInstances(): void {\n WorkspaceManager.instances.clear();\n }\n\n /**\n * Check if workspace isolation is enabled\n */\n isEnabled(): boolean {\n return this.config.enabled;\n }\n\n /**\n * Get the workspace path\n */\n getWorkspacePath(): string {\n return this.workspacePath;\n }\n\n /**\n * Get the original working directory\n */\n getOriginalPath(): string {\n return this.originalPath;\n }\n\n /**\n * Get workspace info (only available after initialize)\n */\n getWorkspaceInfo(): WorkspaceInfo | null {\n return this.mainProjectInfo;\n }\n\n /**\n * Initialize the workspace - creates workspace directory and main project worktree\n */\n async initialize(): Promise<WorkspaceInfo> {\n if (!this.config.enabled) {\n throw new Error('Workspace isolation is not enabled');\n }\n\n if (this.initialized && this.mainProjectInfo) {\n return this.mainProjectInfo;\n }\n\n logger.info(`Initializing workspace: ${this.workspacePath}`);\n\n // Create workspace directory (mkdir with recursive handles existing dirs)\n await fsp.mkdir(this.workspacePath, { recursive: true });\n logger.debug(`Created workspace directory: ${this.workspacePath}`);\n\n // Extract main project name from original path (sanitize for defense in depth)\n const mainProjectName = sanitizePathComponent(this.extractProjectName(this.originalPath));\n this.usedNames.add(mainProjectName);\n\n // Create worktree for main project\n const mainProjectPath = path.join(this.workspacePath, mainProjectName);\n\n // Check if original path is a git repository\n const isGitRepo = await this.isGitRepository(this.originalPath);\n\n if (isGitRepo) {\n // Create worktree for main project\n await this.createMainProjectWorktree(mainProjectPath);\n } else {\n // If not a git repo, create a symlink instead\n logger.debug(`Original path is not a git repo, creating symlink`);\n try {\n await fsp.symlink(this.originalPath, mainProjectPath);\n } catch (error) {\n throw new Error(`Failed to create symlink for main project: ${error}`);\n }\n }\n\n // Register cleanup handlers\n this.registerCleanupHandlers();\n\n this.mainProjectInfo = {\n sessionId: this.sessionId,\n workspacePath: this.workspacePath,\n mainProjectPath,\n mainProjectName,\n originalPath: this.originalPath,\n };\n\n this.initialized = true;\n logger.info(`Workspace initialized: ${this.workspacePath}`);\n\n return this.mainProjectInfo;\n }\n\n /**\n * Add a project to the workspace (creates symlink to worktree)\n */\n async addProject(\n repository: string,\n worktreePath: string,\n description?: string\n ): Promise<string> {\n if (!this.initialized) {\n throw new Error('Workspace not initialized. Call initialize() first.');\n }\n\n // Extract project name and sanitize to prevent path traversal\n let projectName = sanitizePathComponent(description || this.extractRepoName(repository));\n\n // Handle duplicate names\n projectName = this.getUniqueName(projectName);\n this.usedNames.add(projectName);\n\n // Create symlink in workspace\n const workspacePath = path.join(this.workspacePath, projectName);\n\n // Remove existing symlink/directory if present (rm with force handles non-existent)\n await fsp.rm(workspacePath, { recursive: true, force: true });\n\n try {\n await fsp.symlink(worktreePath, workspacePath);\n } catch (error) {\n throw new Error(`Failed to create symlink for project ${projectName}: ${error}`);\n }\n\n // Track project\n this.projects.set(projectName, {\n name: projectName,\n path: workspacePath,\n worktreePath,\n repository,\n });\n\n logger.info(`Added project to workspace: ${projectName} -> ${worktreePath}`);\n\n return workspacePath;\n }\n\n /**\n * List all projects in the workspace\n */\n listProjects(): ProjectInfo[] {\n return Array.from(this.projects.values());\n }\n\n /**\n * Cleanup the workspace\n */\n async cleanup(): Promise<void> {\n logger.info(`Cleaning up workspace: ${this.workspacePath}`);\n\n try {\n // Remove main project worktree if it exists\n if (this.mainProjectInfo) {\n const mainProjectPath = this.mainProjectInfo.mainProjectPath;\n\n // Check if path exists and if it's a worktree (not a symlink)\n try {\n const stats = await fsp.lstat(mainProjectPath);\n if (!stats.isSymbolicLink()) {\n await this.removeMainProjectWorktree(mainProjectPath);\n }\n } catch {\n // Path doesn't exist, nothing to clean up\n }\n }\n\n // Remove workspace directory\n await fsp.rm(this.workspacePath, { recursive: true, force: true });\n logger.debug(`Removed workspace directory: ${this.workspacePath}`);\n\n // Remove from instances\n WorkspaceManager.instances.delete(this.sessionId);\n\n this.initialized = false;\n this.mainProjectInfo = null;\n this.projects.clear();\n this.usedNames.clear();\n\n logger.info(`Workspace cleanup completed: ${this.sessionId}`);\n } catch (error) {\n logger.warn(`Failed to cleanup workspace: ${error}`);\n }\n }\n\n /**\n * Create worktree for the main project\n *\n * visor-disable: architecture - Not using WorktreeManager here because:\n * 1. WorktreeManager expects remote URLs and clones to bare repos first\n * 2. This operates on the LOCAL repo we're already in (no cloning needed)\n * 3. Adding a \"local mode\" to WorktreeManager would add complexity for minimal benefit\n * The git commands here are simpler (just rev-parse + worktree add) vs WorktreeManager's\n * full clone/bare-repo/fetch/worktree pipeline.\n */\n private async createMainProjectWorktree(targetPath: string): Promise<void> {\n logger.debug(`Creating main project worktree: ${targetPath}`);\n\n // Get current HEAD\n const headResult = await commandExecutor.execute(\n `git -C ${shellEscape(this.originalPath)} rev-parse HEAD`,\n {\n timeout: 10000,\n }\n );\n\n if (headResult.exitCode !== 0) {\n throw new Error(`Failed to get HEAD: ${headResult.stderr}`);\n }\n\n const headRef = headResult.stdout.trim();\n\n // Create worktree using detached HEAD to avoid branch conflicts\n const createCmd = `git -C ${shellEscape(this.originalPath)} worktree add --detach ${shellEscape(targetPath)} ${shellEscape(headRef)}`;\n const result = await commandExecutor.execute(createCmd, { timeout: 60000 });\n\n if (result.exitCode !== 0) {\n throw new Error(`Failed to create main project worktree: ${result.stderr}`);\n }\n\n logger.debug(`Created main project worktree at ${targetPath}`);\n }\n\n /**\n * Remove main project worktree\n */\n private async removeMainProjectWorktree(worktreePath: string): Promise<void> {\n logger.debug(`Removing main project worktree: ${worktreePath}`);\n\n const removeCmd = `git -C ${shellEscape(this.originalPath)} worktree remove ${shellEscape(worktreePath)} --force`;\n const result = await commandExecutor.execute(removeCmd, { timeout: 30000 });\n\n if (result.exitCode !== 0) {\n logger.warn(`Failed to remove worktree via git: ${result.stderr}`);\n // Directory will be removed with the workspace anyway\n }\n }\n\n /**\n * Check if a path is a git repository\n */\n private async isGitRepository(dirPath: string): Promise<boolean> {\n try {\n const result = await commandExecutor.execute(\n `git -C ${shellEscape(dirPath)} rev-parse --git-dir`,\n {\n timeout: 5000,\n }\n );\n return result.exitCode === 0;\n } catch {\n return false;\n }\n }\n\n /**\n * Extract project name from path\n */\n private extractProjectName(dirPath: string): string {\n return path.basename(dirPath);\n }\n\n /**\n * Extract repository name from owner/repo format\n */\n private extractRepoName(repository: string): string {\n // Handle URLs\n if (repository.includes('://') || repository.startsWith('git@')) {\n // Extract from URL\n const match = repository.match(/[/:]([^/:]+\\/[^/:]+?)(?:\\.git)?$/);\n if (match) {\n return match[1].split('/').pop() || repository;\n }\n }\n\n // Handle owner/repo format\n if (repository.includes('/')) {\n return repository.split('/').pop() || repository;\n }\n\n return repository;\n }\n\n /**\n * Get a unique name by appending a number if needed\n */\n private getUniqueName(baseName: string): string {\n if (!this.usedNames.has(baseName)) {\n return baseName;\n }\n\n let counter = 2;\n let uniqueName = `${baseName}-${counter}`;\n while (this.usedNames.has(uniqueName)) {\n counter++;\n uniqueName = `${baseName}-${counter}`;\n }\n\n return uniqueName;\n }\n\n /**\n * Register cleanup handlers for process exit\n */\n private registerCleanupHandlers(): void {\n if (this.cleanupHandlersRegistered || !this.config.cleanupOnExit) {\n return;\n }\n\n // Note: We don't register on 'exit' as it must be synchronous\n // SIGINT and SIGTERM handlers are already registered by WorktreeManager\n // We rely on explicit cleanup call or process handlers from the engine\n\n this.cleanupHandlersRegistered = true;\n }\n}\n","import type { VisorConfig, EventTrigger } from '../../types/config';\nimport type { PRInfo } from '../../pr-analyzer';\nimport type { EngineContext, CheckMetadata } from '../../types/engine';\nimport { ExecutionJournal } from '../../snapshot-store';\nimport { MemoryStore } from '../../memory-store';\nimport { v4 as uuidv4 } from 'uuid';\nimport { logger } from '../../logger';\nimport type { VisorConfig as VCfg, CheckConfig as CfgCheck } from '../../types/config';\nimport { WorkspaceManager } from '../../utils/workspace-manager';\n\n/**\n * Apply minimal criticality defaults in-place.\n * This is a no-behavior-change scaffold: we only default missing\n * check.criticality to 'policy' so downstream code can rely on a value.\n * Future mapping (retries/loop budgets) can build on this without\n * changing existing behavior.\n */\nfunction applyCriticalityDefaults(cfg: VCfg): void {\n const checks = cfg.checks || {};\n for (const id of Object.keys(checks)) {\n const c: CfgCheck = (checks as any)[id] as CfgCheck;\n if (!c.criticality) (c.criticality as any) = 'policy';\n // For 'info' checks, default continue_on_failure to true if unset.\n if (c.criticality === 'info' && typeof c.continue_on_failure === 'undefined')\n c.continue_on_failure = true;\n }\n}\n\n/**\n * Pure helper to build an EngineContext for a state-machine run.\n * Extracted to reduce StateMachineExecutionEngine size; behavior unchanged.\n */\nexport function buildEngineContextForRun(\n workingDirectory: string,\n config: VisorConfig,\n prInfo: PRInfo,\n debug?: boolean,\n maxParallelism?: number,\n failFast?: boolean,\n requestedChecks?: string[]\n): EngineContext {\n // Deep clone provided config to avoid cross-run mutations between tests/runs\n const clonedConfig: VisorConfig = JSON.parse(JSON.stringify(config));\n\n // Build check metadata\n const checks: Record<string, CheckMetadata> = {};\n\n // Fill in minimal defaults derived from criticality (no behavior change)\n applyCriticalityDefaults(clonedConfig);\n\n // If config has checks, use them\n for (const [checkId, checkConfig] of Object.entries(clonedConfig.checks || {})) {\n checks[checkId] = {\n tags: checkConfig.tags || [],\n triggers: (Array.isArray(checkConfig.on) ? checkConfig.on : [checkConfig.on]).filter(\n Boolean\n ) as EventTrigger[],\n group: checkConfig.group,\n providerType: checkConfig.type || 'ai',\n // Normalize depends_on to array (supports string | string[])\n dependencies: Array.isArray(checkConfig.depends_on)\n ? checkConfig.depends_on\n : checkConfig.depends_on\n ? [checkConfig.depends_on]\n : [],\n };\n }\n\n // Backward compatibility: synthesize minimal check configs for requested checks\n // that don't exist in the config (e.g., legacy test mode with empty config)\n if (requestedChecks && requestedChecks.length > 0) {\n for (const checkName of requestedChecks) {\n if (!checks[checkName] && !clonedConfig.checks?.[checkName]) {\n // Synthesize a minimal check config for this legacy check name\n logger.debug(`[StateMachine] Synthesizing minimal config for legacy check: ${checkName}`);\n\n // Add to config.checks so providers can find it\n if (!clonedConfig.checks) {\n clonedConfig.checks = {};\n }\n clonedConfig.checks[checkName] = {\n type: 'ai',\n prompt: `Perform ${checkName} analysis`,\n } as any;\n\n // Add metadata\n checks[checkName] = {\n tags: [],\n triggers: [],\n group: 'default',\n providerType: 'ai',\n dependencies: [],\n };\n }\n }\n }\n\n // Initialize journal and memory\n const journal = new ExecutionJournal();\n const memory = MemoryStore.getInstance(clonedConfig.memory);\n\n return {\n mode: 'state-machine',\n config: clonedConfig,\n checks,\n journal,\n memory,\n workingDirectory,\n originalWorkingDirectory: workingDirectory,\n sessionId: uuidv4(),\n event: prInfo.eventType,\n debug,\n maxParallelism,\n failFast,\n requestedChecks: requestedChecks && requestedChecks.length > 0 ? requestedChecks : undefined,\n // Store prInfo for later access (e.g., in getOutputHistorySnapshot)\n prInfo,\n };\n}\n\n/**\n * Initialize workspace isolation for an engine context.\n * Creates an isolated workspace with the main project worktree.\n *\n * @param context - Engine context to update with workspace\n * @returns Updated context (same object, mutated)\n */\nexport async function initializeWorkspace(context: EngineContext): Promise<EngineContext> {\n // Check if workspace isolation is enabled via config or env\n const workspaceConfig = (context.config as any).workspace;\n const isEnabled =\n workspaceConfig?.enabled !== false && process.env.VISOR_WORKSPACE_ENABLED !== 'false';\n\n if (!isEnabled) {\n logger.debug('[Workspace] Workspace isolation is disabled');\n return context;\n }\n\n const originalPath = context.workingDirectory || process.cwd();\n\n try {\n // Check if workspace should be kept (for debugging)\n const keepWorkspace = process.env.VISOR_KEEP_WORKSPACE === 'true';\n\n // Create workspace manager\n const workspace = WorkspaceManager.getInstance(context.sessionId, originalPath, {\n enabled: true,\n basePath:\n workspaceConfig?.base_path || process.env.VISOR_WORKSPACE_PATH || '/tmp/visor-workspaces',\n cleanupOnExit: keepWorkspace ? false : workspaceConfig?.cleanup_on_exit !== false,\n });\n\n // Initialize workspace (creates main project worktree)\n const info = await workspace.initialize();\n\n // Update context with workspace info\n context.workspace = workspace;\n context.workingDirectory = info.mainProjectPath;\n context.originalWorkingDirectory = originalPath;\n\n logger.info(`[Workspace] Initialized workspace: ${info.workspacePath}`);\n logger.debug(`[Workspace] Main project at: ${info.mainProjectPath}`);\n if (keepWorkspace) {\n logger.info(`[Workspace] Keeping workspace after execution (--keep-workspace)`);\n }\n\n return context;\n } catch (error) {\n // Log warning but continue without workspace isolation\n logger.warn(`[Workspace] Failed to initialize workspace: ${error}`);\n logger.debug('[Workspace] Continuing without workspace isolation');\n return context;\n }\n}\n","import type { ReviewIssue, GroupedCheckResults, ReviewSummary } from '../../reviewer';\nimport type { ExecutionStatistics } from '../../types/execution';\n\n/**\n * Pure helper to convert grouped results + statistics into a flat ReviewSummary.\n * Extracted to reduce StateMachineExecutionEngine size; behavior unchanged.\n */\nexport function convertToReviewSummary(\n groupedResults: GroupedCheckResults,\n statistics?: ExecutionStatistics\n): ReviewSummary {\n const allIssues: ReviewIssue[] = [];\n\n // Aggregate issues from all check results\n for (const checkResults of Object.values(groupedResults)) {\n for (const checkResult of checkResults) {\n if (checkResult.issues && checkResult.issues.length > 0) {\n allIssues.push(...checkResult.issues);\n }\n }\n }\n\n // Convert errors from execution statistics into issues\n if (statistics) {\n for (const checkStats of statistics.checks) {\n if (checkStats.errorMessage) {\n allIssues.push({\n file: 'system',\n line: 0,\n endLine: undefined,\n ruleId: 'system/error',\n message: checkStats.errorMessage,\n severity: 'error',\n category: 'logic',\n suggestion: undefined,\n replacement: undefined,\n });\n }\n }\n }\n\n return {\n issues: allIssues,\n };\n}\n","import type { CheckExecutionOptions, ExecutionResult } from './types/execution';\nimport { AnalysisResult } from './output-formatters';\nimport type { VisorConfig } from './types/config';\nimport type { PRInfo } from './pr-analyzer';\nimport { StateMachineRunner } from './state-machine/runner';\nimport type { EngineContext } from './types/engine';\nimport { ExecutionJournal } from './snapshot-store';\nimport { logger } from './logger';\nimport type { DebugVisualizerServer } from './debug-visualizer/ws-server';\nimport * as path from 'path';\nimport * as fs from 'fs';\n\n/**\n * State machine-based execution engine\n *\n * Production-ready state machine implementation with full observability support.\n * M4: Includes OTEL telemetry and debug visualizer event streaming.\n */\nexport class StateMachineExecutionEngine {\n private workingDirectory: string;\n private executionContext?: import('./providers/check-provider.interface').ExecutionContext;\n private debugServer?: DebugVisualizerServer;\n private _lastContext?: EngineContext;\n private _lastRunner?: StateMachineRunner;\n\n constructor(\n workingDirectory?: string,\n octokit?: import('@octokit/rest').Octokit,\n debugServer?: DebugVisualizerServer\n ) {\n this.workingDirectory = workingDirectory || process.cwd();\n this.debugServer = debugServer;\n }\n\n /**\n * Execute checks using the state machine engine\n *\n * Converts CheckExecutionOptions -> executeGroupedChecks() -> AnalysisResult\n */\n async executeChecks(options: CheckExecutionOptions): Promise<AnalysisResult> {\n const startTime = Date.now();\n const timestamp = new Date().toISOString();\n\n try {\n // Initialize memory store if configured\n if (options.config?.memory) {\n const { MemoryStore } = await import('./memory-store');\n const memoryStore = MemoryStore.getInstance(options.config.memory);\n await memoryStore.initialize();\n logger.debug('Memory store initialized');\n }\n\n // Analyze the repository\n const { GitRepositoryAnalyzer } = await import('./git-repository-analyzer');\n const gitAnalyzer = new GitRepositoryAnalyzer(options.workingDirectory);\n logger.info('Analyzing local git repository...');\n const repositoryInfo = await gitAnalyzer.analyzeRepository();\n\n if (!repositoryInfo.isGitRepository) {\n return this.createErrorResult(\n repositoryInfo,\n 'Not a git repository or no changes found',\n startTime,\n timestamp,\n options.checks\n );\n }\n\n // Convert to PRInfo format for compatibility\n const prInfo = gitAnalyzer.toPRInfo(repositoryInfo);\n\n // Propagate event type if provided\n try {\n const evt = (options.webhookContext as any)?.eventType;\n if (evt) (prInfo as any).eventType = evt;\n } catch {}\n\n // Apply tag filtering if specified\n const filteredChecks = this.filterChecksByTags(\n options.checks,\n options.config,\n options.tagFilter || options.config?.tag_filter\n );\n\n if (filteredChecks.length === 0) {\n logger.warn('No checks match the tag filter criteria');\n return this.createErrorResult(\n repositoryInfo,\n 'No checks match the tag filter criteria',\n startTime,\n timestamp,\n options.checks\n );\n }\n\n // If a webhook context is provided (from WebhookServer or Slack socket),\n // attach it to the http_input provider so http_input checks can read data.\n try {\n const map = (options as any)?.webhookContext?.webhookData as\n | Map<string, unknown>\n | undefined;\n if (map) {\n const { CheckProviderRegistry } = await import('./providers/check-provider-registry');\n const reg = CheckProviderRegistry.getInstance();\n const p: any = reg.getProvider('http_input');\n if (p && typeof p.setWebhookContext === 'function') p.setWebhookContext(map);\n const prev: any = this.executionContext || {};\n this.setExecutionContext({ ...prev, webhookContext: { webhookData: map } } as any);\n }\n } catch {}\n\n // Execute checks using state machine\n logger.info(`Executing checks: ${filteredChecks.join(', ')}`);\n const executionResult = await this.executeGroupedChecks(\n prInfo,\n filteredChecks,\n options.timeout,\n options.config,\n options.outputFormat,\n options.debug,\n options.maxParallelism,\n options.failFast,\n options.tagFilter\n );\n\n // Convert ExecutionResult to AnalysisResult format\n const executionTime = Date.now() - startTime;\n\n // Extract review summary from grouped results\n const reviewSummary = this.convertGroupedResultsToReviewSummary(\n executionResult.results,\n executionResult.statistics\n );\n\n // Collect debug information when debug mode is enabled\n let debugInfo: import('./output-formatters').DebugInfo | undefined;\n if (options.debug && reviewSummary.debug) {\n debugInfo = {\n provider: reviewSummary.debug.provider,\n model: reviewSummary.debug.model,\n processingTime: reviewSummary.debug.processingTime,\n parallelExecution: options.checks.length > 1,\n checksExecuted: options.checks,\n totalApiCalls: reviewSummary.debug.totalApiCalls || options.checks.length,\n apiCallDetails: reviewSummary.debug.apiCallDetails,\n };\n }\n\n // Expose output history snapshot\n try {\n const histSnap = this.getOutputHistorySnapshot();\n (reviewSummary as any).history = histSnap;\n } catch {}\n\n return {\n repositoryInfo,\n reviewSummary,\n executionTime,\n timestamp,\n checksExecuted: filteredChecks,\n executionStatistics: executionResult.statistics,\n debug: debugInfo,\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error occurred';\n logger.error('Error executing checks: ' + message);\n\n // In strict test modes, surface errors to callers\n const strictEnv = process.env.VISOR_STRICT_ERRORS === 'true';\n if (strictEnv) {\n throw error;\n }\n\n const fallbackRepositoryInfo: import('./git-repository-analyzer').GitRepositoryInfo = {\n title: 'Error during analysis',\n body: `Error: ${message || 'Unknown error'}`,\n author: 'system',\n base: 'main',\n head: 'HEAD',\n files: [],\n totalAdditions: 0,\n totalDeletions: 0,\n isGitRepository: false,\n workingDirectory: options.workingDirectory || process.cwd(),\n };\n\n return this.createErrorResult(\n fallbackRepositoryInfo,\n message || 'Unknown error occurred',\n startTime,\n timestamp,\n options.checks\n );\n }\n }\n\n /**\n * Get execution context (used by state machine to propagate hooks)\n */\n protected getExecutionContext():\n | import('./providers/check-provider.interface').ExecutionContext\n | undefined {\n return this.executionContext;\n }\n\n /**\n * Set execution context for external callers\n */\n public setExecutionContext(\n context: import('./providers/check-provider.interface').ExecutionContext | undefined\n ): void {\n this.executionContext = context;\n }\n\n /**\n * Reset per-run state (no-op for state machine engine)\n *\n * The state machine engine is stateless per-run by design.\n * Each execution creates a fresh journal and context.\n * This method exists only for backward compatibility with test framework.\n *\n * @deprecated This is a no-op. State machine engine doesn't maintain per-run state.\n */\n public resetPerRunState(): void {\n // No-op: State machine engine is stateless per-run\n // Each execution creates a fresh journal and context\n }\n\n /**\n * Execute grouped checks using the state machine engine\n *\n * M4: Production-ready with full telemetry and debug server support\n */\n async executeGroupedChecks(\n prInfo: PRInfo,\n checks: string[],\n timeout?: number,\n config?: VisorConfig,\n outputFormat?: string,\n debug?: boolean,\n maxParallelism?: number,\n failFast?: boolean,\n tagFilter?: import('./types/config').TagFilter,\n _pauseGate?: () => Promise<void>\n ): Promise<ExecutionResult> {\n if (debug) {\n logger.info('[StateMachine] Using state machine engine');\n }\n\n // Create minimal default config if none provided (backward compatibility)\n if (!config) {\n const { ConfigManager } = await import('./config');\n const configManager = new ConfigManager();\n config = await configManager.getDefaultConfig();\n logger.debug('[StateMachine] Using default configuration (no config provided)');\n }\n\n // Merge tagFilter into config if provided (test runner passes it separately)\n const configWithTagFilter = tagFilter\n ? {\n ...config,\n tag_filter: tagFilter,\n }\n : config;\n\n // Build engine context\n const context = this.buildEngineContext(\n configWithTagFilter,\n prInfo,\n debug,\n maxParallelism,\n failFast,\n checks // Pass the explicit checks list\n );\n\n // Initialize workspace isolation (if enabled)\n const { initializeWorkspace } = require('./state-machine/context/build-engine-context');\n await initializeWorkspace(context);\n\n // Copy execution context (hooks, etc.) from legacy engine\n context.executionContext = this.getExecutionContext();\n\n // Store context for later access (e.g., getOutputHistorySnapshot)\n this._lastContext = context;\n\n // Optionally enable event-driven frontends if configured\n let frontendsHost: any | undefined;\n if (\n Array.isArray((configWithTagFilter as any).frontends) &&\n (configWithTagFilter as any).frontends.length > 0\n ) {\n try {\n const { EventBus } = await import('./event-bus/event-bus');\n const { FrontendsHost } = await import('./frontends/host');\n const bus = new EventBus();\n (context as any).eventBus = bus;\n frontendsHost = new FrontendsHost(bus, logger);\n if (process.env.VISOR_DEBUG === 'true') {\n try {\n const fns = ((configWithTagFilter as any).frontends || []).map((f: any) => ({\n name: f?.name,\n hasConfig: !!f?.config,\n cfg: f?.config || undefined,\n }));\n logger.info(`[Frontends] Loading specs: ${JSON.stringify(fns)}`);\n } catch {}\n }\n await frontendsHost.load((configWithTagFilter as any).frontends);\n // Derive repo/pr/headSha and octokit if available\n let owner: string | undefined;\n let name: string | undefined;\n let prNum: number | undefined;\n let headSha: string | undefined;\n try {\n const anyInfo: any = prInfo as any;\n owner =\n anyInfo?.eventContext?.repository?.owner?.login ||\n process.env.GITHUB_REPOSITORY?.split('/')?.[0];\n name =\n anyInfo?.eventContext?.repository?.name ||\n process.env.GITHUB_REPOSITORY?.split('/')?.[1];\n prNum = typeof anyInfo?.number === 'number' ? anyInfo.number : undefined;\n headSha = anyInfo?.eventContext?.pull_request?.head?.sha || process.env.GITHUB_SHA;\n } catch {}\n const repoObj = owner && name ? { owner, name } : undefined;\n const octokit = (this.executionContext as any)?.octokit;\n // Fallback: if headSha is missing but we have PR info and octokit, fetch it\n if (\n !headSha &&\n repoObj &&\n prNum &&\n octokit &&\n typeof octokit.rest?.pulls?.get === 'function'\n ) {\n try {\n const { data } = await octokit.rest.pulls.get({\n owner: repoObj.owner,\n repo: repoObj.name,\n pull_number: prNum,\n });\n headSha = (data && (data as any).head && (data as any).head.sha) || headSha;\n } catch {\n // ignore; headSha remains undefined\n }\n }\n // Make the event bus available to providers via executionContext\n try {\n const prev: any = this.getExecutionContext() || {};\n this.setExecutionContext({ ...prev, eventBus: bus });\n // Also reflect it into the active engine context so downstream providers see it\n try {\n (context as any).executionContext = this.getExecutionContext();\n } catch {}\n } catch {}\n\n await frontendsHost.startAll(() => ({\n eventBus: bus,\n logger,\n // Provide the active (possibly tag-filtered) config so frontends can read groups, etc.\n config: configWithTagFilter,\n run: {\n runId: (context as any).sessionId,\n repo: repoObj,\n pr: prNum,\n headSha,\n event: (context as any).event || (prInfo as any)?.eventType,\n actor:\n (prInfo as any)?.eventContext?.sender?.login ||\n (typeof process.env.GITHUB_ACTOR === 'string' ? process.env.GITHUB_ACTOR : undefined),\n },\n octokit,\n webhookContext: (this.executionContext as any)?.webhookContext,\n // Surface any injected test doubles for Slack as well\n slack:\n (this.executionContext as any)?.slack || (this.executionContext as any)?.slackClient,\n }));\n\n // Phase 1: Snapshot on HumanInputRequested (experimental pause support)\n try {\n bus.on('HumanInputRequested', async (envelope: any) => {\n try {\n const ev = (envelope && envelope.payload) || envelope;\n // Determine channel/thread from event or inbound payload\n let channel: string | undefined = ev?.channel;\n let threadTs: string | undefined = ev?.threadTs;\n if (!channel || !threadTs) {\n try {\n const anyCfg: any = configWithTagFilter || {};\n const slackCfg: any = anyCfg.slack || {};\n const endpoint: string = slackCfg.endpoint || '/bots/slack/support';\n const map = (this.executionContext as any)?.webhookContext?.webhookData as\n | Map<string, unknown>\n | undefined;\n const payload: any = map?.get(endpoint);\n const e: any = payload?.event;\n const derivedTs = String(e?.thread_ts || e?.ts || e?.event_ts || '');\n const derivedCh = String(e?.channel || '');\n if (derivedCh && derivedTs) {\n channel = channel || derivedCh;\n threadTs = threadTs || derivedTs;\n }\n } catch {}\n }\n\n const checkId = String(ev?.checkId || 'unknown');\n const threadKey =\n ev?.threadKey || (channel && threadTs ? `${channel}:${threadTs}` : 'session');\n const baseDir =\n process.env.VISOR_SNAPSHOT_DIR ||\n path.resolve(process.cwd(), '.visor', 'snapshots');\n fs.mkdirSync(baseDir, { recursive: true });\n const filePath = path.join(baseDir, `${threadKey}-${checkId}.json`);\n await this.saveSnapshotToFile(filePath);\n logger.info(`[Snapshot] Saved run snapshot: ${filePath}`);\n try {\n await bus.emit({\n type: 'SnapshotSaved',\n checkId: ev?.checkId || 'unknown',\n channel,\n threadTs,\n threadKey,\n filePath,\n });\n } catch {}\n } catch (e) {\n logger.warn(\n `[Snapshot] Failed to save snapshot on HumanInputRequested: ${\n e instanceof Error ? e.message : String(e)\n }`\n );\n }\n });\n } catch {}\n } catch (err) {\n logger.warn(\n `[Frontends] Failed to initialize frontends: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n }\n\n // Create and run state machine with debug server support (M4)\n const runner = new StateMachineRunner(context, this.debugServer);\n this._lastRunner = runner;\n const result = await runner.run();\n\n // Stop frontends if started\n if (frontendsHost && typeof frontendsHost.stopAll === 'function') {\n try {\n await frontendsHost.stopAll();\n } catch {}\n }\n\n if (debug) {\n logger.info('[StateMachine] Execution complete');\n }\n\n // Post-grouped comments via legacy reviewer is removed; GitHub frontend handles comments\n\n // Cleanup AI sessions after execution\n try {\n const { SessionRegistry } = await import('./session-registry');\n const sessionRegistry = SessionRegistry.getInstance();\n sessionRegistry.clearAllSessions();\n } catch (error) {\n logger.debug(`[StateMachine] Failed to cleanup sessions: ${error}`);\n }\n\n // Cleanup workspace if enabled\n if (context.workspace) {\n try {\n await context.workspace.cleanup();\n } catch (error) {\n logger.debug(`[StateMachine] Failed to cleanup workspace: ${error}`);\n }\n }\n\n return result;\n }\n\n /**\n * Build the engine context for state machine execution\n */\n private buildEngineContext(\n config: VisorConfig,\n prInfo: PRInfo,\n debug?: boolean,\n maxParallelism?: number,\n failFast?: boolean,\n requestedChecks?: string[]\n ): EngineContext {\n const { buildEngineContextForRun } = require('./state-machine/context/build-engine-context');\n return buildEngineContextForRun(\n this.workingDirectory,\n config,\n prInfo,\n debug,\n maxParallelism,\n failFast,\n requestedChecks\n );\n }\n\n /**\n * Get output history snapshot for test framework compatibility\n * Extracts output history from the journal\n */\n public getOutputHistorySnapshot(): Record<string, unknown[]> {\n // Get the journal from the last execution context\n const journal = (this as any)._lastContext?.journal as ExecutionJournal | undefined;\n if (!journal) {\n logger.debug('[StateMachine][DEBUG] getOutputHistorySnapshot: No journal found');\n return {};\n }\n\n const sessionId = (this as any)._lastContext?.sessionId as string | undefined;\n if (!sessionId) {\n logger.debug('[StateMachine][DEBUG] getOutputHistorySnapshot: No sessionId found');\n return {};\n }\n\n // Read all journal entries for this session\n const snapshot = journal.beginSnapshot();\n const allEntries = journal.readVisible(sessionId, snapshot, undefined);\n\n logger.debug(\n `[StateMachine][DEBUG] getOutputHistorySnapshot: Found ${allEntries.length} journal entries`\n );\n\n // Group by checkId and extract outputs\n const outputHistory: Record<string, unknown[]> = {};\n for (const entry of allEntries) {\n const checkId = entry.checkId;\n\n if (!outputHistory[checkId]) {\n outputHistory[checkId] = [];\n }\n // Skip journal stubs for skipped checks\n try {\n if (entry && typeof entry.result === 'object' && (entry.result as any).__skipped) {\n continue;\n }\n } catch {}\n\n // Prefer explicit .output; fall back to the full result (issues/content)\n // so tests and templates can reference paths like issues[0].severity for\n // code-review schema steps which do not set a separate output object.\n const payload =\n entry.result.output !== undefined ? entry.result.output : (entry.result as unknown);\n\n // Filter out forEach aggregation metadata objects (which contain a\n // forEachItems array) to avoid double-counting per-item executions in\n // tests. The actual per-item outputs are committed as separate entries\n // and should be used for history-based assertions and routing.\n try {\n if (\n payload &&\n typeof payload === 'object' &&\n (payload as any).forEachItems &&\n Array.isArray((payload as any).forEachItems)\n ) {\n continue;\n }\n } catch {}\n\n if (payload !== undefined) outputHistory[checkId].push(payload);\n }\n\n logger.debug(\n `[StateMachine][DEBUG] getOutputHistorySnapshot result: ${JSON.stringify(Object.keys(outputHistory))}`\n );\n for (const [checkId, outputs] of Object.entries(outputHistory)) {\n logger.debug(`[StateMachine][DEBUG] ${checkId}: ${outputs.length} outputs`);\n }\n\n return outputHistory;\n }\n\n /**\n * Save a JSON snapshot of the last run's state and journal to a file (experimental).\n * Does not include secrets. Intended for debugging and future resume support.\n */\n public async saveSnapshotToFile(filePath: string): Promise<void> {\n const fs = await import('fs/promises');\n const ctx = this._lastContext;\n const runner = this._lastRunner;\n if (!ctx || !runner) {\n throw new Error('No prior execution context to snapshot');\n }\n const journal = (ctx as any).journal as ExecutionJournal;\n const snapshotId = journal.beginSnapshot();\n const entries = journal.readVisible(ctx.sessionId, snapshotId, undefined);\n const state = runner.getState();\n const serializableState = serializeRunState(state);\n const payload = {\n version: 1,\n sessionId: ctx.sessionId,\n event: ctx.event,\n wave: state.wave,\n state: serializableState,\n journal: entries,\n requestedChecks: (ctx as any).requestedChecks || [],\n } as const;\n await fs.writeFile(filePath, JSON.stringify(payload, null, 2), 'utf8');\n }\n\n /**\n * Load a snapshot JSON from file and return it. Resume support can build on this.\n */\n public async loadSnapshotFromFile<T = unknown>(filePath: string): Promise<T> {\n const fs = await import('fs/promises');\n const raw = await fs.readFile(filePath, 'utf8');\n return JSON.parse(raw) as T;\n }\n\n /**\n * Filter checks by tag filter\n */\n private filterChecksByTags(\n checks: string[],\n config: VisorConfig | undefined,\n tagFilter: import('./types/config').TagFilter | undefined\n ): string[] {\n // When no tag filter is specified, include only untagged checks by default.\n // Tagged checks are opt-in unless tag_filter is provided.\n return checks.filter(checkName => {\n const checkConfig = config?.checks?.[checkName];\n if (!checkConfig) {\n // If no config for this check, include it by default\n return true;\n }\n\n const checkTags = checkConfig.tags || [];\n\n // If no tag filter is specified, include only untagged checks.\n if (!tagFilter || (!tagFilter.include && !tagFilter.exclude)) {\n return checkTags.length === 0;\n }\n\n // If check has no tags and a tag filter is specified, include it (untagged checks always run)\n if (checkTags.length === 0) {\n return true;\n }\n\n // Check exclude tags first (if any exclude tag matches, skip the check)\n if (tagFilter.exclude && tagFilter.exclude.length > 0) {\n const hasExcludedTag = tagFilter.exclude.some(tag => checkTags.includes(tag));\n if (hasExcludedTag) return false;\n }\n\n // Check include tags (if specified, at least one must match)\n if (tagFilter.include && tagFilter.include.length > 0) {\n const hasIncludedTag = tagFilter.include.some(tag => checkTags.includes(tag));\n if (!hasIncludedTag) return false;\n }\n\n return true;\n });\n }\n\n /**\n * Create an error result in AnalysisResult format\n */\n private createErrorResult(\n repositoryInfo: import('./git-repository-analyzer').GitRepositoryInfo,\n errorMessage: string,\n startTime: number,\n timestamp: string,\n checksExecuted: string[]\n ): AnalysisResult {\n const executionTime = Date.now() - startTime;\n\n return {\n repositoryInfo,\n reviewSummary: {\n issues: [\n {\n file: 'system',\n line: 0,\n endLine: undefined,\n ruleId: 'system/error',\n message: errorMessage,\n severity: 'error',\n category: 'logic',\n suggestion: undefined,\n replacement: undefined,\n },\n ],\n },\n executionTime,\n timestamp,\n checksExecuted,\n };\n }\n\n /**\n * Convert GroupedCheckResults to ReviewSummary\n * Aggregates all check results into a single ReviewSummary\n */\n private convertGroupedResultsToReviewSummary(\n groupedResults: import('./reviewer').GroupedCheckResults,\n statistics?: import('./types/execution').ExecutionStatistics\n ): import('./reviewer').ReviewSummary {\n const { convertToReviewSummary } = require('./state-machine/execution/summary');\n return (convertToReviewSummary as any)(groupedResults as any, statistics as any) as any;\n }\n\n /**\n * Evaluate failure conditions for a check result\n *\n * This method provides backward compatibility with the legacy engine by\n * delegating to the FailureConditionEvaluator.\n *\n * @param checkName - The name of the check being evaluated\n * @param reviewSummary - The review summary containing check results\n * @param config - The Visor configuration containing failure conditions\n * @param previousOutputs - Optional previous check outputs for cross-check conditions\n * @param authorAssociation - Optional GitHub author association for permission checks\n * @returns Array of failure condition evaluation results\n */\n async evaluateFailureConditions(\n checkName: string,\n reviewSummary: import('./reviewer').ReviewSummary,\n config: VisorConfig,\n previousOutputs?: Record<string, import('./reviewer').ReviewSummary>,\n authorAssociation?: string\n ): Promise<import('./types/config').FailureConditionResult[]> {\n const { FailureConditionEvaluator } = await import('./failure-condition-evaluator');\n const evaluator = new FailureConditionEvaluator();\n const { addEvent } = await import('./telemetry/trace-helpers');\n const { addFailIfTriggered } = await import('./telemetry/metrics');\n\n // Extract check configuration\n const checkConfig = config.checks?.[checkName];\n if (!checkConfig) {\n return [];\n }\n\n // Schema can be string or Record<string, unknown>, convert to string for evaluation\n const rawSchema = checkConfig.schema || 'code-review';\n const checkSchema = typeof rawSchema === 'string' ? rawSchema : 'code-review';\n const checkGroup = checkConfig.group || 'default';\n\n // Handle both fail_if (simple string) and failure_conditions (complex object)\n const results: import('./types/config').FailureConditionResult[] = [];\n\n // Evaluate global fail_if\n if (config.fail_if) {\n const failed = await evaluator.evaluateSimpleCondition(\n checkName,\n checkSchema,\n checkGroup,\n reviewSummary,\n config.fail_if,\n previousOutputs || {}\n );\n\n // Telemetry events + metric\n try {\n addEvent('fail_if.evaluated', {\n 'visor.check.id': checkName,\n scope: 'global',\n expression: String(config.fail_if),\n result: failed ? 'triggered' : 'not_triggered',\n });\n if (failed) {\n addEvent('fail_if.triggered', {\n 'visor.check.id': checkName,\n scope: 'global',\n expression: String(config.fail_if),\n });\n addFailIfTriggered(checkName, 'global');\n }\n } catch {}\n\n results.push({\n conditionName: 'global_fail_if',\n failed,\n expression: config.fail_if,\n message: failed ? `Global failure condition met: ${config.fail_if}` : undefined,\n severity: 'error',\n haltExecution: false,\n });\n }\n\n // Evaluate check-specific fail_if (overrides global if present)\n if (checkConfig.fail_if) {\n const failed = await evaluator.evaluateSimpleCondition(\n checkName,\n checkSchema,\n checkGroup,\n reviewSummary,\n checkConfig.fail_if,\n previousOutputs || {}\n );\n\n // Telemetry events + metric\n try {\n addEvent('fail_if.evaluated', {\n 'visor.check.id': checkName,\n scope: 'check',\n expression: String(checkConfig.fail_if),\n result: failed ? 'triggered' : 'not_triggered',\n });\n if (failed) {\n addEvent('fail_if.triggered', {\n 'visor.check.id': checkName,\n scope: 'check',\n expression: String(checkConfig.fail_if),\n });\n addFailIfTriggered(checkName, 'check');\n }\n } catch {}\n\n results.push({\n conditionName: `${checkName}_fail_if`,\n failed,\n expression: checkConfig.fail_if,\n message: failed ? `Check failure condition met: ${checkConfig.fail_if}` : undefined,\n severity: 'error',\n haltExecution: false,\n });\n }\n\n // Also evaluate legacy failure_conditions if present\n const globalConditions = config.failure_conditions;\n const checkConditions = checkConfig.failure_conditions;\n\n if (globalConditions || checkConditions) {\n const legacyResults = await evaluator.evaluateConditions(\n checkName,\n checkSchema,\n checkGroup,\n reviewSummary,\n globalConditions,\n checkConditions,\n previousOutputs,\n authorAssociation\n );\n results.push(...legacyResults);\n }\n\n return results;\n }\n\n /**\n * Get repository status\n * @returns Repository status information\n */\n async getRepositoryStatus(): Promise<{\n isGitRepository: boolean;\n branch?: string;\n hasChanges: boolean;\n filesChanged?: number;\n }> {\n try {\n const { GitRepositoryAnalyzer } = await import('./git-repository-analyzer');\n const analyzer = new GitRepositoryAnalyzer(this.workingDirectory);\n const info = await analyzer.analyzeRepository();\n\n return {\n isGitRepository: info.isGitRepository,\n branch: info.head, // Use head as branch name\n hasChanges: info.isGitRepository && (info.files?.length > 0 || false),\n filesChanged: info.isGitRepository ? info.files?.length || 0 : 0,\n };\n } catch {\n return {\n isGitRepository: false,\n hasChanges: false,\n };\n }\n }\n\n /**\n * Check if current directory is a git repository\n * @returns True if git repository, false otherwise\n */\n async isGitRepository(): Promise<boolean> {\n const status = await this.getRepositoryStatus();\n return status.isGitRepository;\n }\n\n /**\n * Get list of available check types\n * @returns Array of check type names\n */\n static getAvailableCheckTypes(): string[] {\n const { CheckProviderRegistry } = require('./providers/check-provider-registry');\n const registry = CheckProviderRegistry.getInstance();\n return registry.getAvailableProviders();\n }\n\n /**\n * Validate check types and return valid/invalid lists\n * @param checks - Array of check type names to validate\n * @returns Object with valid and invalid check types\n */\n static validateCheckTypes(checks: string[]): { valid: string[]; invalid: string[] } {\n const availableTypes = StateMachineExecutionEngine.getAvailableCheckTypes();\n const valid: string[] = [];\n const invalid: string[] = [];\n\n for (const check of checks) {\n if (availableTypes.includes(check)) {\n valid.push(check);\n } else {\n invalid.push(check);\n }\n }\n\n return { valid, invalid };\n }\n\n /**\n * Format the status column for execution statistics\n * Used by execution-statistics-formatting tests\n */\n private formatStatusColumn(stats: import('./types/execution').CheckExecutionStats): string {\n if (stats.skipped) {\n // Format skip reason\n if (stats.skipReason === 'if_condition') {\n return '⏭ if';\n } else if (stats.skipReason === 'fail_fast') {\n return '⏭ ff';\n } else if (stats.skipReason === 'dependency_failed') {\n return '⏭ dep';\n }\n return '⏭';\n }\n\n const totalRuns = stats.totalRuns;\n const successfulRuns = stats.successfulRuns;\n const failedRuns = stats.failedRuns;\n\n if (failedRuns > 0 && successfulRuns > 0) {\n // Mixed results\n return `✔/✖ ${successfulRuns}/${totalRuns}`;\n } else if (failedRuns > 0) {\n // All failed\n return totalRuns === 1 ? '✖' : `✖ ×${totalRuns}`;\n } else {\n // All successful\n return totalRuns === 1 ? '✔' : `✔ ×${totalRuns}`;\n }\n }\n\n /**\n * Format the details column for execution statistics\n * Used by execution-statistics-formatting tests\n */\n private formatDetailsColumn(stats: import('./types/execution').CheckExecutionStats): string {\n const parts: string[] = [];\n\n // Add outputs produced\n if (stats.outputsProduced !== undefined && stats.outputsProduced > 0) {\n parts.push(`→${stats.outputsProduced}`);\n }\n\n // Add critical issues\n if (stats.issuesBySeverity.critical > 0) {\n parts.push(`${stats.issuesBySeverity.critical}🔴`);\n }\n\n // Add error issues (only if no critical)\n if (stats.issuesBySeverity.error > 0 && stats.issuesBySeverity.critical === 0) {\n parts.push(`${stats.issuesBySeverity.error}❌`);\n }\n\n // Add warnings\n if (stats.issuesBySeverity.warning > 0) {\n parts.push(`${stats.issuesBySeverity.warning}⚠️`);\n }\n\n // Add info issues (only if no critical/error/warning)\n if (\n stats.issuesBySeverity.info > 0 &&\n stats.issuesBySeverity.critical === 0 &&\n stats.issuesBySeverity.error === 0 &&\n stats.issuesBySeverity.warning === 0\n ) {\n parts.push(`${stats.issuesBySeverity.info}💡`);\n }\n\n // Add error message if present\n if (stats.errorMessage) {\n parts.push(this.truncate(stats.errorMessage, 40));\n }\n\n // Add skip condition if present\n if (stats.skipCondition) {\n parts.push(this.truncate(stats.skipCondition, 40));\n }\n\n return parts.join(' ');\n }\n\n /**\n * Truncate a string to a maximum length\n * Used by formatDetailsColumn\n */\n private truncate(str: string, maxLength: number): string {\n if (str.length <= maxLength) {\n return str;\n }\n return str.substring(0, maxLength - 3) + '...';\n }\n}\n\n/** Convert RunState with Maps/Sets into a JSON-safe form */\nfunction serializeRunState(state: import('./types/engine').RunState) {\n return {\n ...state,\n levelQueue: state.levelQueue,\n eventQueue: state.eventQueue,\n activeDispatches: Array.from(state.activeDispatches.entries()),\n completedChecks: Array.from(state.completedChecks.values()),\n stats: Array.from(state.stats.entries()),\n historyLog: state.historyLog,\n forwardRunGuards: Array.from(state.forwardRunGuards.values()),\n currentLevelChecks: Array.from(state.currentLevelChecks.values()),\n currentWaveCompletions: Array.from(\n ((state as any).currentWaveCompletions as Set<string> | undefined) || []\n ),\n // failedChecks is an internal Set added by stats/dispatch layers; keep it if present\n failedChecks: Array.from(((state as any).failedChecks as Set<string> | undefined) || []),\n pendingRunScopes: Array.from((state.pendingRunScopes || new Map()).entries()).map(([k, v]) => [\n k,\n v,\n ]),\n };\n}\n\nexport type SnapshotJson = {\n version: number;\n sessionId: string;\n event?: import('./types/config').EventTrigger;\n wave?: number;\n state: any;\n journal: import('./snapshot-store').JournalEntry[];\n requestedChecks?: string[];\n meta?: Record<string, unknown>;\n};\n\n/**\n * Resume execution from a previously saved snapshot (experimental).\n * Frontends are started to mirror executeGroupedChecks behavior so integrations\n * like Slack can handle CheckCompleted/HumanInputRequested events during resume.\n */\nexport async function resumeFromSnapshot(\n engine: StateMachineExecutionEngine,\n snapshot: SnapshotJson,\n config: VisorConfig,\n opts?: { debug?: boolean; maxParallelism?: number; failFast?: boolean; webhookContext?: any }\n): Promise<import('./types/execution').ExecutionResult> {\n // Recompute PRInfo from the current repository\n const { GitRepositoryAnalyzer } = await import('./git-repository-analyzer');\n const analyzer = new GitRepositoryAnalyzer(process.cwd());\n const repoInfo = await analyzer.analyzeRepository();\n const prInfo = analyzer.toPRInfo(repoInfo);\n\n const context = (engine as any).buildEngineContext(\n config,\n prInfo,\n opts?.debug,\n opts?.maxParallelism,\n opts?.failFast,\n snapshot.requestedChecks\n ) as import('./types/engine').EngineContext;\n\n // Initialize workspace isolation (if enabled) - same as executeGroupedChecks\n const { initializeWorkspace } = require('./state-machine/context/build-engine-context');\n await initializeWorkspace(context);\n\n // Propagate existing executionContext (hooks, octokit, webhookContext, slack, etc.)\n try {\n const prevExecCtx: any = (engine as any).getExecutionContext?.() || {};\n (context as any).executionContext = prevExecCtx;\n } catch {}\n\n // Restore journal entries\n try {\n const journal = (context as any).journal as ExecutionJournal;\n for (const e of snapshot.journal || []) {\n journal.commitEntry({\n // Re-hydrate all entries under the NEW sessionId for this resume run.\n // This ensures helpers like output history and chat_history see both\n // pre-snapshot and post-resume outputs as a single logical session.\n sessionId: (context as any).sessionId,\n scope: e.scope,\n checkId: e.checkId,\n result: e.result,\n event: e.event,\n });\n }\n } catch {}\n\n // Adopt webhookContext and other execution context patches if provided\n try {\n const prev: any = (engine as any).getExecutionContext?.() || {};\n (engine as any).setExecutionContext?.({ ...prev, webhookContext: opts?.webhookContext });\n // Reflect merged executionContext into active engine context\n try {\n (context as any).executionContext = (engine as any).getExecutionContext?.();\n } catch {}\n } catch {}\n\n // Optional frontends (Slack, GitHub, etc.) – mirror executeGroupedChecks\n let frontendsHost: any | undefined;\n if (Array.isArray((config as any).frontends) && (config as any).frontends.length > 0) {\n try {\n const { EventBus } = await import('./event-bus/event-bus');\n const { FrontendsHost } = await import('./frontends/host');\n const bus = new EventBus();\n (context as any).eventBus = bus;\n frontendsHost = new FrontendsHost(bus, logger);\n\n if (process.env.VISOR_DEBUG === 'true') {\n try {\n const fns = ((config as any).frontends || []).map((f: any) => ({\n name: f?.name,\n hasConfig: !!f?.config,\n cfg: f?.config || undefined,\n }));\n logger.info(`[Frontends] Loading specs: ${JSON.stringify(fns)}`);\n } catch {}\n }\n\n await frontendsHost.load((config as any).frontends);\n\n // Derive repo/pr/headSha and octokit if available\n let owner: string | undefined;\n let name: string | undefined;\n let prNum: number | undefined;\n let headSha: string | undefined;\n try {\n const anyInfo: any = prInfo as any;\n owner =\n anyInfo?.eventContext?.repository?.owner?.login ||\n process.env.GITHUB_REPOSITORY?.split('/')?.[0];\n name =\n anyInfo?.eventContext?.repository?.name || process.env.GITHUB_REPOSITORY?.split('/')?.[1];\n prNum = typeof anyInfo?.number === 'number' ? anyInfo.number : undefined;\n headSha = anyInfo?.eventContext?.pull_request?.head?.sha || process.env.GITHUB_SHA;\n } catch {}\n const repoObj = owner && name ? { owner, name } : undefined;\n const octokit = (engine as any).getExecutionContext?.()?.octokit;\n\n // Fallback: if headSha is missing but we have PR info and octokit, fetch it\n if (\n !headSha &&\n repoObj &&\n prNum &&\n octokit &&\n typeof octokit.rest?.pulls?.get === 'function'\n ) {\n try {\n const { data } = await octokit.rest.pulls.get({\n owner: repoObj.owner,\n repo: repoObj.name,\n pull_number: prNum,\n });\n headSha = (data && (data as any).head && (data as any).head.sha) || headSha;\n } catch {\n // ignore; headSha remains undefined\n }\n }\n\n // Make the event bus available to providers via executionContext\n try {\n const prevExec: any = (engine as any).getExecutionContext?.() || {};\n (engine as any).setExecutionContext?.({ ...prevExec, eventBus: bus });\n try {\n (context as any).executionContext = (engine as any).getExecutionContext?.();\n } catch {}\n } catch {}\n\n await frontendsHost.startAll(() => ({\n eventBus: bus,\n logger,\n // Provide the active config so frontends can read groups, etc.\n config,\n run: {\n runId: (context as any).sessionId,\n repo: repoObj,\n pr: prNum,\n headSha,\n event: (context as any).event || (prInfo as any)?.eventType,\n actor:\n (prInfo as any)?.eventContext?.sender?.login ||\n (typeof process.env.GITHUB_ACTOR === 'string' ? process.env.GITHUB_ACTOR : undefined),\n },\n octokit,\n webhookContext: (engine as any).getExecutionContext?.()?.webhookContext,\n // Surface any injected test doubles for Slack as well\n slack:\n (engine as any).getExecutionContext?.()?.slack ||\n (engine as any).getExecutionContext?.()?.slackClient,\n }));\n\n // Snapshot-on-human-input support for resumed runs\n try {\n bus.on('HumanInputRequested', async (envelope: any) => {\n try {\n const ev = (envelope && envelope.payload) || envelope;\n // Determine channel/thread from event or inbound payload\n let channel: string | undefined = ev?.channel;\n let threadTs: string | undefined = ev?.threadTs;\n if (!channel || !threadTs) {\n try {\n const anyCfg: any = config || {};\n const slackCfg: any = anyCfg.slack || {};\n const endpoint: string = slackCfg.endpoint || '/bots/slack/support';\n const map = (engine as any).getExecutionContext?.()?.webhookContext?.webhookData as\n | Map<string, unknown>\n | undefined;\n const payload: any = map?.get(endpoint);\n const e: any = payload?.event;\n const derivedTs = String(e?.thread_ts || e?.ts || e?.event_ts || '');\n const derivedCh = String(e?.channel || '');\n if (derivedCh && derivedTs) {\n channel = channel || derivedCh;\n threadTs = threadTs || derivedTs;\n }\n } catch {}\n }\n\n const checkId = String(ev?.checkId || 'unknown');\n const threadKey =\n ev?.threadKey || (channel && threadTs ? `${channel}:${threadTs}` : 'session');\n const baseDir =\n process.env.VISOR_SNAPSHOT_DIR || path.resolve(process.cwd(), '.visor', 'snapshots');\n fs.mkdirSync(baseDir, { recursive: true });\n const filePath = path.join(baseDir, `${threadKey}-${checkId}.json`);\n await engine.saveSnapshotToFile(filePath);\n logger.info(`[Snapshot] Saved run snapshot: ${filePath}`);\n try {\n await bus.emit({\n type: 'SnapshotSaved',\n checkId: ev?.checkId || 'unknown',\n channel,\n threadTs,\n threadKey,\n filePath,\n });\n } catch {}\n } catch (e) {\n logger.warn(\n `[Snapshot] Failed to save snapshot on HumanInputRequested: ${\n e instanceof Error ? e.message : String(e)\n }`\n );\n }\n });\n } catch {}\n } catch (err) {\n logger.warn(\n `[Frontends] Failed to initialize frontends (resumeFromSnapshot): ${\n err instanceof Error ? err.message : String(err)\n }`\n );\n }\n }\n\n // Create runner and hydrate state\n // For resume flows, we treat the snapshot's journal as prior history and\n // start a fresh run from the normal Init → PlanReady → WavePlanning cycle.\n // This avoids resuming mid-wave and accidentally re-running stale checks\n // (e.g., chat replies) before new input is incorporated.\n const runner = new (require('./state-machine/runner').StateMachineRunner)(\n context,\n (engine as any).debugServer\n );\n (engine as any)._lastContext = context;\n (engine as any)._lastRunner = runner;\n\n const result = await runner.run();\n\n // Stop frontends if started\n if (frontendsHost && typeof frontendsHost.stopAll === 'function') {\n try {\n await frontendsHost.stopAll();\n } catch {}\n }\n\n // Cleanup AI sessions after execution\n try {\n const { SessionRegistry } = await import('./session-registry');\n const sessionRegistry = SessionRegistry.getInstance();\n sessionRegistry.clearAllSessions();\n } catch (error) {\n logger.debug(`[StateMachine] Failed to cleanup sessions: ${error}`);\n }\n\n return result;\n}\n","/*\n Thin SDK façade for programmatic use of Visor.\n - No new execution logic; delegates to existing engine and config manager.\n - Dual ESM/CJS bundle via tsup.\n*/\n\nimport { StateMachineExecutionEngine } from './state-machine-execution-engine';\nimport { ConfigManager } from './config';\nimport type { AnalysisResult } from './output-formatters';\nimport type { VisorConfig, TagFilter, HumanInputRequest } from './types/config';\nimport type { ExecutionContext } from './providers/check-provider.interface';\n\nexport type { VisorConfig, TagFilter, HumanInputRequest, ExecutionContext };\n\nexport interface VisorOptions {\n cwd?: string;\n debug?: boolean;\n maxParallelism?: number;\n failFast?: boolean;\n tagFilter?: TagFilter;\n}\n\nexport interface RunOptions extends VisorOptions {\n config?: VisorConfig;\n configPath?: string;\n checks?: string[]; // default: all checks from config\n timeoutMs?: number;\n output?: { format?: 'table' | 'json' | 'markdown' | 'sarif' };\n /** Strict mode: treat config warnings (like unknown keys) as errors (default: false) */\n strictValidation?: boolean;\n /** Execution context for providers (CLI message, hooks, etc.) */\n executionContext?: ExecutionContext;\n}\n\n/**\n * Load and validate a Visor config.\n * @param configOrPath - Config object, file path, or omit to discover defaults\n * @param options - Validation options\n * @returns Validated config with defaults applied\n */\nexport async function loadConfig(\n configOrPath?: string | Partial<VisorConfig>,\n options?: { strict?: boolean }\n): Promise<VisorConfig> {\n const cm = new ConfigManager();\n\n // If it's an object, validate and return with defaults\n if (typeof configOrPath === 'object' && configOrPath !== null) {\n cm.validateConfig(configOrPath, options?.strict ?? false);\n\n // Apply lightweight defaults without expensive file system operations\n const defaultConfig: Partial<VisorConfig> = {\n version: '1.0',\n checks: {},\n max_parallelism: 3,\n fail_fast: false,\n };\n\n return {\n ...defaultConfig,\n ...configOrPath,\n checks: configOrPath.checks || {},\n } as VisorConfig;\n }\n\n // If it's a string, load from file\n if (typeof configOrPath === 'string') {\n return cm.loadConfig(configOrPath);\n }\n\n // Otherwise discover default config file\n return cm.findAndLoadConfig();\n}\n\n/** Expand check IDs by including their dependencies (shallow->deep). */\nexport function resolveChecks(checkIds: string[], config: VisorConfig | undefined): string[] {\n if (!config?.checks) return Array.from(new Set(checkIds));\n const resolved = new Set<string>();\n const visiting = new Set<string>();\n const result: string[] = [];\n\n const dfs = (id: string, stack: string[] = []) => {\n if (resolved.has(id)) return;\n if (visiting.has(id)) {\n const cycle = [...stack, id].join(' -> ');\n throw new Error(`Circular dependency detected involving check: ${id} (path: ${cycle})`);\n }\n visiting.add(id);\n const deps = config.checks![id]?.depends_on || [];\n for (const d of deps) dfs(d, [...stack, id]);\n if (!result.includes(id)) result.push(id);\n visiting.delete(id);\n resolved.add(id);\n };\n\n for (const id of checkIds) dfs(id);\n return result;\n}\n\n/**\n * Run Visor checks programmatically. Returns the same AnalysisResult shape used by the CLI.\n * Thin wrapper around CheckExecutionEngine.executeChecks.\n */\nexport async function runChecks(opts: RunOptions = {}): Promise<AnalysisResult> {\n const cm = new ConfigManager();\n let config: VisorConfig;\n\n if (opts.config) {\n // Validate manually constructed config\n // In strict mode, unknown keys are treated as errors\n cm.validateConfig(opts.config, opts.strictValidation ?? false);\n config = opts.config;\n } else if (opts.configPath) {\n config = await cm.loadConfig(opts.configPath);\n } else {\n config = await cm.findAndLoadConfig();\n }\n\n const checks =\n opts.checks && opts.checks.length > 0\n ? resolveChecks(opts.checks, config)\n : Object.keys(config.checks || {});\n\n // Always use StateMachineExecutionEngine\n const engine = new StateMachineExecutionEngine(opts.cwd);\n\n // Set execution context if provided\n if (opts.executionContext) {\n engine.setExecutionContext(opts.executionContext);\n }\n\n const result = await engine.executeChecks({\n checks,\n workingDirectory: opts.cwd,\n timeout: opts.timeoutMs,\n maxParallelism: opts.maxParallelism,\n failFast: opts.failFast,\n outputFormat: opts.output?.format,\n config,\n debug: opts.debug,\n tagFilter: opts.tagFilter,\n });\n\n return result;\n}\n"],"mappings":"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","names":["path","MemoryStore","ConfigManager","initializeWorkspace","buildEngineContextForRun","fs","convertToReviewSummary"]}
|
|
1
|
+
{"version":3,"sources":["../../src/utils/workspace-manager.ts","../../src/state-machine/context/build-engine-context.ts","../../src/state-machine/execution/summary.ts","../../src/state-machine-execution-engine.ts","../../src/sdk.ts"],"sourcesContent":["/**\n * Workspace Manager\n *\n * Provides full isolation between parallel visor runs with human-readable project names.\n * Each run gets its own workspace in /tmp containing worktrees for all projects.\n */\n\nimport * as fsp from 'fs/promises';\nimport * as path from 'path';\nimport { commandExecutor } from './command-executor';\nimport { logger } from '../logger';\n\n/**\n * Escape a string for safe use in shell commands.\n * Uses single quotes and escapes any embedded single quotes.\n */\nfunction shellEscape(str: string): string {\n // Replace single quotes with '\\'' (end quote, escaped quote, start quote)\n // Then wrap the whole thing in single quotes\n return \"'\" + str.replace(/'/g, \"'\\\\''\") + \"'\";\n}\n\n/**\n * Sanitize a path component to prevent path traversal attacks.\n * Removes directory separators and parent directory references.\n */\nfunction sanitizePathComponent(name: string): string {\n return (\n name\n .replace(/\\.\\./g, '') // Remove parent directory references\n .replace(/[\\/\\\\]/g, '-') // Replace path separators with dashes\n .replace(/^\\.+/, '') // Remove leading dots\n .trim() || 'unnamed'\n ); // Ensure non-empty result\n}\n\nexport interface WorkspaceConfig {\n enabled: boolean;\n basePath: string;\n cleanupOnExit: boolean;\n name?: string;\n mainProjectName?: string;\n}\n\nexport interface WorkspaceInfo {\n sessionId: string;\n workspacePath: string;\n mainProjectPath: string;\n mainProjectName: string;\n originalPath: string;\n}\n\nexport interface ProjectInfo {\n name: string;\n path: string;\n worktreePath: string;\n repository: string;\n}\n\n/**\n * WorkspaceManager creates isolated workspaces for parallel visor runs.\n * Each run gets a unique workspace directory containing worktrees for all projects.\n */\nexport class WorkspaceManager {\n private static instances: Map<string, WorkspaceManager> = new Map();\n\n private sessionId: string;\n private basePath: string;\n private workspacePath: string;\n private originalPath: string;\n private config: WorkspaceConfig;\n private initialized: boolean = false;\n private mainProjectInfo: WorkspaceInfo | null = null;\n private projects: Map<string, ProjectInfo> = new Map();\n private cleanupHandlersRegistered: boolean = false;\n private usedNames: Set<string> = new Set();\n\n private constructor(sessionId: string, originalPath: string, config?: Partial<WorkspaceConfig>) {\n this.sessionId = sessionId;\n this.originalPath = originalPath;\n\n const configuredName = config?.name || process.env.VISOR_WORKSPACE_NAME;\n const configuredMainProjectName =\n config?.mainProjectName || process.env.VISOR_WORKSPACE_PROJECT;\n\n // Default configuration\n this.config = {\n enabled: true,\n basePath: process.env.VISOR_WORKSPACE_PATH || '/tmp/visor-workspaces',\n cleanupOnExit: true,\n name: configuredName,\n mainProjectName: configuredMainProjectName,\n ...config,\n };\n\n this.basePath = this.config.basePath;\n const workspaceDirName = sanitizePathComponent(this.config.name || this.sessionId);\n this.workspacePath = path.join(this.basePath, workspaceDirName);\n }\n\n /**\n * Get or create a WorkspaceManager instance for a session\n */\n static getInstance(\n sessionId: string,\n originalPath: string,\n config?: Partial<WorkspaceConfig>\n ): WorkspaceManager {\n if (!WorkspaceManager.instances.has(sessionId)) {\n WorkspaceManager.instances.set(\n sessionId,\n new WorkspaceManager(sessionId, originalPath, config)\n );\n }\n return WorkspaceManager.instances.get(sessionId)!;\n }\n\n /**\n * Clear all instances (for testing)\n */\n static clearInstances(): void {\n WorkspaceManager.instances.clear();\n }\n\n /**\n * Check if workspace isolation is enabled\n */\n isEnabled(): boolean {\n return this.config.enabled;\n }\n\n /**\n * Get the workspace path\n */\n getWorkspacePath(): string {\n return this.workspacePath;\n }\n\n /**\n * Get the original working directory\n */\n getOriginalPath(): string {\n return this.originalPath;\n }\n\n /**\n * Get workspace info (only available after initialize)\n */\n getWorkspaceInfo(): WorkspaceInfo | null {\n return this.mainProjectInfo;\n }\n\n /**\n * Initialize the workspace - creates workspace directory and main project worktree\n */\n async initialize(): Promise<WorkspaceInfo> {\n if (!this.config.enabled) {\n throw new Error('Workspace isolation is not enabled');\n }\n\n if (this.initialized && this.mainProjectInfo) {\n return this.mainProjectInfo;\n }\n\n logger.info(`Initializing workspace: ${this.workspacePath}`);\n\n // Create workspace directory (mkdir with recursive handles existing dirs)\n await fsp.mkdir(this.workspacePath, { recursive: true });\n logger.debug(`Created workspace directory: ${this.workspacePath}`);\n\n // Extract main project name from original path (sanitize for defense in depth)\n const configuredMainProjectName = this.config.mainProjectName;\n const mainProjectName = sanitizePathComponent(\n configuredMainProjectName || this.extractProjectName(this.originalPath)\n );\n this.usedNames.add(mainProjectName);\n\n // Create worktree for main project\n const mainProjectPath = path.join(this.workspacePath, mainProjectName);\n\n // Check if original path is a git repository\n const isGitRepo = await this.isGitRepository(this.originalPath);\n\n if (isGitRepo) {\n // Create worktree for main project\n await this.createMainProjectWorktree(mainProjectPath);\n } else {\n // If not a git repo, create a symlink instead\n logger.debug(`Original path is not a git repo, creating symlink`);\n try {\n await fsp.symlink(this.originalPath, mainProjectPath);\n } catch (error) {\n throw new Error(`Failed to create symlink for main project: ${error}`);\n }\n }\n\n // Register cleanup handlers\n this.registerCleanupHandlers();\n\n this.mainProjectInfo = {\n sessionId: this.sessionId,\n workspacePath: this.workspacePath,\n mainProjectPath,\n mainProjectName,\n originalPath: this.originalPath,\n };\n\n this.initialized = true;\n logger.info(`Workspace initialized: ${this.workspacePath}`);\n\n return this.mainProjectInfo;\n }\n\n /**\n * Add a project to the workspace (creates symlink to worktree)\n */\n async addProject(\n repository: string,\n worktreePath: string,\n description?: string\n ): Promise<string> {\n if (!this.initialized) {\n throw new Error('Workspace not initialized. Call initialize() first.');\n }\n\n // Extract project name and sanitize to prevent path traversal\n let projectName = sanitizePathComponent(description || this.extractRepoName(repository));\n\n // Handle duplicate names\n projectName = this.getUniqueName(projectName);\n this.usedNames.add(projectName);\n\n // Create symlink in workspace\n const workspacePath = path.join(this.workspacePath, projectName);\n\n // Remove existing symlink/directory if present (rm with force handles non-existent)\n await fsp.rm(workspacePath, { recursive: true, force: true });\n\n try {\n await fsp.symlink(worktreePath, workspacePath);\n } catch (error) {\n throw new Error(`Failed to create symlink for project ${projectName}: ${error}`);\n }\n\n // Track project\n this.projects.set(projectName, {\n name: projectName,\n path: workspacePath,\n worktreePath,\n repository,\n });\n\n logger.info(`Added project to workspace: ${projectName} -> ${worktreePath}`);\n\n return workspacePath;\n }\n\n /**\n * List all projects in the workspace\n */\n listProjects(): ProjectInfo[] {\n return Array.from(this.projects.values());\n }\n\n /**\n * Cleanup the workspace\n */\n async cleanup(): Promise<void> {\n logger.info(`Cleaning up workspace: ${this.workspacePath}`);\n\n try {\n // Remove main project worktree if it exists\n if (this.mainProjectInfo) {\n const mainProjectPath = this.mainProjectInfo.mainProjectPath;\n\n // Check if path exists and if it's a worktree (not a symlink)\n try {\n const stats = await fsp.lstat(mainProjectPath);\n if (!stats.isSymbolicLink()) {\n await this.removeMainProjectWorktree(mainProjectPath);\n }\n } catch {\n // Path doesn't exist, nothing to clean up\n }\n }\n\n // Remove workspace directory\n await fsp.rm(this.workspacePath, { recursive: true, force: true });\n logger.debug(`Removed workspace directory: ${this.workspacePath}`);\n\n // Remove from instances\n WorkspaceManager.instances.delete(this.sessionId);\n\n this.initialized = false;\n this.mainProjectInfo = null;\n this.projects.clear();\n this.usedNames.clear();\n\n logger.info(`Workspace cleanup completed: ${this.sessionId}`);\n } catch (error) {\n logger.warn(`Failed to cleanup workspace: ${error}`);\n }\n }\n\n /**\n * Create worktree for the main project\n *\n * visor-disable: architecture - Not using WorktreeManager here because:\n * 1. WorktreeManager expects remote URLs and clones to bare repos first\n * 2. This operates on the LOCAL repo we're already in (no cloning needed)\n * 3. Adding a \"local mode\" to WorktreeManager would add complexity for minimal benefit\n * The git commands here are simpler (just rev-parse + worktree add) vs WorktreeManager's\n * full clone/bare-repo/fetch/worktree pipeline.\n */\n private async createMainProjectWorktree(targetPath: string): Promise<void> {\n logger.debug(`Creating main project worktree: ${targetPath}`);\n\n // Get current HEAD\n const headResult = await commandExecutor.execute(\n `git -C ${shellEscape(this.originalPath)} rev-parse HEAD`,\n {\n timeout: 10000,\n }\n );\n\n if (headResult.exitCode !== 0) {\n throw new Error(`Failed to get HEAD: ${headResult.stderr}`);\n }\n\n const headRef = headResult.stdout.trim();\n\n // Create worktree using detached HEAD to avoid branch conflicts\n const createCmd = `git -C ${shellEscape(this.originalPath)} worktree add --detach ${shellEscape(targetPath)} ${shellEscape(headRef)}`;\n const result = await commandExecutor.execute(createCmd, { timeout: 60000 });\n\n if (result.exitCode !== 0) {\n throw new Error(`Failed to create main project worktree: ${result.stderr}`);\n }\n\n logger.debug(`Created main project worktree at ${targetPath}`);\n }\n\n /**\n * Remove main project worktree\n */\n private async removeMainProjectWorktree(worktreePath: string): Promise<void> {\n logger.debug(`Removing main project worktree: ${worktreePath}`);\n\n const removeCmd = `git -C ${shellEscape(this.originalPath)} worktree remove ${shellEscape(worktreePath)} --force`;\n const result = await commandExecutor.execute(removeCmd, { timeout: 30000 });\n\n if (result.exitCode !== 0) {\n logger.warn(`Failed to remove worktree via git: ${result.stderr}`);\n // Directory will be removed with the workspace anyway\n }\n }\n\n /**\n * Check if a path is a git repository\n */\n private async isGitRepository(dirPath: string): Promise<boolean> {\n try {\n const result = await commandExecutor.execute(\n `git -C ${shellEscape(dirPath)} rev-parse --git-dir`,\n {\n timeout: 5000,\n }\n );\n return result.exitCode === 0;\n } catch {\n return false;\n }\n }\n\n /**\n * Extract project name from path\n */\n private extractProjectName(dirPath: string): string {\n return path.basename(dirPath);\n }\n\n /**\n * Extract repository name from owner/repo format\n */\n private extractRepoName(repository: string): string {\n // Handle URLs\n if (repository.includes('://') || repository.startsWith('git@')) {\n // Extract from URL\n const match = repository.match(/[/:]([^/:]+\\/[^/:]+?)(?:\\.git)?$/);\n if (match) {\n return match[1].split('/').pop() || repository;\n }\n }\n\n // Handle owner/repo format\n if (repository.includes('/')) {\n return repository.split('/').pop() || repository;\n }\n\n return repository;\n }\n\n /**\n * Get a unique name by appending a number if needed\n */\n private getUniqueName(baseName: string): string {\n if (!this.usedNames.has(baseName)) {\n return baseName;\n }\n\n let counter = 2;\n let uniqueName = `${baseName}-${counter}`;\n while (this.usedNames.has(uniqueName)) {\n counter++;\n uniqueName = `${baseName}-${counter}`;\n }\n\n return uniqueName;\n }\n\n /**\n * Register cleanup handlers for process exit\n */\n private registerCleanupHandlers(): void {\n if (this.cleanupHandlersRegistered || !this.config.cleanupOnExit) {\n return;\n }\n\n // Note: We don't register on 'exit' as it must be synchronous\n // SIGINT and SIGTERM handlers are already registered by WorktreeManager\n // We rely on explicit cleanup call or process handlers from the engine\n\n this.cleanupHandlersRegistered = true;\n }\n}\n","import type { VisorConfig, EventTrigger } from '../../types/config';\nimport type { PRInfo } from '../../pr-analyzer';\nimport type { EngineContext, CheckMetadata } from '../../types/engine';\nimport { ExecutionJournal } from '../../snapshot-store';\nimport { MemoryStore } from '../../memory-store';\nimport { v4 as uuidv4 } from 'uuid';\nimport { logger } from '../../logger';\nimport type { VisorConfig as VCfg, CheckConfig as CfgCheck } from '../../types/config';\nimport { WorkspaceManager } from '../../utils/workspace-manager';\n\n/**\n * Apply minimal criticality defaults in-place.\n * This is a no-behavior-change scaffold: we only default missing\n * check.criticality to 'policy' so downstream code can rely on a value.\n * Future mapping (retries/loop budgets) can build on this without\n * changing existing behavior.\n */\nfunction applyCriticalityDefaults(cfg: VCfg): void {\n const checks = cfg.checks || {};\n for (const id of Object.keys(checks)) {\n const c: CfgCheck = (checks as any)[id] as CfgCheck;\n if (!c.criticality) (c.criticality as any) = 'policy';\n // For 'info' checks, default continue_on_failure to true if unset.\n if (c.criticality === 'info' && typeof c.continue_on_failure === 'undefined')\n c.continue_on_failure = true;\n }\n}\n\n/**\n * Pure helper to build an EngineContext for a state-machine run.\n * Extracted to reduce StateMachineExecutionEngine size; behavior unchanged.\n */\nexport function buildEngineContextForRun(\n workingDirectory: string,\n config: VisorConfig,\n prInfo: PRInfo,\n debug?: boolean,\n maxParallelism?: number,\n failFast?: boolean,\n requestedChecks?: string[]\n): EngineContext {\n // Deep clone provided config to avoid cross-run mutations between tests/runs\n const clonedConfig: VisorConfig = JSON.parse(JSON.stringify(config));\n\n // Build check metadata\n const checks: Record<string, CheckMetadata> = {};\n\n // Fill in minimal defaults derived from criticality (no behavior change)\n applyCriticalityDefaults(clonedConfig);\n\n // If config has checks, use them\n for (const [checkId, checkConfig] of Object.entries(clonedConfig.checks || {})) {\n checks[checkId] = {\n tags: checkConfig.tags || [],\n triggers: (Array.isArray(checkConfig.on) ? checkConfig.on : [checkConfig.on]).filter(\n Boolean\n ) as EventTrigger[],\n group: checkConfig.group,\n providerType: checkConfig.type || 'ai',\n // Normalize depends_on to array (supports string | string[])\n dependencies: Array.isArray(checkConfig.depends_on)\n ? checkConfig.depends_on\n : checkConfig.depends_on\n ? [checkConfig.depends_on]\n : [],\n };\n }\n\n // Backward compatibility: synthesize minimal check configs for requested checks\n // that don't exist in the config (e.g., legacy test mode with empty config)\n if (requestedChecks && requestedChecks.length > 0) {\n for (const checkName of requestedChecks) {\n if (!checks[checkName] && !clonedConfig.checks?.[checkName]) {\n // Synthesize a minimal check config for this legacy check name\n logger.debug(`[StateMachine] Synthesizing minimal config for legacy check: ${checkName}`);\n\n // Add to config.checks so providers can find it\n if (!clonedConfig.checks) {\n clonedConfig.checks = {};\n }\n clonedConfig.checks[checkName] = {\n type: 'ai',\n prompt: `Perform ${checkName} analysis`,\n } as any;\n\n // Add metadata\n checks[checkName] = {\n tags: [],\n triggers: [],\n group: 'default',\n providerType: 'ai',\n dependencies: [],\n };\n }\n }\n }\n\n // Initialize journal and memory\n const journal = new ExecutionJournal();\n const memory = MemoryStore.getInstance(clonedConfig.memory);\n\n return {\n mode: 'state-machine',\n config: clonedConfig,\n checks,\n journal,\n memory,\n workingDirectory,\n originalWorkingDirectory: workingDirectory,\n sessionId: uuidv4(),\n event: prInfo.eventType,\n debug,\n maxParallelism,\n failFast,\n requestedChecks: requestedChecks && requestedChecks.length > 0 ? requestedChecks : undefined,\n // Store prInfo for later access (e.g., in getOutputHistorySnapshot)\n prInfo,\n };\n}\n\n/**\n * Initialize workspace isolation for an engine context.\n * Creates an isolated workspace with the main project worktree.\n *\n * @param context - Engine context to update with workspace\n * @returns Updated context (same object, mutated)\n */\nexport async function initializeWorkspace(context: EngineContext): Promise<EngineContext> {\n // Check if workspace isolation is enabled via config or env\n const workspaceConfig = (context.config as any).workspace;\n const isEnabled =\n workspaceConfig?.enabled !== false && process.env.VISOR_WORKSPACE_ENABLED !== 'false';\n\n if (!isEnabled) {\n logger.debug('[Workspace] Workspace isolation is disabled');\n return context;\n }\n\n const originalPath = context.workingDirectory || process.cwd();\n\n try {\n // Check if workspace should be kept (for debugging)\n const keepWorkspace = process.env.VISOR_KEEP_WORKSPACE === 'true';\n\n // Create workspace manager\n const workspace = WorkspaceManager.getInstance(context.sessionId, originalPath, {\n enabled: true,\n basePath:\n workspaceConfig?.base_path || process.env.VISOR_WORKSPACE_PATH || '/tmp/visor-workspaces',\n cleanupOnExit: keepWorkspace ? false : workspaceConfig?.cleanup_on_exit !== false,\n name: workspaceConfig?.name || process.env.VISOR_WORKSPACE_NAME,\n mainProjectName: workspaceConfig?.main_project_name || process.env.VISOR_WORKSPACE_PROJECT,\n });\n\n // Initialize workspace (creates main project worktree)\n const info = await workspace.initialize();\n\n // Update context with workspace info\n context.workspace = workspace;\n context.workingDirectory = info.mainProjectPath;\n context.originalWorkingDirectory = originalPath;\n\n // Export workspace paths for templates/commands\n try {\n process.env.VISOR_WORKSPACE_ROOT = info.workspacePath;\n process.env.VISOR_WORKSPACE_MAIN_PROJECT = info.mainProjectPath;\n process.env.VISOR_WORKSPACE_MAIN_PROJECT_NAME = info.mainProjectName;\n process.env.VISOR_ORIGINAL_WORKDIR = originalPath;\n } catch {}\n\n logger.info(`[Workspace] Initialized workspace: ${info.workspacePath}`);\n logger.debug(`[Workspace] Main project at: ${info.mainProjectPath}`);\n if (keepWorkspace) {\n logger.info(`[Workspace] Keeping workspace after execution (--keep-workspace)`);\n }\n\n return context;\n } catch (error) {\n // Log warning but continue without workspace isolation\n logger.warn(`[Workspace] Failed to initialize workspace: ${error}`);\n logger.debug('[Workspace] Continuing without workspace isolation');\n return context;\n }\n}\n","import type { ReviewIssue, GroupedCheckResults, ReviewSummary } from '../../reviewer';\nimport type { ExecutionStatistics } from '../../types/execution';\n\n/**\n * Pure helper to convert grouped results + statistics into a flat ReviewSummary.\n * Extracted to reduce StateMachineExecutionEngine size; behavior unchanged.\n */\nexport function convertToReviewSummary(\n groupedResults: GroupedCheckResults,\n statistics?: ExecutionStatistics\n): ReviewSummary {\n const allIssues: ReviewIssue[] = [];\n\n // Aggregate issues from all check results\n for (const checkResults of Object.values(groupedResults)) {\n for (const checkResult of checkResults) {\n if (checkResult.issues && checkResult.issues.length > 0) {\n allIssues.push(...checkResult.issues);\n }\n }\n }\n\n // Convert errors from execution statistics into issues\n if (statistics) {\n for (const checkStats of statistics.checks) {\n if (checkStats.errorMessage) {\n allIssues.push({\n file: 'system',\n line: 0,\n endLine: undefined,\n ruleId: 'system/error',\n message: checkStats.errorMessage,\n severity: 'error',\n category: 'logic',\n suggestion: undefined,\n replacement: undefined,\n });\n }\n }\n }\n\n return {\n issues: allIssues,\n };\n}\n","import type { CheckExecutionOptions, ExecutionResult } from './types/execution';\nimport { AnalysisResult } from './output-formatters';\nimport type { VisorConfig } from './types/config';\nimport type { PRInfo } from './pr-analyzer';\nimport { StateMachineRunner } from './state-machine/runner';\nimport type { EngineContext } from './types/engine';\nimport { ExecutionJournal } from './snapshot-store';\nimport { logger } from './logger';\nimport type { DebugVisualizerServer } from './debug-visualizer/ws-server';\nimport * as path from 'path';\nimport * as fs from 'fs';\n\n/**\n * State machine-based execution engine\n *\n * Production-ready state machine implementation with full observability support.\n * M4: Includes OTEL telemetry and debug visualizer event streaming.\n */\nexport class StateMachineExecutionEngine {\n private workingDirectory: string;\n private executionContext?: import('./providers/check-provider.interface').ExecutionContext;\n private debugServer?: DebugVisualizerServer;\n private _lastContext?: EngineContext;\n private _lastRunner?: StateMachineRunner;\n\n constructor(\n workingDirectory?: string,\n octokit?: import('@octokit/rest').Octokit,\n debugServer?: DebugVisualizerServer\n ) {\n this.workingDirectory = workingDirectory || process.cwd();\n this.debugServer = debugServer;\n }\n\n /**\n * Execute checks using the state machine engine\n *\n * Converts CheckExecutionOptions -> executeGroupedChecks() -> AnalysisResult\n */\n async executeChecks(options: CheckExecutionOptions): Promise<AnalysisResult> {\n const startTime = Date.now();\n const timestamp = new Date().toISOString();\n\n try {\n // Initialize memory store if configured\n if (options.config?.memory) {\n const { MemoryStore } = await import('./memory-store');\n const memoryStore = MemoryStore.getInstance(options.config.memory);\n await memoryStore.initialize();\n logger.debug('Memory store initialized');\n }\n\n // Analyze the repository\n const { GitRepositoryAnalyzer } = await import('./git-repository-analyzer');\n const gitAnalyzer = new GitRepositoryAnalyzer(options.workingDirectory);\n logger.info('Analyzing local git repository...');\n const repositoryInfo = await gitAnalyzer.analyzeRepository();\n\n if (!repositoryInfo.isGitRepository) {\n return this.createErrorResult(\n repositoryInfo,\n 'Not a git repository or no changes found',\n startTime,\n timestamp,\n options.checks\n );\n }\n\n // Convert to PRInfo format for compatibility\n const prInfo = gitAnalyzer.toPRInfo(repositoryInfo);\n\n // Propagate event type if provided\n try {\n const evt = (options.webhookContext as any)?.eventType;\n if (evt) (prInfo as any).eventType = evt;\n } catch {}\n\n // Apply tag filtering if specified\n const filteredChecks = this.filterChecksByTags(\n options.checks,\n options.config,\n options.tagFilter || options.config?.tag_filter\n );\n\n if (filteredChecks.length === 0) {\n logger.warn('No checks match the tag filter criteria');\n return this.createErrorResult(\n repositoryInfo,\n 'No checks match the tag filter criteria',\n startTime,\n timestamp,\n options.checks\n );\n }\n\n // If a webhook context is provided (from WebhookServer or Slack socket),\n // attach it to the http_input provider so http_input checks can read data.\n try {\n const map = (options as any)?.webhookContext?.webhookData as\n | Map<string, unknown>\n | undefined;\n if (map) {\n const { CheckProviderRegistry } = await import('./providers/check-provider-registry');\n const reg = CheckProviderRegistry.getInstance();\n const p: any = reg.getProvider('http_input');\n if (p && typeof p.setWebhookContext === 'function') p.setWebhookContext(map);\n const prev: any = this.executionContext || {};\n this.setExecutionContext({ ...prev, webhookContext: { webhookData: map } } as any);\n }\n } catch {}\n\n // Execute checks using state machine\n logger.info(`Executing checks: ${filteredChecks.join(', ')}`);\n const executionResult = await this.executeGroupedChecks(\n prInfo,\n filteredChecks,\n options.timeout,\n options.config,\n options.outputFormat,\n options.debug,\n options.maxParallelism,\n options.failFast,\n options.tagFilter\n );\n\n // Convert ExecutionResult to AnalysisResult format\n const executionTime = Date.now() - startTime;\n\n // Extract review summary from grouped results\n const reviewSummary = this.convertGroupedResultsToReviewSummary(\n executionResult.results,\n executionResult.statistics\n );\n\n // Collect debug information when debug mode is enabled\n let debugInfo: import('./output-formatters').DebugInfo | undefined;\n if (options.debug && reviewSummary.debug) {\n debugInfo = {\n provider: reviewSummary.debug.provider,\n model: reviewSummary.debug.model,\n processingTime: reviewSummary.debug.processingTime,\n parallelExecution: options.checks.length > 1,\n checksExecuted: options.checks,\n totalApiCalls: reviewSummary.debug.totalApiCalls || options.checks.length,\n apiCallDetails: reviewSummary.debug.apiCallDetails,\n };\n }\n\n // Expose output history snapshot\n try {\n const histSnap = this.getOutputHistorySnapshot();\n (reviewSummary as any).history = histSnap;\n } catch {}\n\n return {\n repositoryInfo,\n reviewSummary,\n executionTime,\n timestamp,\n checksExecuted: filteredChecks,\n executionStatistics: executionResult.statistics,\n debug: debugInfo,\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error occurred';\n logger.error('Error executing checks: ' + message);\n\n // In strict test modes, surface errors to callers\n const strictEnv = process.env.VISOR_STRICT_ERRORS === 'true';\n if (strictEnv) {\n throw error;\n }\n\n const fallbackRepositoryInfo: import('./git-repository-analyzer').GitRepositoryInfo = {\n title: 'Error during analysis',\n body: `Error: ${message || 'Unknown error'}`,\n author: 'system',\n base: 'main',\n head: 'HEAD',\n files: [],\n totalAdditions: 0,\n totalDeletions: 0,\n isGitRepository: false,\n workingDirectory: options.workingDirectory || process.cwd(),\n };\n\n return this.createErrorResult(\n fallbackRepositoryInfo,\n message || 'Unknown error occurred',\n startTime,\n timestamp,\n options.checks\n );\n }\n }\n\n /**\n * Get execution context (used by state machine to propagate hooks)\n */\n protected getExecutionContext():\n | import('./providers/check-provider.interface').ExecutionContext\n | undefined {\n return this.executionContext;\n }\n\n /**\n * Set execution context for external callers\n */\n public setExecutionContext(\n context: import('./providers/check-provider.interface').ExecutionContext | undefined\n ): void {\n this.executionContext = context;\n }\n\n /**\n * Reset per-run state (no-op for state machine engine)\n *\n * The state machine engine is stateless per-run by design.\n * Each execution creates a fresh journal and context.\n * This method exists only for backward compatibility with test framework.\n *\n * @deprecated This is a no-op. State machine engine doesn't maintain per-run state.\n */\n public resetPerRunState(): void {\n // No-op: State machine engine is stateless per-run\n // Each execution creates a fresh journal and context\n }\n\n /**\n * Execute grouped checks using the state machine engine\n *\n * M4: Production-ready with full telemetry and debug server support\n */\n async executeGroupedChecks(\n prInfo: PRInfo,\n checks: string[],\n timeout?: number,\n config?: VisorConfig,\n outputFormat?: string,\n debug?: boolean,\n maxParallelism?: number,\n failFast?: boolean,\n tagFilter?: import('./types/config').TagFilter,\n _pauseGate?: () => Promise<void>\n ): Promise<ExecutionResult> {\n if (debug) {\n logger.info('[StateMachine] Using state machine engine');\n }\n\n // Create minimal default config if none provided (backward compatibility)\n if (!config) {\n const { ConfigManager } = await import('./config');\n const configManager = new ConfigManager();\n config = await configManager.getDefaultConfig();\n logger.debug('[StateMachine] Using default configuration (no config provided)');\n }\n\n // Merge tagFilter into config if provided (test runner passes it separately)\n const configWithTagFilter = tagFilter\n ? {\n ...config,\n tag_filter: tagFilter,\n }\n : config;\n\n // Build engine context\n const context = this.buildEngineContext(\n configWithTagFilter,\n prInfo,\n debug,\n maxParallelism,\n failFast,\n checks // Pass the explicit checks list\n );\n\n // Initialize workspace isolation (if enabled)\n const { initializeWorkspace } = require('./state-machine/context/build-engine-context');\n await initializeWorkspace(context);\n\n // Copy execution context (hooks, etc.) from legacy engine\n context.executionContext = this.getExecutionContext();\n\n // Store context for later access (e.g., getOutputHistorySnapshot)\n this._lastContext = context;\n\n // Optionally enable event-driven frontends if configured\n let frontendsHost: any | undefined;\n if (\n Array.isArray((configWithTagFilter as any).frontends) &&\n (configWithTagFilter as any).frontends.length > 0\n ) {\n try {\n const { EventBus } = await import('./event-bus/event-bus');\n const { FrontendsHost } = await import('./frontends/host');\n const bus = new EventBus();\n (context as any).eventBus = bus;\n frontendsHost = new FrontendsHost(bus, logger);\n if (process.env.VISOR_DEBUG === 'true') {\n try {\n const fns = ((configWithTagFilter as any).frontends || []).map((f: any) => ({\n name: f?.name,\n hasConfig: !!f?.config,\n cfg: f?.config || undefined,\n }));\n logger.info(`[Frontends] Loading specs: ${JSON.stringify(fns)}`);\n } catch {}\n }\n await frontendsHost.load((configWithTagFilter as any).frontends);\n // Derive repo/pr/headSha and octokit if available\n let owner: string | undefined;\n let name: string | undefined;\n let prNum: number | undefined;\n let headSha: string | undefined;\n try {\n const anyInfo: any = prInfo as any;\n owner =\n anyInfo?.eventContext?.repository?.owner?.login ||\n process.env.GITHUB_REPOSITORY?.split('/')?.[0];\n name =\n anyInfo?.eventContext?.repository?.name ||\n process.env.GITHUB_REPOSITORY?.split('/')?.[1];\n prNum = typeof anyInfo?.number === 'number' ? anyInfo.number : undefined;\n headSha = anyInfo?.eventContext?.pull_request?.head?.sha || process.env.GITHUB_SHA;\n } catch {}\n const repoObj = owner && name ? { owner, name } : undefined;\n const octokit = (this.executionContext as any)?.octokit;\n // Fallback: if headSha is missing but we have PR info and octokit, fetch it\n if (\n !headSha &&\n repoObj &&\n prNum &&\n octokit &&\n typeof octokit.rest?.pulls?.get === 'function'\n ) {\n try {\n const { data } = await octokit.rest.pulls.get({\n owner: repoObj.owner,\n repo: repoObj.name,\n pull_number: prNum,\n });\n headSha = (data && (data as any).head && (data as any).head.sha) || headSha;\n } catch {\n // ignore; headSha remains undefined\n }\n }\n // Make the event bus available to providers via executionContext\n try {\n const prev: any = this.getExecutionContext() || {};\n this.setExecutionContext({ ...prev, eventBus: bus });\n // Also reflect it into the active engine context so downstream providers see it\n try {\n (context as any).executionContext = this.getExecutionContext();\n } catch {}\n } catch {}\n\n await frontendsHost.startAll(() => ({\n eventBus: bus,\n logger,\n // Provide the active (possibly tag-filtered) config so frontends can read groups, etc.\n config: configWithTagFilter,\n run: {\n runId: (context as any).sessionId,\n repo: repoObj,\n pr: prNum,\n headSha,\n event: (context as any).event || (prInfo as any)?.eventType,\n actor:\n (prInfo as any)?.eventContext?.sender?.login ||\n (typeof process.env.GITHUB_ACTOR === 'string' ? process.env.GITHUB_ACTOR : undefined),\n },\n octokit,\n webhookContext: (this.executionContext as any)?.webhookContext,\n // Surface any injected test doubles for Slack as well\n slack:\n (this.executionContext as any)?.slack || (this.executionContext as any)?.slackClient,\n }));\n\n // Phase 1: Snapshot on HumanInputRequested (experimental pause support)\n try {\n bus.on('HumanInputRequested', async (envelope: any) => {\n try {\n const ev = (envelope && envelope.payload) || envelope;\n // Determine channel/thread from event or inbound payload\n let channel: string | undefined = ev?.channel;\n let threadTs: string | undefined = ev?.threadTs;\n if (!channel || !threadTs) {\n try {\n const anyCfg: any = configWithTagFilter || {};\n const slackCfg: any = anyCfg.slack || {};\n const endpoint: string = slackCfg.endpoint || '/bots/slack/support';\n const map = (this.executionContext as any)?.webhookContext?.webhookData as\n | Map<string, unknown>\n | undefined;\n const payload: any = map?.get(endpoint);\n const e: any = payload?.event;\n const derivedTs = String(e?.thread_ts || e?.ts || e?.event_ts || '');\n const derivedCh = String(e?.channel || '');\n if (derivedCh && derivedTs) {\n channel = channel || derivedCh;\n threadTs = threadTs || derivedTs;\n }\n } catch {}\n }\n\n const checkId = String(ev?.checkId || 'unknown');\n const threadKey =\n ev?.threadKey || (channel && threadTs ? `${channel}:${threadTs}` : 'session');\n const baseDir =\n process.env.VISOR_SNAPSHOT_DIR ||\n path.resolve(process.cwd(), '.visor', 'snapshots');\n fs.mkdirSync(baseDir, { recursive: true });\n const filePath = path.join(baseDir, `${threadKey}-${checkId}.json`);\n await this.saveSnapshotToFile(filePath);\n logger.info(`[Snapshot] Saved run snapshot: ${filePath}`);\n try {\n await bus.emit({\n type: 'SnapshotSaved',\n checkId: ev?.checkId || 'unknown',\n channel,\n threadTs,\n threadKey,\n filePath,\n });\n } catch {}\n } catch (e) {\n logger.warn(\n `[Snapshot] Failed to save snapshot on HumanInputRequested: ${\n e instanceof Error ? e.message : String(e)\n }`\n );\n }\n });\n } catch {}\n } catch (err) {\n logger.warn(\n `[Frontends] Failed to initialize frontends: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n }\n\n // Create and run state machine with debug server support (M4)\n const runner = new StateMachineRunner(context, this.debugServer);\n this._lastRunner = runner;\n const result = await runner.run();\n\n // Stop frontends if started\n if (frontendsHost && typeof frontendsHost.stopAll === 'function') {\n try {\n await frontendsHost.stopAll();\n } catch {}\n }\n\n if (debug) {\n logger.info('[StateMachine] Execution complete');\n }\n\n // Post-grouped comments via legacy reviewer is removed; GitHub frontend handles comments\n\n // Cleanup AI sessions after execution\n try {\n const { SessionRegistry } = await import('./session-registry');\n const sessionRegistry = SessionRegistry.getInstance();\n sessionRegistry.clearAllSessions();\n } catch (error) {\n logger.debug(`[StateMachine] Failed to cleanup sessions: ${error}`);\n }\n\n // Cleanup workspace if enabled\n if (context.workspace) {\n try {\n await context.workspace.cleanup();\n } catch (error) {\n logger.debug(`[StateMachine] Failed to cleanup workspace: ${error}`);\n }\n }\n\n return result;\n }\n\n /**\n * Build the engine context for state machine execution\n */\n private buildEngineContext(\n config: VisorConfig,\n prInfo: PRInfo,\n debug?: boolean,\n maxParallelism?: number,\n failFast?: boolean,\n requestedChecks?: string[]\n ): EngineContext {\n const { buildEngineContextForRun } = require('./state-machine/context/build-engine-context');\n return buildEngineContextForRun(\n this.workingDirectory,\n config,\n prInfo,\n debug,\n maxParallelism,\n failFast,\n requestedChecks\n );\n }\n\n /**\n * Get output history snapshot for test framework compatibility\n * Extracts output history from the journal\n */\n public getOutputHistorySnapshot(): Record<string, unknown[]> {\n // Get the journal from the last execution context\n const journal = (this as any)._lastContext?.journal as ExecutionJournal | undefined;\n if (!journal) {\n logger.debug('[StateMachine][DEBUG] getOutputHistorySnapshot: No journal found');\n return {};\n }\n\n const sessionId = (this as any)._lastContext?.sessionId as string | undefined;\n if (!sessionId) {\n logger.debug('[StateMachine][DEBUG] getOutputHistorySnapshot: No sessionId found');\n return {};\n }\n\n // Read all journal entries for this session\n const snapshot = journal.beginSnapshot();\n const allEntries = journal.readVisible(sessionId, snapshot, undefined);\n\n logger.debug(\n `[StateMachine][DEBUG] getOutputHistorySnapshot: Found ${allEntries.length} journal entries`\n );\n\n // Group by checkId and extract outputs\n const outputHistory: Record<string, unknown[]> = {};\n for (const entry of allEntries) {\n const checkId = entry.checkId;\n\n if (!outputHistory[checkId]) {\n outputHistory[checkId] = [];\n }\n // Skip journal stubs for skipped checks\n try {\n if (entry && typeof entry.result === 'object' && (entry.result as any).__skipped) {\n continue;\n }\n } catch {}\n\n // Prefer explicit .output; fall back to the full result (issues/content)\n // so tests and templates can reference paths like issues[0].severity for\n // code-review schema steps which do not set a separate output object.\n const payload =\n entry.result.output !== undefined ? entry.result.output : (entry.result as unknown);\n\n // Filter out forEach aggregation metadata objects (which contain a\n // forEachItems array) to avoid double-counting per-item executions in\n // tests. The actual per-item outputs are committed as separate entries\n // and should be used for history-based assertions and routing.\n try {\n if (\n payload &&\n typeof payload === 'object' &&\n (payload as any).forEachItems &&\n Array.isArray((payload as any).forEachItems)\n ) {\n continue;\n }\n } catch {}\n\n if (payload !== undefined) outputHistory[checkId].push(payload);\n }\n\n logger.debug(\n `[StateMachine][DEBUG] getOutputHistorySnapshot result: ${JSON.stringify(Object.keys(outputHistory))}`\n );\n for (const [checkId, outputs] of Object.entries(outputHistory)) {\n logger.debug(`[StateMachine][DEBUG] ${checkId}: ${outputs.length} outputs`);\n }\n\n return outputHistory;\n }\n\n /**\n * Save a JSON snapshot of the last run's state and journal to a file (experimental).\n * Does not include secrets. Intended for debugging and future resume support.\n */\n public async saveSnapshotToFile(filePath: string): Promise<void> {\n const fs = await import('fs/promises');\n const ctx = this._lastContext;\n const runner = this._lastRunner;\n if (!ctx || !runner) {\n throw new Error('No prior execution context to snapshot');\n }\n const journal = (ctx as any).journal as ExecutionJournal;\n const snapshotId = journal.beginSnapshot();\n const entries = journal.readVisible(ctx.sessionId, snapshotId, undefined);\n const state = runner.getState();\n const serializableState = serializeRunState(state);\n const payload = {\n version: 1,\n sessionId: ctx.sessionId,\n event: ctx.event,\n wave: state.wave,\n state: serializableState,\n journal: entries,\n requestedChecks: (ctx as any).requestedChecks || [],\n } as const;\n await fs.writeFile(filePath, JSON.stringify(payload, null, 2), 'utf8');\n }\n\n /**\n * Load a snapshot JSON from file and return it. Resume support can build on this.\n */\n public async loadSnapshotFromFile<T = unknown>(filePath: string): Promise<T> {\n const fs = await import('fs/promises');\n const raw = await fs.readFile(filePath, 'utf8');\n return JSON.parse(raw) as T;\n }\n\n /**\n * Filter checks by tag filter\n */\n private filterChecksByTags(\n checks: string[],\n config: VisorConfig | undefined,\n tagFilter: import('./types/config').TagFilter | undefined\n ): string[] {\n // When no tag filter is specified, include only untagged checks by default.\n // Tagged checks are opt-in unless tag_filter is provided.\n return checks.filter(checkName => {\n const checkConfig = config?.checks?.[checkName];\n if (!checkConfig) {\n // If no config for this check, include it by default\n return true;\n }\n\n const checkTags = checkConfig.tags || [];\n\n // If no tag filter is specified, include only untagged checks.\n if (!tagFilter || (!tagFilter.include && !tagFilter.exclude)) {\n return checkTags.length === 0;\n }\n\n // If check has no tags and a tag filter is specified, include it (untagged checks always run)\n if (checkTags.length === 0) {\n return true;\n }\n\n // Check exclude tags first (if any exclude tag matches, skip the check)\n if (tagFilter.exclude && tagFilter.exclude.length > 0) {\n const hasExcludedTag = tagFilter.exclude.some(tag => checkTags.includes(tag));\n if (hasExcludedTag) return false;\n }\n\n // Check include tags (if specified, at least one must match)\n if (tagFilter.include && tagFilter.include.length > 0) {\n const hasIncludedTag = tagFilter.include.some(tag => checkTags.includes(tag));\n if (!hasIncludedTag) return false;\n }\n\n return true;\n });\n }\n\n /**\n * Create an error result in AnalysisResult format\n */\n private createErrorResult(\n repositoryInfo: import('./git-repository-analyzer').GitRepositoryInfo,\n errorMessage: string,\n startTime: number,\n timestamp: string,\n checksExecuted: string[]\n ): AnalysisResult {\n const executionTime = Date.now() - startTime;\n\n return {\n repositoryInfo,\n reviewSummary: {\n issues: [\n {\n file: 'system',\n line: 0,\n endLine: undefined,\n ruleId: 'system/error',\n message: errorMessage,\n severity: 'error',\n category: 'logic',\n suggestion: undefined,\n replacement: undefined,\n },\n ],\n },\n executionTime,\n timestamp,\n checksExecuted,\n };\n }\n\n /**\n * Convert GroupedCheckResults to ReviewSummary\n * Aggregates all check results into a single ReviewSummary\n */\n private convertGroupedResultsToReviewSummary(\n groupedResults: import('./reviewer').GroupedCheckResults,\n statistics?: import('./types/execution').ExecutionStatistics\n ): import('./reviewer').ReviewSummary {\n const { convertToReviewSummary } = require('./state-machine/execution/summary');\n return (convertToReviewSummary as any)(groupedResults as any, statistics as any) as any;\n }\n\n /**\n * Evaluate failure conditions for a check result\n *\n * This method provides backward compatibility with the legacy engine by\n * delegating to the FailureConditionEvaluator.\n *\n * @param checkName - The name of the check being evaluated\n * @param reviewSummary - The review summary containing check results\n * @param config - The Visor configuration containing failure conditions\n * @param previousOutputs - Optional previous check outputs for cross-check conditions\n * @param authorAssociation - Optional GitHub author association for permission checks\n * @returns Array of failure condition evaluation results\n */\n async evaluateFailureConditions(\n checkName: string,\n reviewSummary: import('./reviewer').ReviewSummary,\n config: VisorConfig,\n previousOutputs?: Record<string, import('./reviewer').ReviewSummary>,\n authorAssociation?: string\n ): Promise<import('./types/config').FailureConditionResult[]> {\n const { FailureConditionEvaluator } = await import('./failure-condition-evaluator');\n const evaluator = new FailureConditionEvaluator();\n const { addEvent } = await import('./telemetry/trace-helpers');\n const { addFailIfTriggered } = await import('./telemetry/metrics');\n\n // Extract check configuration\n const checkConfig = config.checks?.[checkName];\n if (!checkConfig) {\n return [];\n }\n\n // Schema can be string or Record<string, unknown>, convert to string for evaluation\n const rawSchema = checkConfig.schema || 'code-review';\n const checkSchema = typeof rawSchema === 'string' ? rawSchema : 'code-review';\n const checkGroup = checkConfig.group || 'default';\n\n // Handle both fail_if (simple string) and failure_conditions (complex object)\n const results: import('./types/config').FailureConditionResult[] = [];\n\n // Evaluate global fail_if\n if (config.fail_if) {\n const failed = await evaluator.evaluateSimpleCondition(\n checkName,\n checkSchema,\n checkGroup,\n reviewSummary,\n config.fail_if,\n previousOutputs || {}\n );\n\n // Telemetry events + metric\n try {\n addEvent('fail_if.evaluated', {\n 'visor.check.id': checkName,\n scope: 'global',\n expression: String(config.fail_if),\n result: failed ? 'triggered' : 'not_triggered',\n });\n if (failed) {\n addEvent('fail_if.triggered', {\n 'visor.check.id': checkName,\n scope: 'global',\n expression: String(config.fail_if),\n });\n addFailIfTriggered(checkName, 'global');\n }\n } catch {}\n\n results.push({\n conditionName: 'global_fail_if',\n failed,\n expression: config.fail_if,\n message: failed ? `Global failure condition met: ${config.fail_if}` : undefined,\n severity: 'error',\n haltExecution: false,\n });\n }\n\n // Evaluate check-specific fail_if (overrides global if present)\n if (checkConfig.fail_if) {\n const failed = await evaluator.evaluateSimpleCondition(\n checkName,\n checkSchema,\n checkGroup,\n reviewSummary,\n checkConfig.fail_if,\n previousOutputs || {}\n );\n\n // Telemetry events + metric\n try {\n addEvent('fail_if.evaluated', {\n 'visor.check.id': checkName,\n scope: 'check',\n expression: String(checkConfig.fail_if),\n result: failed ? 'triggered' : 'not_triggered',\n });\n if (failed) {\n addEvent('fail_if.triggered', {\n 'visor.check.id': checkName,\n scope: 'check',\n expression: String(checkConfig.fail_if),\n });\n addFailIfTriggered(checkName, 'check');\n }\n } catch {}\n\n results.push({\n conditionName: `${checkName}_fail_if`,\n failed,\n expression: checkConfig.fail_if,\n message: failed ? `Check failure condition met: ${checkConfig.fail_if}` : undefined,\n severity: 'error',\n haltExecution: false,\n });\n }\n\n // Also evaluate legacy failure_conditions if present\n const globalConditions = config.failure_conditions;\n const checkConditions = checkConfig.failure_conditions;\n\n if (globalConditions || checkConditions) {\n const legacyResults = await evaluator.evaluateConditions(\n checkName,\n checkSchema,\n checkGroup,\n reviewSummary,\n globalConditions,\n checkConditions,\n previousOutputs,\n authorAssociation\n );\n results.push(...legacyResults);\n }\n\n return results;\n }\n\n /**\n * Get repository status\n * @returns Repository status information\n */\n async getRepositoryStatus(): Promise<{\n isGitRepository: boolean;\n branch?: string;\n hasChanges: boolean;\n filesChanged?: number;\n }> {\n try {\n const { GitRepositoryAnalyzer } = await import('./git-repository-analyzer');\n const analyzer = new GitRepositoryAnalyzer(this.workingDirectory);\n const info = await analyzer.analyzeRepository();\n\n return {\n isGitRepository: info.isGitRepository,\n branch: info.head, // Use head as branch name\n hasChanges: info.isGitRepository && (info.files?.length > 0 || false),\n filesChanged: info.isGitRepository ? info.files?.length || 0 : 0,\n };\n } catch {\n return {\n isGitRepository: false,\n hasChanges: false,\n };\n }\n }\n\n /**\n * Check if current directory is a git repository\n * @returns True if git repository, false otherwise\n */\n async isGitRepository(): Promise<boolean> {\n const status = await this.getRepositoryStatus();\n return status.isGitRepository;\n }\n\n /**\n * Get list of available check types\n * @returns Array of check type names\n */\n static getAvailableCheckTypes(): string[] {\n const { CheckProviderRegistry } = require('./providers/check-provider-registry');\n const registry = CheckProviderRegistry.getInstance();\n return registry.getAvailableProviders();\n }\n\n /**\n * Validate check types and return valid/invalid lists\n * @param checks - Array of check type names to validate\n * @returns Object with valid and invalid check types\n */\n static validateCheckTypes(checks: string[]): { valid: string[]; invalid: string[] } {\n const availableTypes = StateMachineExecutionEngine.getAvailableCheckTypes();\n const valid: string[] = [];\n const invalid: string[] = [];\n\n for (const check of checks) {\n if (availableTypes.includes(check)) {\n valid.push(check);\n } else {\n invalid.push(check);\n }\n }\n\n return { valid, invalid };\n }\n\n /**\n * Format the status column for execution statistics\n * Used by execution-statistics-formatting tests\n */\n private formatStatusColumn(stats: import('./types/execution').CheckExecutionStats): string {\n if (stats.skipped) {\n // Format skip reason\n if (stats.skipReason === 'if_condition') {\n return '⏭ if';\n } else if (stats.skipReason === 'fail_fast') {\n return '⏭ ff';\n } else if (stats.skipReason === 'dependency_failed') {\n return '⏭ dep';\n }\n return '⏭';\n }\n\n const totalRuns = stats.totalRuns;\n const successfulRuns = stats.successfulRuns;\n const failedRuns = stats.failedRuns;\n\n if (failedRuns > 0 && successfulRuns > 0) {\n // Mixed results\n return `✔/✖ ${successfulRuns}/${totalRuns}`;\n } else if (failedRuns > 0) {\n // All failed\n return totalRuns === 1 ? '✖' : `✖ ×${totalRuns}`;\n } else {\n // All successful\n return totalRuns === 1 ? '✔' : `✔ ×${totalRuns}`;\n }\n }\n\n /**\n * Format the details column for execution statistics\n * Used by execution-statistics-formatting tests\n */\n private formatDetailsColumn(stats: import('./types/execution').CheckExecutionStats): string {\n const parts: string[] = [];\n\n // Add outputs produced\n if (stats.outputsProduced !== undefined && stats.outputsProduced > 0) {\n parts.push(`→${stats.outputsProduced}`);\n }\n\n // Add critical issues\n if (stats.issuesBySeverity.critical > 0) {\n parts.push(`${stats.issuesBySeverity.critical}🔴`);\n }\n\n // Add error issues (only if no critical)\n if (stats.issuesBySeverity.error > 0 && stats.issuesBySeverity.critical === 0) {\n parts.push(`${stats.issuesBySeverity.error}❌`);\n }\n\n // Add warnings\n if (stats.issuesBySeverity.warning > 0) {\n parts.push(`${stats.issuesBySeverity.warning}⚠️`);\n }\n\n // Add info issues (only if no critical/error/warning)\n if (\n stats.issuesBySeverity.info > 0 &&\n stats.issuesBySeverity.critical === 0 &&\n stats.issuesBySeverity.error === 0 &&\n stats.issuesBySeverity.warning === 0\n ) {\n parts.push(`${stats.issuesBySeverity.info}💡`);\n }\n\n // Add error message if present\n if (stats.errorMessage) {\n parts.push(this.truncate(stats.errorMessage, 40));\n }\n\n // Add skip condition if present\n if (stats.skipCondition) {\n parts.push(this.truncate(stats.skipCondition, 40));\n }\n\n return parts.join(' ');\n }\n\n /**\n * Truncate a string to a maximum length\n * Used by formatDetailsColumn\n */\n private truncate(str: string, maxLength: number): string {\n if (str.length <= maxLength) {\n return str;\n }\n return str.substring(0, maxLength - 3) + '...';\n }\n}\n\n/** Convert RunState with Maps/Sets into a JSON-safe form */\nfunction serializeRunState(state: import('./types/engine').RunState) {\n return {\n ...state,\n levelQueue: state.levelQueue,\n eventQueue: state.eventQueue,\n activeDispatches: Array.from(state.activeDispatches.entries()),\n completedChecks: Array.from(state.completedChecks.values()),\n stats: Array.from(state.stats.entries()),\n historyLog: state.historyLog,\n forwardRunGuards: Array.from(state.forwardRunGuards.values()),\n currentLevelChecks: Array.from(state.currentLevelChecks.values()),\n currentWaveCompletions: Array.from(\n ((state as any).currentWaveCompletions as Set<string> | undefined) || []\n ),\n // failedChecks is an internal Set added by stats/dispatch layers; keep it if present\n failedChecks: Array.from(((state as any).failedChecks as Set<string> | undefined) || []),\n pendingRunScopes: Array.from((state.pendingRunScopes || new Map()).entries()).map(([k, v]) => [\n k,\n v,\n ]),\n };\n}\n\nexport type SnapshotJson = {\n version: number;\n sessionId: string;\n event?: import('./types/config').EventTrigger;\n wave?: number;\n state: any;\n journal: import('./snapshot-store').JournalEntry[];\n requestedChecks?: string[];\n meta?: Record<string, unknown>;\n};\n\n/**\n * Resume execution from a previously saved snapshot (experimental).\n * Frontends are started to mirror executeGroupedChecks behavior so integrations\n * like Slack can handle CheckCompleted/HumanInputRequested events during resume.\n */\nexport async function resumeFromSnapshot(\n engine: StateMachineExecutionEngine,\n snapshot: SnapshotJson,\n config: VisorConfig,\n opts?: { debug?: boolean; maxParallelism?: number; failFast?: boolean; webhookContext?: any }\n): Promise<import('./types/execution').ExecutionResult> {\n // Recompute PRInfo from the current repository\n const { GitRepositoryAnalyzer } = await import('./git-repository-analyzer');\n const analyzer = new GitRepositoryAnalyzer(process.cwd());\n const repoInfo = await analyzer.analyzeRepository();\n const prInfo = analyzer.toPRInfo(repoInfo);\n\n const context = (engine as any).buildEngineContext(\n config,\n prInfo,\n opts?.debug,\n opts?.maxParallelism,\n opts?.failFast,\n snapshot.requestedChecks\n ) as import('./types/engine').EngineContext;\n\n // Initialize workspace isolation (if enabled) - same as executeGroupedChecks\n const { initializeWorkspace } = require('./state-machine/context/build-engine-context');\n await initializeWorkspace(context);\n\n // Propagate existing executionContext (hooks, octokit, webhookContext, slack, etc.)\n try {\n const prevExecCtx: any = (engine as any).getExecutionContext?.() || {};\n (context as any).executionContext = prevExecCtx;\n } catch {}\n\n // Restore journal entries\n try {\n const journal = (context as any).journal as ExecutionJournal;\n for (const e of snapshot.journal || []) {\n journal.commitEntry({\n // Re-hydrate all entries under the NEW sessionId for this resume run.\n // This ensures helpers like output history and chat_history see both\n // pre-snapshot and post-resume outputs as a single logical session.\n sessionId: (context as any).sessionId,\n scope: e.scope,\n checkId: e.checkId,\n result: e.result,\n event: e.event,\n });\n }\n } catch {}\n\n // Adopt webhookContext and other execution context patches if provided\n try {\n const prev: any = (engine as any).getExecutionContext?.() || {};\n (engine as any).setExecutionContext?.({ ...prev, webhookContext: opts?.webhookContext });\n // Reflect merged executionContext into active engine context\n try {\n (context as any).executionContext = (engine as any).getExecutionContext?.();\n } catch {}\n } catch {}\n\n // Optional frontends (Slack, GitHub, etc.) – mirror executeGroupedChecks\n let frontendsHost: any | undefined;\n if (Array.isArray((config as any).frontends) && (config as any).frontends.length > 0) {\n try {\n const { EventBus } = await import('./event-bus/event-bus');\n const { FrontendsHost } = await import('./frontends/host');\n const bus = new EventBus();\n (context as any).eventBus = bus;\n frontendsHost = new FrontendsHost(bus, logger);\n\n if (process.env.VISOR_DEBUG === 'true') {\n try {\n const fns = ((config as any).frontends || []).map((f: any) => ({\n name: f?.name,\n hasConfig: !!f?.config,\n cfg: f?.config || undefined,\n }));\n logger.info(`[Frontends] Loading specs: ${JSON.stringify(fns)}`);\n } catch {}\n }\n\n await frontendsHost.load((config as any).frontends);\n\n // Derive repo/pr/headSha and octokit if available\n let owner: string | undefined;\n let name: string | undefined;\n let prNum: number | undefined;\n let headSha: string | undefined;\n try {\n const anyInfo: any = prInfo as any;\n owner =\n anyInfo?.eventContext?.repository?.owner?.login ||\n process.env.GITHUB_REPOSITORY?.split('/')?.[0];\n name =\n anyInfo?.eventContext?.repository?.name || process.env.GITHUB_REPOSITORY?.split('/')?.[1];\n prNum = typeof anyInfo?.number === 'number' ? anyInfo.number : undefined;\n headSha = anyInfo?.eventContext?.pull_request?.head?.sha || process.env.GITHUB_SHA;\n } catch {}\n const repoObj = owner && name ? { owner, name } : undefined;\n const octokit = (engine as any).getExecutionContext?.()?.octokit;\n\n // Fallback: if headSha is missing but we have PR info and octokit, fetch it\n if (\n !headSha &&\n repoObj &&\n prNum &&\n octokit &&\n typeof octokit.rest?.pulls?.get === 'function'\n ) {\n try {\n const { data } = await octokit.rest.pulls.get({\n owner: repoObj.owner,\n repo: repoObj.name,\n pull_number: prNum,\n });\n headSha = (data && (data as any).head && (data as any).head.sha) || headSha;\n } catch {\n // ignore; headSha remains undefined\n }\n }\n\n // Make the event bus available to providers via executionContext\n try {\n const prevExec: any = (engine as any).getExecutionContext?.() || {};\n (engine as any).setExecutionContext?.({ ...prevExec, eventBus: bus });\n try {\n (context as any).executionContext = (engine as any).getExecutionContext?.();\n } catch {}\n } catch {}\n\n await frontendsHost.startAll(() => ({\n eventBus: bus,\n logger,\n // Provide the active config so frontends can read groups, etc.\n config,\n run: {\n runId: (context as any).sessionId,\n repo: repoObj,\n pr: prNum,\n headSha,\n event: (context as any).event || (prInfo as any)?.eventType,\n actor:\n (prInfo as any)?.eventContext?.sender?.login ||\n (typeof process.env.GITHUB_ACTOR === 'string' ? process.env.GITHUB_ACTOR : undefined),\n },\n octokit,\n webhookContext: (engine as any).getExecutionContext?.()?.webhookContext,\n // Surface any injected test doubles for Slack as well\n slack:\n (engine as any).getExecutionContext?.()?.slack ||\n (engine as any).getExecutionContext?.()?.slackClient,\n }));\n\n // Snapshot-on-human-input support for resumed runs\n try {\n bus.on('HumanInputRequested', async (envelope: any) => {\n try {\n const ev = (envelope && envelope.payload) || envelope;\n // Determine channel/thread from event or inbound payload\n let channel: string | undefined = ev?.channel;\n let threadTs: string | undefined = ev?.threadTs;\n if (!channel || !threadTs) {\n try {\n const anyCfg: any = config || {};\n const slackCfg: any = anyCfg.slack || {};\n const endpoint: string = slackCfg.endpoint || '/bots/slack/support';\n const map = (engine as any).getExecutionContext?.()?.webhookContext?.webhookData as\n | Map<string, unknown>\n | undefined;\n const payload: any = map?.get(endpoint);\n const e: any = payload?.event;\n const derivedTs = String(e?.thread_ts || e?.ts || e?.event_ts || '');\n const derivedCh = String(e?.channel || '');\n if (derivedCh && derivedTs) {\n channel = channel || derivedCh;\n threadTs = threadTs || derivedTs;\n }\n } catch {}\n }\n\n const checkId = String(ev?.checkId || 'unknown');\n const threadKey =\n ev?.threadKey || (channel && threadTs ? `${channel}:${threadTs}` : 'session');\n const baseDir =\n process.env.VISOR_SNAPSHOT_DIR || path.resolve(process.cwd(), '.visor', 'snapshots');\n fs.mkdirSync(baseDir, { recursive: true });\n const filePath = path.join(baseDir, `${threadKey}-${checkId}.json`);\n await engine.saveSnapshotToFile(filePath);\n logger.info(`[Snapshot] Saved run snapshot: ${filePath}`);\n try {\n await bus.emit({\n type: 'SnapshotSaved',\n checkId: ev?.checkId || 'unknown',\n channel,\n threadTs,\n threadKey,\n filePath,\n });\n } catch {}\n } catch (e) {\n logger.warn(\n `[Snapshot] Failed to save snapshot on HumanInputRequested: ${\n e instanceof Error ? e.message : String(e)\n }`\n );\n }\n });\n } catch {}\n } catch (err) {\n logger.warn(\n `[Frontends] Failed to initialize frontends (resumeFromSnapshot): ${\n err instanceof Error ? err.message : String(err)\n }`\n );\n }\n }\n\n // Create runner and hydrate state\n // For resume flows, we treat the snapshot's journal as prior history and\n // start a fresh run from the normal Init → PlanReady → WavePlanning cycle.\n // This avoids resuming mid-wave and accidentally re-running stale checks\n // (e.g., chat replies) before new input is incorporated.\n const runner = new (require('./state-machine/runner').StateMachineRunner)(\n context,\n (engine as any).debugServer\n );\n (engine as any)._lastContext = context;\n (engine as any)._lastRunner = runner;\n\n const result = await runner.run();\n\n // Stop frontends if started\n if (frontendsHost && typeof frontendsHost.stopAll === 'function') {\n try {\n await frontendsHost.stopAll();\n } catch {}\n }\n\n // Cleanup AI sessions after execution\n try {\n const { SessionRegistry } = await import('./session-registry');\n const sessionRegistry = SessionRegistry.getInstance();\n sessionRegistry.clearAllSessions();\n } catch (error) {\n logger.debug(`[StateMachine] Failed to cleanup sessions: ${error}`);\n }\n\n return result;\n}\n","/*\n Thin SDK façade for programmatic use of Visor.\n - No new execution logic; delegates to existing engine and config manager.\n - Dual ESM/CJS bundle via tsup.\n*/\n\nimport { StateMachineExecutionEngine } from './state-machine-execution-engine';\nimport { ConfigManager } from './config';\nimport type { AnalysisResult } from './output-formatters';\nimport type { VisorConfig, TagFilter, HumanInputRequest } from './types/config';\nimport type { ExecutionContext } from './providers/check-provider.interface';\n\nexport type { VisorConfig, TagFilter, HumanInputRequest, ExecutionContext };\n\nexport interface VisorOptions {\n cwd?: string;\n debug?: boolean;\n maxParallelism?: number;\n failFast?: boolean;\n tagFilter?: TagFilter;\n}\n\nexport interface RunOptions extends VisorOptions {\n config?: VisorConfig;\n configPath?: string;\n checks?: string[]; // default: all checks from config\n timeoutMs?: number;\n output?: { format?: 'table' | 'json' | 'markdown' | 'sarif' };\n /** Strict mode: treat config warnings (like unknown keys) as errors (default: false) */\n strictValidation?: boolean;\n /** Execution context for providers (CLI message, hooks, etc.) */\n executionContext?: ExecutionContext;\n}\n\n/**\n * Load and validate a Visor config.\n * @param configOrPath - Config object, file path, or omit to discover defaults\n * @param options - Validation options\n * @returns Validated config with defaults applied\n */\nexport async function loadConfig(\n configOrPath?: string | Partial<VisorConfig>,\n options?: { strict?: boolean }\n): Promise<VisorConfig> {\n const cm = new ConfigManager();\n\n // If it's an object, validate and return with defaults\n if (typeof configOrPath === 'object' && configOrPath !== null) {\n cm.validateConfig(configOrPath, options?.strict ?? false);\n\n // Apply lightweight defaults without expensive file system operations\n const defaultConfig: Partial<VisorConfig> = {\n version: '1.0',\n checks: {},\n max_parallelism: 3,\n fail_fast: false,\n };\n\n return {\n ...defaultConfig,\n ...configOrPath,\n checks: configOrPath.checks || {},\n } as VisorConfig;\n }\n\n // If it's a string, load from file\n if (typeof configOrPath === 'string') {\n return cm.loadConfig(configOrPath);\n }\n\n // Otherwise discover default config file\n return cm.findAndLoadConfig();\n}\n\n/** Expand check IDs by including their dependencies (shallow->deep). */\nexport function resolveChecks(checkIds: string[], config: VisorConfig | undefined): string[] {\n if (!config?.checks) return Array.from(new Set(checkIds));\n const resolved = new Set<string>();\n const visiting = new Set<string>();\n const result: string[] = [];\n\n const dfs = (id: string, stack: string[] = []) => {\n if (resolved.has(id)) return;\n if (visiting.has(id)) {\n const cycle = [...stack, id].join(' -> ');\n throw new Error(`Circular dependency detected involving check: ${id} (path: ${cycle})`);\n }\n visiting.add(id);\n const deps = config.checks![id]?.depends_on || [];\n for (const d of deps) dfs(d, [...stack, id]);\n if (!result.includes(id)) result.push(id);\n visiting.delete(id);\n resolved.add(id);\n };\n\n for (const id of checkIds) dfs(id);\n return result;\n}\n\n/**\n * Run Visor checks programmatically. Returns the same AnalysisResult shape used by the CLI.\n * Thin wrapper around CheckExecutionEngine.executeChecks.\n */\nexport async function runChecks(opts: RunOptions = {}): Promise<AnalysisResult> {\n const cm = new ConfigManager();\n let config: VisorConfig;\n\n if (opts.config) {\n // Validate manually constructed config\n // In strict mode, unknown keys are treated as errors\n cm.validateConfig(opts.config, opts.strictValidation ?? false);\n config = opts.config;\n } else if (opts.configPath) {\n config = await cm.loadConfig(opts.configPath);\n } else {\n config = await cm.findAndLoadConfig();\n }\n\n const checks =\n opts.checks && opts.checks.length > 0\n ? resolveChecks(opts.checks, config)\n : Object.keys(config.checks || {});\n\n // Always use StateMachineExecutionEngine\n const engine = new StateMachineExecutionEngine(opts.cwd);\n\n // Set execution context if provided\n if (opts.executionContext) {\n engine.setExecutionContext(opts.executionContext);\n }\n\n const result = await engine.executeChecks({\n checks,\n workingDirectory: opts.cwd,\n timeout: opts.timeoutMs,\n maxParallelism: opts.maxParallelism,\n failFast: opts.failFast,\n outputFormat: opts.output?.format,\n config,\n debug: opts.debug,\n tagFilter: opts.tagFilter,\n });\n\n return result;\n}\n"],"mappings":"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","names":["path","MemoryStore","ConfigManager","initializeWorkspace","buildEngineContextForRun","fs","convertToReviewSummary"]}
|
|
@@ -372,6 +372,7 @@ var SlackFrontend = class {
|
|
|
372
372
|
ackRef = null;
|
|
373
373
|
ackName = "eyes";
|
|
374
374
|
doneName = "thumbsup";
|
|
375
|
+
errorNotified = false;
|
|
375
376
|
constructor(config) {
|
|
376
377
|
this.cfg = config || {};
|
|
377
378
|
}
|
|
@@ -404,6 +405,14 @@ var SlackFrontend = class {
|
|
|
404
405
|
});
|
|
405
406
|
})
|
|
406
407
|
);
|
|
408
|
+
this.subs.push(
|
|
409
|
+
bus.on("CheckErrored", async (env) => {
|
|
410
|
+
const ev = env && env.payload || env;
|
|
411
|
+
const message = ev?.error?.message || "Execution error";
|
|
412
|
+
await this.maybePostError(ctx, "Check failed", message, ev?.checkId).catch(() => {
|
|
413
|
+
});
|
|
414
|
+
})
|
|
415
|
+
);
|
|
407
416
|
this.subs.push(
|
|
408
417
|
bus.on("StateTransition", async (env) => {
|
|
409
418
|
const ev = env && env.payload || env;
|
|
@@ -413,6 +422,14 @@ var SlackFrontend = class {
|
|
|
413
422
|
}
|
|
414
423
|
})
|
|
415
424
|
);
|
|
425
|
+
this.subs.push(
|
|
426
|
+
bus.on("Shutdown", async (env) => {
|
|
427
|
+
const ev = env && env.payload || env;
|
|
428
|
+
const message = ev?.error?.message || "Fatal error";
|
|
429
|
+
await this.maybePostError(ctx, "Run failed", message).catch(() => {
|
|
430
|
+
});
|
|
431
|
+
})
|
|
432
|
+
);
|
|
416
433
|
this.subs.push(
|
|
417
434
|
bus.on("CheckScheduled", async () => {
|
|
418
435
|
await this.ensureAcknowledgement(ctx).catch(() => {
|
|
@@ -437,7 +454,7 @@ var SlackFrontend = class {
|
|
|
437
454
|
}
|
|
438
455
|
}
|
|
439
456
|
if (!channel || !threadTs) return;
|
|
440
|
-
const { getPromptStateManager } = await import("./prompt-state-
|
|
457
|
+
const { getPromptStateManager } = await import("./prompt-state-EZYOUG75.mjs");
|
|
441
458
|
const mgr = getPromptStateManager();
|
|
442
459
|
const prev = mgr.getWaiting(channel, threadTs);
|
|
443
460
|
const text = String(ev.prompt);
|
|
@@ -471,7 +488,7 @@ var SlackFrontend = class {
|
|
|
471
488
|
const threadTs = String(ev?.threadTs || "");
|
|
472
489
|
const filePath = String(ev?.filePath || "");
|
|
473
490
|
if (!channel || !threadTs || !filePath) return;
|
|
474
|
-
const { getPromptStateManager } = await import("./prompt-state-
|
|
491
|
+
const { getPromptStateManager } = await import("./prompt-state-EZYOUG75.mjs");
|
|
475
492
|
const mgr = getPromptStateManager();
|
|
476
493
|
mgr.update(channel, threadTs, { snapshotPath: filePath });
|
|
477
494
|
try {
|
|
@@ -523,6 +540,47 @@ var SlackFrontend = class {
|
|
|
523
540
|
}
|
|
524
541
|
return null;
|
|
525
542
|
}
|
|
543
|
+
isTelemetryEnabled(ctx) {
|
|
544
|
+
try {
|
|
545
|
+
const anyCfg = ctx.config || {};
|
|
546
|
+
const slackCfg = anyCfg.slack || {};
|
|
547
|
+
const telemetryCfg = slackCfg.telemetry ?? this.cfg?.telemetry;
|
|
548
|
+
return telemetryCfg === true || telemetryCfg && typeof telemetryCfg === "object" && telemetryCfg.enabled === true;
|
|
549
|
+
} catch {
|
|
550
|
+
return false;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
async maybePostError(ctx, title, message, checkId) {
|
|
554
|
+
if (this.errorNotified) return;
|
|
555
|
+
if (!this.isTelemetryEnabled(ctx)) return;
|
|
556
|
+
const slack = this.getSlack(ctx);
|
|
557
|
+
if (!slack) return;
|
|
558
|
+
const payload = this.getInboundSlackPayload(ctx);
|
|
559
|
+
const ev = payload?.event;
|
|
560
|
+
const channel = String(ev?.channel || "");
|
|
561
|
+
const threadTs = String(ev?.thread_ts || ev?.ts || ev?.event_ts || "");
|
|
562
|
+
if (!channel || !threadTs) return;
|
|
563
|
+
let text = `\u274C ${title}`;
|
|
564
|
+
if (checkId) text += `
|
|
565
|
+
Check: ${checkId}`;
|
|
566
|
+
if (message) text += `
|
|
567
|
+
${message}`;
|
|
568
|
+
const traceInfo = this.getTraceInfo();
|
|
569
|
+
if (traceInfo?.traceId) {
|
|
570
|
+
text += `
|
|
571
|
+
|
|
572
|
+
\`trace_id: ${traceInfo.traceId}\``;
|
|
573
|
+
}
|
|
574
|
+
const formattedText = formatSlackText(text);
|
|
575
|
+
await slack.chat.postMessage({ channel, text: formattedText, thread_ts: threadTs });
|
|
576
|
+
try {
|
|
577
|
+
ctx.logger.info(
|
|
578
|
+
`[slack-frontend] posted error notice to ${channel} thread=${threadTs} check=${checkId || "run"}`
|
|
579
|
+
);
|
|
580
|
+
} catch {
|
|
581
|
+
}
|
|
582
|
+
this.errorNotified = true;
|
|
583
|
+
}
|
|
526
584
|
async ensureAcknowledgement(ctx) {
|
|
527
585
|
if (this.acked) return;
|
|
528
586
|
const ref = this.getInboundSlackEvent(ctx);
|
|
@@ -732,4 +790,4 @@ ${suffix}`;
|
|
|
732
790
|
export {
|
|
733
791
|
SlackFrontend
|
|
734
792
|
};
|
|
735
|
-
//# sourceMappingURL=slack-frontend-
|
|
793
|
+
//# sourceMappingURL=slack-frontend-J442FJWZ.mjs.map
|