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.
Files changed (144) hide show
  1. package/README.md +93 -0
  2. package/bin/sela.js +3 -0
  3. package/dist/cli/ErrorHandler.d.ts +10 -0
  4. package/dist/cli/ErrorHandler.d.ts.map +1 -0
  5. package/dist/cli/ErrorHandler.js +70 -0
  6. package/dist/cli/commands/bulk.d.ts +3 -0
  7. package/dist/cli/commands/bulk.d.ts.map +1 -0
  8. package/dist/cli/commands/bulk.js +140 -0
  9. package/dist/cli/commands/find.d.ts +3 -0
  10. package/dist/cli/commands/find.d.ts.map +1 -0
  11. package/dist/cli/commands/find.js +51 -0
  12. package/dist/cli/commands/init.d.ts +3 -0
  13. package/dist/cli/commands/init.d.ts.map +1 -0
  14. package/dist/cli/commands/init.js +133 -0
  15. package/dist/cli/commands/list.d.ts +3 -0
  16. package/dist/cli/commands/list.d.ts.map +1 -0
  17. package/dist/cli/commands/list.js +56 -0
  18. package/dist/cli/commands/refactor.d.ts +3 -0
  19. package/dist/cli/commands/refactor.d.ts.map +1 -0
  20. package/dist/cli/commands/refactor.js +30 -0
  21. package/dist/cli/commands/status.d.ts +3 -0
  22. package/dist/cli/commands/status.d.ts.map +1 -0
  23. package/dist/cli/commands/status.js +51 -0
  24. package/dist/cli/commands/sync.d.ts +3 -0
  25. package/dist/cli/commands/sync.d.ts.map +1 -0
  26. package/dist/cli/commands/sync.js +123 -0
  27. package/dist/cli/index.d.ts +2 -0
  28. package/dist/cli/index.d.ts.map +1 -0
  29. package/dist/cli/index.js +42 -0
  30. package/dist/cli/ui/DnaTable.d.ts +3 -0
  31. package/dist/cli/ui/DnaTable.d.ts.map +1 -0
  32. package/dist/cli/ui/DnaTable.js +70 -0
  33. package/dist/cli/ui/LocatorPicker.d.ts +8 -0
  34. package/dist/cli/ui/LocatorPicker.d.ts.map +1 -0
  35. package/dist/cli/ui/LocatorPicker.js +33 -0
  36. package/dist/cli/ui/ProgressReporter.d.ts +11 -0
  37. package/dist/cli/ui/ProgressReporter.d.ts.map +1 -0
  38. package/dist/cli/ui/ProgressReporter.js +33 -0
  39. package/dist/cli/ui/RefactorWizard.d.ts +26 -0
  40. package/dist/cli/ui/RefactorWizard.d.ts.map +1 -0
  41. package/dist/cli/ui/RefactorWizard.js +269 -0
  42. package/dist/config/ConfigLoader.d.ts +15 -0
  43. package/dist/config/ConfigLoader.d.ts.map +1 -0
  44. package/dist/config/ConfigLoader.js +174 -0
  45. package/dist/config/SelaConfig.d.ts +67 -0
  46. package/dist/config/SelaConfig.d.ts.map +1 -0
  47. package/dist/config/SelaConfig.js +57 -0
  48. package/dist/config/defineConfig.d.ts +3 -0
  49. package/dist/config/defineConfig.d.ts.map +1 -0
  50. package/dist/config/defineConfig.js +9 -0
  51. package/dist/engine/FixwrightEngine.d.ts +24 -0
  52. package/dist/engine/FixwrightEngine.d.ts.map +1 -0
  53. package/dist/engine/FixwrightEngine.js +403 -0
  54. package/dist/engine/HealingRegistry.d.ts +40 -0
  55. package/dist/engine/HealingRegistry.d.ts.map +1 -0
  56. package/dist/engine/HealingRegistry.js +98 -0
  57. package/dist/engine/singleton.d.ts +3 -0
  58. package/dist/engine/singleton.d.ts.map +1 -0
  59. package/dist/engine/singleton.js +5 -0
  60. package/dist/fixtures/expectProxy.d.ts +12 -0
  61. package/dist/fixtures/expectProxy.d.ts.map +1 -0
  62. package/dist/fixtures/expectProxy.js +228 -0
  63. package/dist/fixtures/index.d.ts +6 -0
  64. package/dist/fixtures/index.d.ts.map +1 -0
  65. package/dist/fixtures/index.js +688 -0
  66. package/dist/fixtures/moduleExpect.d.ts +2 -0
  67. package/dist/fixtures/moduleExpect.d.ts.map +1 -0
  68. package/dist/fixtures/moduleExpect.js +46 -0
  69. package/dist/index.d.ts +7 -0
  70. package/dist/index.d.ts.map +1 -0
  71. package/dist/index.js +28 -0
  72. package/dist/services/ASTSourceUpdater.d.ts +79 -0
  73. package/dist/services/ASTSourceUpdater.d.ts.map +1 -0
  74. package/dist/services/ASTSourceUpdater.js +3177 -0
  75. package/dist/services/ArgumentTypeAnalyzer.d.ts +26 -0
  76. package/dist/services/ArgumentTypeAnalyzer.d.ts.map +1 -0
  77. package/dist/services/ArgumentTypeAnalyzer.js +92 -0
  78. package/dist/services/BlastRadiusAnalyzer.d.ts +15 -0
  79. package/dist/services/BlastRadiusAnalyzer.d.ts.map +1 -0
  80. package/dist/services/BlastRadiusAnalyzer.js +103 -0
  81. package/dist/services/ChainValidator.d.ts +76 -0
  82. package/dist/services/ChainValidator.d.ts.map +1 -0
  83. package/dist/services/ChainValidator.js +569 -0
  84. package/dist/services/CrossFileHealer.d.ts +6 -0
  85. package/dist/services/CrossFileHealer.d.ts.map +1 -0
  86. package/dist/services/CrossFileHealer.js +134 -0
  87. package/dist/services/DefinitionTracer.d.ts +41 -0
  88. package/dist/services/DefinitionTracer.d.ts.map +1 -0
  89. package/dist/services/DefinitionTracer.js +350 -0
  90. package/dist/services/DnaEditorService.d.ts +31 -0
  91. package/dist/services/DnaEditorService.d.ts.map +1 -0
  92. package/dist/services/DnaEditorService.js +198 -0
  93. package/dist/services/DnaIndexService.d.ts +24 -0
  94. package/dist/services/DnaIndexService.d.ts.map +1 -0
  95. package/dist/services/DnaIndexService.js +131 -0
  96. package/dist/services/HealingAdvisory.d.ts +22 -0
  97. package/dist/services/HealingAdvisory.d.ts.map +1 -0
  98. package/dist/services/HealingAdvisory.js +42 -0
  99. package/dist/services/HealthReportService.d.ts +10 -0
  100. package/dist/services/HealthReportService.d.ts.map +1 -0
  101. package/dist/services/HealthReportService.js +84 -0
  102. package/dist/services/InitializerUpdater.d.ts +16 -0
  103. package/dist/services/InitializerUpdater.d.ts.map +1 -0
  104. package/dist/services/InitializerUpdater.js +37 -0
  105. package/dist/services/IntentAuditor.d.ts +39 -0
  106. package/dist/services/IntentAuditor.d.ts.map +1 -0
  107. package/dist/services/IntentAuditor.js +302 -0
  108. package/dist/services/LLMService.d.ts +100 -0
  109. package/dist/services/LLMService.d.ts.map +1 -0
  110. package/dist/services/LLMService.js +439 -0
  111. package/dist/services/SafetyGuard.d.ts +65 -0
  112. package/dist/services/SafetyGuard.d.ts.map +1 -0
  113. package/dist/services/SafetyGuard.js +524 -0
  114. package/dist/services/SnapshotService.d.ts +11 -0
  115. package/dist/services/SnapshotService.d.ts.map +1 -0
  116. package/dist/services/SnapshotService.js +349 -0
  117. package/dist/services/SourceLinkService.d.ts +26 -0
  118. package/dist/services/SourceLinkService.d.ts.map +1 -0
  119. package/dist/services/SourceLinkService.js +156 -0
  120. package/dist/services/SourceUpdater.d.ts +45 -0
  121. package/dist/services/SourceUpdater.d.ts.map +1 -0
  122. package/dist/services/SourceUpdater.js +1021 -0
  123. package/dist/services/TemplateDiffService.d.ts +25 -0
  124. package/dist/services/TemplateDiffService.d.ts.map +1 -0
  125. package/dist/services/TemplateDiffService.js +331 -0
  126. package/dist/services/types.d.ts +59 -0
  127. package/dist/services/types.d.ts.map +1 -0
  128. package/dist/services/types.js +2 -0
  129. package/dist/storage/SnapshotManager.d.ts +5 -0
  130. package/dist/storage/SnapshotManager.d.ts.map +1 -0
  131. package/dist/storage/SnapshotManager.js +31 -0
  132. package/dist/types/index.d.ts +95 -0
  133. package/dist/types/index.d.ts.map +1 -0
  134. package/dist/types/index.js +7 -0
  135. package/dist/utils/DOMUtils.d.ts +33 -0
  136. package/dist/utils/DOMUtils.d.ts.map +1 -0
  137. package/dist/utils/DOMUtils.js +179 -0
  138. package/dist/utils/StackUtils.d.ts +11 -0
  139. package/dist/utils/StackUtils.d.ts.map +1 -0
  140. package/dist/utils/StackUtils.js +120 -0
  141. package/dist/vendor/enquirer.d.ts +33 -0
  142. package/dist/vendor/enquirer.d.ts.map +1 -0
  143. package/dist/vendor/enquirer.js +11 -0
  144. 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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"}