@yasserkhanorg/impact-gate 2.0.0
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/LICENSE +168 -0
- package/README.md +520 -0
- package/dist/adapters/cypress.d.ts +10 -0
- package/dist/adapters/cypress.d.ts.map +1 -0
- package/dist/adapters/cypress.js +86 -0
- package/dist/adapters/framework_adapter.d.ts +41 -0
- package/dist/adapters/framework_adapter.d.ts.map +1 -0
- package/dist/adapters/framework_adapter.js +152 -0
- package/dist/adapters/playwright.d.ts +10 -0
- package/dist/adapters/playwright.d.ts.map +1 -0
- package/dist/adapters/playwright.js +86 -0
- package/dist/adapters/pytest.d.ts +10 -0
- package/dist/adapters/pytest.d.ts.map +1 -0
- package/dist/adapters/pytest.js +96 -0
- package/dist/adapters/supertest.d.ts +12 -0
- package/dist/adapters/supertest.d.ts.map +1 -0
- package/dist/adapters/supertest.js +85 -0
- package/dist/agent/api_catalog.d.ts +11 -0
- package/dist/agent/api_catalog.d.ts.map +1 -0
- package/dist/agent/api_catalog.js +210 -0
- package/dist/agent/config.d.ts +193 -0
- package/dist/agent/config.d.ts.map +1 -0
- package/dist/agent/config.js +875 -0
- package/dist/agent/feedback.d.ts +91 -0
- package/dist/agent/feedback.d.ts.map +1 -0
- package/dist/agent/feedback.js +323 -0
- package/dist/agent/git.d.ts +19 -0
- package/dist/agent/git.d.ts.map +1 -0
- package/dist/agent/git.js +257 -0
- package/dist/agent/handoff.d.ts +22 -0
- package/dist/agent/handoff.d.ts.map +1 -0
- package/dist/agent/handoff.js +180 -0
- package/dist/agent/llm_agents_flow.d.ts +15 -0
- package/dist/agent/llm_agents_flow.d.ts.map +1 -0
- package/dist/agent/llm_agents_flow.js +434 -0
- package/dist/agent/native_flow.d.ts +6 -0
- package/dist/agent/native_flow.d.ts.map +1 -0
- package/dist/agent/native_flow.js +179 -0
- package/dist/agent/pipeline.d.ts +7 -0
- package/dist/agent/pipeline.d.ts.map +1 -0
- package/dist/agent/pipeline.js +260 -0
- package/dist/agent/pipeline_types.d.ts +54 -0
- package/dist/agent/pipeline_types.d.ts.map +1 -0
- package/dist/agent/pipeline_types.js +4 -0
- package/dist/agent/pipeline_utils.d.ts +12 -0
- package/dist/agent/pipeline_utils.d.ts.map +1 -0
- package/dist/agent/pipeline_utils.js +156 -0
- package/dist/agent/plan.d.ts +170 -0
- package/dist/agent/plan.d.ts.map +1 -0
- package/dist/agent/plan.js +86 -0
- package/dist/agent/playwright_report.d.ts +8 -0
- package/dist/agent/playwright_report.d.ts.map +1 -0
- package/dist/agent/playwright_report.js +126 -0
- package/dist/agent/process_runner.d.ts +10 -0
- package/dist/agent/process_runner.d.ts.map +1 -0
- package/dist/agent/process_runner.js +92 -0
- package/dist/agent/spec_generator.d.ts +5 -0
- package/dist/agent/spec_generator.d.ts.map +1 -0
- package/dist/agent/spec_generator.js +253 -0
- package/dist/agent/test_path.d.ts +2 -0
- package/dist/agent/test_path.d.ts.map +1 -0
- package/dist/agent/test_path.js +23 -0
- package/dist/agent/traceability_capture.d.ts +18 -0
- package/dist/agent/traceability_capture.d.ts.map +1 -0
- package/dist/agent/traceability_capture.js +313 -0
- package/dist/agent/traceability_ingest.d.ts +21 -0
- package/dist/agent/traceability_ingest.d.ts.map +1 -0
- package/dist/agent/traceability_ingest.js +237 -0
- package/dist/agent/types.d.ts +42 -0
- package/dist/agent/types.d.ts.map +1 -0
- package/dist/agent/types.js +4 -0
- package/dist/agent/utils.d.ts +13 -0
- package/dist/agent/utils.d.ts.map +1 -0
- package/dist/agent/utils.js +152 -0
- package/dist/agent/validation_runner.d.ts +5 -0
- package/dist/agent/validation_runner.d.ts.map +1 -0
- package/dist/agent/validation_runner.js +77 -0
- package/dist/agentic/fix_loop.d.ts +26 -0
- package/dist/agentic/fix_loop.d.ts.map +1 -0
- package/dist/agentic/fix_loop.js +96 -0
- package/dist/agentic/playwright_runner.d.ts +43 -0
- package/dist/agentic/playwright_runner.d.ts.map +1 -0
- package/dist/agentic/playwright_runner.js +165 -0
- package/dist/agentic/runner.d.ts +27 -0
- package/dist/agentic/runner.d.ts.map +1 -0
- package/dist/agentic/runner.js +210 -0
- package/dist/agentic/types.d.ts +62 -0
- package/dist/agentic/types.d.ts.map +1 -0
- package/dist/agentic/types.js +4 -0
- package/dist/agents/coverage-evaluator.d.ts +8 -0
- package/dist/agents/coverage-evaluator.d.ts.map +1 -0
- package/dist/agents/coverage-evaluator.js +41 -0
- package/dist/agents/cross-impact.d.ts +13 -0
- package/dist/agents/cross-impact.d.ts.map +1 -0
- package/dist/agents/cross-impact.js +140 -0
- package/dist/agents/executor.d.ts +8 -0
- package/dist/agents/executor.d.ts.map +1 -0
- package/dist/agents/executor.js +75 -0
- package/dist/agents/explorer.d.ts +12 -0
- package/dist/agents/explorer.d.ts.map +1 -0
- package/dist/agents/explorer.js +43 -0
- package/dist/agents/generator.d.ts +8 -0
- package/dist/agents/generator.d.ts.map +1 -0
- package/dist/agents/generator.js +77 -0
- package/dist/agents/healer.d.ts +8 -0
- package/dist/agents/healer.d.ts.map +1 -0
- package/dist/agents/healer.js +31 -0
- package/dist/agents/impact-analyst.d.ts +8 -0
- package/dist/agents/impact-analyst.d.ts.map +1 -0
- package/dist/agents/impact-analyst.js +38 -0
- package/dist/agents/regression-advisor.d.ts +8 -0
- package/dist/agents/regression-advisor.d.ts.map +1 -0
- package/dist/agents/regression-advisor.js +116 -0
- package/dist/agents/strategist.d.ts +9 -0
- package/dist/agents/strategist.d.ts.map +1 -0
- package/dist/agents/strategist.js +92 -0
- package/dist/agents/test-designer.d.ts +8 -0
- package/dist/agents/test-designer.d.ts.map +1 -0
- package/dist/agents/test-designer.js +111 -0
- package/dist/anthropic_provider.d.ts +65 -0
- package/dist/anthropic_provider.d.ts.map +1 -0
- package/dist/anthropic_provider.js +334 -0
- package/dist/api.d.ts +48 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +151 -0
- package/dist/base_provider.d.ts +109 -0
- package/dist/base_provider.d.ts.map +1 -0
- package/dist/base_provider.js +203 -0
- package/dist/budget_ledger.d.ts +28 -0
- package/dist/budget_ledger.d.ts.map +1 -0
- package/dist/budget_ledger.js +62 -0
- package/dist/cache/cached_provider.d.ts +49 -0
- package/dist/cache/cached_provider.d.ts.map +1 -0
- package/dist/cache/cached_provider.js +91 -0
- package/dist/cache/response_cache.d.ts +79 -0
- package/dist/cache/response_cache.d.ts.map +1 -0
- package/dist/cache/response_cache.js +177 -0
- package/dist/cli/commands/analyze.d.ts +3 -0
- package/dist/cli/commands/analyze.d.ts.map +1 -0
- package/dist/cli/commands/analyze.js +77 -0
- package/dist/cli/commands/bootstrap.d.ts +3 -0
- package/dist/cli/commands/bootstrap.d.ts.map +1 -0
- package/dist/cli/commands/bootstrap.js +109 -0
- package/dist/cli/commands/cost_report.d.ts +3 -0
- package/dist/cli/commands/cost_report.d.ts.map +1 -0
- package/dist/cli/commands/cost_report.js +115 -0
- package/dist/cli/commands/crew.d.ts +3 -0
- package/dist/cli/commands/crew.d.ts.map +1 -0
- package/dist/cli/commands/crew.js +255 -0
- package/dist/cli/commands/feedback.d.ts +3 -0
- package/dist/cli/commands/feedback.d.ts.map +1 -0
- package/dist/cli/commands/feedback.js +39 -0
- package/dist/cli/commands/finalize.d.ts +3 -0
- package/dist/cli/commands/finalize.d.ts.map +1 -0
- package/dist/cli/commands/finalize.js +41 -0
- package/dist/cli/commands/gate.d.ts +3 -0
- package/dist/cli/commands/gate.d.ts.map +1 -0
- package/dist/cli/commands/gate.js +89 -0
- package/dist/cli/commands/generate.d.ts +4 -0
- package/dist/cli/commands/generate.d.ts.map +1 -0
- package/dist/cli/commands/generate.js +108 -0
- package/dist/cli/commands/heal.d.ts +3 -0
- package/dist/cli/commands/heal.d.ts.map +1 -0
- package/dist/cli/commands/heal.js +60 -0
- package/dist/cli/commands/impact.d.ts +4 -0
- package/dist/cli/commands/impact.d.ts.map +1 -0
- package/dist/cli/commands/impact.js +33 -0
- package/dist/cli/commands/init.d.ts +2 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +169 -0
- package/dist/cli/commands/llm_health.d.ts +2 -0
- package/dist/cli/commands/llm_health.d.ts.map +1 -0
- package/dist/cli/commands/llm_health.js +22 -0
- package/dist/cli/commands/plan.d.ts +4 -0
- package/dist/cli/commands/plan.d.ts.map +1 -0
- package/dist/cli/commands/plan.js +120 -0
- package/dist/cli/commands/plan_crew.d.ts +17 -0
- package/dist/cli/commands/plan_crew.d.ts.map +1 -0
- package/dist/cli/commands/plan_crew.js +316 -0
- package/dist/cli/commands/traceability.d.ts +4 -0
- package/dist/cli/commands/traceability.d.ts.map +1 -0
- package/dist/cli/commands/traceability.js +77 -0
- package/dist/cli/commands/train.d.ts +3 -0
- package/dist/cli/commands/train.d.ts.map +1 -0
- package/dist/cli/commands/train.js +391 -0
- package/dist/cli/defaults.d.ts +35 -0
- package/dist/cli/defaults.d.ts.map +1 -0
- package/dist/cli/defaults.js +172 -0
- package/dist/cli/errors.d.ts +27 -0
- package/dist/cli/errors.d.ts.map +1 -0
- package/dist/cli/errors.js +57 -0
- package/dist/cli/parse_args.d.ts +6 -0
- package/dist/cli/parse_args.d.ts.map +1 -0
- package/dist/cli/parse_args.js +257 -0
- package/dist/cli/types.d.ts +87 -0
- package/dist/cli/types.d.ts.map +1 -0
- package/dist/cli/types.js +4 -0
- package/dist/cli/usage.d.ts +2 -0
- package/dist/cli/usage.d.ts.map +1 -0
- package/dist/cli/usage.js +109 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +194 -0
- package/dist/crew/context.d.ts +55 -0
- package/dist/crew/context.d.ts.map +1 -0
- package/dist/crew/context.js +36 -0
- package/dist/crew/orchestrator.d.ts +50 -0
- package/dist/crew/orchestrator.d.ts.map +1 -0
- package/dist/crew/orchestrator.js +329 -0
- package/dist/crew/protocol.d.ts +46 -0
- package/dist/crew/protocol.d.ts.map +1 -0
- package/dist/crew/protocol.js +4 -0
- package/dist/crew/provider.d.ts +17 -0
- package/dist/crew/provider.d.ts.map +1 -0
- package/dist/crew/provider.js +36 -0
- package/dist/crew/sanitize.d.ts +3 -0
- package/dist/crew/sanitize.d.ts.map +1 -0
- package/dist/crew/sanitize.js +31 -0
- package/dist/crew/types.d.ts +52 -0
- package/dist/crew/types.d.ts.map +1 -0
- package/dist/crew/types.js +4 -0
- package/dist/crew/workflows.d.ts +52 -0
- package/dist/crew/workflows.d.ts.map +1 -0
- package/dist/crew/workflows.js +36 -0
- package/dist/custom_provider.d.ts +20 -0
- package/dist/custom_provider.d.ts.map +1 -0
- package/dist/custom_provider.js +277 -0
- package/dist/engine/ai_enrichment.d.ts +44 -0
- package/dist/engine/ai_enrichment.d.ts.map +1 -0
- package/dist/engine/ai_enrichment.js +267 -0
- package/dist/engine/diff_loader.d.ts +11 -0
- package/dist/engine/diff_loader.d.ts.map +1 -0
- package/dist/engine/diff_loader.js +63 -0
- package/dist/engine/impact_engine.d.ts +72 -0
- package/dist/engine/impact_engine.d.ts.map +1 -0
- package/dist/engine/impact_engine.js +298 -0
- package/dist/engine/plan_builder.d.ts +11 -0
- package/dist/engine/plan_builder.d.ts.map +1 -0
- package/dist/engine/plan_builder.js +599 -0
- package/dist/esm/adapters/cypress.js +49 -0
- package/dist/esm/adapters/framework_adapter.js +114 -0
- package/dist/esm/adapters/playwright.js +49 -0
- package/dist/esm/adapters/pytest.js +59 -0
- package/dist/esm/adapters/supertest.js +48 -0
- package/dist/esm/agent/api_catalog.js +199 -0
- package/dist/esm/agent/config.js +872 -0
- package/dist/esm/agent/feedback.js +317 -0
- package/dist/esm/agent/git.js +252 -0
- package/dist/esm/agent/handoff.js +177 -0
- package/dist/esm/agent/llm_agents_flow.js +421 -0
- package/dist/esm/agent/native_flow.js +175 -0
- package/dist/esm/agent/pipeline.js +256 -0
- package/dist/esm/agent/pipeline_types.js +3 -0
- package/dist/esm/agent/pipeline_utils.js +146 -0
- package/dist/esm/agent/plan.js +83 -0
- package/dist/esm/agent/playwright_report.js +123 -0
- package/dist/esm/agent/process_runner.js +83 -0
- package/dist/esm/agent/spec_generator.js +249 -0
- package/dist/esm/agent/test_path.js +20 -0
- package/dist/esm/agent/traceability_capture.js +310 -0
- package/dist/esm/agent/traceability_ingest.js +234 -0
- package/dist/esm/agent/types.js +3 -0
- package/dist/esm/agent/utils.js +138 -0
- package/dist/esm/agent/validation_runner.js +73 -0
- package/dist/esm/agentic/fix_loop.js +91 -0
- package/dist/esm/agentic/playwright_runner.js +161 -0
- package/dist/esm/agentic/runner.js +207 -0
- package/dist/esm/agentic/types.js +3 -0
- package/dist/esm/agents/coverage-evaluator.js +37 -0
- package/dist/esm/agents/cross-impact.js +136 -0
- package/dist/esm/agents/executor.js +71 -0
- package/dist/esm/agents/explorer.js +39 -0
- package/dist/esm/agents/generator.js +73 -0
- package/dist/esm/agents/healer.js +27 -0
- package/dist/esm/agents/impact-analyst.js +34 -0
- package/dist/esm/agents/regression-advisor.js +112 -0
- package/dist/esm/agents/strategist.js +88 -0
- package/dist/esm/agents/test-designer.js +107 -0
- package/dist/esm/anthropic_provider.js +326 -0
- package/dist/esm/api.js +143 -0
- package/dist/esm/base_provider.js +198 -0
- package/dist/esm/budget_ledger.js +58 -0
- package/dist/esm/cache/cached_provider.js +85 -0
- package/dist/esm/cache/response_cache.js +140 -0
- package/dist/esm/cli/commands/analyze.js +74 -0
- package/dist/esm/cli/commands/bootstrap.js +106 -0
- package/dist/esm/cli/commands/cost_report.js +112 -0
- package/dist/esm/cli/commands/crew.js +252 -0
- package/dist/esm/cli/commands/feedback.js +36 -0
- package/dist/esm/cli/commands/finalize.js +38 -0
- package/dist/esm/cli/commands/gate.js +86 -0
- package/dist/esm/cli/commands/generate.js +105 -0
- package/dist/esm/cli/commands/heal.js +57 -0
- package/dist/esm/cli/commands/impact.js +30 -0
- package/dist/esm/cli/commands/init.js +133 -0
- package/dist/esm/cli/commands/llm_health.js +19 -0
- package/dist/esm/cli/commands/plan.js +117 -0
- package/dist/esm/cli/commands/plan_crew.js +309 -0
- package/dist/esm/cli/commands/traceability.js +73 -0
- package/dist/esm/cli/commands/train.js +355 -0
- package/dist/esm/cli/defaults.js +165 -0
- package/dist/esm/cli/errors.js +52 -0
- package/dist/esm/cli/parse_args.js +251 -0
- package/dist/esm/cli/types.js +3 -0
- package/dist/esm/cli/usage.js +106 -0
- package/dist/esm/cli.js +192 -0
- package/dist/esm/crew/context.js +32 -0
- package/dist/esm/crew/orchestrator.js +325 -0
- package/dist/esm/crew/protocol.js +3 -0
- package/dist/esm/crew/provider.js +33 -0
- package/dist/esm/crew/sanitize.js +27 -0
- package/dist/esm/crew/types.js +3 -0
- package/dist/esm/crew/workflows.js +33 -0
- package/dist/esm/custom_provider.js +273 -0
- package/dist/esm/engine/ai_enrichment.js +264 -0
- package/dist/esm/engine/diff_loader.js +59 -0
- package/dist/esm/engine/impact_engine.js +291 -0
- package/dist/esm/engine/plan_builder.js +593 -0
- package/dist/esm/index.js +72 -0
- package/dist/esm/knowledge/api_surface.js +408 -0
- package/dist/esm/knowledge/cluster_utils.js +60 -0
- package/dist/esm/knowledge/context_loader.js +85 -0
- package/dist/esm/knowledge/failure_history.js +121 -0
- package/dist/esm/knowledge/kg_bridge.js +381 -0
- package/dist/esm/knowledge/kg_types.js +3 -0
- package/dist/esm/knowledge/route_families.js +393 -0
- package/dist/esm/knowledge/spec_index.js +122 -0
- package/dist/esm/logger.js +115 -0
- package/dist/esm/mcp-server.js +621 -0
- package/dist/esm/metrics/prometheus.js +149 -0
- package/dist/esm/model_router.js +59 -0
- package/dist/esm/ollama_provider.js +301 -0
- package/dist/esm/openai_provider.js +243 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/pipeline/orchestrator.js +228 -0
- package/dist/esm/pipeline/spec_verifier.js +75 -0
- package/dist/esm/pipeline/stage0_preprocess.js +102 -0
- package/dist/esm/pipeline/stage1_impact.js +140 -0
- package/dist/esm/pipeline/stage2_coverage.js +153 -0
- package/dist/esm/pipeline/stage3_generation.js +284 -0
- package/dist/esm/pipeline/stage4_heal.js +288 -0
- package/dist/esm/progress.js +112 -0
- package/dist/esm/prompts/coverage.js +57 -0
- package/dist/esm/prompts/cross-impact.js +53 -0
- package/dist/esm/prompts/generation.js +297 -0
- package/dist/esm/prompts/generation_profile.js +147 -0
- package/dist/esm/prompts/heal.js +91 -0
- package/dist/esm/prompts/impact.js +63 -0
- package/dist/esm/prompts/json_extract.js +36 -0
- package/dist/esm/prompts/strategist.js +61 -0
- package/dist/esm/prompts/test-designer.js +92 -0
- package/dist/esm/provider_factory.js +366 -0
- package/dist/esm/provider_interface.js +23 -0
- package/dist/esm/provider_utils.js +96 -0
- package/dist/esm/qa-agent/cli.js +205 -0
- package/dist/esm/qa-agent/orchestrator.js +120 -0
- package/dist/esm/qa-agent/phase1/runner.js +139 -0
- package/dist/esm/qa-agent/phase1/scope.js +126 -0
- package/dist/esm/qa-agent/phase2/agent_browser.js +95 -0
- package/dist/esm/qa-agent/phase2/agent_loop.js +351 -0
- package/dist/esm/qa-agent/phase2/exploration_state.js +97 -0
- package/dist/esm/qa-agent/phase2/tools.js +386 -0
- package/dist/esm/qa-agent/phase2/vision.js +75 -0
- package/dist/esm/qa-agent/phase3/feedback.js +34 -0
- package/dist/esm/qa-agent/phase3/reporter.js +145 -0
- package/dist/esm/qa-agent/phase3/spec_generator.js +62 -0
- package/dist/esm/qa-agent/phase3/verdict.js +66 -0
- package/dist/esm/qa-agent/safe_env.js +23 -0
- package/dist/esm/qa-agent/types.js +3 -0
- package/dist/esm/reporters/junit.js +86 -0
- package/dist/esm/reporters/reporter.js +3 -0
- package/dist/esm/reporters/sarif.js +132 -0
- package/dist/esm/resilience/circuit_breaker.js +78 -0
- package/dist/esm/resilience/retry.js +56 -0
- package/dist/esm/sanitize.js +66 -0
- package/dist/esm/training/enricher.js +345 -0
- package/dist/esm/training/kg_scanner.js +115 -0
- package/dist/esm/training/merger.js +204 -0
- package/dist/esm/training/scanner.js +923 -0
- package/dist/esm/training/types.js +6 -0
- package/dist/esm/training/validator.js +254 -0
- package/dist/esm/validation/guardrails.js +101 -0
- package/dist/esm/validation/output_schema.js +80 -0
- package/dist/esm/version.js +33 -0
- package/dist/index.d.ts +99 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +169 -0
- package/dist/knowledge/api_surface.d.ts +37 -0
- package/dist/knowledge/api_surface.d.ts.map +1 -0
- package/dist/knowledge/api_surface.js +418 -0
- package/dist/knowledge/cluster_utils.d.ts +28 -0
- package/dist/knowledge/cluster_utils.d.ts.map +1 -0
- package/dist/knowledge/cluster_utils.js +67 -0
- package/dist/knowledge/context_loader.d.ts +13 -0
- package/dist/knowledge/context_loader.d.ts.map +1 -0
- package/dist/knowledge/context_loader.js +90 -0
- package/dist/knowledge/failure_history.d.ts +39 -0
- package/dist/knowledge/failure_history.d.ts.map +1 -0
- package/dist/knowledge/failure_history.js +128 -0
- package/dist/knowledge/kg_bridge.d.ts +31 -0
- package/dist/knowledge/kg_bridge.d.ts.map +1 -0
- package/dist/knowledge/kg_bridge.js +388 -0
- package/dist/knowledge/kg_types.d.ts +75 -0
- package/dist/knowledge/kg_types.d.ts.map +1 -0
- package/dist/knowledge/kg_types.js +4 -0
- package/dist/knowledge/route_families.d.ts +98 -0
- package/dist/knowledge/route_families.d.ts.map +1 -0
- package/dist/knowledge/route_families.js +410 -0
- package/dist/knowledge/spec_index.d.ts +18 -0
- package/dist/knowledge/spec_index.d.ts.map +1 -0
- package/dist/knowledge/spec_index.js +128 -0
- package/dist/logger.d.ts +31 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +119 -0
- package/dist/mcp-server.d.ts +68 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +629 -0
- package/dist/metrics/prometheus.d.ts +37 -0
- package/dist/metrics/prometheus.d.ts.map +1 -0
- package/dist/metrics/prometheus.js +153 -0
- package/dist/model_router.d.ts +28 -0
- package/dist/model_router.d.ts.map +1 -0
- package/dist/model_router.js +63 -0
- package/dist/ollama_provider.d.ts +65 -0
- package/dist/ollama_provider.d.ts.map +1 -0
- package/dist/ollama_provider.js +309 -0
- package/dist/openai_provider.d.ts +23 -0
- package/dist/openai_provider.d.ts.map +1 -0
- package/dist/openai_provider.js +251 -0
- package/dist/pipeline/orchestrator.d.ts +33 -0
- package/dist/pipeline/orchestrator.d.ts.map +1 -0
- package/dist/pipeline/orchestrator.js +231 -0
- package/dist/pipeline/spec_verifier.d.ts +20 -0
- package/dist/pipeline/spec_verifier.d.ts.map +1 -0
- package/dist/pipeline/spec_verifier.js +79 -0
- package/dist/pipeline/stage0_preprocess.d.ts +31 -0
- package/dist/pipeline/stage0_preprocess.d.ts.map +1 -0
- package/dist/pipeline/stage0_preprocess.js +105 -0
- package/dist/pipeline/stage1_impact.d.ts +19 -0
- package/dist/pipeline/stage1_impact.d.ts.map +1 -0
- package/dist/pipeline/stage1_impact.js +143 -0
- package/dist/pipeline/stage2_coverage.d.ts +19 -0
- package/dist/pipeline/stage2_coverage.d.ts.map +1 -0
- package/dist/pipeline/stage2_coverage.js +156 -0
- package/dist/pipeline/stage3_generation.d.ts +43 -0
- package/dist/pipeline/stage3_generation.d.ts.map +1 -0
- package/dist/pipeline/stage3_generation.js +287 -0
- package/dist/pipeline/stage4_heal.d.ts +62 -0
- package/dist/pipeline/stage4_heal.d.ts.map +1 -0
- package/dist/pipeline/stage4_heal.js +294 -0
- package/dist/progress.d.ts +22 -0
- package/dist/progress.d.ts.map +1 -0
- package/dist/progress.js +116 -0
- package/dist/prompts/coverage.d.ts +39 -0
- package/dist/prompts/coverage.d.ts.map +1 -0
- package/dist/prompts/coverage.js +61 -0
- package/dist/prompts/cross-impact.d.ts +23 -0
- package/dist/prompts/cross-impact.d.ts.map +1 -0
- package/dist/prompts/cross-impact.js +57 -0
- package/dist/prompts/generation.d.ts +25 -0
- package/dist/prompts/generation.d.ts.map +1 -0
- package/dist/prompts/generation.js +302 -0
- package/dist/prompts/generation_profile.d.ts +29 -0
- package/dist/prompts/generation_profile.d.ts.map +1 -0
- package/dist/prompts/generation_profile.js +151 -0
- package/dist/prompts/heal.d.ts +23 -0
- package/dist/prompts/heal.d.ts.map +1 -0
- package/dist/prompts/heal.js +95 -0
- package/dist/prompts/impact.d.ts +31 -0
- package/dist/prompts/impact.d.ts.map +1 -0
- package/dist/prompts/impact.js +67 -0
- package/dist/prompts/json_extract.d.ts +14 -0
- package/dist/prompts/json_extract.d.ts.map +1 -0
- package/dist/prompts/json_extract.js +39 -0
- package/dist/prompts/strategist.d.ts +25 -0
- package/dist/prompts/strategist.d.ts.map +1 -0
- package/dist/prompts/strategist.js +65 -0
- package/dist/prompts/test-designer.d.ts +35 -0
- package/dist/prompts/test-designer.d.ts.map +1 -0
- package/dist/prompts/test-designer.js +96 -0
- package/dist/provider_factory.d.ts +104 -0
- package/dist/provider_factory.d.ts.map +1 -0
- package/dist/provider_factory.js +371 -0
- package/dist/provider_interface.d.ts +365 -0
- package/dist/provider_interface.d.ts.map +1 -0
- package/dist/provider_interface.js +28 -0
- package/dist/provider_utils.d.ts +39 -0
- package/dist/provider_utils.d.ts.map +1 -0
- package/dist/provider_utils.js +103 -0
- package/dist/qa-agent/cli.d.ts +3 -0
- package/dist/qa-agent/cli.d.ts.map +1 -0
- package/dist/qa-agent/cli.js +207 -0
- package/dist/qa-agent/orchestrator.d.ts +3 -0
- package/dist/qa-agent/orchestrator.d.ts.map +1 -0
- package/dist/qa-agent/orchestrator.js +123 -0
- package/dist/qa-agent/phase1/runner.d.ts +3 -0
- package/dist/qa-agent/phase1/runner.d.ts.map +1 -0
- package/dist/qa-agent/phase1/runner.js +142 -0
- package/dist/qa-agent/phase1/scope.d.ts +6 -0
- package/dist/qa-agent/phase1/scope.d.ts.map +1 -0
- package/dist/qa-agent/phase1/scope.js +129 -0
- package/dist/qa-agent/phase2/agent_browser.d.ts +35 -0
- package/dist/qa-agent/phase2/agent_browser.d.ts.map +1 -0
- package/dist/qa-agent/phase2/agent_browser.js +99 -0
- package/dist/qa-agent/phase2/agent_loop.d.ts +3 -0
- package/dist/qa-agent/phase2/agent_loop.d.ts.map +1 -0
- package/dist/qa-agent/phase2/agent_loop.js +357 -0
- package/dist/qa-agent/phase2/exploration_state.d.ts +12 -0
- package/dist/qa-agent/phase2/exploration_state.d.ts.map +1 -0
- package/dist/qa-agent/phase2/exploration_state.js +109 -0
- package/dist/qa-agent/phase2/tools.d.ts +28 -0
- package/dist/qa-agent/phase2/tools.d.ts.map +1 -0
- package/dist/qa-agent/phase2/tools.js +390 -0
- package/dist/qa-agent/phase2/vision.d.ts +3 -0
- package/dist/qa-agent/phase2/vision.d.ts.map +1 -0
- package/dist/qa-agent/phase2/vision.js +78 -0
- package/dist/qa-agent/phase3/feedback.d.ts +3 -0
- package/dist/qa-agent/phase3/feedback.d.ts.map +1 -0
- package/dist/qa-agent/phase3/feedback.js +37 -0
- package/dist/qa-agent/phase3/reporter.d.ts +3 -0
- package/dist/qa-agent/phase3/reporter.d.ts.map +1 -0
- package/dist/qa-agent/phase3/reporter.js +148 -0
- package/dist/qa-agent/phase3/spec_generator.d.ts +3 -0
- package/dist/qa-agent/phase3/spec_generator.d.ts.map +1 -0
- package/dist/qa-agent/phase3/spec_generator.js +65 -0
- package/dist/qa-agent/phase3/verdict.d.ts +3 -0
- package/dist/qa-agent/phase3/verdict.d.ts.map +1 -0
- package/dist/qa-agent/phase3/verdict.js +69 -0
- package/dist/qa-agent/safe_env.d.ts +3 -0
- package/dist/qa-agent/safe_env.d.ts.map +1 -0
- package/dist/qa-agent/safe_env.js +26 -0
- package/dist/qa-agent/types.d.ts +130 -0
- package/dist/qa-agent/types.d.ts.map +1 -0
- package/dist/qa-agent/types.js +4 -0
- package/dist/reporters/junit.d.ts +6 -0
- package/dist/reporters/junit.d.ts.map +1 -0
- package/dist/reporters/junit.js +89 -0
- package/dist/reporters/reporter.d.ts +42 -0
- package/dist/reporters/reporter.d.ts.map +1 -0
- package/dist/reporters/reporter.js +4 -0
- package/dist/reporters/sarif.d.ts +7 -0
- package/dist/reporters/sarif.d.ts.map +1 -0
- package/dist/reporters/sarif.js +135 -0
- package/dist/resilience/circuit_breaker.d.ts +36 -0
- package/dist/resilience/circuit_breaker.d.ts.map +1 -0
- package/dist/resilience/circuit_breaker.js +82 -0
- package/dist/resilience/retry.d.ts +11 -0
- package/dist/resilience/retry.d.ts.map +1 -0
- package/dist/resilience/retry.js +59 -0
- package/dist/sanitize.d.ts +15 -0
- package/dist/sanitize.d.ts.map +1 -0
- package/dist/sanitize.js +71 -0
- package/dist/training/enricher.d.ts +17 -0
- package/dist/training/enricher.d.ts.map +1 -0
- package/dist/training/enricher.js +350 -0
- package/dist/training/kg_scanner.d.ts +13 -0
- package/dist/training/kg_scanner.d.ts.map +1 -0
- package/dist/training/kg_scanner.js +118 -0
- package/dist/training/merger.d.ts +15 -0
- package/dist/training/merger.d.ts.map +1 -0
- package/dist/training/merger.js +208 -0
- package/dist/training/scanner.d.ts +36 -0
- package/dist/training/scanner.d.ts.map +1 -0
- package/dist/training/scanner.js +932 -0
- package/dist/training/types.d.ts +117 -0
- package/dist/training/types.d.ts.map +1 -0
- package/dist/training/types.js +9 -0
- package/dist/training/validator.d.ts +21 -0
- package/dist/training/validator.d.ts.map +1 -0
- package/dist/training/validator.js +262 -0
- package/dist/validation/guardrails.d.ts +31 -0
- package/dist/validation/guardrails.d.ts.map +1 -0
- package/dist/validation/guardrails.js +112 -0
- package/dist/validation/output_schema.d.ts +67 -0
- package/dist/validation/output_schema.d.ts.map +1 -0
- package/dist/validation/output_schema.js +84 -0
- package/dist/version.d.ts +6 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +36 -0
- package/package.json +126 -0
- package/schemas/flow-decision.schema.json +83 -0
- package/schemas/gap.schema.json +18 -0
- package/schemas/impact.schema.json +455 -0
- package/schemas/plan.schema.json +491 -0
- package/schemas/route-families.schema.json +137 -0
- package/schemas/subsystem-risk-map.schema.json +62 -0
- package/schemas/traceability-input.schema.json +122 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
2
|
+
// See LICENSE.txt for license information.
|
|
3
|
+
import { withRetry } from './resilience/retry.js';
|
|
4
|
+
import { CircuitBreaker } from './resilience/circuit_breaker.js';
|
|
5
|
+
/**
|
|
6
|
+
* Abstract base class for all LLM providers
|
|
7
|
+
* Eliminates 240+ lines of duplicate stats management code
|
|
8
|
+
* Provides common functionality for token tracking, cost calculation, and stats management
|
|
9
|
+
*/
|
|
10
|
+
export class BudgetExceededError extends Error {
|
|
11
|
+
constructor(currentCost, budgetUSD) {
|
|
12
|
+
super(`Budget exceeded: $${currentCost.toFixed(4)} >= $${budgetUSD} limit`);
|
|
13
|
+
this.currentCost = currentCost;
|
|
14
|
+
this.budgetUSD = budgetUSD;
|
|
15
|
+
this.name = 'BudgetExceededError';
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export class BaseProvider {
|
|
19
|
+
constructor() {
|
|
20
|
+
/** Tracks the current in-flight budget reservation for this provider instance. */
|
|
21
|
+
this._activeReservation = 0;
|
|
22
|
+
this.initializeStats();
|
|
23
|
+
}
|
|
24
|
+
/** Lazily get-or-create a circuit breaker shared across all instances of this provider type. */
|
|
25
|
+
get circuitBreaker() {
|
|
26
|
+
let cb = BaseProvider._sharedBreakers.get(this.name);
|
|
27
|
+
if (!cb) {
|
|
28
|
+
cb = new CircuitBreaker({
|
|
29
|
+
shouldCount: (error) => {
|
|
30
|
+
if (error instanceof BudgetExceededError)
|
|
31
|
+
return false;
|
|
32
|
+
if (!(error instanceof Error))
|
|
33
|
+
return true;
|
|
34
|
+
const msg = error.message.toLowerCase();
|
|
35
|
+
return msg.includes('429') || msg.includes('rate limit') ||
|
|
36
|
+
msg.includes('500') || msg.includes('502') || msg.includes('503') || msg.includes('504') ||
|
|
37
|
+
msg.includes('econnreset') || msg.includes('econnrefused') || msg.includes('etimedout') ||
|
|
38
|
+
msg.includes('overloaded') || msg.includes('socket hang up') || msg.includes('network error');
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
BaseProvider._sharedBreakers.set(this.name, cb);
|
|
42
|
+
}
|
|
43
|
+
return cb;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Set a hard budget limit. Once totalCost reaches this value,
|
|
47
|
+
* subsequent calls will throw BudgetExceededError.
|
|
48
|
+
*/
|
|
49
|
+
setBudget(usd) {
|
|
50
|
+
this._budgetUSD = usd;
|
|
51
|
+
}
|
|
52
|
+
get budgetUSD() {
|
|
53
|
+
return this._budgetUSD;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Attach a shared budget ledger so aggregate cost across all providers
|
|
57
|
+
* in a crew run is checked before each LLM call.
|
|
58
|
+
*/
|
|
59
|
+
setBudgetLedger(ledger) {
|
|
60
|
+
this._ledger = ledger;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Check budget and pre-reserve estimated cost for the upcoming LLM call.
|
|
64
|
+
*
|
|
65
|
+
* When a shared ledger exists, reserves an estimate derived from the provider's
|
|
66
|
+
* output token cost × maxTokens (default 4096). This blocks parallel agents from
|
|
67
|
+
* spending into the same headroom — like a credit card authorization hold.
|
|
68
|
+
*
|
|
69
|
+
* Self-healing: if a prior call failed without reaching updateStats(), the stale
|
|
70
|
+
* reservation is released here before placing the new one.
|
|
71
|
+
*/
|
|
72
|
+
checkBudget() {
|
|
73
|
+
if (this._ledger) {
|
|
74
|
+
// Release stale reservation from a prior failed call that never hit updateStats
|
|
75
|
+
if (this._activeReservation > 0) {
|
|
76
|
+
this._ledger.release(this._activeReservation);
|
|
77
|
+
this._activeReservation = 0;
|
|
78
|
+
}
|
|
79
|
+
// Reserve estimated cost for the upcoming call
|
|
80
|
+
const estimate = this.estimateCallCost();
|
|
81
|
+
this._ledger.reserve(estimate);
|
|
82
|
+
this._activeReservation = estimate;
|
|
83
|
+
try {
|
|
84
|
+
this._ledger.check();
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
// Budget exceeded — release reservation immediately so it doesn't leak
|
|
88
|
+
this._ledger.release(estimate);
|
|
89
|
+
this._activeReservation = 0;
|
|
90
|
+
throw err;
|
|
91
|
+
}
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (this._budgetUSD !== undefined && this.stats.totalCost >= this._budgetUSD) {
|
|
95
|
+
throw new BudgetExceededError(this.stats.totalCost, this._budgetUSD);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Conservative cost estimate for the upcoming call.
|
|
100
|
+
* Uses maxTokens (or 4096 default) × output cost rate.
|
|
101
|
+
* Overestimating is safe — the reservation is replaced with actual cost in updateStats.
|
|
102
|
+
*/
|
|
103
|
+
estimateCallCost() {
|
|
104
|
+
const outputTokenEstimate = 4096;
|
|
105
|
+
const costRate = this.capabilities?.costPer1MOutputTokens ?? 15; // default to ~Sonnet
|
|
106
|
+
return (outputTokenEstimate / 1000000) * costRate;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Initialize stats object with default values
|
|
110
|
+
*/
|
|
111
|
+
initializeStats() {
|
|
112
|
+
this.stats = {
|
|
113
|
+
requestCount: 0,
|
|
114
|
+
totalInputTokens: 0,
|
|
115
|
+
totalOutputTokens: 0,
|
|
116
|
+
totalTokens: 0,
|
|
117
|
+
totalCost: 0,
|
|
118
|
+
averageResponseTimeMs: 0,
|
|
119
|
+
failedRequests: 0,
|
|
120
|
+
startTime: new Date(),
|
|
121
|
+
lastUpdated: new Date(),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Update stats with new usage data
|
|
126
|
+
* Maintains rolling average for response time
|
|
127
|
+
*/
|
|
128
|
+
updateStats(usage, responseTime, cost) {
|
|
129
|
+
this.stats.requestCount++;
|
|
130
|
+
this.stats.totalInputTokens += usage.inputTokens;
|
|
131
|
+
this.stats.totalOutputTokens += usage.outputTokens;
|
|
132
|
+
this.stats.totalTokens += usage.totalTokens;
|
|
133
|
+
this.stats.totalCost += cost;
|
|
134
|
+
if (this._ledger) {
|
|
135
|
+
// Settle: release the estimate, record actual
|
|
136
|
+
if (this._activeReservation > 0) {
|
|
137
|
+
this._ledger.release(this._activeReservation);
|
|
138
|
+
this._activeReservation = 0;
|
|
139
|
+
}
|
|
140
|
+
this._ledger.record(cost);
|
|
141
|
+
}
|
|
142
|
+
// Update rolling average response time
|
|
143
|
+
const totalRequests = this.stats.requestCount;
|
|
144
|
+
this.stats.averageResponseTimeMs =
|
|
145
|
+
(this.stats.averageResponseTimeMs * (totalRequests - 1) + responseTime) / totalRequests;
|
|
146
|
+
this.stats.lastUpdated = new Date();
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Get a copy of current usage stats
|
|
150
|
+
*/
|
|
151
|
+
getUsageStats() {
|
|
152
|
+
return { ...this.stats };
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Reset all usage stats to initial state
|
|
156
|
+
*/
|
|
157
|
+
resetUsageStats() {
|
|
158
|
+
this.initializeStats();
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Wrap an async call with circuit breaker + retry logic.
|
|
162
|
+
* Circuit breaker protects against cascading failures from a down provider;
|
|
163
|
+
* retry handles transient failures within a healthy circuit.
|
|
164
|
+
*
|
|
165
|
+
* Non-transient errors (budget, auth, validation) are thrown directly and
|
|
166
|
+
* bypass the circuit breaker so they don't incorrectly trip it.
|
|
167
|
+
*/
|
|
168
|
+
retryCall(fn) {
|
|
169
|
+
return this.circuitBreaker.call(() => withRetry(fn, { maxRetries: 2, baseDelayMs: 1000, maxDelayMs: 10000, jitter: true }), () => { throw new Error(`${this.name} provider circuit open — too many consecutive failures`); });
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Calculate cost for token usage, accounting for prompt caching discounts
|
|
173
|
+
* Cached tokens cost 90% less than regular tokens
|
|
174
|
+
*/
|
|
175
|
+
calculateCost(usage, costPer1MInputTokens, costPer1MOutputTokens) {
|
|
176
|
+
// Calculate input token cost
|
|
177
|
+
let inputCost = 0;
|
|
178
|
+
// Cached tokens cost 90% less
|
|
179
|
+
if (usage.cachedTokens) {
|
|
180
|
+
const cachedCost = (usage.cachedTokens / 1000000) * (costPer1MInputTokens * 0.1);
|
|
181
|
+
const uncachedInputTokens = usage.inputTokens - usage.cachedTokens;
|
|
182
|
+
const uncachedCost = (uncachedInputTokens / 1000000) * costPer1MInputTokens;
|
|
183
|
+
inputCost = cachedCost + uncachedCost;
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
inputCost = (usage.inputTokens / 1000000) * costPer1MInputTokens;
|
|
187
|
+
}
|
|
188
|
+
// Calculate output token cost
|
|
189
|
+
const outputCost = (usage.outputTokens / 1000000) * costPer1MOutputTokens;
|
|
190
|
+
return inputCost + outputCost;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Shared circuit breakers keyed by provider name (e.g., "anthropic", "openai").
|
|
195
|
+
* All instances of the same provider type share one breaker, so if Anthropic is
|
|
196
|
+
* down, ALL agents discover it after 3 total failures instead of 3 × N.
|
|
197
|
+
*/
|
|
198
|
+
BaseProvider._sharedBreakers = new Map();
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
2
|
+
// See LICENSE.txt for license information.
|
|
3
|
+
/**
|
|
4
|
+
* Shared budget ledger — tracks aggregate cost across all provider instances
|
|
5
|
+
* in a single crew run. Prevents parallel agents from each seeing only 1/N
|
|
6
|
+
* of actual spend and overshooting the budget by N×limit.
|
|
7
|
+
*
|
|
8
|
+
* Usage: create one BudgetLedger per crew run, pass it to getCrewProvider(),
|
|
9
|
+
* which attaches it to each provider via setBudgetLedger().
|
|
10
|
+
*/
|
|
11
|
+
import { BudgetExceededError } from './base_provider.js';
|
|
12
|
+
export class BudgetLedger {
|
|
13
|
+
constructor(limitUSD) {
|
|
14
|
+
this._totalCost = 0;
|
|
15
|
+
this._reserved = 0;
|
|
16
|
+
this._limitUSD = limitUSD;
|
|
17
|
+
}
|
|
18
|
+
get totalCost() {
|
|
19
|
+
return this._totalCost;
|
|
20
|
+
}
|
|
21
|
+
get limitUSD() {
|
|
22
|
+
return this._limitUSD;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Record actual cost from a completed LLM call.
|
|
26
|
+
*/
|
|
27
|
+
record(cost) {
|
|
28
|
+
if (!Number.isFinite(cost) || cost < 0)
|
|
29
|
+
return;
|
|
30
|
+
this._totalCost += cost;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Pre-reserve estimated cost before an LLM call begins.
|
|
34
|
+
* Blocks parallel agents from spending into the same headroom.
|
|
35
|
+
* Like a credit card authorization hold.
|
|
36
|
+
*/
|
|
37
|
+
reserve(estimate) {
|
|
38
|
+
if (!Number.isFinite(estimate) || estimate <= 0)
|
|
39
|
+
return;
|
|
40
|
+
this._reserved += estimate;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Release a prior reservation (after API response or on error).
|
|
44
|
+
*/
|
|
45
|
+
release(estimate) {
|
|
46
|
+
this._reserved = Math.max(0, this._reserved - estimate);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Throws BudgetExceededError if committed cost + in-flight reservations
|
|
50
|
+
* have reached the limit.
|
|
51
|
+
*/
|
|
52
|
+
check() {
|
|
53
|
+
const effective = this._totalCost + this._reserved;
|
|
54
|
+
if (effective >= this._limitUSD) {
|
|
55
|
+
throw new BudgetExceededError(effective, this._limitUSD);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
2
|
+
// See LICENSE.txt for license information.
|
|
3
|
+
import { ResponseCache, TTL } from './response_cache.js';
|
|
4
|
+
/**
|
|
5
|
+
* Decorator that adds transparent response caching to any LLMProvider.
|
|
6
|
+
*
|
|
7
|
+
* - `generateText()` checks the cache first and returns a cached response on hit.
|
|
8
|
+
* On a miss it delegates to the inner provider, stores the result, and returns it.
|
|
9
|
+
* - All other methods (analyzeImage, streamText, capabilities, usage stats)
|
|
10
|
+
* delegate directly to the wrapped provider.
|
|
11
|
+
*
|
|
12
|
+
* The TTL is selected based on the agent role: agents whose name contains
|
|
13
|
+
* "generat" use the shorter GENERATION TTL; all others use ANALYSIS.
|
|
14
|
+
*/
|
|
15
|
+
export class CachedProvider {
|
|
16
|
+
constructor(inner, cache, cacheContext) {
|
|
17
|
+
this.inner = inner;
|
|
18
|
+
this.cache = cache;
|
|
19
|
+
this.ctx = cacheContext;
|
|
20
|
+
this.name = inner.name;
|
|
21
|
+
this.capabilities = inner.capabilities;
|
|
22
|
+
// Pick TTL based on agent role
|
|
23
|
+
this.ttlMs = cacheContext.agent.toLowerCase().includes('generat')
|
|
24
|
+
? TTL.GENERATION
|
|
25
|
+
: TTL.ANALYSIS;
|
|
26
|
+
// Wire optional methods only when the inner provider supports them
|
|
27
|
+
if (inner.analyzeImage) {
|
|
28
|
+
this.analyzeImage = (images, prompt, options) => inner.analyzeImage(images, prompt, options);
|
|
29
|
+
}
|
|
30
|
+
if (inner.streamText) {
|
|
31
|
+
this.streamText = (prompt, options) => inner.streamText(prompt, options);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Generate text with cache-through semantics.
|
|
36
|
+
* On a cache hit the inner provider is never called, saving tokens and latency.
|
|
37
|
+
*/
|
|
38
|
+
async generateText(prompt, options) {
|
|
39
|
+
const { agent, family, fileHashes } = this.ctx;
|
|
40
|
+
const model = this.inner.name;
|
|
41
|
+
// Check cache
|
|
42
|
+
const cached = this.cache.get(agent, family, fileHashes, model);
|
|
43
|
+
if (cached) {
|
|
44
|
+
return {
|
|
45
|
+
text: cached.response,
|
|
46
|
+
usage: {
|
|
47
|
+
inputTokens: cached.usage.inputTokens,
|
|
48
|
+
outputTokens: cached.usage.outputTokens,
|
|
49
|
+
totalTokens: cached.usage.inputTokens + cached.usage.outputTokens,
|
|
50
|
+
cachedTokens: cached.usage.inputTokens,
|
|
51
|
+
},
|
|
52
|
+
cost: 0, // No cost on cache hit
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
// Cache miss - call inner provider
|
|
56
|
+
const response = await this.inner.generateText(prompt, options);
|
|
57
|
+
// Store in cache
|
|
58
|
+
const key = ResponseCache.buildKey({ agent, family, fileHashes, model });
|
|
59
|
+
const entry = {
|
|
60
|
+
key,
|
|
61
|
+
family,
|
|
62
|
+
response: response.text,
|
|
63
|
+
usage: {
|
|
64
|
+
inputTokens: response.usage.inputTokens,
|
|
65
|
+
outputTokens: response.usage.outputTokens,
|
|
66
|
+
cost: response.cost,
|
|
67
|
+
},
|
|
68
|
+
createdAt: new Date().toISOString(),
|
|
69
|
+
ttlMs: this.ttlMs,
|
|
70
|
+
};
|
|
71
|
+
this.cache.set(entry);
|
|
72
|
+
return response;
|
|
73
|
+
}
|
|
74
|
+
getUsageStats() {
|
|
75
|
+
return this.inner.getUsageStats();
|
|
76
|
+
}
|
|
77
|
+
resetUsageStats() {
|
|
78
|
+
this.inner.resetUsageStats();
|
|
79
|
+
}
|
|
80
|
+
async checkHealth() {
|
|
81
|
+
return this.inner.checkHealth();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// Re-export for convenience
|
|
85
|
+
export { ResponseCache, TTL } from './response_cache.js';
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
2
|
+
// See LICENSE.txt for license information.
|
|
3
|
+
import * as fs from 'node:fs';
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
import * as crypto from 'node:crypto';
|
|
6
|
+
/**
|
|
7
|
+
* TTL presets for different cache entry types.
|
|
8
|
+
*/
|
|
9
|
+
export const TTL = {
|
|
10
|
+
/** 24 hours - for analysis results that change infrequently */
|
|
11
|
+
ANALYSIS: 24 * 60 * 60 * 1000,
|
|
12
|
+
/** 1 hour - for generated content that may need fresher context */
|
|
13
|
+
GENERATION: 1 * 60 * 60 * 1000,
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Cross-run LLM response cache backed by JSON files.
|
|
17
|
+
*
|
|
18
|
+
* Stores entries as `{cacheDir}/{sha256}.json` using a content-addressed key
|
|
19
|
+
* derived from (agentRole + familyName + sorted file hashes + model).
|
|
20
|
+
*/
|
|
21
|
+
export class ResponseCache {
|
|
22
|
+
constructor(workspaceRoot) {
|
|
23
|
+
this.cacheDir = path.join(workspaceRoot, '.e2e-ai-agents', 'cache');
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Build a deterministic SHA-256 cache key from the provided parameters.
|
|
27
|
+
*/
|
|
28
|
+
static buildKey(params) {
|
|
29
|
+
const sorted = [...params.fileHashes].sort();
|
|
30
|
+
const payload = params.agent + params.family + JSON.stringify(sorted) + params.model;
|
|
31
|
+
return crypto.createHash('sha256').update(payload).digest('hex');
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Retrieve a cached response if it exists and has not expired.
|
|
35
|
+
* Returns `null` on cache miss or expiry.
|
|
36
|
+
*/
|
|
37
|
+
get(agent, family, fileHashes, model) {
|
|
38
|
+
const key = ResponseCache.buildKey({ agent, family, fileHashes, model });
|
|
39
|
+
const filePath = path.join(this.cacheDir, `${key}.json`);
|
|
40
|
+
try {
|
|
41
|
+
if (!fs.existsSync(filePath)) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
45
|
+
const entry = JSON.parse(raw);
|
|
46
|
+
const age = Date.now() - new Date(entry.createdAt).getTime();
|
|
47
|
+
if (age > entry.ttlMs) {
|
|
48
|
+
// Expired - clean up eagerly
|
|
49
|
+
fs.unlinkSync(filePath);
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
return entry;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// Corrupted or unreadable - treat as miss
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Write a cache entry to disk.
|
|
61
|
+
* Creates the cache directory if it does not yet exist.
|
|
62
|
+
*/
|
|
63
|
+
set(entry) {
|
|
64
|
+
this.ensureCacheDir();
|
|
65
|
+
const filePath = path.join(this.cacheDir, `${entry.key}.json`);
|
|
66
|
+
fs.writeFileSync(filePath, JSON.stringify(entry, null, 2), 'utf-8');
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Remove all cache entries belonging to the given family.
|
|
70
|
+
*
|
|
71
|
+
* Because the cache key is a one-way SHA-256 hash, we scan each file and
|
|
72
|
+
* check its stored `family` field. The directory is scoped to a single
|
|
73
|
+
* workspace so the scan is bounded.
|
|
74
|
+
*/
|
|
75
|
+
invalidateFamily(familyName) {
|
|
76
|
+
if (!fs.existsSync(this.cacheDir)) {
|
|
77
|
+
return 0;
|
|
78
|
+
}
|
|
79
|
+
let deleted = 0;
|
|
80
|
+
const files = fs.readdirSync(this.cacheDir).filter((f) => f.endsWith('.json'));
|
|
81
|
+
for (const file of files) {
|
|
82
|
+
const filePath = path.join(this.cacheDir, file);
|
|
83
|
+
try {
|
|
84
|
+
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
85
|
+
const entry = JSON.parse(raw);
|
|
86
|
+
if (entry.family === familyName) {
|
|
87
|
+
fs.unlinkSync(filePath);
|
|
88
|
+
deleted++;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// Skip unreadable files
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return deleted;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Remove all expired entries from the cache directory.
|
|
99
|
+
* Returns the number of entries deleted.
|
|
100
|
+
*/
|
|
101
|
+
prune() {
|
|
102
|
+
if (!fs.existsSync(this.cacheDir)) {
|
|
103
|
+
return 0;
|
|
104
|
+
}
|
|
105
|
+
let deleted = 0;
|
|
106
|
+
const now = Date.now();
|
|
107
|
+
const files = fs.readdirSync(this.cacheDir).filter((f) => f.endsWith('.json'));
|
|
108
|
+
for (const file of files) {
|
|
109
|
+
const filePath = path.join(this.cacheDir, file);
|
|
110
|
+
try {
|
|
111
|
+
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
112
|
+
const entry = JSON.parse(raw);
|
|
113
|
+
const age = now - new Date(entry.createdAt).getTime();
|
|
114
|
+
if (age > entry.ttlMs) {
|
|
115
|
+
fs.unlinkSync(filePath);
|
|
116
|
+
deleted++;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
// Corrupted file - remove it
|
|
121
|
+
try {
|
|
122
|
+
fs.unlinkSync(filePath);
|
|
123
|
+
deleted++;
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
// Ignore if removal also fails
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return deleted;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Ensure the cache directory exists on disk.
|
|
134
|
+
*/
|
|
135
|
+
ensureCacheDir() {
|
|
136
|
+
if (!fs.existsSync(this.cacheDir)) {
|
|
137
|
+
fs.mkdirSync(this.cacheDir, { recursive: true });
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
2
|
+
// See LICENSE.txt for license information.
|
|
3
|
+
import { resolveConfig } from '../../agent/config.js';
|
|
4
|
+
import { runPipeline } from '../../pipeline/orchestrator.js';
|
|
5
|
+
export async function runAnalyzeCommand(args, autoConfig) {
|
|
6
|
+
if (!args.path && !autoConfig) {
|
|
7
|
+
console.error('Error: --path is required for analyze command');
|
|
8
|
+
process.exit(1);
|
|
9
|
+
}
|
|
10
|
+
const { config } = resolveConfig(process.cwd(), autoConfig, {
|
|
11
|
+
path: args.path,
|
|
12
|
+
profile: args.profile,
|
|
13
|
+
testsRoot: args.testsRoot,
|
|
14
|
+
mode: 'impact',
|
|
15
|
+
gitSince: args.gitSince,
|
|
16
|
+
llmProvider: args.llmProvider,
|
|
17
|
+
});
|
|
18
|
+
const testsRoot = config.testsRoot || config.path;
|
|
19
|
+
const analyzeStages = [
|
|
20
|
+
'preprocess', 'impact', 'coverage',
|
|
21
|
+
];
|
|
22
|
+
if (args.analyzeGenerate) {
|
|
23
|
+
analyzeStages.push('generation');
|
|
24
|
+
}
|
|
25
|
+
if (args.analyzeHeal || args.analyzeHealReport) {
|
|
26
|
+
analyzeStages.push('heal');
|
|
27
|
+
}
|
|
28
|
+
const result = await runPipeline({
|
|
29
|
+
appPath: config.path,
|
|
30
|
+
testsRoot,
|
|
31
|
+
gitSince: args.gitSince || config.git.since,
|
|
32
|
+
routeFamilies: config.routeFamilies,
|
|
33
|
+
apiSurface: config.apiSurface,
|
|
34
|
+
stages: analyzeStages,
|
|
35
|
+
generation: args.analyzeGenerate
|
|
36
|
+
? {
|
|
37
|
+
defaultOutputDir: args.analyzeGenerateOutputDir || 'specs/functional/ai-assisted',
|
|
38
|
+
dryRun: args.dryRun,
|
|
39
|
+
}
|
|
40
|
+
: undefined,
|
|
41
|
+
heal: (args.analyzeHeal || args.analyzeHealReport)
|
|
42
|
+
? {
|
|
43
|
+
mcp: args.pipelineMcp ?? true,
|
|
44
|
+
mcpAllowFallback: args.pipelineMcpAllowFallback ?? false,
|
|
45
|
+
mcpOnly: args.pipelineMcpOnly ?? false,
|
|
46
|
+
mcpCommandTimeoutMs: args.pipelineMcpTimeoutMs,
|
|
47
|
+
mcpRetries: args.pipelineMcpRetries ?? 1,
|
|
48
|
+
dryRun: args.dryRun,
|
|
49
|
+
}
|
|
50
|
+
: undefined,
|
|
51
|
+
playwrightReportPath: args.analyzeHealReport,
|
|
52
|
+
});
|
|
53
|
+
console.log(`Analyze report: ${result.reportPath}`);
|
|
54
|
+
console.log(`Analyze flows identified: ${result.report.summary.flowsIdentified}`);
|
|
55
|
+
console.log(`Analyze flows covered: ${result.report.summary.flowsCovered}`);
|
|
56
|
+
console.log(`Analyze flows uncovered: ${result.report.summary.flowsUncovered}`);
|
|
57
|
+
console.log(`Analyze overall confidence: ${result.report.summary.overallConfidence}`);
|
|
58
|
+
console.log(`Analyze route families: ${result.report.summary.routeFamiliesImpacted.join(', ') || 'none'}`);
|
|
59
|
+
if (result.generated && result.generated.length > 0) {
|
|
60
|
+
const written = result.generated.filter((g) => g.written).length;
|
|
61
|
+
console.log(`Analyze generated specs: ${result.generated.length} (written=${written})`);
|
|
62
|
+
for (const g of result.generated) {
|
|
63
|
+
console.log(` ${g.mode}: ${g.specPath}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (result.healResult) {
|
|
67
|
+
const healed = result.healResult.summary.results.filter((r) => r.healStatus === 'success').length;
|
|
68
|
+
const healFailed = result.healResult.summary.results.filter((r) => r.healStatus === 'failed').length;
|
|
69
|
+
console.log(`Analyze heal targets: ${result.healResult.targets.length} (healed=${healed}, failed=${healFailed})`);
|
|
70
|
+
}
|
|
71
|
+
if (result.warnings.length > 0) {
|
|
72
|
+
console.log(`Analyze warnings: ${result.warnings.join(' | ')}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
2
|
+
// See LICENSE.txt for license information.
|
|
3
|
+
/**
|
|
4
|
+
* Bootstrap command — takes a project with an Understand-Anything knowledge graph
|
|
5
|
+
* and generates route-families.json + initial test stubs.
|
|
6
|
+
*/
|
|
7
|
+
import { existsSync, mkdirSync, writeFileSync } from 'fs';
|
|
8
|
+
import { join, resolve } from 'path';
|
|
9
|
+
import { logger, LogLevel } from '../../logger.js';
|
|
10
|
+
import { loadKnowledgeGraph, classifyProjectType, transformKGToFamilies } from '../../knowledge/kg_bridge.js';
|
|
11
|
+
import { serializeManifest } from '../../knowledge/route_families.js';
|
|
12
|
+
import { detectFramework, detectTestMode } from '../../adapters/framework_adapter.js';
|
|
13
|
+
import { resolveGenerationProfile } from '../../prompts/generation_profile.js';
|
|
14
|
+
class BootstrapError extends Error {
|
|
15
|
+
constructor(message) {
|
|
16
|
+
super(message);
|
|
17
|
+
this.name = 'BootstrapError';
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export async function runBootstrapCommand(args) {
|
|
21
|
+
const projectRoot = resolve(args.path || '.');
|
|
22
|
+
if (args.verbose)
|
|
23
|
+
logger.setLevel(LogLevel.DEBUG);
|
|
24
|
+
if (args.jsonOutput)
|
|
25
|
+
logger.setJsonMode(true);
|
|
26
|
+
logger.info('impact-gate bootstrap');
|
|
27
|
+
logger.info('=======================');
|
|
28
|
+
// ---------- Step 1: Check for knowledge graph ----------
|
|
29
|
+
const kgPath = args.bootstrapKgPath
|
|
30
|
+
? resolve(args.bootstrapKgPath)
|
|
31
|
+
: join(projectRoot, '.understand-anything', 'knowledge-graph.json');
|
|
32
|
+
if (!existsSync(kgPath)) {
|
|
33
|
+
throw new BootstrapError(`Knowledge graph not found at: ${kgPath}\n\n` +
|
|
34
|
+
'To bootstrap, first generate a knowledge graph for your project:\n' +
|
|
35
|
+
' 1. Install Understand-Anything: npm install -g understand-anything\n' +
|
|
36
|
+
' 2. Run: understand-anything analyze .\n' +
|
|
37
|
+
' 3. Then run: impact-gate bootstrap\n\n' +
|
|
38
|
+
'Or provide a path: impact-gate bootstrap --kg-path /path/to/knowledge-graph.json');
|
|
39
|
+
}
|
|
40
|
+
// ---------- Step 2: Load KG and classify ----------
|
|
41
|
+
logger.info('Loading knowledge graph...');
|
|
42
|
+
const kg = loadKnowledgeGraph(projectRoot, args.bootstrapKgPath ? kgPath : undefined);
|
|
43
|
+
if (!kg) {
|
|
44
|
+
throw new BootstrapError('Failed to load knowledge graph. Ensure it is valid JSON with nodes and edges arrays.');
|
|
45
|
+
}
|
|
46
|
+
const projectType = classifyProjectType(kg);
|
|
47
|
+
logger.info(`Project: ${kg.project.name || '(unnamed)'}`);
|
|
48
|
+
logger.info(`Type: ${projectType}`);
|
|
49
|
+
logger.info(`Frameworks: ${kg.project.frameworks.join(', ')}`);
|
|
50
|
+
logger.info(`Languages: ${kg.project.languages.join(', ')}`);
|
|
51
|
+
logger.info(`Nodes: ${kg.nodes.length}, Edges: ${kg.edges.length}`);
|
|
52
|
+
// ---------- Step 3: Transform KG to route families ----------
|
|
53
|
+
logger.info('');
|
|
54
|
+
logger.info('Generating route families from knowledge graph...');
|
|
55
|
+
const manifest = transformKGToFamilies(kg);
|
|
56
|
+
const maxFamilies = args.bootstrapMaxFamilies || 50;
|
|
57
|
+
if (manifest.families.length > maxFamilies) {
|
|
58
|
+
logger.info(`Limiting to top ${maxFamilies} families (of ${manifest.families.length} discovered). Use --max-families to adjust.`);
|
|
59
|
+
manifest.families = manifest.families.slice(0, maxFamilies);
|
|
60
|
+
}
|
|
61
|
+
const p0Count = manifest.families.filter((f) => f.priority === 'P0').length;
|
|
62
|
+
const p1Count = manifest.families.filter((f) => f.priority === 'P1').length;
|
|
63
|
+
const p2Count = manifest.families.filter((f) => f.priority === 'P2').length;
|
|
64
|
+
logger.info(`Discovered ${manifest.families.length} families: ${p0Count} P0, ${p1Count} P1, ${p2Count} P2`);
|
|
65
|
+
// ---------- Step 4: Detect/scaffold test framework ----------
|
|
66
|
+
const framework = detectFramework(projectRoot);
|
|
67
|
+
const testMode = args.bootstrapTestMode || detectTestMode(projectRoot, kg);
|
|
68
|
+
const profile = resolveGenerationProfile({ profile: args.profile, testMode }, kg);
|
|
69
|
+
logger.info(`Test framework: ${framework.name}`);
|
|
70
|
+
logger.info(`Test mode: ${testMode}`);
|
|
71
|
+
logger.info(`Generation profile: ${profile.projectName} (${profile.testFramework})`);
|
|
72
|
+
// ---------- Step 5: Write route-families.json ----------
|
|
73
|
+
const outputDir = join(projectRoot, '.e2e-ai-agents');
|
|
74
|
+
const outputPath = join(outputDir, 'route-families.json');
|
|
75
|
+
if (args.dryRun) {
|
|
76
|
+
logger.info('');
|
|
77
|
+
logger.info('Dry run — proposed manifest:');
|
|
78
|
+
console.log(serializeManifest(manifest));
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
if (!existsSync(outputDir)) {
|
|
82
|
+
mkdirSync(outputDir, { recursive: true });
|
|
83
|
+
}
|
|
84
|
+
writeFileSync(outputPath, serializeManifest(manifest), 'utf-8');
|
|
85
|
+
logger.info(`Wrote ${outputPath}`);
|
|
86
|
+
}
|
|
87
|
+
// ---------- Step 6: Summary and next steps ----------
|
|
88
|
+
logger.info('');
|
|
89
|
+
logger.info('Bootstrap complete!');
|
|
90
|
+
logger.info('');
|
|
91
|
+
logger.info('Route families summary:');
|
|
92
|
+
for (const family of manifest.families.slice(0, 15)) {
|
|
93
|
+
const endpoints = family.apiEndpoints?.length || 0;
|
|
94
|
+
const endpointSuffix = endpoints > 0 ? ` (${endpoints} API endpoints)` : '';
|
|
95
|
+
logger.info(` ${family.priority || 'P2'} ${family.id}: ${family.routes.join(', ')}${endpointSuffix}`);
|
|
96
|
+
}
|
|
97
|
+
if (manifest.families.length > 15) {
|
|
98
|
+
logger.info(` ... and ${manifest.families.length - 15} more`);
|
|
99
|
+
}
|
|
100
|
+
logger.info('');
|
|
101
|
+
logger.info('Next steps:');
|
|
102
|
+
logger.info(' 1. Review and refine .e2e-ai-agents/route-families.json');
|
|
103
|
+
logger.info(' 2. Run `impact-gate train --enrich` to add LLM-enriched metadata');
|
|
104
|
+
logger.info(' 3. Run `impact-gate plan` to see what tests are needed');
|
|
105
|
+
logger.info(' 4. Run `impact-gate generate` to create test stubs');
|
|
106
|
+
}
|