@yasserkhanorg/e2e-agents 1.8.5 → 1.10.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/README.md +95 -8
- 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/config.d.ts +1 -1
- package/dist/agent/config.d.ts.map +1 -1
- package/dist/agent/git.d.ts +1 -0
- package/dist/agent/git.d.ts.map +1 -1
- package/dist/agent/git.js +3 -0
- package/dist/agentic/fix_loop.d.ts.map +1 -1
- package/dist/agentic/fix_loop.js +5 -4
- package/dist/agentic/runner.d.ts +2 -0
- package/dist/agentic/runner.d.ts.map +1 -1
- package/dist/agentic/runner.js +15 -12
- package/dist/agents/cross-impact.d.ts.map +1 -1
- package/dist/agents/cross-impact.js +6 -1
- package/dist/agents/executor.d.ts.map +1 -1
- package/dist/agents/executor.js +6 -1
- package/dist/agents/strategist.d.ts.map +1 -1
- package/dist/agents/strategist.js +6 -1
- package/dist/agents/test-designer.d.ts.map +1 -1
- package/dist/agents/test-designer.js +6 -1
- package/dist/anthropic_provider.d.ts.map +1 -1
- package/dist/anthropic_provider.js +1 -0
- package/dist/base_provider.d.ts +56 -0
- package/dist/base_provider.d.ts.map +1 -1
- package/dist/base_provider.js +123 -1
- 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 +45 -0
- package/dist/cache/cached_provider.d.ts.map +1 -0
- package/dist/cache/cached_provider.js +88 -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/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.map +1 -1
- package/dist/cli/commands/crew.js +118 -1
- 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 +86 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +7 -62
- package/dist/cli/commands/train.d.ts.map +1 -1
- package/dist/cli/commands/train.js +16 -21
- package/dist/cli/defaults.d.ts +35 -0
- package/dist/cli/defaults.d.ts.map +1 -0
- package/dist/cli/defaults.js +125 -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.map +1 -1
- package/dist/cli/parse_args.js +24 -2
- package/dist/cli/types.d.ts +7 -1
- package/dist/cli/types.d.ts.map +1 -1
- package/dist/cli.js +47 -2
- package/dist/crew/context.d.ts +15 -0
- package/dist/crew/context.d.ts.map +1 -1
- package/dist/crew/orchestrator.d.ts +14 -0
- package/dist/crew/orchestrator.d.ts.map +1 -1
- package/dist/crew/orchestrator.js +162 -4
- package/dist/crew/protocol.d.ts +13 -0
- package/dist/crew/protocol.d.ts.map +1 -1
- package/dist/crew/provider.d.ts +15 -1
- package/dist/crew/provider.d.ts.map +1 -1
- package/dist/crew/provider.js +24 -4
- package/dist/custom_provider.d.ts.map +1 -1
- package/dist/custom_provider.js +1 -0
- package/dist/engine/diff_loader.d.ts.map +1 -1
- package/dist/engine/diff_loader.js +3 -14
- package/dist/engine/impact_engine.d.ts.map +1 -1
- package/dist/engine/impact_engine.js +9 -23
- 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/git.js +3 -1
- package/dist/esm/agentic/fix_loop.js +5 -4
- package/dist/esm/agentic/runner.js +15 -12
- package/dist/esm/agents/cross-impact.js +6 -1
- package/dist/esm/agents/executor.js +6 -1
- package/dist/esm/agents/strategist.js +6 -1
- package/dist/esm/agents/test-designer.js +6 -1
- package/dist/esm/anthropic_provider.js +1 -0
- package/dist/esm/base_provider.js +121 -0
- package/dist/esm/budget_ledger.js +58 -0
- package/dist/esm/cache/cached_provider.js +82 -0
- package/dist/esm/cache/response_cache.js +140 -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 +118 -1
- package/dist/esm/cli/commands/gate.js +83 -0
- package/dist/esm/cli/commands/init.js +3 -58
- package/dist/esm/cli/commands/train.js +16 -21
- package/dist/esm/cli/defaults.js +118 -0
- package/dist/esm/cli/errors.js +52 -0
- package/dist/esm/cli/parse_args.js +24 -2
- package/dist/esm/cli.js +47 -2
- package/dist/esm/crew/orchestrator.js +162 -4
- package/dist/esm/crew/provider.js +24 -4
- package/dist/esm/custom_provider.js +1 -0
- package/dist/esm/engine/diff_loader.js +1 -12
- package/dist/esm/engine/impact_engine.js +9 -23
- package/dist/esm/index.js +21 -0
- package/dist/esm/knowledge/api_surface.js +265 -34
- package/dist/esm/knowledge/cluster_utils.js +60 -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 +119 -0
- package/dist/esm/mcp-server.js +2 -4
- package/dist/esm/metrics/prometheus.js +149 -0
- package/dist/esm/model_router.js +59 -0
- package/dist/esm/ollama_provider.js +1 -0
- package/dist/esm/openai_provider.js +1 -0
- package/dist/esm/pipeline/orchestrator.js +6 -12
- package/dist/esm/pipeline/stage0_preprocess.js +12 -19
- package/dist/esm/pipeline/stage1_impact.js +19 -3
- package/dist/esm/pipeline/stage2_coverage.js +29 -7
- package/dist/esm/pipeline/stage3_generation.js +21 -1
- package/dist/esm/progress.js +112 -0
- package/dist/esm/prompts/coverage.js +17 -24
- package/dist/esm/prompts/cross-impact.js +3 -21
- package/dist/esm/prompts/generation.js +201 -45
- package/dist/esm/prompts/generation_profile.js +147 -0
- package/dist/esm/prompts/heal.js +33 -15
- package/dist/esm/prompts/impact.js +3 -22
- package/dist/esm/prompts/json_extract.js +36 -0
- package/dist/esm/prompts/strategist.js +2 -20
- package/dist/esm/prompts/test-designer.js +6 -21
- package/dist/esm/provider_factory.js +6 -4
- package/dist/esm/reporters/junit.js +86 -0
- package/dist/esm/reporters/reporter.js +3 -0
- package/dist/esm/reporters/sarif.js +131 -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/kg_scanner.js +115 -0
- package/dist/esm/training/scanner.js +27 -34
- package/dist/esm/validation/guardrails.js +5 -0
- package/dist/esm/version.js +33 -0
- package/dist/index.d.ts +21 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +45 -1
- package/dist/knowledge/api_surface.d.ts +12 -0
- package/dist/knowledge/api_surface.d.ts.map +1 -1
- package/dist/knowledge/api_surface.js +268 -34
- 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/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 +29 -0
- package/dist/knowledge/route_families.d.ts.map +1 -1
- package/dist/knowledge/route_families.js +122 -0
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +2 -4
- 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.map +1 -1
- package/dist/ollama_provider.js +1 -0
- package/dist/openai_provider.d.ts.map +1 -1
- package/dist/openai_provider.js +1 -0
- package/dist/pipeline/orchestrator.d.ts +2 -0
- package/dist/pipeline/orchestrator.d.ts.map +1 -1
- package/dist/pipeline/orchestrator.js +6 -12
- package/dist/pipeline/stage0_preprocess.d.ts.map +1 -1
- package/dist/pipeline/stage0_preprocess.js +11 -18
- package/dist/pipeline/stage1_impact.d.ts +1 -1
- package/dist/pipeline/stage1_impact.d.ts.map +1 -1
- package/dist/pipeline/stage1_impact.js +18 -2
- package/dist/pipeline/stage2_coverage.d.ts +2 -0
- package/dist/pipeline/stage2_coverage.d.ts.map +1 -1
- package/dist/pipeline/stage2_coverage.js +29 -7
- package/dist/pipeline/stage3_generation.d.ts +2 -0
- package/dist/pipeline/stage3_generation.d.ts.map +1 -1
- package/dist/pipeline/stage3_generation.js +21 -1
- package/dist/pipeline/stage4_heal.d.ts +2 -0
- package/dist/pipeline/stage4_heal.d.ts.map +1 -1
- 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 +2 -0
- package/dist/prompts/coverage.d.ts.map +1 -1
- package/dist/prompts/coverage.js +17 -24
- package/dist/prompts/cross-impact.d.ts +1 -0
- package/dist/prompts/cross-impact.d.ts.map +1 -1
- package/dist/prompts/cross-impact.js +3 -21
- package/dist/prompts/generation.d.ts +4 -2
- package/dist/prompts/generation.d.ts.map +1 -1
- package/dist/prompts/generation.js +201 -45
- 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 +3 -1
- package/dist/prompts/heal.d.ts.map +1 -1
- package/dist/prompts/heal.js +33 -15
- package/dist/prompts/impact.d.ts +1 -0
- package/dist/prompts/impact.d.ts.map +1 -1
- package/dist/prompts/impact.js +3 -22
- 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.map +1 -1
- package/dist/prompts/strategist.js +2 -20
- package/dist/prompts/test-designer.d.ts +2 -0
- package/dist/prompts/test-designer.d.ts.map +1 -1
- package/dist/prompts/test-designer.js +6 -21
- package/dist/provider_factory.d.ts.map +1 -1
- package/dist/provider_factory.js +6 -4
- 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 +134 -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/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/scanner.d.ts +7 -2
- package/dist/training/scanner.d.ts.map +1 -1
- package/dist/training/scanner.js +27 -34
- package/dist/validation/guardrails.d.ts +2 -0
- package/dist/validation/guardrails.d.ts.map +1 -1
- package/dist/validation/guardrails.js +5 -0
- package/dist/validation/output_schema.d.ts +3 -0
- package/dist/validation/output_schema.d.ts.map +1 -1
- package/dist/version.d.ts +6 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +36 -0
- package/package.json +7 -2
- package/schemas/route-families.schema.json +31 -1
|
@@ -34,10 +34,11 @@ function loadFileSnippet(appPath, filePath) {
|
|
|
34
34
|
}
|
|
35
35
|
function preprocess(changedFiles, config) {
|
|
36
36
|
const warnings = [];
|
|
37
|
-
// Load route family manifest
|
|
38
|
-
|
|
37
|
+
// Load route family manifest, fall back to heuristic families
|
|
38
|
+
let manifest = (0, route_families_js_1.loadRouteFamilyManifest)(config.testsRoot, config.routeFamilies);
|
|
39
39
|
if (!manifest) {
|
|
40
|
-
|
|
40
|
+
manifest = (0, route_families_js_1.buildHeuristicFamilies)(changedFiles, config.testsRoot);
|
|
41
|
+
warnings.push('Route family manifest not found. Using directory-based heuristics (lower accuracy).', 'Tip: Run `e2e-ai-agents train` to generate a proper manifest.');
|
|
41
42
|
}
|
|
42
43
|
// Load API surface catalog
|
|
43
44
|
const apiSurface = (0, api_surface_js_1.loadOrBuildApiSurface)(config.testsRoot, config.apiSurface);
|
|
@@ -49,21 +50,13 @@ function preprocess(changedFiles, config) {
|
|
|
49
50
|
// Load context documents
|
|
50
51
|
const context = (0, context_loader_js_1.loadContextDocuments)(config.testsRoot, config.appPath);
|
|
51
52
|
warnings.push(...context.warnings);
|
|
52
|
-
// Bind files to families
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
.map((fb) => fb.file);
|
|
60
|
-
if (unboundFiles.length > 0) {
|
|
61
|
-
warnings.push(`${unboundFiles.length} changed file(s) did not match any route family: ${unboundFiles.slice(0, 5).join(', ')}${unboundFiles.length > 5 ? '...' : ''}`);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
else {
|
|
65
|
-
fileBindings = changedFiles.map((f) => ({ file: f, bindings: [] }));
|
|
66
|
-
unboundFiles = changedFiles;
|
|
53
|
+
// Bind files to families (manifest is always non-null now — either real or heuristic)
|
|
54
|
+
const fileBindings = (0, route_families_js_1.bindFilesToFamilies)(changedFiles, manifest);
|
|
55
|
+
const unboundFiles = fileBindings
|
|
56
|
+
.filter((fb) => fb.bindings.length === 0)
|
|
57
|
+
.map((fb) => fb.file);
|
|
58
|
+
if (unboundFiles.length > 0) {
|
|
59
|
+
warnings.push(`${unboundFiles.length} changed file(s) did not match any route family: ${unboundFiles.slice(0, 5).join(', ')}${unboundFiles.length > 5 ? '...' : ''}`);
|
|
67
60
|
}
|
|
68
61
|
// Group files by family+feature
|
|
69
62
|
const groupMap = new Map();
|
|
@@ -15,5 +15,5 @@ export interface ImpactResult {
|
|
|
15
15
|
warnings: string[];
|
|
16
16
|
providerName: string;
|
|
17
17
|
}
|
|
18
|
-
export declare function runImpactStage(familyGroups: FamilyGroup[], manifest: RouteFamilyManifest | null, specIndex: SpecIndex, apiSurface: ApiSurfaceCatalog, context: LoadedContext, config: ImpactConfig): Promise<ImpactResult>;
|
|
18
|
+
export declare function runImpactStage(familyGroups: FamilyGroup[], manifest: RouteFamilyManifest | null, specIndex: SpecIndex, apiSurface: ApiSurfaceCatalog, context: LoadedContext, config: ImpactConfig, testsRoot?: string): Promise<ImpactResult>;
|
|
19
19
|
//# sourceMappingURL=stage1_impact.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stage1_impact.d.ts","sourceRoot":"","sources":["../../src/pipeline/stage1_impact.ts"],"names":[],"mappings":"AAOA,OAAO,
|
|
1
|
+
{"version":3,"file":"stage1_impact.d.ts","sourceRoot":"","sources":["../../src/pipeline/stage1_impact.ts"],"names":[],"mappings":"AAOA,OAAO,EAAgD,KAAK,mBAAmB,EAAC,MAAM,gCAAgC,CAAC;AAEvH,OAAO,EAAoB,KAAK,SAAS,EAAC,MAAM,4BAA4B,CAAC;AAC7E,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,6BAA6B,CAAC;AACnE,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,gCAAgC,CAAC;AAClE,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,EAAC,YAAY,EAA+B,MAAM,gCAAgC,CAAC;AAG/F,MAAM,WAAW,YAAY;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IACzB,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACxB;AAgBD,wBAAsB,cAAc,CAChC,YAAY,EAAE,WAAW,EAAE,EAC3B,QAAQ,EAAE,mBAAmB,GAAG,IAAI,EACpC,SAAS,EAAE,SAAS,EACpB,UAAU,EAAE,iBAAiB,EAC7B,OAAO,EAAE,aAAa,EACtB,MAAM,EAAE,YAAY,EACpB,SAAS,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,YAAY,CAAC,CAqIvB"}
|
|
@@ -7,6 +7,7 @@ const provider_factory_js_1 = require("../provider_factory.js");
|
|
|
7
7
|
const impact_js_1 = require("../prompts/impact.js");
|
|
8
8
|
const context_loader_js_1 = require("../knowledge/context_loader.js");
|
|
9
9
|
const route_families_js_1 = require("../knowledge/route_families.js");
|
|
10
|
+
const failure_history_js_1 = require("../knowledge/failure_history.js");
|
|
10
11
|
const spec_index_js_1 = require("../knowledge/spec_index.js");
|
|
11
12
|
const guardrails_js_1 = require("../validation/guardrails.js");
|
|
12
13
|
function normalizePriority(value) {
|
|
@@ -21,7 +22,7 @@ async function getProvider(config) {
|
|
|
21
22
|
}
|
|
22
23
|
return provider_factory_js_1.LLMProviderFactory.createFromEnv();
|
|
23
24
|
}
|
|
24
|
-
async function runImpactStage(familyGroups, manifest, specIndex, apiSurface, context, config) {
|
|
25
|
+
async function runImpactStage(familyGroups, manifest, specIndex, apiSurface, context, config, testsRoot) {
|
|
25
26
|
const warnings = [];
|
|
26
27
|
const allDecisions = [];
|
|
27
28
|
if (familyGroups.length === 0) {
|
|
@@ -38,6 +39,8 @@ async function runImpactStage(familyGroups, manifest, specIndex, apiSurface, con
|
|
|
38
39
|
return { decisions: [], warnings, providerName: 'none' };
|
|
39
40
|
}
|
|
40
41
|
const contextBlock = (0, context_loader_js_1.formatContextForPrompt)(context);
|
|
42
|
+
// Load historical failure correlations for confidence boosting
|
|
43
|
+
const failureHistory = testsRoot ? (0, failure_history_js_1.loadFailureHistory)(testsRoot) : null;
|
|
41
44
|
for (const group of familyGroups) {
|
|
42
45
|
const family = manifest ? (0, route_families_js_1.getFamilyById)(manifest, group.familyId) : null;
|
|
43
46
|
if (!family) {
|
|
@@ -86,15 +89,27 @@ async function runImpactStage(familyGroups, manifest, specIndex, apiSurface, con
|
|
|
86
89
|
if (!flow.id || !flow.changedFiles || !Array.isArray(flow.changedFiles)) {
|
|
87
90
|
continue;
|
|
88
91
|
}
|
|
92
|
+
// Compute confidence with optional historical failure boost
|
|
93
|
+
const changedFilesList = Array.isArray(flow.changedFiles)
|
|
94
|
+
? flow.changedFiles.filter((f) => typeof f === 'string')
|
|
95
|
+
: [];
|
|
96
|
+
const historyBoost = failureHistory
|
|
97
|
+
? Math.max(...changedFilesList.map((f) => (0, failure_history_js_1.getConfidenceBoost)(failureHistory, f)), 0)
|
|
98
|
+
: 0;
|
|
89
99
|
const confidence = typeof flow.confidence === 'number'
|
|
90
|
-
? Math.
|
|
100
|
+
? Math.min(100, Math.max(0, flow.confidence) + historyBoost)
|
|
91
101
|
: (0, guardrails_js_1.computeConfidence)({
|
|
92
102
|
hasRouteFamily: true,
|
|
93
103
|
hasSpecificRoute: Boolean(flow.route),
|
|
94
104
|
hasPageObject: Boolean(flow.pageObjects && flow.pageObjects.length > 0),
|
|
95
105
|
hasUserAction: Boolean(flow.userActions && flow.userActions.length > 0),
|
|
96
106
|
hasExistingSpecCited: false,
|
|
107
|
+
historyBoost,
|
|
97
108
|
});
|
|
109
|
+
// Resolve assertion patterns from manifest for this flow's family/feature
|
|
110
|
+
const assertionPatterns = manifest
|
|
111
|
+
? (0, route_families_js_1.getAssertionPatternsForBinding)(manifest, { family: group.familyId, feature: group.featureId })
|
|
112
|
+
: [];
|
|
98
113
|
const decision = {
|
|
99
114
|
flowId: flow.id,
|
|
100
115
|
flowName: flow.name || flow.id,
|
|
@@ -110,6 +125,7 @@ async function runImpactStage(familyGroups, manifest, specIndex, apiSurface, con
|
|
|
110
125
|
blockingReason: (0, guardrails_js_1.shouldForceCannotDetermine)(confidence) ? 'Confidence too low to determine action.' : undefined,
|
|
111
126
|
priority: normalizePriority(flow.priority),
|
|
112
127
|
userActions: Array.isArray(flow.userActions) ? flow.userActions.filter((a) => typeof a === 'string') : [],
|
|
128
|
+
assertionPatterns: assertionPatterns.length > 0 ? assertionPatterns : undefined,
|
|
113
129
|
};
|
|
114
130
|
allDecisions.push(decision);
|
|
115
131
|
}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { type LoadedContext } from '../knowledge/context_loader.js';
|
|
2
2
|
import { type SpecIndex } from '../knowledge/spec_index.js';
|
|
3
3
|
import type { FlowDecision } from '../validation/output_schema.js';
|
|
4
|
+
import type { GenerationProfile } from '../prompts/generation_profile.js';
|
|
4
5
|
export interface CoverageConfig {
|
|
5
6
|
provider?: string;
|
|
6
7
|
maxTokens?: number;
|
|
7
8
|
temperature?: number;
|
|
8
9
|
timeout?: number;
|
|
9
10
|
maxSpecContentChars?: number;
|
|
11
|
+
profile?: GenerationProfile;
|
|
10
12
|
}
|
|
11
13
|
export interface CoverageResult {
|
|
12
14
|
decisions: FlowDecision[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stage2_coverage.d.ts","sourceRoot":"","sources":["../../src/pipeline/stage2_coverage.ts"],"names":[],"mappings":"AAMA,OAAO,EAA8C,KAAK,aAAa,EAAC,MAAM,gCAAgC,CAAC;AAC/G,OAAO,EAAoB,KAAK,SAAS,EAAC,MAAM,4BAA4B,CAAC;AAC7E,OAAO,KAAK,EAAC,YAAY,EAA4B,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"stage2_coverage.d.ts","sourceRoot":"","sources":["../../src/pipeline/stage2_coverage.ts"],"names":[],"mappings":"AAMA,OAAO,EAA8C,KAAK,aAAa,EAAC,MAAM,gCAAgC,CAAC;AAC/G,OAAO,EAAoB,KAAK,SAAS,EAAC,MAAM,4BAA4B,CAAC;AAC7E,OAAO,KAAK,EAAC,YAAY,EAA4B,MAAM,gCAAgC,CAAC;AAC5F,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,kCAAkC,CAAC;AAExE,MAAM,WAAW,cAAc;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,OAAO,CAAC,EAAE,iBAAiB,CAAC;CAC/B;AAED,MAAM,WAAW,cAAc;IAC3B,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACxB;AAYD,wBAAsB,gBAAgB,CAClC,SAAS,EAAE,YAAY,EAAE,EACzB,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,aAAa,EACtB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,cAAc,GACvB,OAAO,CAAC,cAAc,CAAC,CAyJzB"}
|
|
@@ -46,13 +46,26 @@ async function runCoverageStage(decisions, specIndex, context, testsRoot, config
|
|
|
46
46
|
for (const [familyId, familyDecisions] of byFamily) {
|
|
47
47
|
// Gather relevant specs
|
|
48
48
|
const specs = (0, spec_index_js_1.getSpecsForFamily)(specIndex, familyId);
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
// Two-tier approach: send all spec titles (compact), full content for top matches only
|
|
50
|
+
const allSpecSummaries = specs.map((s) => ({
|
|
51
|
+
relativePath: s.relativePath,
|
|
52
|
+
testTitles: s.testTitles,
|
|
53
|
+
}));
|
|
54
|
+
// Load full content with a total budget of 200K chars (~50K tokens) to avoid blowing context windows
|
|
55
|
+
const MAX_TOTAL_SPEC_CHARS = 200000;
|
|
56
|
+
let totalSpecChars = 0;
|
|
57
|
+
const specsWithContent = [];
|
|
58
|
+
for (const s of specs) {
|
|
59
|
+
if (specsWithContent.length >= 30)
|
|
60
|
+
break;
|
|
51
61
|
const content = (0, context_loader_js_1.loadSpecFileContent)(testsRoot, s.relativePath, maxSpecChars);
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
62
|
+
if (!content)
|
|
63
|
+
continue;
|
|
64
|
+
if (totalSpecChars + content.length > MAX_TOTAL_SPEC_CHARS)
|
|
65
|
+
break;
|
|
66
|
+
totalSpecChars += content.length;
|
|
67
|
+
specsWithContent.push({ relativePath: s.relativePath, content, testTitles: s.testTitles });
|
|
68
|
+
}
|
|
56
69
|
if (specsWithContent.length === 0) {
|
|
57
70
|
// No specs to evaluate — mark all as create_spec
|
|
58
71
|
for (const d of familyDecisions) {
|
|
@@ -73,10 +86,19 @@ async function runCoverageStage(decisions, specIndex, context, testsRoot, config
|
|
|
73
86
|
evidence: d.evidence,
|
|
74
87
|
priority: d.priority,
|
|
75
88
|
}));
|
|
89
|
+
// Include titles-only summaries for specs beyond the content limit
|
|
90
|
+
const extraSummaries = allSpecSummaries
|
|
91
|
+
.slice(specsWithContent.length)
|
|
92
|
+
.map((s) => ` - ${s.relativePath}: ${s.testTitles.join(', ')}`)
|
|
93
|
+
.join('\n');
|
|
94
|
+
const extraContext = extraSummaries
|
|
95
|
+
? `\nADDITIONAL SPECS (titles only, no content loaded):\n${extraSummaries}\n`
|
|
96
|
+
: '';
|
|
76
97
|
const prompt = (0, coverage_js_1.buildCoveragePrompt)({
|
|
77
98
|
flows,
|
|
78
99
|
specs: specsWithContent,
|
|
79
|
-
contextBlock,
|
|
100
|
+
contextBlock: contextBlock + extraContext,
|
|
101
|
+
profile: config.profile,
|
|
80
102
|
});
|
|
81
103
|
try {
|
|
82
104
|
const response = await provider.generateText(prompt, {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { loadSpecFileContent } from '../knowledge/context_loader.js';
|
|
2
2
|
import type { FlowDecision } from '../validation/output_schema.js';
|
|
3
3
|
import type { ApiSurfaceCatalog } from '../knowledge/api_surface.js';
|
|
4
|
+
import type { GenerationProfile } from '../prompts/generation_profile.js';
|
|
4
5
|
export interface GenerationConfig {
|
|
5
6
|
provider?: string;
|
|
6
7
|
maxTokens?: number;
|
|
@@ -12,6 +13,7 @@ export interface GenerationConfig {
|
|
|
12
13
|
warnOnHallucinations?: boolean;
|
|
13
14
|
/** When true, only log what would be written without actually writing files */
|
|
14
15
|
dryRun?: boolean;
|
|
16
|
+
profile?: GenerationProfile;
|
|
15
17
|
}
|
|
16
18
|
export interface GeneratedSpec {
|
|
17
19
|
flowId: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stage3_generation.d.ts","sourceRoot":"","sources":["../../src/pipeline/stage3_generation.ts"],"names":[],"mappings":"AAQA,OAAO,EAAC,mBAAmB,EAAC,MAAM,gCAAgC,CAAC;AAInE,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,gCAAgC,CAAC;AACjE,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"stage3_generation.d.ts","sourceRoot":"","sources":["../../src/pipeline/stage3_generation.ts"],"names":[],"mappings":"AAQA,OAAO,EAAC,mBAAmB,EAAC,MAAM,gCAAgC,CAAC;AAInE,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,gCAAgC,CAAC;AACjE,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,6BAA6B,CAAC;AACnE,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,kCAAkC,CAAC;AAExE,MAAM,WAAW,gBAAgB;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wEAAwE;IACxE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,yEAAyE;IACzE,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,+EAA+E;IAC/E,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,iBAAiB,CAAC;CAC/B;AAED,MAAM,WAAW,aAAa;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,aAAa,GAAG,eAAe,CAAC;IACtC,OAAO,EAAE,OAAO,CAAC;IACjB,qBAAqB,EAAE,MAAM,EAAE,CAAC;IAChC,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,yCAAyC;IACzC,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,gBAAgB;IAC7B,SAAS,EAAE,aAAa,EAAE,CAAC;IAC3B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,sCAAsC;IACtC,cAAc,EAAE,MAAM,CAAC;IACvB,6CAA6C;IAC7C,aAAa,EAAE,MAAM,CAAC;IACtB,sCAAsC;IACtC,WAAW,EAAE,MAAM,CAAC;CACvB;AAyCD,wBAAsB,kBAAkB,CACpC,SAAS,EAAE,YAAY,EAAE,EACzB,UAAU,EAAE,iBAAiB,EAC7B,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,gBAAgB,GACzB,OAAO,CAAC,gBAAgB,CAAC,CA8J3B;AAqHD,OAAO,EAAC,mBAAmB,EAAC,CAAC"}
|
|
@@ -91,6 +91,7 @@ async function runGenerationStage(decisions, apiSurface, testsRoot, config) {
|
|
|
91
91
|
existingSpecContent,
|
|
92
92
|
specPath,
|
|
93
93
|
mode,
|
|
94
|
+
profile: config.profile,
|
|
94
95
|
});
|
|
95
96
|
try {
|
|
96
97
|
const response = await provider.generateText(prompt, {
|
|
@@ -105,10 +106,29 @@ async function runGenerationStage(decisions, apiSurface, testsRoot, config) {
|
|
|
105
106
|
skipped.push(`${decision.flowId}: invalid code returned`);
|
|
106
107
|
continue;
|
|
107
108
|
}
|
|
108
|
-
// Hallucination detection
|
|
109
|
+
// Hallucination detection — block specs with hallucinated methods
|
|
109
110
|
const hallucinationWarnings = (0, generation_js_1.detectHallucinatedMethods)(parsed.code, apiSurface);
|
|
110
111
|
if (hallucinationWarnings.length > 0) {
|
|
111
112
|
warnings.push(`Flow ${decision.flowId}: suspected hallucinated methods: ${hallucinationWarnings.join(', ')}`);
|
|
113
|
+
if (!config.warnOnHallucinations) {
|
|
114
|
+
// Block: move to needs-review instead of writing to specs dir
|
|
115
|
+
if (!dryRun) {
|
|
116
|
+
const reviewDir = (0, path_1.join)(testsRoot, 'generated-needs-review');
|
|
117
|
+
(0, fs_1.mkdirSync)(reviewDir, { recursive: true });
|
|
118
|
+
const safeName = decision.flowId.replace(/[^a-zA-Z0-9_-]/g, '_').toLowerCase();
|
|
119
|
+
const reviewPath = (0, path_1.join)(reviewDir, `${safeName}-${Date.now().toString(36)}.spec.ts`);
|
|
120
|
+
(0, fs_1.writeFileSync)(reviewPath, `${parsed.code}\n`, 'utf-8');
|
|
121
|
+
warnings.push(`Flow ${decision.flowId}: blocked — moved to ${reviewPath}`);
|
|
122
|
+
}
|
|
123
|
+
generated.push({
|
|
124
|
+
flowId: decision.flowId,
|
|
125
|
+
specPath,
|
|
126
|
+
mode,
|
|
127
|
+
written: false,
|
|
128
|
+
hallucinationWarnings,
|
|
129
|
+
});
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
112
132
|
}
|
|
113
133
|
let written = false;
|
|
114
134
|
if (!dryRun) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { PipelineSummary } from '../agent/pipeline.js';
|
|
2
2
|
import type { FlowDecision, FlowDecisionReport } from '../validation/output_schema.js';
|
|
3
3
|
import type { GeneratedSpec } from './stage3_generation.js';
|
|
4
|
+
import type { GenerationProfile } from '../prompts/generation_profile.js';
|
|
4
5
|
export interface HealConfig {
|
|
5
6
|
/** Enable MCP-backed heal via playwright-test-healer agent */
|
|
6
7
|
mcp?: boolean;
|
|
@@ -11,6 +12,7 @@ export interface HealConfig {
|
|
|
11
12
|
dryRun?: boolean;
|
|
12
13
|
/** Output directory for healed/re-generated specs */
|
|
13
14
|
outputDir?: string;
|
|
15
|
+
profile?: GenerationProfile;
|
|
14
16
|
}
|
|
15
17
|
export interface HealTarget {
|
|
16
18
|
specPath: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stage4_heal.d.ts","sourceRoot":"","sources":["../../src/pipeline/stage4_heal.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAiB,eAAe,EAAC,MAAM,sBAAsB,CAAC;AAI1E,OAAO,KAAK,EAAC,YAAY,EAAE,kBAAkB,EAAC,MAAM,gCAAgC,CAAC;AACrF,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"stage4_heal.d.ts","sourceRoot":"","sources":["../../src/pipeline/stage4_heal.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAiB,eAAe,EAAC,MAAM,sBAAsB,CAAC;AAI1E,OAAO,KAAK,EAAC,YAAY,EAAE,kBAAkB,EAAC,MAAM,gCAAgC,CAAC;AACrF,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,wBAAwB,CAAC;AAC1D,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,kCAAkC,CAAC;AAExE,MAAM,WAAW,UAAU;IACvB,8DAA8D;IAC9D,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,qDAAqD;IACrD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,iBAAiB,CAAC;CAC/B;AAED,MAAM,WAAW,UAAU;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,QAAQ,GAAG,OAAO,CAAC;IAC3B,yDAAyD;IACzD,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,UAAU;IACvB,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,OAAO,EAAE,eAAe,CAAC;IACzB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,iDAAiD;IACjD,YAAY,EAAE,MAAM,CAAC;IACrB,+DAA+D;IAC/D,WAAW,EAAE,MAAM,CAAC;CACvB;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAC9B,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE;IACL,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,cAAc,CAAC,EAAE,aAAa,EAAE,CAAC;IACjC,eAAe,CAAC,EAAE,KAAK,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,QAAQ,GAAG,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAC,CAAC,CAAC;CAC5F,EACD,SAAS,EAAE,YAAY,EAAE,GAC1B,UAAU,EAAE,CAqDd;AAsGD,wBAAsB,YAAY,CAC9B,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,UAAU,EAAE,EACrB,MAAM,EAAE,UAAU,GACnB,OAAO,CAAC,UAAU,CAAC,CA4GrB;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAChC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,kBAAkB,EAC1B,OAAO,EAAE;IACL,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,cAAc,CAAC,EAAE,aAAa,EAAE,CAAC;IACjC,UAAU,CAAC,EAAE,UAAU,CAAC;CAC3B,GACF,OAAO,CAAC,UAAU,CAAC,CAWrB;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAqC7D"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
interface ProgressReporterOptions {
|
|
3
|
+
isTTY?: boolean;
|
|
4
|
+
quiet?: boolean;
|
|
5
|
+
jsonMode?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare class ProgressReporter extends EventEmitter {
|
|
8
|
+
private isTTY;
|
|
9
|
+
private silent;
|
|
10
|
+
private completedAgents;
|
|
11
|
+
private totalAgents;
|
|
12
|
+
private currentPhase;
|
|
13
|
+
constructor(options?: ProgressReporterOptions);
|
|
14
|
+
phaseStart(phase: string, agentCount: number): void;
|
|
15
|
+
agentStart(agent: string, family?: string): void;
|
|
16
|
+
agentComplete(agent: string, family: string | undefined, tokens: number, cost: number, durationMs: number): void;
|
|
17
|
+
phaseComplete(phase: string, elapsedMs: number): void;
|
|
18
|
+
workflowComplete(totalCost: number, totalTokens: number, elapsedMs: number): void;
|
|
19
|
+
private writeLine;
|
|
20
|
+
}
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=progress.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"progress.d.ts","sourceRoot":"","sources":["../src/progress.ts"],"names":[],"mappings":"AAGA,OAAO,EAAC,YAAY,EAAC,MAAM,QAAQ,CAAC;AAEpC,UAAU,uBAAuB;IAC7B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACtB;AA+BD,qBAAa,gBAAiB,SAAQ,YAAY;IAC9C,OAAO,CAAC,KAAK,CAAU;IACvB,OAAO,CAAC,MAAM,CAAU;IACxB,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,YAAY,CAAS;gBAEjB,OAAO,CAAC,EAAE,uBAAuB;IAS7C,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;IAgBnD,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI;IAoBhD,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;IAwBhH,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAarD,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAejF,OAAO,CAAC,SAAS;CAGpB"}
|
package/dist/progress.js
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
3
|
+
// See LICENSE.txt for license information.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.ProgressReporter = void 0;
|
|
6
|
+
const events_1 = require("events");
|
|
7
|
+
class ProgressReporter extends events_1.EventEmitter {
|
|
8
|
+
constructor(options) {
|
|
9
|
+
super();
|
|
10
|
+
this.isTTY = options?.isTTY ?? (process.stdout.isTTY === true);
|
|
11
|
+
this.silent = (options?.quiet ?? false) || (options?.jsonMode ?? false);
|
|
12
|
+
this.completedAgents = 0;
|
|
13
|
+
this.totalAgents = 0;
|
|
14
|
+
this.currentPhase = '';
|
|
15
|
+
}
|
|
16
|
+
phaseStart(phase, agentCount) {
|
|
17
|
+
const payload = { phase, agentCount };
|
|
18
|
+
this.emit('phase-start', payload);
|
|
19
|
+
if (this.silent) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
this.currentPhase = phase;
|
|
23
|
+
this.completedAgents = 0;
|
|
24
|
+
this.totalAgents = agentCount;
|
|
25
|
+
const message = `--- Phase: ${phase} (${agentCount} agent${agentCount !== 1 ? 's' : ''}) ---`;
|
|
26
|
+
this.writeLine(message);
|
|
27
|
+
}
|
|
28
|
+
agentStart(agent, family) {
|
|
29
|
+
const payload = { agent, family };
|
|
30
|
+
this.emit('agent-start', payload);
|
|
31
|
+
if (this.silent) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const familyLabel = family ? ` processing ${family}` : '';
|
|
35
|
+
if (this.isTTY) {
|
|
36
|
+
const progress = `[${this.completedAgents}/${this.totalAgents} agents]`;
|
|
37
|
+
const message = `${progress} ${this.currentPhase}: ${agent}${familyLabel}...`;
|
|
38
|
+
process.stdout.write(`\r${clearLine()}${message}`);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
const message = `[${this.currentPhase}] ${agent} started${familyLabel ? ':' + familyLabel : ''}`;
|
|
42
|
+
this.writeLine(message);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
agentComplete(agent, family, tokens, cost, durationMs) {
|
|
46
|
+
const payload = { agent, family, tokens, cost, durationMs };
|
|
47
|
+
this.emit('agent-complete', payload);
|
|
48
|
+
if (this.silent) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
this.completedAgents++;
|
|
52
|
+
const costStr = formatCost(cost);
|
|
53
|
+
const durationStr = formatDuration(durationMs);
|
|
54
|
+
const tokensStr = formatTokens(tokens);
|
|
55
|
+
const familyLabel = family ? ` ${family}` : '';
|
|
56
|
+
if (this.isTTY) {
|
|
57
|
+
const progress = `[${this.completedAgents}/${this.totalAgents} agents]`;
|
|
58
|
+
const message = `${progress} ${this.currentPhase}: ${agent} complete${familyLabel} (${tokensStr}, ${costStr}, ${durationStr})`;
|
|
59
|
+
process.stdout.write(`\r${clearLine()}${message}\n`);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
const message = `[${this.currentPhase}] ${agent} complete:${familyLabel} (${tokensStr}, ${costStr}, ${durationStr})`;
|
|
63
|
+
this.writeLine(message);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
phaseComplete(phase, elapsedMs) {
|
|
67
|
+
const payload = { phase, elapsedMs };
|
|
68
|
+
this.emit('phase-complete', payload);
|
|
69
|
+
if (this.silent) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const durationStr = formatDuration(elapsedMs);
|
|
73
|
+
const message = `--- Phase ${phase} complete (${durationStr}) ---`;
|
|
74
|
+
this.writeLine(message);
|
|
75
|
+
}
|
|
76
|
+
workflowComplete(totalCost, totalTokens, elapsedMs) {
|
|
77
|
+
const payload = { totalCost, totalTokens, elapsedMs };
|
|
78
|
+
this.emit('workflow-complete', payload);
|
|
79
|
+
if (this.silent) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const costStr = formatCost(totalCost);
|
|
83
|
+
const tokensStr = formatTokens(totalTokens);
|
|
84
|
+
const durationStr = formatDuration(elapsedMs);
|
|
85
|
+
const message = `=== Workflow complete: ${tokensStr}, ${costStr}, ${durationStr} ===`;
|
|
86
|
+
this.writeLine(message);
|
|
87
|
+
}
|
|
88
|
+
writeLine(message) {
|
|
89
|
+
process.stdout.write(message + '\n');
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
exports.ProgressReporter = ProgressReporter;
|
|
93
|
+
function clearLine() {
|
|
94
|
+
return '\x1B[2K';
|
|
95
|
+
}
|
|
96
|
+
function formatCost(cost) {
|
|
97
|
+
return `$${cost.toFixed(2)}`;
|
|
98
|
+
}
|
|
99
|
+
function formatTokens(tokens) {
|
|
100
|
+
if (tokens >= 1000000) {
|
|
101
|
+
return `${(tokens / 1000000).toFixed(1)}M tokens`;
|
|
102
|
+
}
|
|
103
|
+
if (tokens >= 1000) {
|
|
104
|
+
return `${(tokens / 1000).toFixed(0).replace(/\B(?=(\d{3})+(?!\d))/g, ',')} tokens`;
|
|
105
|
+
}
|
|
106
|
+
return `${tokens} tokens`;
|
|
107
|
+
}
|
|
108
|
+
function formatDuration(ms) {
|
|
109
|
+
const seconds = Math.round(ms / 1000);
|
|
110
|
+
if (seconds >= 60) {
|
|
111
|
+
const minutes = Math.floor(seconds / 60);
|
|
112
|
+
const remainingSeconds = seconds % 60;
|
|
113
|
+
return remainingSeconds > 0 ? `${minutes}m${remainingSeconds}s` : `${minutes}m`;
|
|
114
|
+
}
|
|
115
|
+
return `${seconds}s`;
|
|
116
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { GenerationProfile } from './generation_profile.js';
|
|
1
2
|
export interface CoveragePromptFlow {
|
|
2
3
|
flowId: string;
|
|
3
4
|
flowName: string;
|
|
@@ -14,6 +15,7 @@ export interface CoveragePromptContext {
|
|
|
14
15
|
testTitles: string[];
|
|
15
16
|
}>;
|
|
16
17
|
contextBlock: string;
|
|
18
|
+
profile?: GenerationProfile;
|
|
17
19
|
}
|
|
18
20
|
export declare function buildCoveragePrompt(ctx: CoveragePromptContext): string;
|
|
19
21
|
export interface CoverageAgentResponse {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"coverage.d.ts","sourceRoot":"","sources":["../../src/prompts/coverage.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"coverage.d.ts","sourceRoot":"","sources":["../../src/prompts/coverage.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,yBAAyB,CAAC;AAE/D,MAAM,WAAW,kBAAkB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,qBAAqB;IAClC,KAAK,EAAE,kBAAkB,EAAE,CAAC;IAC5B,KAAK,EAAE,KAAK,CAAC;QACT,YAAY,EAAE,MAAM,CAAC;QACrB,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,EAAE,CAAC;KACxB,CAAC,CAAC;IACH,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,iBAAiB,CAAC;CAC/B;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,qBAAqB,GAAG,MAAM,CAmDtE;AAED,MAAM,WAAW,qBAAqB;IAClC,QAAQ,EAAE,KAAK,CAAC;QACZ,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;QACf,aAAa,CAAC,EAAE,KAAK,CAAC;YAClB,IAAI,EAAE,MAAM,CAAC;YACb,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;YACtB,aAAa,CAAC,EAAE,MAAM,CAAC;YACvB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;SAC/B,CAAC,CAAC;QACH,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;QAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,UAAU,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC,CAAC;CACN;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,qBAAqB,GAAG,IAAI,CAMhF"}
|
package/dist/prompts/coverage.js
CHANGED
|
@@ -4,11 +4,13 @@
|
|
|
4
4
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
5
|
exports.buildCoveragePrompt = buildCoveragePrompt;
|
|
6
6
|
exports.parseCoverageResponse = parseCoverageResponse;
|
|
7
|
+
const json_extract_js_1 = require("./json_extract.js");
|
|
8
|
+
const sanitize_js_1 = require("../crew/sanitize.js");
|
|
7
9
|
function buildCoveragePrompt(ctx) {
|
|
8
10
|
const flowsBlock = ctx.flows
|
|
9
11
|
.map((f) => {
|
|
10
|
-
const actions = f.userActions.length > 0 ? f.userActions.join('; ') : 'unknown';
|
|
11
|
-
return `- ${f.flowId} (${f.priority}): ${f.flowName}\n Route: ${f.route}\n User actions: ${actions}\n Evidence: ${f.evidence}`;
|
|
12
|
+
const actions = f.userActions.length > 0 ? f.userActions.map((a) => (0, sanitize_js_1.sanitizeForPrompt)(a)).join('; ') : 'unknown';
|
|
13
|
+
return `- ${f.flowId} (${f.priority}): ${f.flowName}\n Route: ${f.route}\n User actions: ${actions}\n Evidence: ${(0, sanitize_js_1.sanitizeForPrompt)(f.evidence)}`;
|
|
12
14
|
})
|
|
13
15
|
.join('\n\n');
|
|
14
16
|
const specsBlock = ctx.specs
|
|
@@ -17,7 +19,7 @@ function buildCoveragePrompt(ctx) {
|
|
|
17
19
|
})
|
|
18
20
|
.join('\n\n');
|
|
19
21
|
return [
|
|
20
|
-
|
|
22
|
+
`You are evaluating whether existing ${ctx.profile?.projectName || 'Mattermost'} ${ctx.profile?.testFramework || 'Playwright'} E2E tests cover the impacted flows.`,
|
|
21
23
|
'',
|
|
22
24
|
`IMPACTED FLOWS (${ctx.flows.length}):`,
|
|
23
25
|
flowsBlock,
|
|
@@ -40,29 +42,20 @@ function buildCoveragePrompt(ctx) {
|
|
|
40
42
|
' Wrong: "test the new isEditing state"',
|
|
41
43
|
' Right: "test editing a scheduled message while it is in pending state"',
|
|
42
44
|
'- For add_scenarios, specify which existing spec file to extend in targetSpec.',
|
|
43
|
-
|
|
45
|
+
`- For create_spec, suggest a path following ${ctx.profile?.projectName || 'Mattermost'} conventions.`,
|
|
44
46
|
'- Prefer adding scenarios to existing specs over creating new spec files.',
|
|
47
|
+
'',
|
|
48
|
+
'SEMANTIC MATCHING RULES (critical for accuracy):',
|
|
49
|
+
'- A happy-path test does NOT cover the negative/error path of the same feature.',
|
|
50
|
+
' "user can edit post" does NOT cover "user without permission cannot edit post".',
|
|
51
|
+
'- A test for one user role does NOT cover a different role.',
|
|
52
|
+
' "admin can delete channel" does NOT cover "member cannot delete channel".',
|
|
53
|
+
'- A test for creation does NOT cover editing or deletion of the same entity.',
|
|
54
|
+
'- "partial" means: same feature area but different specific scenario.',
|
|
55
|
+
'- "full" means: the exact user action sequence and outcome is tested.',
|
|
56
|
+
'- When in doubt between "full" and "partial", choose "partial".',
|
|
45
57
|
].join('\n');
|
|
46
58
|
}
|
|
47
59
|
function parseCoverageResponse(text) {
|
|
48
|
-
|
|
49
|
-
const candidates = fenced ? [fenced[1], text] : [text];
|
|
50
|
-
for (const candidate of candidates) {
|
|
51
|
-
const start = candidate.indexOf('{');
|
|
52
|
-
const end = candidate.lastIndexOf('}');
|
|
53
|
-
if (start < 0 || end <= start) {
|
|
54
|
-
continue;
|
|
55
|
-
}
|
|
56
|
-
const raw = candidate.slice(start, end + 1);
|
|
57
|
-
try {
|
|
58
|
-
const parsed = JSON.parse(raw);
|
|
59
|
-
if (parsed && Array.isArray(parsed.coverage)) {
|
|
60
|
-
return parsed;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
catch {
|
|
64
|
-
continue;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
return null;
|
|
60
|
+
return (0, json_extract_js_1.extractJsonFromResponse)(text, (obj) => obj != null && typeof obj === 'object' && Array.isArray(obj.coverage));
|
|
68
61
|
}
|
|
@@ -7,6 +7,7 @@ export interface CrossImpactPromptContext {
|
|
|
7
7
|
families: RouteFamily[];
|
|
8
8
|
/** The families directly impacted by changed files */
|
|
9
9
|
directlyImpactedFamilyIds: string[];
|
|
10
|
+
projectName?: string;
|
|
10
11
|
}
|
|
11
12
|
export declare function buildCrossImpactPrompt(ctx: CrossImpactPromptContext): string;
|
|
12
13
|
export interface CrossImpactAgentResponse {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cross-impact.d.ts","sourceRoot":"","sources":["../../src/prompts/cross-impact.ts"],"names":[],"mappings":"AAGA;;GAEG;AAEH,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"cross-impact.d.ts","sourceRoot":"","sources":["../../src/prompts/cross-impact.ts"],"names":[],"mappings":"AAGA;;GAEG;AAEH,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,gCAAgC,CAAC;AAKhE,MAAM,WAAW,wBAAwB;IACrC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,sDAAsD;IACtD,yBAAyB,EAAE,MAAM,EAAE,CAAC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,wBAAwB,GAAG,MAAM,CA+C5E;AAED,MAAM,WAAW,wBAAwB;IACrC,YAAY,EAAE,KAAK,CAAC;QAChB,YAAY,EAAE,MAAM,CAAC;QACrB,cAAc,EAAE,MAAM,CAAC;QACvB,gBAAgB,EAAE,MAAM,CAAC;QACzB,SAAS,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;QAC9C,QAAQ,EAAE,MAAM,CAAC;KACpB,CAAC,CAAC;CACN;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,wBAAwB,GAAG,IAAI,CAMtF"}
|
|
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
5
5
|
exports.buildCrossImpactPrompt = buildCrossImpactPrompt;
|
|
6
6
|
exports.parseCrossImpactResponse = parseCrossImpactResponse;
|
|
7
7
|
const sanitize_js_1 = require("../crew/sanitize.js");
|
|
8
|
+
const json_extract_js_1 = require("./json_extract.js");
|
|
8
9
|
function buildCrossImpactPrompt(ctx) {
|
|
9
10
|
const familiesBlock = ctx.families
|
|
10
11
|
.map((f) => {
|
|
@@ -18,7 +19,7 @@ function buildCrossImpactPrompt(ctx) {
|
|
|
18
19
|
.join('\n');
|
|
19
20
|
const changedBlock = ctx.changedFiles.map((f) => (0, sanitize_js_1.sanitizeForPrompt)(f)).join('\n');
|
|
20
21
|
return [
|
|
21
|
-
|
|
22
|
+
`You are analyzing code changes in ${ctx.projectName || 'Mattermost'} to identify cross-family ripple effects.`,
|
|
22
23
|
'When a change in one route family could affect another family through shared dependencies,',
|
|
23
24
|
'that is a cross-impact.',
|
|
24
25
|
'',
|
|
@@ -52,24 +53,5 @@ function buildCrossImpactPrompt(ctx) {
|
|
|
52
53
|
].join('\n');
|
|
53
54
|
}
|
|
54
55
|
function parseCrossImpactResponse(text) {
|
|
55
|
-
|
|
56
|
-
const candidates = fenced ? [fenced[1], text] : [text];
|
|
57
|
-
for (const candidate of candidates) {
|
|
58
|
-
const start = candidate.indexOf('{');
|
|
59
|
-
const end = candidate.lastIndexOf('}');
|
|
60
|
-
if (start < 0 || end <= start) {
|
|
61
|
-
continue;
|
|
62
|
-
}
|
|
63
|
-
const raw = candidate.slice(start, end + 1);
|
|
64
|
-
try {
|
|
65
|
-
const parsed = JSON.parse(raw);
|
|
66
|
-
if (parsed && Array.isArray(parsed.crossImpacts)) {
|
|
67
|
-
return parsed;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
catch {
|
|
71
|
-
continue;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
return null;
|
|
56
|
+
return (0, json_extract_js_1.extractJsonFromResponse)(text, (obj) => obj != null && typeof obj === 'object' && Array.isArray(obj.crossImpacts));
|
|
75
57
|
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import type { FlowDecision } from '../validation/output_schema.js';
|
|
2
2
|
import type { ApiSurfaceCatalog } from '../knowledge/api_surface.js';
|
|
3
|
+
import type { GenerationProfile } from './generation_profile.js';
|
|
3
4
|
export interface GenerationPromptContext {
|
|
4
5
|
decision: FlowDecision;
|
|
5
6
|
apiSurface: ApiSurfaceCatalog;
|
|
6
7
|
existingSpecContent?: string;
|
|
7
8
|
specPath: string;
|
|
8
9
|
mode: 'create_spec' | 'add_scenarios';
|
|
10
|
+
profile?: GenerationProfile;
|
|
9
11
|
}
|
|
10
12
|
export declare function buildGenerationPrompt(ctx: GenerationPromptContext): string;
|
|
11
13
|
export interface GenerationAgentResponse {
|
|
@@ -14,10 +16,10 @@ export interface GenerationAgentResponse {
|
|
|
14
16
|
mode: 'create_spec' | 'add_scenarios';
|
|
15
17
|
flowId: string;
|
|
16
18
|
}
|
|
17
|
-
export declare function parseGenerationResponse(text: string, expectedPath: string, mode: 'create_spec' | 'add_scenarios', flowId: string): GenerationAgentResponse | null;
|
|
19
|
+
export declare function parseGenerationResponse(text: string, expectedPath: string, mode: 'create_spec' | 'add_scenarios', flowId: string, profile?: GenerationProfile): GenerationAgentResponse | null;
|
|
18
20
|
/**
|
|
19
21
|
* Returns method names that appear in generated code but do not exist in the API surface.
|
|
20
|
-
*
|
|
22
|
+
* Detects all call patterns: await X.Y(), X.Y(), const z = X.Y(), chained calls.
|
|
21
23
|
*/
|
|
22
24
|
export declare function detectHallucinatedMethods(code: string, apiSurface: ApiSurfaceCatalog): string[];
|
|
23
25
|
//# sourceMappingURL=generation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generation.d.ts","sourceRoot":"","sources":["../../src/prompts/generation.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,YAAY,
|
|
1
|
+
{"version":3,"file":"generation.d.ts","sourceRoot":"","sources":["../../src/prompts/generation.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,YAAY,EAAmB,MAAM,gCAAgC,CAAC;AACnF,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,6BAA6B,CAAC;AAGnE,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,yBAAyB,CAAC;AAG/D,MAAM,WAAW,uBAAuB;IACpC,QAAQ,EAAE,YAAY,CAAC;IACvB,UAAU,EAAE,iBAAiB,CAAC;IAC9B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,aAAa,GAAG,eAAe,CAAC;IACtC,OAAO,CAAC,EAAE,iBAAiB,CAAC;CAC/B;AA0CD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,uBAAuB,GAAG,MAAM,CA0H1E;AAgFD,MAAM,WAAW,uBAAuB;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,aAAa,GAAG,eAAe,CAAC;IACtC,MAAM,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,uBAAuB,CACnC,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,aAAa,GAAG,eAAe,EACrC,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,iBAAiB,GAC5B,uBAAuB,GAAG,IAAI,CAuBhC;AAwBD;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,iBAAiB,GAAG,MAAM,EAAE,CAsC/F"}
|