sela-core 1.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/README.md +93 -0
- package/bin/sela.js +3 -0
- package/dist/cli/ErrorHandler.d.ts +10 -0
- package/dist/cli/ErrorHandler.d.ts.map +1 -0
- package/dist/cli/ErrorHandler.js +70 -0
- package/dist/cli/commands/bulk.d.ts +3 -0
- package/dist/cli/commands/bulk.d.ts.map +1 -0
- package/dist/cli/commands/bulk.js +140 -0
- package/dist/cli/commands/find.d.ts +3 -0
- package/dist/cli/commands/find.d.ts.map +1 -0
- package/dist/cli/commands/find.js +51 -0
- package/dist/cli/commands/init.d.ts +3 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +133 -0
- package/dist/cli/commands/list.d.ts +3 -0
- package/dist/cli/commands/list.d.ts.map +1 -0
- package/dist/cli/commands/list.js +56 -0
- package/dist/cli/commands/refactor.d.ts +3 -0
- package/dist/cli/commands/refactor.d.ts.map +1 -0
- package/dist/cli/commands/refactor.js +30 -0
- package/dist/cli/commands/status.d.ts +3 -0
- package/dist/cli/commands/status.d.ts.map +1 -0
- package/dist/cli/commands/status.js +51 -0
- package/dist/cli/commands/sync.d.ts +3 -0
- package/dist/cli/commands/sync.d.ts.map +1 -0
- package/dist/cli/commands/sync.js +123 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +42 -0
- package/dist/cli/ui/DnaTable.d.ts +3 -0
- package/dist/cli/ui/DnaTable.d.ts.map +1 -0
- package/dist/cli/ui/DnaTable.js +70 -0
- package/dist/cli/ui/LocatorPicker.d.ts +8 -0
- package/dist/cli/ui/LocatorPicker.d.ts.map +1 -0
- package/dist/cli/ui/LocatorPicker.js +33 -0
- package/dist/cli/ui/ProgressReporter.d.ts +11 -0
- package/dist/cli/ui/ProgressReporter.d.ts.map +1 -0
- package/dist/cli/ui/ProgressReporter.js +33 -0
- package/dist/cli/ui/RefactorWizard.d.ts +26 -0
- package/dist/cli/ui/RefactorWizard.d.ts.map +1 -0
- package/dist/cli/ui/RefactorWizard.js +269 -0
- package/dist/config/ConfigLoader.d.ts +15 -0
- package/dist/config/ConfigLoader.d.ts.map +1 -0
- package/dist/config/ConfigLoader.js +174 -0
- package/dist/config/SelaConfig.d.ts +67 -0
- package/dist/config/SelaConfig.d.ts.map +1 -0
- package/dist/config/SelaConfig.js +57 -0
- package/dist/config/defineConfig.d.ts +3 -0
- package/dist/config/defineConfig.d.ts.map +1 -0
- package/dist/config/defineConfig.js +9 -0
- package/dist/engine/FixwrightEngine.d.ts +24 -0
- package/dist/engine/FixwrightEngine.d.ts.map +1 -0
- package/dist/engine/FixwrightEngine.js +403 -0
- package/dist/engine/HealingRegistry.d.ts +40 -0
- package/dist/engine/HealingRegistry.d.ts.map +1 -0
- package/dist/engine/HealingRegistry.js +98 -0
- package/dist/engine/singleton.d.ts +3 -0
- package/dist/engine/singleton.d.ts.map +1 -0
- package/dist/engine/singleton.js +5 -0
- package/dist/fixtures/expectProxy.d.ts +12 -0
- package/dist/fixtures/expectProxy.d.ts.map +1 -0
- package/dist/fixtures/expectProxy.js +228 -0
- package/dist/fixtures/index.d.ts +6 -0
- package/dist/fixtures/index.d.ts.map +1 -0
- package/dist/fixtures/index.js +688 -0
- package/dist/fixtures/moduleExpect.d.ts +2 -0
- package/dist/fixtures/moduleExpect.d.ts.map +1 -0
- package/dist/fixtures/moduleExpect.js +46 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/services/ASTSourceUpdater.d.ts +79 -0
- package/dist/services/ASTSourceUpdater.d.ts.map +1 -0
- package/dist/services/ASTSourceUpdater.js +3177 -0
- package/dist/services/ArgumentTypeAnalyzer.d.ts +26 -0
- package/dist/services/ArgumentTypeAnalyzer.d.ts.map +1 -0
- package/dist/services/ArgumentTypeAnalyzer.js +92 -0
- package/dist/services/BlastRadiusAnalyzer.d.ts +15 -0
- package/dist/services/BlastRadiusAnalyzer.d.ts.map +1 -0
- package/dist/services/BlastRadiusAnalyzer.js +103 -0
- package/dist/services/ChainValidator.d.ts +76 -0
- package/dist/services/ChainValidator.d.ts.map +1 -0
- package/dist/services/ChainValidator.js +569 -0
- package/dist/services/CrossFileHealer.d.ts +6 -0
- package/dist/services/CrossFileHealer.d.ts.map +1 -0
- package/dist/services/CrossFileHealer.js +134 -0
- package/dist/services/DefinitionTracer.d.ts +41 -0
- package/dist/services/DefinitionTracer.d.ts.map +1 -0
- package/dist/services/DefinitionTracer.js +350 -0
- package/dist/services/DnaEditorService.d.ts +31 -0
- package/dist/services/DnaEditorService.d.ts.map +1 -0
- package/dist/services/DnaEditorService.js +198 -0
- package/dist/services/DnaIndexService.d.ts +24 -0
- package/dist/services/DnaIndexService.d.ts.map +1 -0
- package/dist/services/DnaIndexService.js +131 -0
- package/dist/services/HealingAdvisory.d.ts +22 -0
- package/dist/services/HealingAdvisory.d.ts.map +1 -0
- package/dist/services/HealingAdvisory.js +42 -0
- package/dist/services/HealthReportService.d.ts +10 -0
- package/dist/services/HealthReportService.d.ts.map +1 -0
- package/dist/services/HealthReportService.js +84 -0
- package/dist/services/InitializerUpdater.d.ts +16 -0
- package/dist/services/InitializerUpdater.d.ts.map +1 -0
- package/dist/services/InitializerUpdater.js +37 -0
- package/dist/services/IntentAuditor.d.ts +39 -0
- package/dist/services/IntentAuditor.d.ts.map +1 -0
- package/dist/services/IntentAuditor.js +302 -0
- package/dist/services/LLMService.d.ts +100 -0
- package/dist/services/LLMService.d.ts.map +1 -0
- package/dist/services/LLMService.js +439 -0
- package/dist/services/SafetyGuard.d.ts +65 -0
- package/dist/services/SafetyGuard.d.ts.map +1 -0
- package/dist/services/SafetyGuard.js +524 -0
- package/dist/services/SnapshotService.d.ts +11 -0
- package/dist/services/SnapshotService.d.ts.map +1 -0
- package/dist/services/SnapshotService.js +349 -0
- package/dist/services/SourceLinkService.d.ts +26 -0
- package/dist/services/SourceLinkService.d.ts.map +1 -0
- package/dist/services/SourceLinkService.js +156 -0
- package/dist/services/SourceUpdater.d.ts +45 -0
- package/dist/services/SourceUpdater.d.ts.map +1 -0
- package/dist/services/SourceUpdater.js +1021 -0
- package/dist/services/TemplateDiffService.d.ts +25 -0
- package/dist/services/TemplateDiffService.d.ts.map +1 -0
- package/dist/services/TemplateDiffService.js +331 -0
- package/dist/services/types.d.ts +59 -0
- package/dist/services/types.d.ts.map +1 -0
- package/dist/services/types.js +2 -0
- package/dist/storage/SnapshotManager.d.ts +5 -0
- package/dist/storage/SnapshotManager.d.ts.map +1 -0
- package/dist/storage/SnapshotManager.js +31 -0
- package/dist/types/index.d.ts +95 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +7 -0
- package/dist/utils/DOMUtils.d.ts +33 -0
- package/dist/utils/DOMUtils.d.ts.map +1 -0
- package/dist/utils/DOMUtils.js +179 -0
- package/dist/utils/StackUtils.d.ts +11 -0
- package/dist/utils/StackUtils.d.ts.map +1 -0
- package/dist/utils/StackUtils.js +120 -0
- package/dist/vendor/enquirer.d.ts +33 -0
- package/dist/vendor/enquirer.d.ts.map +1 -0
- package/dist/vendor/enquirer.js +11 -0
- package/package.json +67 -0
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.IntentAuditor = void 0;
|
|
40
|
+
const sdk_1 = __importDefault(require("@anthropic-ai/sdk"));
|
|
41
|
+
const dotenv = __importStar(require("dotenv"));
|
|
42
|
+
dotenv.config();
|
|
43
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
44
|
+
// CONSTANTS
|
|
45
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
46
|
+
const FALLBACK_VERDICT = {
|
|
47
|
+
verdict: "SUSPICIOUS",
|
|
48
|
+
confidence: 0,
|
|
49
|
+
reason: "Auditor unavailable — defaulting to cautious SUSPICIOUS verdict",
|
|
50
|
+
inversionType: null,
|
|
51
|
+
};
|
|
52
|
+
// Ancestry drift penalties (spec §2.1).
|
|
53
|
+
// Applied as negative adjustments to the LLM's confidence score.
|
|
54
|
+
// Maximum combined penalty: −30 pts.
|
|
55
|
+
const DRIFT_PENALTIES = [
|
|
56
|
+
{ full: -20, partial: -10 }, // depth 1 — direct parent
|
|
57
|
+
{ full: -10, partial: -5 }, // depth 2 — grandparent
|
|
58
|
+
];
|
|
59
|
+
// Anchor bonuses: positive-only (spec §3.3).
|
|
60
|
+
// closestLabel exact match: +10 pts
|
|
61
|
+
// rowAnchor exact match: +5 pts
|
|
62
|
+
// Maximum combined bonus: +15 pts
|
|
63
|
+
const ANCHOR_BONUS_LABEL = 10;
|
|
64
|
+
const ANCHOR_BONUS_ROW = 5;
|
|
65
|
+
const SYSTEM_PROMPT = `You are a semantic intent auditor for a self-healing test automation system.
|
|
66
|
+
|
|
67
|
+
Your job: given a BEFORE text (DNA baseline) and an AFTER text (live element), decide whether the element's semantic intent is consistent, inconsistent, or suspicious.
|
|
68
|
+
|
|
69
|
+
You must respond with ONLY a JSON object. No prose, no markdown, no explanation outside the JSON.
|
|
70
|
+
|
|
71
|
+
VERDICT DEFINITIONS:
|
|
72
|
+
- CONSISTENT: The semantic intent is preserved. The element represents the same concept, state, or action. Minor wording, phrasing, or language differences are acceptable (e.g. "Submit" → "Send", "Username" → "Email address", "Connexion" → "Login"). Small quantitative changes that are not inversions are acceptable (e.g. "3 items" → "4 items").
|
|
73
|
+
- INCONSISTENT: The semantic intent has inverted or fundamentally changed. The element now represents an opposite or unrelated concept. Examples: "Success" → "Error", "Enabled" → "Disabled", "Login" → "Logout", "Confirm" → "Cancel", "Add" → "Remove".
|
|
74
|
+
- SUSPICIOUS: Cannot determine with confidence. The change is ambiguous, the texts are too short to judge, or the relationship is unclear. Err toward SUSPICIOUS when unsure.
|
|
75
|
+
|
|
76
|
+
INVERSION TYPES (populate when INCONSISTENT, null otherwise):
|
|
77
|
+
- "polarity": Boolean/state opposites (enabled/disabled, on/off, true/false, success/failure)
|
|
78
|
+
- "state": Workflow or lifecycle state inversions (login/logout, open/closed, connected/disconnected)
|
|
79
|
+
- "quantitative": Numeric inversions that suggest logic change (not just count changes)
|
|
80
|
+
- "identity": Fundamentally different concept (e.g. "Delete" button became "Save" button)
|
|
81
|
+
|
|
82
|
+
RULES:
|
|
83
|
+
1. Language is irrelevant. "Connexion" and "Login" are CONSISTENT.
|
|
84
|
+
2. Casing is irrelevant. "SUBMIT" and "submit" are CONSISTENT.
|
|
85
|
+
3. Minor reformatting is irrelevant. "Save Changes" and "Save" are CONSISTENT.
|
|
86
|
+
4. Role/aria context matters. A button that was "Submit" becoming "Cancel" is INCONSISTENT (identity inversion).
|
|
87
|
+
5. When both texts are very short (3 chars or less) and differ, prefer SUSPICIOUS unless an obvious opposite pair.
|
|
88
|
+
6. In "assertion" mode, be stricter — lean toward SUSPICIOUS or INCONSISTENT when the change is ambiguous.
|
|
89
|
+
7. In "action" mode, be more lenient — lean toward CONSISTENT when the change looks like a UI refactor.
|
|
90
|
+
|
|
91
|
+
RESPONSE FORMAT (strict JSON, no other text):
|
|
92
|
+
{
|
|
93
|
+
"verdict": "CONSISTENT" | "INCONSISTENT" | "SUSPICIOUS",
|
|
94
|
+
"confidence": <integer 0-100>,
|
|
95
|
+
"reason": "<one sentence explaining the verdict>",
|
|
96
|
+
"inversionType": "polarity" | "quantitative" | "state" | "identity" | null
|
|
97
|
+
}`;
|
|
98
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
99
|
+
// IntentAuditor
|
|
100
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
101
|
+
class IntentAuditor {
|
|
102
|
+
anthropic;
|
|
103
|
+
disabled;
|
|
104
|
+
constructor() {
|
|
105
|
+
const apiKey = process.env.ANTHROPIC_API_KEY;
|
|
106
|
+
if (!apiKey) {
|
|
107
|
+
console.warn("[IntentAuditor] ANTHROPIC_API_KEY not set — auditor disabled, will return SUSPICIOUS for all audited heals");
|
|
108
|
+
this.anthropic = null;
|
|
109
|
+
this.disabled = true;
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
this.anthropic = new sdk_1.default({ apiKey });
|
|
113
|
+
this.disabled = false;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
async auditIntent(req) {
|
|
117
|
+
if (this.disabled || !this.anthropic) {
|
|
118
|
+
return { ...FALLBACK_VERDICT };
|
|
119
|
+
}
|
|
120
|
+
const userMessage = [
|
|
121
|
+
`BEFORE (DNA baseline): "${req.dnaText}"`,
|
|
122
|
+
req.dnaRole ? `BEFORE role: ${req.dnaRole}` : null,
|
|
123
|
+
`AFTER (live text): "${req.liveText}"`,
|
|
124
|
+
req.liveRole ? `AFTER role: ${req.liveRole}` : null,
|
|
125
|
+
`Heal mode: ${req.healMode}`,
|
|
126
|
+
]
|
|
127
|
+
.filter(Boolean)
|
|
128
|
+
.join("\n");
|
|
129
|
+
try {
|
|
130
|
+
const response = await this.anthropic.messages.create({
|
|
131
|
+
model: "claude-haiku-4-5-20251001",
|
|
132
|
+
max_tokens: 256,
|
|
133
|
+
temperature: 0.1,
|
|
134
|
+
system: SYSTEM_PROMPT,
|
|
135
|
+
messages: [{ role: "user", content: userMessage }],
|
|
136
|
+
});
|
|
137
|
+
const raw = response.content[0].type === "text" ? response.content[0].text : "";
|
|
138
|
+
// parseVerdict returns the raw LLM verdict without the low-confidence
|
|
139
|
+
// downgrade so that scoring adjustments below are applied first.
|
|
140
|
+
const rawVerdict = this.parseVerdict(raw);
|
|
141
|
+
// ── Structural scoring ────────────────────────────────────────
|
|
142
|
+
// Apply ancestry drift penalty and anchor bonus to the LLM's raw
|
|
143
|
+
// confidence. The semantic verdict category (CONSISTENT / SUSPICIOUS /
|
|
144
|
+
// INCONSISTENT) is never changed by scoring — only the confidence value.
|
|
145
|
+
// The low-confidence CONSISTENT→SUSPICIOUS downgrade is re-evaluated
|
|
146
|
+
// after scoring so the adjusted value is used for the threshold.
|
|
147
|
+
const penalty = this.computeAncestryPenalty(req.dnaAncestry ?? [], req.liveAncestry ?? []);
|
|
148
|
+
const bonus = this.computeAnchorBonus(req.dnaAnchors, req.liveAnchors);
|
|
149
|
+
const scoreAdjustment = penalty + bonus;
|
|
150
|
+
let finalVerdict;
|
|
151
|
+
if (scoreAdjustment !== 0) {
|
|
152
|
+
const adjConf = Math.max(0, Math.min(100, rawVerdict.confidence + scoreAdjustment));
|
|
153
|
+
const parts = [];
|
|
154
|
+
if (penalty !== 0)
|
|
155
|
+
parts.push(`drift ${penalty}`);
|
|
156
|
+
if (bonus !== 0)
|
|
157
|
+
parts.push(`anchor +${bonus}`);
|
|
158
|
+
console.log(`[IntentAuditor] 📊 Confidence: ${rawVerdict.confidence}% → ${adjConf}% (${parts.join(", ")})`);
|
|
159
|
+
// Re-apply low-confidence CONSISTENT downgrade with the adjusted value.
|
|
160
|
+
// The raw check in parseVerdict used the pre-adjustment confidence; a
|
|
161
|
+
// penalty may push a borderline CONSISTENT below the 50% threshold.
|
|
162
|
+
const scoring = { rawConfidence: rawVerdict.confidence, penalty, bonus };
|
|
163
|
+
if (rawVerdict.verdict === "CONSISTENT" && adjConf < 50) {
|
|
164
|
+
finalVerdict = {
|
|
165
|
+
verdict: "SUSPICIOUS",
|
|
166
|
+
confidence: adjConf,
|
|
167
|
+
reason: `Low-confidence CONSISTENT downgraded after drift/anchor scoring: ${rawVerdict.reason}`,
|
|
168
|
+
inversionType: null,
|
|
169
|
+
scoreBreakdown: scoring,
|
|
170
|
+
};
|
|
171
|
+
console.log(`[IntentAuditor] CONSISTENT→SUSPICIOUS after scoring (${rawVerdict.confidence}%→${adjConf}%)`);
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
finalVerdict = { ...rawVerdict, confidence: adjConf, scoreBreakdown: scoring };
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
finalVerdict = rawVerdict;
|
|
179
|
+
}
|
|
180
|
+
console.log(`[IntentAuditor] verdict=${finalVerdict.verdict} (${finalVerdict.confidence}%) — ${finalVerdict.reason}`);
|
|
181
|
+
return finalVerdict;
|
|
182
|
+
}
|
|
183
|
+
catch (err) {
|
|
184
|
+
console.warn(`[IntentAuditor] Warning: Auditor call failed: ${err.message} — returning SUSPICIOUS`);
|
|
185
|
+
return { ...FALLBACK_VERDICT, reason: `Auditor call failed: ${err.message}` };
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
// ─────────────────────────────────────────────────────────────
|
|
189
|
+
// computeAncestryPenalty — DNA v2 Ancestry Drift (spec §2)
|
|
190
|
+
//
|
|
191
|
+
// Compares at most 2 ancestor depths between the DNA baseline and
|
|
192
|
+
// the live candidate. Returns a value in [−30, 0]; 0 = no drift.
|
|
193
|
+
//
|
|
194
|
+
// Empty dnaPath → 0 penalty (v1 migrated snapshot has no data).
|
|
195
|
+
// livePath shorter than dnaPath → compare up to min length (§2.5).
|
|
196
|
+
//
|
|
197
|
+
// Penalty table (§2.1):
|
|
198
|
+
// depth 1 full mismatch −20 pts partial mismatch −10 pts
|
|
199
|
+
// depth 2 full mismatch −10 pts partial mismatch −5 pts
|
|
200
|
+
//
|
|
201
|
+
// Full mismatch = tags differ
|
|
202
|
+
// Partial mismatch = same tag, fingerprint differs (id or classes)
|
|
203
|
+
// ─────────────────────────────────────────────────────────────
|
|
204
|
+
computeAncestryPenalty(dnaPath, livePath) {
|
|
205
|
+
if (dnaPath.length === 0)
|
|
206
|
+
return 0; // v1 migrated snapshot — skip
|
|
207
|
+
let totalPenalty = 0;
|
|
208
|
+
const maxDepth = Math.min(dnaPath.length, livePath.length, 2);
|
|
209
|
+
for (let i = 0; i < maxDepth; i++) {
|
|
210
|
+
const dna = dnaPath[i];
|
|
211
|
+
const live = livePath[i];
|
|
212
|
+
const { full, partial } = DRIFT_PENALTIES[i];
|
|
213
|
+
if (dna.fingerprint === live.fingerprint) {
|
|
214
|
+
continue; // exact match, no penalty
|
|
215
|
+
}
|
|
216
|
+
const depthLabel = i === 0 ? "depth-1 (parent)" : "depth-2 (grandparent)";
|
|
217
|
+
if (dna.tag !== live.tag) {
|
|
218
|
+
totalPenalty += full;
|
|
219
|
+
console.log(`[IntentAuditor] 🏗️ Ancestry drift ${full} pts — ${depthLabel}: ` +
|
|
220
|
+
`tag mismatch (${dna.fingerprint} → ${live.fingerprint})`);
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
totalPenalty += partial;
|
|
224
|
+
console.log(`[IntentAuditor] 🏗️ Ancestry drift ${partial} pts — ${depthLabel}: ` +
|
|
225
|
+
`same tag, id/class changed (${dna.fingerprint} → ${live.fingerprint})`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return totalPenalty;
|
|
229
|
+
}
|
|
230
|
+
// ─────────────────────────────────────────────────────────────
|
|
231
|
+
// computeAnchorBonus — DNA v2 Anchor Confirmation (spec §3.3)
|
|
232
|
+
//
|
|
233
|
+
// Anchors only provide positive confirmation — no penalty for
|
|
234
|
+
// mismatch. Returns a value in [0, +15].
|
|
235
|
+
//
|
|
236
|
+
// closestLabel exact match (case-insensitive): +10 pts
|
|
237
|
+
// rowAnchor exact match (case-insensitive): +5 pts
|
|
238
|
+
// ─────────────────────────────────────────────────────────────
|
|
239
|
+
computeAnchorBonus(dnaAnchors, liveAnchors) {
|
|
240
|
+
if (!dnaAnchors || !liveAnchors)
|
|
241
|
+
return 0;
|
|
242
|
+
let bonus = 0;
|
|
243
|
+
const dnaLabel = dnaAnchors.closestLabel?.toLowerCase().trim();
|
|
244
|
+
const liveLabel = liveAnchors.closestLabel?.toLowerCase().trim();
|
|
245
|
+
if (dnaLabel && liveLabel && dnaLabel === liveLabel) {
|
|
246
|
+
bonus += ANCHOR_BONUS_LABEL;
|
|
247
|
+
console.log(`[IntentAuditor] 🎯 Anchor +${ANCHOR_BONUS_LABEL} pts — closestLabel match: "${dnaAnchors.closestLabel}"`);
|
|
248
|
+
}
|
|
249
|
+
const dnaRow = dnaAnchors.rowAnchor?.toLowerCase().trim();
|
|
250
|
+
const liveRow = liveAnchors.rowAnchor?.toLowerCase().trim();
|
|
251
|
+
if (dnaRow && liveRow && dnaRow === liveRow) {
|
|
252
|
+
bonus += ANCHOR_BONUS_ROW;
|
|
253
|
+
console.log(`[IntentAuditor] 🎯 Anchor +${ANCHOR_BONUS_ROW} pts — rowAnchor match: "${dnaAnchors.rowAnchor}"`);
|
|
254
|
+
}
|
|
255
|
+
return bonus;
|
|
256
|
+
}
|
|
257
|
+
// ─────────────────────────────────────────────────────────────
|
|
258
|
+
// PRIVATE: parse and validate raw LLM JSON response.
|
|
259
|
+
//
|
|
260
|
+
// Returns the verdict as-is from the LLM. The low-confidence
|
|
261
|
+
// CONSISTENT downgrade is intentionally NOT performed here so
|
|
262
|
+
// that auditIntent() can apply structural scoring first and then
|
|
263
|
+
// run the downgrade once against the adjusted confidence.
|
|
264
|
+
// ─────────────────────────────────────────────────────────────
|
|
265
|
+
parseVerdict(raw) {
|
|
266
|
+
try {
|
|
267
|
+
const jsonMatch = raw.match(/\{[\s\S]*\}/);
|
|
268
|
+
if (!jsonMatch)
|
|
269
|
+
throw new Error("No JSON object found in response");
|
|
270
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
271
|
+
const verdict = parsed.verdict === "CONSISTENT" ||
|
|
272
|
+
parsed.verdict === "INCONSISTENT" ||
|
|
273
|
+
parsed.verdict === "SUSPICIOUS"
|
|
274
|
+
? parsed.verdict
|
|
275
|
+
: "SUSPICIOUS";
|
|
276
|
+
const confidence = typeof parsed.confidence === "number"
|
|
277
|
+
? Math.max(0, Math.min(100, Math.round(parsed.confidence)))
|
|
278
|
+
: 0;
|
|
279
|
+
const reason = typeof parsed.reason === "string" && parsed.reason.length > 0
|
|
280
|
+
? parsed.reason
|
|
281
|
+
: "No reason provided";
|
|
282
|
+
const validInversionTypes = new Set([
|
|
283
|
+
"polarity",
|
|
284
|
+
"quantitative",
|
|
285
|
+
"state",
|
|
286
|
+
"identity",
|
|
287
|
+
]);
|
|
288
|
+
const inversionType = validInversionTypes.has(parsed.inversionType)
|
|
289
|
+
? parsed.inversionType
|
|
290
|
+
: null;
|
|
291
|
+
return { verdict, confidence, reason, inversionType };
|
|
292
|
+
}
|
|
293
|
+
catch (err) {
|
|
294
|
+
console.warn(`[IntentAuditor] Warning: Failed to parse Auditor response: ${err.message}`);
|
|
295
|
+
return {
|
|
296
|
+
...FALLBACK_VERDICT,
|
|
297
|
+
reason: `Parse failure: ${err.message}`,
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
exports.IntentAuditor = IntentAuditor;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { FixRequest } from "../types";
|
|
2
|
+
export interface SelectorSegment {
|
|
3
|
+
type: "frame" | "element";
|
|
4
|
+
selector: string;
|
|
5
|
+
}
|
|
6
|
+
export interface ContentChange {
|
|
7
|
+
oldText: string;
|
|
8
|
+
newText: string;
|
|
9
|
+
}
|
|
10
|
+
export type SmartChainSegment = {
|
|
11
|
+
type: "locator";
|
|
12
|
+
selector: string;
|
|
13
|
+
hasText?: string;
|
|
14
|
+
has?: string;
|
|
15
|
+
hasNot?: string;
|
|
16
|
+
} | {
|
|
17
|
+
type: "frameLocator";
|
|
18
|
+
selector: string;
|
|
19
|
+
} | {
|
|
20
|
+
type: "getByRole";
|
|
21
|
+
role: string;
|
|
22
|
+
name?: string;
|
|
23
|
+
exact?: boolean;
|
|
24
|
+
level?: number;
|
|
25
|
+
checked?: boolean;
|
|
26
|
+
pressed?: boolean;
|
|
27
|
+
expanded?: boolean;
|
|
28
|
+
selected?: boolean;
|
|
29
|
+
disabled?: boolean;
|
|
30
|
+
} | {
|
|
31
|
+
type: "getByLabel";
|
|
32
|
+
text: string;
|
|
33
|
+
exact?: boolean;
|
|
34
|
+
} | {
|
|
35
|
+
type: "getByText";
|
|
36
|
+
text: string;
|
|
37
|
+
exact?: boolean;
|
|
38
|
+
} | {
|
|
39
|
+
type: "getByPlaceholder";
|
|
40
|
+
text: string;
|
|
41
|
+
exact?: boolean;
|
|
42
|
+
} | {
|
|
43
|
+
type: "getByTestId";
|
|
44
|
+
testId: string;
|
|
45
|
+
} | {
|
|
46
|
+
type: "getByAltText";
|
|
47
|
+
text: string;
|
|
48
|
+
exact?: boolean;
|
|
49
|
+
} | {
|
|
50
|
+
type: "getByTitle";
|
|
51
|
+
text: string;
|
|
52
|
+
exact?: boolean;
|
|
53
|
+
} | {
|
|
54
|
+
type: "filter";
|
|
55
|
+
hasText?: string;
|
|
56
|
+
hasNotText?: string;
|
|
57
|
+
has?: string;
|
|
58
|
+
hasNot?: string;
|
|
59
|
+
} | {
|
|
60
|
+
type: "first";
|
|
61
|
+
} | {
|
|
62
|
+
type: "last";
|
|
63
|
+
} | {
|
|
64
|
+
type: "nth";
|
|
65
|
+
index: number;
|
|
66
|
+
};
|
|
67
|
+
export interface FixResponse {
|
|
68
|
+
status: "FIXED" | "NOT_FOUND";
|
|
69
|
+
/**
|
|
70
|
+
* LEGACY/PROXY: This selector is used for IMMEDIATE RETRY in the proxy.
|
|
71
|
+
* CRITICAL: Must be as specific as the chainSegments to avoid Strict Mode errors.
|
|
72
|
+
*/
|
|
73
|
+
segments: SelectorSegment[];
|
|
74
|
+
/**
|
|
75
|
+
* NEW: Rich semantic chain segments for AST source update.
|
|
76
|
+
*/
|
|
77
|
+
chainSegments?: SmartChainSegment[];
|
|
78
|
+
originalChainHint?: SmartChainSegment[];
|
|
79
|
+
confidence: number;
|
|
80
|
+
explanation: string;
|
|
81
|
+
new_selector?: string | null;
|
|
82
|
+
contentChange?: ContentChange;
|
|
83
|
+
}
|
|
84
|
+
export declare class LLMService {
|
|
85
|
+
private anthropic;
|
|
86
|
+
constructor();
|
|
87
|
+
private cleanJson;
|
|
88
|
+
/**
|
|
89
|
+
* Attempts to infer contentChange when the AI did not return the field.
|
|
90
|
+
*
|
|
91
|
+
* Strategies (in order of confidence):
|
|
92
|
+
*
|
|
93
|
+
* 1. previousState.text vs has-text() in new selector
|
|
94
|
+
* 2. has-text() in old selector vs new selector
|
|
95
|
+
* 3. intentDescription text vs new selector
|
|
96
|
+
*/
|
|
97
|
+
private inferContentChange;
|
|
98
|
+
getFix(request: FixRequest): Promise<FixResponse>;
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=LLMService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LLMService.d.ts","sourceRoot":"","sources":["../../src/services/LLMService.ts"],"names":[],"mappings":"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"}
|