sela-core 0.1.0-alpha.1 → 0.1.0-alpha.2

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/bin/sela.js CHANGED
File without changes
@@ -1 +1 @@
1
- {"version":3,"file":"SelaEngine.d.ts","sourceRoot":"","sources":["../../src/engine/SelaEngine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAexC,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAG7C,OAAO,EAAe,QAAQ,EAAkB,MAAM,yBAAyB,CAAC;AA0FhF,qBAAa,UAAU;IACrB,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,mBAAmB,CAAmB;IAC9C,OAAO,CAAC,YAAY,CAAsB;IAC1C,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,SAAS,CAA6C;IAC9D,OAAO,CAAC,cAAc,CAAoC;IAC1D,OAAO,CAAC,eAAe,CAAkC;;IAuBnD,YAAY,CAChB,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IA0DnB,IAAI,CACR,IAAI,EAAE,IAAI,EACV,YAAY,EAAE,MAAM,EACpB,mBAAmB,EAAE,MAAM,EAC3B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,QAAQ,GAAE,QAAmB,GAC5B,OAAO,CAAC,MAAM,CAAC;IA8qBlB,OAAO,CAAC,aAAa;IAYrB,OAAO,CAAC,aAAa;IAerB;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,oBAAoB;IAoB5B;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,2BAA2B;IA4BnC,OAAO,CAAC,yBAAyB;IAmCjC,OAAO,CAAC,kBAAkB;IA8D1B,OAAO,CAAC,oBAAoB;IAyD5B,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAIrD,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAItE,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;YAgLtB,aAAa;IAsB3B,OAAO,CAAC,0BAA0B;IA+BlC,OAAO,CAAC,WAAW;IA6Bb,wBAAwB,CAC5B,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM;CAqBnB;AAED,OAAO,EAAE,CAAC"}
1
+ {"version":3,"file":"SelaEngine.d.ts","sourceRoot":"","sources":["../../src/engine/SelaEngine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAexC,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAG7C,OAAO,EAAe,QAAQ,EAAkB,MAAM,yBAAyB,CAAC;AA0FhF,qBAAa,UAAU;IACrB,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,mBAAmB,CAAmB;IAC9C,OAAO,CAAC,YAAY,CAAsB;IAC1C,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,SAAS,CAA6C;IAC9D,OAAO,CAAC,cAAc,CAAoC;IAC1D,OAAO,CAAC,eAAe,CAAkC;;IAuBnD,YAAY,CAChB,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IA0DnB,IAAI,CACR,IAAI,EAAE,IAAI,EACV,YAAY,EAAE,MAAM,EACpB,mBAAmB,EAAE,MAAM,EAC3B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,QAAQ,GAAE,QAAmB,GAC5B,OAAO,CAAC,MAAM,CAAC;IA8qBlB,OAAO,CAAC,aAAa;IAYrB,OAAO,CAAC,aAAa;IAerB;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,oBAAoB;IAoB5B;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,2BAA2B;IA4BnC,OAAO,CAAC,yBAAyB;IAgDjC,OAAO,CAAC,kBAAkB;IA8D1B,OAAO,CAAC,oBAAoB;IAyD5B,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAIrD,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAItE,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;YAgLtB,aAAa;IAsB3B,OAAO,CAAC,0BAA0B;IA+BlC,OAAO,CAAC,WAAW;IA6Bb,wBAAwB,CAC5B,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM;CAqBnB;AAED,OAAO,EAAE,CAAC"}
@@ -826,13 +826,18 @@ If the text has NOT changed, omit the "contentChange" field entirely.`;
826
826
  Number.isFinite(meta.auditorConfidence)
827
827
  ? meta.auditorConfidence
828
828
  : undefined;
829
+ // Prefer the auditor's own reasoning so the report explains WHY it blocked
830
+ // (e.g. "Semantic intent inverted from Update to Delete"). Fall back to the
831
+ // structural score breakdown, then a generic note.
832
+ const reason = meta.auditorReason ??
833
+ (meta.auditScoreBreakdown
834
+ ? `Adjusted ${meta.auditScoreBreakdown.rawConfidence}% → ${meta.auditScoreBreakdown.adjustedConfidence}% (penalty ${meta.auditScoreBreakdown.penalty}, bonus +${meta.auditScoreBreakdown.bonus}).`
835
+ : "Auditor verdict recorded by SafetyGuard.");
829
836
  return {
830
837
  verdict: meta.auditorVerdict,
831
838
  confidence: conf,
832
- reason: meta.auditScoreBreakdown
833
- ? `Adjusted ${meta.auditScoreBreakdown.rawConfidence}% ${meta.auditScoreBreakdown.adjustedConfidence}% (penalty ${meta.auditScoreBreakdown.penalty}, bonus +${meta.auditScoreBreakdown.bonus}).`
834
- : "Auditor verdict recorded by SafetyGuard.",
835
- inversionType: null,
839
+ reason,
840
+ inversionType: meta.auditorInversionType ?? null,
836
841
  rawConfidence: meta.auditScoreBreakdown?.rawConfidence,
837
842
  penalty: meta.auditScoreBreakdown?.penalty,
838
843
  bonus: meta.auditScoreBreakdown?.bonus,
@@ -579,6 +579,7 @@ const REPORT_CSS = [
579
579
  ".conf-high{background:rgba(0,229,160,.12);color:var(--green);border:1px solid rgba(0,229,160,.2)}",
580
580
  ".conf-mid{background:rgba(255,184,77,.1);color:var(--amber);border:1px solid rgba(255,184,77,.2)}",
581
581
  ".conf-low{background:rgba(255,77,106,.1);color:var(--red);border:1px solid rgba(255,77,106,.2)}",
582
+ ".conf-blocked{background:rgba(255,77,106,.16);color:var(--red);border:1px solid rgba(255,77,106,.35);text-transform:uppercase;letter-spacing:.6px}",
582
583
  ".chev{width:16px;height:16px;color:var(--text3);flex-shrink:0;transition:transform .25s ease}",
583
584
  ".ecard.open .chev{transform:rotate(90deg)}",
584
585
  ".ecard-body{display:none;padding:0 16px 18px;border-top:1px solid var(--line)}",
@@ -829,6 +830,13 @@ const REPORT_JS = [
829
830
  " var cls=pct>=80?'conf-high':pct>=60?'conf-mid':'conf-low';",
830
831
  " return'<span class=\"conf-badge '+cls+'\">'+pct+'%</span>';",
831
832
  "}",
833
+ // PROTECTED events were BLOCKED - showing the AI's high confidence would be
834
+ // a lie. Surface a strict Blocked pill instead; the honest consistency score
835
+ // lives in the Intent Auditor gauge below.
836
+ "function headerBadge(ev){",
837
+ " if(ev.kind==='PROTECTED')return'<span class=\"conf-badge conf-blocked\">Blocked</span>';",
838
+ " return confBadge(ev.aiConfidence);",
839
+ "}",
832
840
  // ── Diff renderer
833
841
  "function renderDiff(ev){",
834
842
  " if(ev.kind==='PROTECTED')",
@@ -932,7 +940,6 @@ const REPORT_JS = [
932
940
  " steps.push('Reason: '+(ev.reason||'unknown'));",
933
941
  " steps.push('Open the file in your editor to inspect the original locator.');",
934
942
  " }",
935
- " var issueUrl='https://github.com/sela-core/sela/issues/new?title='+encodeURIComponent('[Report] '+ev.kind+' '+ev.stableId)+'&body='+encodeURIComponent('Stable ID: '+ev.stableId+'\\nFile: '+ev.sourceFile+':'+ev.sourceLine);",
936
943
  " var link=vscodeLink(ev.sourceFile,ev.sourceLine);",
937
944
  " var actions='';",
938
945
  " if(ev.kind==='HEALED'){",
@@ -940,18 +947,15 @@ const REPORT_JS = [
940
947
  " +'<button class=\"btn btn-primary\" data-action=\"keep\" data-id=\"'+ev.stableId+'\" onclick=\"handleAction(this,\\'keep\\',\\''+ev.stableId+'\\')\">\u2713 Keep changes</button>'",
941
948
  " +'<button class=\"btn btn-danger\" onclick=\"handleAction(this,\\'rollback\\',\\''+ev.stableId+'\\')\">&#x21B6; Rollback</button>'",
942
949
  " +'<a class=\"btn\" href=\"'+link+'\">&#x2317; Open in VS Code</a>'",
943
- ' +\'<a class="btn" href="\'+issueUrl+\'" target="_blank" rel="noopener">&#x2691; Report issue</a>\'',
944
950
  " +'</div>';",
945
951
  " }else if(ev.kind==='PROTECTED'){",
946
952
  " actions='<div class=\"actions\">'",
947
953
  " +'<a class=\"btn\" href=\"'+link+'\">\u2192 Open in VS Code</a>'",
948
954
  " +'<button class=\"btn btn-primary\" onclick=\"handleAction(this,\\'ack\\',\\''+ev.stableId+'\\')\">&#x2713; Acknowledged \u2014 will investigate</button>'",
949
- ' +\'<a class="btn" href="\'+issueUrl+\'" target="_blank" rel="noopener">&#x2691; File regression</a>\'',
950
955
  " +'</div>';",
951
956
  " }else{",
952
957
  " actions='<div class=\"actions\">'",
953
958
  " +'<a class=\"btn btn-primary\" href=\"'+link+'\">\u2192 Open in VS Code</a>'",
954
- ' +\'<a class="btn" href="\'+issueUrl+\'" target="_blank" rel="noopener">&#x2691; Report issue</a>\'',
955
959
  " +'</div>';",
956
960
  " }",
957
961
  " var alts=(ev.kind==='HEALED'&&ev.aiAlternatives&&ev.aiAlternatives.length)",
@@ -989,7 +993,7 @@ const REPORT_JS = [
989
993
  " +'<div class=\"ecard-meta\">'+(ev.testTitle?esc(ev.testTitle)+' &nbsp;\xb7&nbsp; ':'')",
990
994
  " +'<a href=\"'+link+'\">'+esc(ev.sourceFile)+':'+ev.sourceLine+'</a>'+tags+' &nbsp;\xb7&nbsp; '+fmt(ev.timestamp)+'</div>'",
991
995
  " +'</div>'",
992
- " +confBadge(ev.aiConfidence)",
996
+ " +headerBadge(ev)",
993
997
  ' +\'<svg class="chev" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2"><polyline points="5 3 11 8 5 13"/></svg>\'',
994
998
  " +'</div>'",
995
999
  " +'<div class=\"ecard-body\">'+renderCardBody(ev)+'</div>'",
@@ -107,6 +107,17 @@ export declare class ReportGenerator {
107
107
  * events pass. Throws on the first violation.
108
108
  */
109
109
  static validateAll(events: ReadonlyArray<unknown>): HealEvent[];
110
+ /**
111
+ * Returns sanitised copies of the events ready for the report UI:
112
+ * • Playwright `internal:` locators → readable `page.getBy...` form,
113
+ * across selector fields, alternatives, free-text, and contentChange,
114
+ * so no implementation detail leaks into the HTML or the JSON payload.
115
+ * • PROTECTED events missing a structured auditor block get one
116
+ * reconstructed from `reason`, so the Auditor panel renders the real
117
+ * rejection context instead of "not consulted".
118
+ * Pure: never mutates the input buffer (returns shallow clones).
119
+ */
120
+ static normalizeEventsForReport(events: ReadonlyArray<HealEvent>): HealEvent[];
110
121
  /**
111
122
  * Pure aggregate counters. No clock, no IO — trivially pinnable in tests.
112
123
  */
@@ -1 +1 @@
1
- {"version":3,"file":"ReportGenerator.d.ts","sourceRoot":"","sources":["../../src/services/ReportGenerator.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ReportGenerator.d.ts","sourceRoot":"","sources":["../../src/services/ReportGenerator.ts"],"names":[],"mappings":"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"}
@@ -16,6 +16,7 @@
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  exports.ReportGenerator = exports.MANDATORY_FIELDS_BY_KIND = exports.TIME_SAVED_CONSTANTS = void 0;
18
18
  const SelaError_1 = require("../errors/SelaError");
19
+ const locatorFormat_1 = require("../utils/locatorFormat");
19
20
  // ─────────────────────────────────────────────────────────────────────────────
20
21
  // TIME-SAVED HEURISTICS (minutes per event kind)
21
22
  // Exposed as a constant so tests can pin the math against the same multipliers
@@ -95,6 +96,39 @@ function mean(values) {
95
96
  return null;
96
97
  return Math.round(values.reduce((a, b) => a + b, 0) / values.length);
97
98
  }
99
+ /**
100
+ * Reconstruct a display {@link AuditorBlock} from a PROTECTED event's
101
+ * `reason` string when the engine failed to bind a structured auditor block
102
+ * (the "Auditor black hole"). Matches the canonical SafetyGuard format:
103
+ * "Intent Auditor - INCONSISTENT: <reason> [inversion: identity]"
104
+ * Confidence is intentionally left `undefined` so the gauge renders "n/a"
105
+ * rather than fabricating a percentage.
106
+ */
107
+ function deriveAuditorFromReason(reason) {
108
+ if (typeof reason !== "string")
109
+ return null;
110
+ const m = /Intent Auditor\s*-\s*(CONSISTENT|INCONSISTENT|SUSPICIOUS):\s*([\s\S]*?)(?:\s*\[inversion:\s*(\w+)\])?\s*$/i.exec(reason);
111
+ if (!m)
112
+ return null;
113
+ return {
114
+ verdict: m[1].toUpperCase(),
115
+ confidence: undefined,
116
+ reason: m[2].trim(),
117
+ inversionType: m[3]
118
+ ? m[3].toLowerCase()
119
+ : null,
120
+ };
121
+ }
122
+ // Fields scrubbed as whole Playwright locators (CSS strings pass through).
123
+ const LOCATOR_FIELDS = ["oldSelector", "newSelector", "candidateNewSelector"];
124
+ // Free-text fields that may embed internal locators inline.
125
+ const FREETEXT_FIELDS = [
126
+ "reason",
127
+ "aiExplanation",
128
+ "oldCodeLine",
129
+ "newCodeLine",
130
+ "gitUnifiedDiff",
131
+ ];
98
132
  // ─────────────────────────────────────────────────────────────────────────────
99
133
  // ReportGenerator
100
134
  // ─────────────────────────────────────────────────────────────────────────────
@@ -132,6 +166,56 @@ class ReportGenerator {
132
166
  ReportGenerator.validateEvent(e);
133
167
  return events;
134
168
  }
169
+ // ── Normalisation (locator scrub + auditor binding) ────────────────────────
170
+ /**
171
+ * Returns sanitised copies of the events ready for the report UI:
172
+ * • Playwright `internal:` locators → readable `page.getBy...` form,
173
+ * across selector fields, alternatives, free-text, and contentChange,
174
+ * so no implementation detail leaks into the HTML or the JSON payload.
175
+ * • PROTECTED events missing a structured auditor block get one
176
+ * reconstructed from `reason`, so the Auditor panel renders the real
177
+ * rejection context instead of "not consulted".
178
+ * Pure: never mutates the input buffer (returns shallow clones).
179
+ */
180
+ static normalizeEventsForReport(events) {
181
+ return events.map((ev) => {
182
+ const e = { ...ev };
183
+ for (const f of LOCATOR_FIELDS) {
184
+ const v = e[f];
185
+ if (typeof v === "string")
186
+ e[f] = (0, locatorFormat_1.prettifyLocator)(v);
187
+ }
188
+ if (Array.isArray(e["aiAlternatives"])) {
189
+ e["aiAlternatives"] = e["aiAlternatives"].map((a) => typeof a === "string" ? (0, locatorFormat_1.prettifyLocator)(a) : a);
190
+ }
191
+ for (const f of FREETEXT_FIELDS) {
192
+ const v = e[f];
193
+ if (typeof v === "string")
194
+ e[f] = (0, locatorFormat_1.scrubInlineLocators)(v);
195
+ }
196
+ const cc = e["contentChange"];
197
+ if (cc && typeof cc === "object") {
198
+ e["contentChange"] = {
199
+ ...cc,
200
+ oldText: typeof cc.oldText === "string"
201
+ ? (0, locatorFormat_1.scrubInlineLocators)(cc.oldText)
202
+ : cc.oldText,
203
+ newText: typeof cc.newText === "string"
204
+ ? (0, locatorFormat_1.scrubInlineLocators)(cc.newText)
205
+ : cc.newText,
206
+ };
207
+ }
208
+ // The "black hole": engine bound no auditor block. Reconstruct a display
209
+ // one from `reason` so the panel explains the block rather than claiming
210
+ // the auditor was never consulted. A populated block is left untouched.
211
+ if (e["kind"] === "PROTECTED" && e["auditor"] == null) {
212
+ const derived = deriveAuditorFromReason(e["reason"]);
213
+ if (derived)
214
+ e["auditor"] = derived;
215
+ }
216
+ return e;
217
+ });
218
+ }
135
219
  // ── Stats ─────────────────────────────────────────────────────────────────
136
220
  /**
137
221
  * Pure aggregate counters. No clock, no IO — trivially pinnable in tests.
@@ -243,20 +327,23 @@ class ReportGenerator {
243
327
  */
244
328
  static generate(events, opts = {}) {
245
329
  const validated = ReportGenerator.validateAll(events);
330
+ // Sanitise BEFORE serialisation so both the embedded payload and the
331
+ // HTML render consume scrubbed locators + bound auditor blocks.
332
+ const normalized = ReportGenerator.normalizeEventsForReport(validated);
246
333
  const now = opts.now ?? (() => new Date());
247
- const stats = ReportGenerator.computeStats(validated);
248
- const currentRun = ReportGenerator.buildCurrentHistoricalRun(validated, stats, { runId: opts.runId, now });
334
+ const stats = ReportGenerator.computeStats(normalized);
335
+ const currentRun = ReportGenerator.buildCurrentHistoricalRun(normalized, stats, { runId: opts.runId, now });
249
336
  // Merge history: drop any prior entry with the same id, append current,
250
337
  // then keep only the most recent HISTORY_WINDOW entries.
251
338
  const HISTORY_WINDOW = 14;
252
339
  const prior = (opts.historicalRuns ?? []).filter((r) => r.id !== currentRun.id);
253
340
  const historical_runs = [...prior, currentRun].slice(-HISTORY_WINDOW);
254
- const roi = ReportGenerator.computeRoiMetrics(validated, historical_runs, stats);
341
+ const roi = ReportGenerator.computeRoiMetrics(normalized, historical_runs, stats);
255
342
  return {
256
343
  generatedAt: now().toISOString(),
257
344
  startedAt: opts.startedAt ?? now().toISOString(),
258
345
  cwd: opts.cwd ?? process.cwd(),
259
- events: validated,
346
+ events: normalized,
260
347
  stats,
261
348
  roi,
262
349
  historical_runs,
@@ -76,6 +76,14 @@ export interface SafetyDecision {
76
76
  meta?: {
77
77
  auditorVerdict?: "CONSISTENT" | "SUSPICIOUS" | "INCONSISTENT";
78
78
  auditorConfidence?: number;
79
+ /**
80
+ * Human-readable auditor reasoning, forwarded so the engine can bind it
81
+ * onto the ProtectedEvent for the report's Auditor panel (instead of
82
+ * "not consulted").
83
+ */
84
+ auditorReason?: string;
85
+ /** Semantic inversion category when the verdict is INCONSISTENT. */
86
+ auditorInversionType?: "polarity" | "quantitative" | "state" | "identity" | null;
79
87
  /** Score breakdown forwarded from IntentAuditor for engine-level summary logging. */
80
88
  auditScoreBreakdown?: {
81
89
  rawConfidence: number;
@@ -1 +1 @@
1
- {"version":3,"file":"SafetyGuard.d.ts","sourceRoot":"","sources":["../../src/services/SafetyGuard.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAGtD,OAAO,EAEL,cAAc,EACf,MAAM,yBAAyB,CAAC;AAMjC,MAAM,MAAM,QAAQ,GAAG,WAAW,GAAG,QAAQ,CAAC;AAE9C,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,yEAAyE;IACzE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IACrD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4EAA4E;IAC5E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oEAAoE;IACpE,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,0EAA0E;IAC1E,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,uEAAuE;IACvE,YAAY,CAAC,EAAE,YAAY,EAAE,CAAC;IAC9B,6DAA6D;IAC7D,WAAW,CAAC,EAAE,UAAU,CAAC;IACzB,4CAA4C;IAC5C,WAAW,CAAC,EAAE,YAAY,EAAE,CAAC;IAC7B,+CAA+C;IAC/C,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB;AAED,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,iBAAiB,GAAG,WAAW,GAAG,SAAS,CAAC;AAE7E;;;;;;;;;;GAUG;AACH,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE5D,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,GAAG,iBAAiB,CAUlE;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,SAAS,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,OAAO,CAAC;IACpB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC;;;;;OAKG;IACH,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,mEAAmE;IACnE,IAAI,CAAC,EAAE;QACL,cAAc,CAAC,EAAE,YAAY,GAAG,YAAY,GAAG,cAAc,CAAC;QAC9D,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAC3B,qFAAqF;QACrF,mBAAmB,CAAC,EAAE;YACpB,aAAa,EAAE,MAAM,CAAC;YACtB,OAAO,EAAE,MAAM,CAAC;YAChB,KAAK,EAAE,MAAM,CAAC;YACd,kBAAkB,EAAE,MAAM,CAAC;SAC5B,CAAC;KACH,CAAC;CACH;AAkBD;;;;;;GAMG;AACH,MAAM,WAAW,kBAAkB;IACjC,UAAU,CAAC,EAAE,kBAAkB,CAAC;IAChC,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,UAAU,CAAqB;IAEvC;;;;OAIG;gBACS,GAAG,CAAC,EAAE,kBAAkB,GAAG,kBAAkB;IAMzD,OAAO,CAAC,MAAM,CAAC,aAAa;IAqBtB,QAAQ,CACZ,OAAO,EAAE,WAAW,EACpB,KAAK,EAAE,YAAY,GAClB,OAAO,CAAC,cAAc,CAAC;IAgC1B,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc;CAGrD"}
1
+ {"version":3,"file":"SafetyGuard.d.ts","sourceRoot":"","sources":["../../src/services/SafetyGuard.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAGtD,OAAO,EAEL,cAAc,EACf,MAAM,yBAAyB,CAAC;AAMjC,MAAM,MAAM,QAAQ,GAAG,WAAW,GAAG,QAAQ,CAAC;AAE9C,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,yEAAyE;IACzE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IACrD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4EAA4E;IAC5E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oEAAoE;IACpE,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,0EAA0E;IAC1E,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,uEAAuE;IACvE,YAAY,CAAC,EAAE,YAAY,EAAE,CAAC;IAC9B,6DAA6D;IAC7D,WAAW,CAAC,EAAE,UAAU,CAAC;IACzB,4CAA4C;IAC5C,WAAW,CAAC,EAAE,YAAY,EAAE,CAAC;IAC7B,+CAA+C;IAC/C,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB;AAED,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,iBAAiB,GAAG,WAAW,GAAG,SAAS,CAAC;AAE7E;;;;;;;;;;GAUG;AACH,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE5D,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,GAAG,iBAAiB,CAUlE;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,SAAS,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,OAAO,CAAC;IACpB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC;;;;;OAKG;IACH,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,mEAAmE;IACnE,IAAI,CAAC,EAAE;QACL,cAAc,CAAC,EAAE,YAAY,GAAG,YAAY,GAAG,cAAc,CAAC;QAC9D,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAC3B;;;;WAIG;QACH,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,oEAAoE;QACpE,oBAAoB,CAAC,EACjB,UAAU,GACV,cAAc,GACd,OAAO,GACP,UAAU,GACV,IAAI,CAAC;QACT,qFAAqF;QACrF,mBAAmB,CAAC,EAAE;YACpB,aAAa,EAAE,MAAM,CAAC;YACtB,OAAO,EAAE,MAAM,CAAC;YAChB,KAAK,EAAE,MAAM,CAAC;YACd,kBAAkB,EAAE,MAAM,CAAC;SAC5B,CAAC;KACH,CAAC;CACH;AAkBD;;;;;;GAMG;AACH,MAAM,WAAW,kBAAkB;IACjC,UAAU,CAAC,EAAE,kBAAkB,CAAC;IAChC,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,UAAU,CAAqB;IAEvC;;;;OAIG;gBACS,GAAG,CAAC,EAAE,kBAAkB,GAAG,kBAAkB;IAMzD,OAAO,CAAC,MAAM,CAAC,aAAa;IAqBtB,QAAQ,CACZ,OAAO,EAAE,WAAW,EACpB,KAAK,EAAE,YAAY,GAClB,OAAO,CAAC,cAAc,CAAC;IAgC1B,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc;CAGrD"}
@@ -1 +1 @@
1
- {"version":3,"file":"IntentAuditorRule.d.ts","sourceRoot":"","sources":["../../../../src/services/safety/rules/IntentAuditorRule.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,UAAU,EAAqC,MAAM,oBAAoB,CAAC;AAExF,eAAO,MAAM,iBAAiB,EAAE,UAyF/B,CAAC"}
1
+ {"version":3,"file":"IntentAuditorRule.d.ts","sourceRoot":"","sources":["../../../../src/services/safety/rules/IntentAuditorRule.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,UAAU,EAAqC,MAAM,oBAAoB,CAAC;AAExF,eAAO,MAAM,iBAAiB,EAAE,UAoG/B,CAAC"}
@@ -50,12 +50,23 @@ exports.IntentAuditorRule = {
50
50
  ? ` [inversion: ${auditVerdict.inversionType}]`
51
51
  : ""),
52
52
  canProceed: false,
53
+ // Forward the full verdict so the engine binds a structured auditor
54
+ // block onto the ProtectedEvent - otherwise the report renders
55
+ // "Intent Auditor was not consulted" for a block the auditor caused.
56
+ meta: {
57
+ auditorVerdict: "INCONSISTENT",
58
+ auditorConfidence: auditVerdict.confidence,
59
+ auditorReason: auditVerdict.reason,
60
+ auditorInversionType: auditVerdict.inversionType,
61
+ },
53
62
  };
54
63
  }
55
64
  if (auditVerdict.verdict === "SUSPICIOUS") {
56
65
  const meta = {
57
66
  auditorVerdict: "SUSPICIOUS",
58
67
  auditorConfidence: auditVerdict.confidence,
68
+ auditorReason: auditVerdict.reason,
69
+ auditorInversionType: auditVerdict.inversionType,
59
70
  };
60
71
  // Block by default in assertion mode; allowSuspicious (loose policy) downgrades to REQUIRES_REVIEW
61
72
  if (!ctx.thresholds.allowSuspicious && ctx.mode === "assertion") {
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Convert a single Playwright internal locator string into its readable
3
+ * `page.getBy...` form. Chains (segments joined by " >> ") are preserved as
4
+ * a method chain. Non-internal strings are returned unchanged.
5
+ */
6
+ export declare function prettifyLocator(raw: string): string;
7
+ /**
8
+ * Scrub Playwright internal locators that appear *inside* free-text strings
9
+ * (rejection reasons, AI explanations, diff bodies). Replaces each embedded
10
+ * `internal:...` token with its readable form, then applies a final guard
11
+ * pass so no residual `internal:` prefix can survive anywhere.
12
+ */
13
+ export declare function scrubInlineLocators(text: string): string;
14
+ //# sourceMappingURL=locatorFormat.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"locatorFormat.d.ts","sourceRoot":"","sources":["../../src/utils/locatorFormat.ts"],"names":[],"mappings":"AAeA;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAInD;AA2CD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAUxD"}
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ // src/utils/locatorFormat.ts
3
+ //
4
+ // Translate Playwright *internal* engine locators into the readable
5
+ // `page.getBy...` API form for human-facing surfaces (the HTML report).
6
+ //
7
+ // Playwright resolves `page.getByRole('button', { name: 'X' })` down to an
8
+ // internal selector string such as `internal:role=button[name="X"i]`. Those
9
+ // strings are an implementation detail and must NEVER reach the report UI —
10
+ // not in headers, diffs, DNA evidence, or the embedded JSON payload.
11
+ //
12
+ // This is the inverse of the getByX → internal mapping in
13
+ // `src/fixtures/index.ts`. Kept here as a pure, dependency-free module so it
14
+ // is trivially unit-testable and can run server-side before the payload is
15
+ // serialised.
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.prettifyLocator = prettifyLocator;
18
+ exports.scrubInlineLocators = scrubInlineLocators;
19
+ /**
20
+ * Convert a single Playwright internal locator string into its readable
21
+ * `page.getBy...` form. Chains (segments joined by " >> ") are preserved as
22
+ * a method chain. Non-internal strings are returned unchanged.
23
+ */
24
+ function prettifyLocator(raw) {
25
+ if (typeof raw !== "string" || !raw.includes("internal:"))
26
+ return raw;
27
+ const parts = raw.split(" >> ").map(prettifySegment);
28
+ return parts.map((p, i) => (i === 0 ? `page.${p}` : p)).join(".");
29
+ }
30
+ function prettifySegment(seg) {
31
+ const s = seg.trim();
32
+ let m;
33
+ // internal:role=button[name="Update System"i] → getByRole('button', { name: 'Update System' })
34
+ if ((m = /^internal:role=([a-z-]+)(?:\[name="(.*?)"[is]*\])?/i.exec(s))) {
35
+ return m[2]
36
+ ? `getByRole('${m[1]}', { name: '${m[2]}' })`
37
+ : `getByRole('${m[1]}')`;
38
+ }
39
+ // internal:text / label / placeholder = "..."i (quotes + trailing flags optional)
40
+ if ((m = /^internal:(text|label|placeholder)="?(.*?)"?[is]*$/i.exec(s))) {
41
+ const fn = {
42
+ text: "getByText",
43
+ label: "getByLabel",
44
+ placeholder: "getByPlaceholder",
45
+ }[m[1].toLowerCase()];
46
+ return `${fn}('${m[2]}')`;
47
+ }
48
+ // internal:testid=[data-testid="x"] or internal:testid=x
49
+ if ((m = /^internal:testid=\[?(?:[a-z-]+=)?"?(.*?)"?\]?$/i.exec(s))) {
50
+ return `getByTestId('${m[1]}')`;
51
+ }
52
+ // internal:attr=[title="..."i] / [alt="..."i]
53
+ if ((m = /^internal:attr=\[(title|alt)="(.*?)"[is]*\]/i.exec(s))) {
54
+ return m[1].toLowerCase() === "alt"
55
+ ? `getByAltText('${m[2]}')`
56
+ : `getByTitle('${m[2]}')`;
57
+ }
58
+ // Unknown internal form: strip the prefix so the implementation detail
59
+ // never leaks, falling back to a generic locator() representation.
60
+ if (s.startsWith("internal:")) {
61
+ return `locator(${JSON.stringify(s.replace(/^internal:/, ""))})`;
62
+ }
63
+ return s;
64
+ }
65
+ /**
66
+ * Scrub Playwright internal locators that appear *inside* free-text strings
67
+ * (rejection reasons, AI explanations, diff bodies). Replaces each embedded
68
+ * `internal:...` token with its readable form, then applies a final guard
69
+ * pass so no residual `internal:` prefix can survive anywhere.
70
+ */
71
+ function scrubInlineLocators(text) {
72
+ if (typeof text !== "string" || !text.includes("internal:"))
73
+ return text;
74
+ return text
75
+ .replace(/internal:role=[a-z-]+(?:\[name="[^"]*"[is]*\])?/gi, (mm) => prettifyLocator(mm))
76
+ .replace(/internal:(?:text|label|placeholder|testid|attr)=\S+/gi, (mm) => prettifyLocator(mm))
77
+ .replace(/internal:/g, ""); // final guard — never leak the prefix
78
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sela-core",
3
- "version": "0.1.0-alpha.1",
3
+ "version": "0.1.0-alpha.2",
4
4
  "description": "AI self-healing Playwright wrapper - drop-in replacement for @playwright/test",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -21,6 +21,26 @@
21
21
  "bin": {
22
22
  "sela": "bin/sela.js"
23
23
  },
24
+ "scripts": {
25
+ "sela": "tsx src/cli/index.ts",
26
+ "build": "tsc -p tsconfig.build.json",
27
+ "build:watch": "tsc -p tsconfig.build.json --watch",
28
+ "prepublishOnly": "npm run build",
29
+ "test": "npx playwright test demo.spec.ts --headed --retries=0",
30
+ "test-shadow": "npx playwright test shadow.spec.ts --headed --retries=0",
31
+ "test-core": "npx playwright test tests/Core/CoreExample.spec.ts --headed --retries=0",
32
+ "test-DNA": "npx playwright test tests/DnaSurvival/DnaSurvival.spec.ts --headed --retries=0",
33
+ "test-advanced": "npx playwright test tests/AdvancedLogic/AdvancedLogic.spec.ts --headed --retries=0",
34
+ "test-isolation": "npx playwright test tests/Isolation/Isolation.spec.ts --headed --retries=0",
35
+ "test-playwright-actions": "npx playwright test tests/PlaywrightActions/BasicActions/BasicActions.spec.ts --headed --retries=0",
36
+ "test-forms-actions": "npx playwright test tests/PlaywrightActions/FormsActions/FormsActions.spec.ts --headed --retries=0",
37
+ "test-locators-actions": "npx playwright test tests/PlaywrightActions/LocatorsActions/LocatorsActions.spec.ts --headed --retries=0",
38
+ "test-config": "npx playwright test tests/TestConfig/TestConfig.spec.ts --headed --retries=0",
39
+ "test-safety-guard": "npx playwright test tests/SafetyGuard/SafetyGuard.spec.ts --headed --retries=0",
40
+ "test-improve-methods": "npx playwright test tests/ImproveMethods/ImproveMethods.spec.ts --headed --retries=0",
41
+ "test-shadow-dom": "npx playwright test tests/ShadowDom/ShadowDom.spec.ts --headed --retries=0",
42
+ "test-ut": "npx vitest run tests/unit/safetyguard/ tests/unit/engine/ tests/unit/ast/ tests/unit/proxy/ tests/unit/_harness/ tests/unit/config/ tests/unit/services/ tests/unit/reporter/ tests/unit/utils/"
43
+ },
24
44
  "author": "",
25
45
  "license": "ISC",
26
46
  "peerDependencies": {
@@ -48,24 +68,5 @@
48
68
  "ts-jest": "^29.4.6",
49
69
  "tsx": "^4.21.0",
50
70
  "vitest": "^4.1.1"
51
- },
52
- "scripts": {
53
- "sela": "tsx src/cli/index.ts",
54
- "build": "tsc -p tsconfig.build.json",
55
- "build:watch": "tsc -p tsconfig.build.json --watch",
56
- "test": "npx playwright test demo.spec.ts --headed --retries=0",
57
- "test-shadow": "npx playwright test shadow.spec.ts --headed --retries=0",
58
- "test-core": "npx playwright test tests/Core/CoreExample.spec.ts --headed --retries=0",
59
- "test-DNA": "npx playwright test tests/DnaSurvival/DnaSurvival.spec.ts --headed --retries=0",
60
- "test-advanced": "npx playwright test tests/AdvancedLogic/AdvancedLogic.spec.ts --headed --retries=0",
61
- "test-isolation": "npx playwright test tests/Isolation/Isolation.spec.ts --headed --retries=0",
62
- "test-playwright-actions": "npx playwright test tests/PlaywrightActions/BasicActions/BasicActions.spec.ts --headed --retries=0",
63
- "test-forms-actions": "npx playwright test tests/PlaywrightActions/FormsActions/FormsActions.spec.ts --headed --retries=0",
64
- "test-locators-actions": "npx playwright test tests/PlaywrightActions/LocatorsActions/LocatorsActions.spec.ts --headed --retries=0",
65
- "test-config": "npx playwright test tests/TestConfig/TestConfig.spec.ts --headed --retries=0",
66
- "test-safety-guard": "npx playwright test tests/SafetyGuard/SafetyGuard.spec.ts --headed --retries=0",
67
- "test-improve-methods": "npx playwright test tests/ImproveMethods/ImproveMethods.spec.ts --headed --retries=0",
68
- "test-shadow-dom": "npx playwright test tests/ShadowDom/ShadowDom.spec.ts --headed --retries=0",
69
- "test-ut": "npx vitest run tests/unit/safetyguard/ tests/unit/engine/ tests/unit/ast/ tests/unit/proxy/ tests/unit/_harness/ tests/unit/config/ tests/unit/services/ tests/unit/reporter/ tests/unit/utils/"
70
71
  }
71
- }
72
+ }