sela-core 1.0.7 → 1.0.9
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 +27 -17
- package/dist/cli/ErrorHandler.d.ts +1 -1
- package/dist/cli/ErrorHandler.js +14 -14
- package/dist/cli/commands/bulk.d.ts +1 -1
- package/dist/cli/commands/bulk.d.ts.map +1 -1
- package/dist/cli/commands/bulk.js +51 -33
- package/dist/cli/commands/init.d.ts +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +26 -18
- package/dist/cli/commands/merge.d.ts +13 -0
- package/dist/cli/commands/merge.d.ts.map +1 -0
- package/dist/cli/commands/merge.js +119 -0
- package/dist/cli/commands/showReport.d.ts +1 -1
- package/dist/cli/commands/showReport.d.ts.map +1 -1
- package/dist/cli/commands/showReport.js +12 -10
- package/dist/cli/commands/status.d.ts +1 -1
- package/dist/cli/commands/status.d.ts.map +1 -1
- package/dist/cli/commands/status.js +8 -8
- package/dist/cli/commands/sync.d.ts +1 -1
- package/dist/cli/commands/sync.d.ts.map +1 -1
- package/dist/cli/commands/sync.js +38 -30
- package/dist/cli/index.js +12 -9
- package/dist/cli/ui/DnaTable.d.ts +1 -1
- package/dist/cli/ui/DnaTable.d.ts.map +1 -1
- package/dist/cli/ui/DnaTable.js +23 -16
- package/dist/cli/ui/RefactorWizard.d.ts +3 -3
- package/dist/cli/ui/RefactorWizard.d.ts.map +1 -1
- package/dist/cli/ui/RefactorWizard.js +108 -82
- package/dist/config/ConfigLoader.d.ts +2 -2
- package/dist/config/ConfigLoader.d.ts.map +1 -1
- package/dist/config/ConfigLoader.js +15 -15
- package/dist/config/DryRunGuard.d.ts +1 -1
- package/dist/config/DryRunGuard.js +7 -7
- package/dist/config/SelaConfig.d.ts +13 -17
- package/dist/config/SelaConfig.d.ts.map +1 -1
- package/dist/config/SelaConfig.js +32 -26
- package/dist/engine/HealingRegistry.d.ts +1 -1
- package/dist/engine/HealingRegistry.js +3 -3
- package/dist/engine/SelaEngine.d.ts +1 -1
- package/dist/engine/SelaEngine.d.ts.map +1 -1
- package/dist/engine/SelaEngine.js +117 -51
- package/dist/errors/SelaError.d.ts +8 -1
- package/dist/errors/SelaError.d.ts.map +1 -1
- package/dist/errors/SelaError.js +14 -7
- package/dist/fixtures/expectProxy.d.ts +2 -2
- package/dist/fixtures/expectProxy.js +16 -16
- package/dist/fixtures/index.d.ts +18 -2
- package/dist/fixtures/index.d.ts.map +1 -1
- package/dist/fixtures/index.js +47 -27
- package/dist/fixtures/moduleExpect.js +7 -7
- package/dist/fixtures/proxyTag.d.ts +1 -1
- package/dist/fixtures/proxyTag.js +4 -4
- package/dist/index.d.ts +8 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/reporter/SelaReporter.d.ts +45 -12
- package/dist/reporter/SelaReporter.d.ts.map +1 -1
- package/dist/reporter/SelaReporter.js +139 -39
- package/dist/services/ASTSourceUpdater.d.ts +48 -3
- package/dist/services/ASTSourceUpdater.d.ts.map +1 -1
- package/dist/services/ASTSourceUpdater.js +283 -105
- package/dist/services/AnchorResolver.d.ts +157 -0
- package/dist/services/AnchorResolver.d.ts.map +1 -0
- package/dist/services/AnchorResolver.js +289 -0
- package/dist/services/ArgumentTypeAnalyzer.js +7 -7
- package/dist/services/BlastRadiusAnalyzer.d.ts.map +1 -1
- package/dist/services/BlastRadiusAnalyzer.js +5 -2
- package/dist/services/ChainValidator.js +22 -22
- package/dist/services/CrossFileHealer.js +7 -7
- package/dist/services/DecisionEngine.d.ts +51 -0
- package/dist/services/DecisionEngine.d.ts.map +1 -0
- package/dist/services/DecisionEngine.js +260 -0
- package/dist/services/DefinitionTracer.d.ts +1 -1
- package/dist/services/DefinitionTracer.d.ts.map +1 -1
- package/dist/services/DefinitionTracer.js +28 -13
- package/dist/services/DnaEditorService.d.ts +2 -2
- package/dist/services/DnaEditorService.d.ts.map +1 -1
- package/dist/services/DnaEditorService.js +81 -62
- package/dist/services/DnaIndexService.d.ts +1 -1
- package/dist/services/DnaIndexService.d.ts.map +1 -1
- package/dist/services/DnaIndexService.js +14 -14
- package/dist/services/HealReportService.d.ts +31 -55
- package/dist/services/HealReportService.d.ts.map +1 -1
- package/dist/services/HealReportService.js +864 -1240
- package/dist/services/HealingAdvisory.d.ts +9 -1
- package/dist/services/HealingAdvisory.d.ts.map +1 -1
- package/dist/services/HealingAdvisory.js +9 -1
- package/dist/services/HealingCacheService.d.ts +8 -8
- package/dist/services/HealingCacheService.d.ts.map +1 -1
- package/dist/services/HealingCacheService.js +24 -24
- package/dist/services/HealthReportService.d.ts +1 -1
- package/dist/services/HealthReportService.d.ts.map +1 -1
- package/dist/services/HealthReportService.js +25 -25
- package/dist/services/InitializerUpdater.d.ts.map +1 -1
- package/dist/services/InitializerUpdater.js +12 -2
- package/dist/services/IntentAuditor.d.ts.map +1 -1
- package/dist/services/IntentAuditor.js +32 -21
- package/dist/services/InteractiveReview.d.ts +8 -17
- package/dist/services/InteractiveReview.d.ts.map +1 -1
- package/dist/services/InteractiveReview.js +34 -124
- package/dist/services/LLMService.d.ts +1 -1
- package/dist/services/LLMService.js +16 -16
- package/dist/services/MutationApplier.d.ts +55 -0
- package/dist/services/MutationApplier.d.ts.map +1 -0
- package/dist/services/MutationApplier.js +322 -0
- package/dist/services/PRAutomationService.d.ts +2 -2
- package/dist/services/PRAutomationService.d.ts.map +1 -1
- package/dist/services/PRAutomationService.js +43 -39
- package/dist/services/PendingPromptLedger.d.ts +9 -2
- package/dist/services/PendingPromptLedger.d.ts.map +1 -1
- package/dist/services/PendingPromptLedger.js +31 -6
- package/dist/services/ReportGenerator.d.ts +116 -30
- package/dist/services/ReportGenerator.d.ts.map +1 -1
- package/dist/services/ReportGenerator.js +149 -62
- package/dist/services/ReportMergeService.d.ts +95 -0
- package/dist/services/ReportMergeService.d.ts.map +1 -0
- package/dist/services/ReportMergeService.js +0 -0
- package/dist/services/SafetyGuard.d.ts +4 -7
- package/dist/services/SafetyGuard.d.ts.map +1 -1
- package/dist/services/SafetyGuard.js +29 -106
- package/dist/services/SelectorSanitizer.d.ts +52 -0
- package/dist/services/SelectorSanitizer.d.ts.map +1 -0
- package/dist/services/SelectorSanitizer.js +318 -0
- package/dist/services/SnapshotService.js +6 -6
- package/dist/services/SourceLinkService.d.ts +2 -2
- package/dist/services/SourceLinkService.d.ts.map +1 -1
- package/dist/services/SourceLinkService.js +31 -22
- package/dist/services/SourceUpdater.d.ts +1 -1
- package/dist/services/SourceUpdater.d.ts.map +1 -1
- package/dist/services/SourceUpdater.js +44 -26
- package/dist/services/TemplateDiffService.d.ts.map +1 -1
- package/dist/services/TemplateDiffService.js +18 -15
- package/dist/services/TraceBackEngine.d.ts +67 -0
- package/dist/services/TraceBackEngine.d.ts.map +1 -0
- package/dist/services/TraceBackEngine.js +672 -0
- package/dist/services/WorkspaceSnapshotService.d.ts +6 -6
- package/dist/services/WorkspaceSnapshotService.d.ts.map +1 -1
- package/dist/services/WorkspaceSnapshotService.js +10 -12
- package/dist/types/index.d.ts +1 -1
- package/dist/utils/DOMUtils.d.ts +18 -2
- package/dist/utils/DOMUtils.d.ts.map +1 -1
- package/dist/utils/DOMUtils.js +335 -49
- package/dist/utils/IsolatedDiff.d.ts +2 -2
- package/dist/utils/IsolatedDiff.d.ts.map +1 -1
- package/dist/utils/IsolatedDiff.js +13 -10
- package/package.json +2 -2
|
@@ -6,9 +6,18 @@ exports.resolveThresholds = resolveThresholds;
|
|
|
6
6
|
exports.parseEnvBoolean = parseEnvBoolean;
|
|
7
7
|
exports.resolveConfig = resolveConfig;
|
|
8
8
|
const PRESET_MAP = {
|
|
9
|
-
conservative: {
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
conservative: {
|
|
10
|
+
minHealerConfidence: 92,
|
|
11
|
+
auditorStrictness: "strict",
|
|
12
|
+
},
|
|
13
|
+
balanced: {
|
|
14
|
+
minHealerConfidence: 85,
|
|
15
|
+
auditorStrictness: "balanced",
|
|
16
|
+
},
|
|
17
|
+
aggressive: {
|
|
18
|
+
minHealerConfidence: 75,
|
|
19
|
+
auditorStrictness: "loose",
|
|
20
|
+
},
|
|
12
21
|
};
|
|
13
22
|
const STRICTNESS_CONFIDENCE = {
|
|
14
23
|
strict: 90,
|
|
@@ -26,15 +35,13 @@ function resolveThresholds(config) {
|
|
|
26
35
|
// Explicit thresholds always win over preset expansion
|
|
27
36
|
const merged = { ...fromPreset, ...config.thresholds };
|
|
28
37
|
const minHealerConfidence = merged.minHealerConfidence ?? 85;
|
|
29
|
-
const auditorStrictness = merged.auditorStrictness ??
|
|
30
|
-
const assertionSimilarityFloor = merged.assertionSimilarityFloor ?? 0.7;
|
|
38
|
+
const auditorStrictness = merged.auditorStrictness ?? "balanced";
|
|
31
39
|
return {
|
|
32
40
|
confidenceHardStop: minHealerConfidence,
|
|
33
41
|
confidenceReviewThreshold: 95,
|
|
34
|
-
assertionSimilarityFloor,
|
|
35
42
|
auditorConfidenceMin: STRICTNESS_CONFIDENCE[auditorStrictness],
|
|
36
|
-
// allowSuspicious is derived
|
|
37
|
-
allowSuspicious: auditorStrictness ===
|
|
43
|
+
// allowSuspicious is derived - never independently settable to avoid footguns
|
|
44
|
+
allowSuspicious: auditorStrictness === "loose",
|
|
38
45
|
auditorStrictness,
|
|
39
46
|
};
|
|
40
47
|
}
|
|
@@ -42,37 +49,36 @@ function resolvePRAutomation(config) {
|
|
|
42
49
|
const pr = config.prAutomation ?? {};
|
|
43
50
|
// Legacy bridge: autoCommit:true with no explicit prAutomation block
|
|
44
51
|
// → enable prAutomation in directCommit mode. Explicit prAutomation always wins.
|
|
45
|
-
const legacyEnabled = config.autoCommit === true &&
|
|
46
|
-
config.prAutomation === undefined;
|
|
52
|
+
const legacyEnabled = config.autoCommit === true && config.prAutomation === undefined;
|
|
47
53
|
const enabled = pr.enabled ?? legacyEnabled;
|
|
48
|
-
const strategy = pr.strategy ?? (legacyEnabled ?
|
|
54
|
+
const strategy = pr.strategy ?? (legacyEnabled ? "directCommit" : "readOnly");
|
|
49
55
|
return {
|
|
50
56
|
enabled,
|
|
51
57
|
strategy,
|
|
52
|
-
protectedBranches: pr.protectedBranches ?? [
|
|
58
|
+
protectedBranches: pr.protectedBranches ?? ["main", "master", "release"],
|
|
53
59
|
reviewThresholds: {
|
|
54
60
|
minConfidenceForDirectCommit: pr.reviewThresholds?.minConfidenceForDirectCommit ?? 0,
|
|
55
61
|
minAuditorScoreForDirectCommit: pr.reviewThresholds?.minAuditorScoreForDirectCommit ?? 0,
|
|
56
|
-
downgradeTo: pr.reviewThresholds?.downgradeTo ??
|
|
62
|
+
downgradeTo: pr.reviewThresholds?.downgradeTo ?? "draftPR",
|
|
57
63
|
},
|
|
58
|
-
onBugDetected: pr.onBugDetected ??
|
|
59
|
-
branchNameTemplate: pr.branchNameTemplate ??
|
|
60
|
-
titleTemplate: pr.titleTemplate ??
|
|
64
|
+
onBugDetected: pr.onBugDetected ?? "failCI",
|
|
65
|
+
branchNameTemplate: pr.branchNameTemplate ?? "sela/heal-${id}",
|
|
66
|
+
titleTemplate: pr.titleTemplate ?? "fix(sela): heal selector in ${fileName}",
|
|
61
67
|
bodyTemplate: pr.bodyTemplate ?? null,
|
|
62
68
|
labels: pr.labels ?? [],
|
|
63
|
-
reportArtifactPath: pr.reportArtifactPath ??
|
|
69
|
+
reportArtifactPath: pr.reportArtifactPath ?? "sela-report.html",
|
|
64
70
|
};
|
|
65
71
|
}
|
|
66
72
|
// ═══════════════════════════════════════════════════════════════════
|
|
67
|
-
// ENV FLAG PARSING
|
|
73
|
+
// ENV FLAG PARSING - strict, fail-fast
|
|
68
74
|
//
|
|
69
75
|
// Accepted truthy values (case-insensitive): "1", "true", "yes", "on".
|
|
70
76
|
// Accepted falsy values (case-insensitive): "0", "false", "no", "off", "".
|
|
71
77
|
// Anything else → throws so misconfigured CI environments fail loudly
|
|
72
78
|
// instead of silently running the wrong mode.
|
|
73
79
|
// ═══════════════════════════════════════════════════════════════════
|
|
74
|
-
const ENV_TRUTHY = new Set([
|
|
75
|
-
const ENV_FALSY = new Set([
|
|
80
|
+
const ENV_TRUTHY = new Set(["1", "true", "yes", "on"]);
|
|
81
|
+
const ENV_FALSY = new Set(["0", "false", "no", "off", ""]);
|
|
76
82
|
function parseEnvBoolean(varName, rawValue) {
|
|
77
83
|
if (rawValue === undefined)
|
|
78
84
|
return false;
|
|
@@ -89,18 +95,18 @@ function resolveConfig(config, env = process.env) {
|
|
|
89
95
|
// When prAutomation owns the git pipeline, suppress per-heal git side-effects
|
|
90
96
|
// in SourceUpdater (autoCommit + updateStrategy:'branch') to avoid double-commits.
|
|
91
97
|
const suppressLegacyGit = prAutomation.enabled;
|
|
92
|
-
const requestedUpdateStrategy = config.updateStrategy ??
|
|
93
|
-
const updateStrategy = suppressLegacyGit && requestedUpdateStrategy ===
|
|
94
|
-
?
|
|
98
|
+
const requestedUpdateStrategy = config.updateStrategy ?? "inPlace";
|
|
99
|
+
const updateStrategy = suppressLegacyGit && requestedUpdateStrategy === "branch"
|
|
100
|
+
? "inPlace"
|
|
95
101
|
: requestedUpdateStrategy;
|
|
96
102
|
const autoCommit = suppressLegacyGit ? false : (config.autoCommit ?? false);
|
|
97
|
-
const dryRun = parseEnvBoolean(
|
|
98
|
-
const noCache = parseEnvBoolean(
|
|
103
|
+
const dryRun = parseEnvBoolean("SELA_DRY_RUN", env.SELA_DRY_RUN);
|
|
104
|
+
const noCache = parseEnvBoolean("SELA_NO_CACHE", env.SELA_NO_CACHE);
|
|
99
105
|
return {
|
|
100
106
|
thresholds: resolveThresholds(config),
|
|
101
107
|
updateStrategy,
|
|
102
108
|
autoCommit,
|
|
103
|
-
dnaStoragePath: config.dnaStoragePath ??
|
|
109
|
+
dnaStoragePath: config.dnaStoragePath ?? "sela-snapshots",
|
|
104
110
|
prAutomation,
|
|
105
111
|
agenticDiscovery: {
|
|
106
112
|
enabled: config.agenticDiscovery?.enabled ?? false,
|
|
@@ -29,7 +29,7 @@ export declare class HealingRegistry {
|
|
|
29
29
|
/**
|
|
30
30
|
* Resolve a selector within a specific call-site context.
|
|
31
31
|
* Falls back to a context-free lookup ONLY when the fingerprint
|
|
32
|
-
* has no entry
|
|
32
|
+
* has no entry - prevents poisoning while still enabling same-file reuse.
|
|
33
33
|
*/
|
|
34
34
|
resolveSelector(selector: string, ctx?: SelectorContext): string;
|
|
35
35
|
private walkChain;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// src/engine/HealingRegistry.ts
|
|
2
|
+
// src/engine/HealingRegistry.ts - full replacement
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
4
|
exports.HealingRegistry = void 0;
|
|
5
5
|
const crypto_1 = require("crypto");
|
|
@@ -60,7 +60,7 @@ class HealingRegistry {
|
|
|
60
60
|
/**
|
|
61
61
|
* Resolve a selector within a specific call-site context.
|
|
62
62
|
* Falls back to a context-free lookup ONLY when the fingerprint
|
|
63
|
-
* has no entry
|
|
63
|
+
* has no entry - prevents poisoning while still enabling same-file reuse.
|
|
64
64
|
*/
|
|
65
65
|
resolveSelector(selector, ctx) {
|
|
66
66
|
if (ctx) {
|
|
@@ -72,7 +72,7 @@ class HealingRegistry {
|
|
|
72
72
|
return resolved;
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
|
-
return selector; // No global fallback
|
|
75
|
+
return selector; // No global fallback - intentional
|
|
76
76
|
}
|
|
77
77
|
walkChain(selector, store) {
|
|
78
78
|
let current = selector;
|
|
@@ -36,7 +36,7 @@ export declare class SelaEngine {
|
|
|
36
36
|
* `gitUnifiedDiff` payload yet.
|
|
37
37
|
*
|
|
38
38
|
* Carefully skips the diff preamble (--- a/file, +++ b/file, "diff ",
|
|
39
|
-
* "index ", rename headers)
|
|
39
|
+
* "index ", rename headers) - those lines would otherwise be mistaken for
|
|
40
40
|
* a -/+ change pair and produce nonsense like the original "line replaced
|
|
41
41
|
* by itself" output. Real change lines only count once the parser has seen
|
|
42
42
|
* a "@@" hunk header.
|
|
@@ -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;
|
|
1
|
+
{"version":3,"file":"SelaEngine.d.ts","sourceRoot":"","sources":["../../src/engine/SelaEngine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAaxC,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAG7C,OAAO,EAAe,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAmEhE,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;;IAqBnD,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;IAiDnB,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;IA+oBlB,OAAO,CAAC,aAAa;IAYrB,OAAO,CAAC,aAAa;IAerB;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,oBAAoB;IAsB5B;;;;;;;;;;;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;YAqItB,aAAa;IAsB3B,OAAO,CAAC,0BAA0B;IAiClC,OAAO,CAAC,WAAW;IA6Bb,wBAAwB,CAC5B,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM;CAqBnB;AAED,OAAO,EAAE,CAAC"}
|
|
@@ -91,13 +91,13 @@ class SelaEngine {
|
|
|
91
91
|
// pre-scan. Keeping it separate from the SourceUpdater's project avoids
|
|
92
92
|
// polluting that cache with files we may never actually mutate.
|
|
93
93
|
this.astDirectiveScanner = new ASTSourceUpdater_1.ASTSourceUpdater();
|
|
94
|
-
// Local healing cache
|
|
94
|
+
// Local healing cache - short-circuits the LLM call when an identical
|
|
95
95
|
// (filePath, oldSelector) pair was healed safely in a prior session.
|
|
96
96
|
// Disabled when SELA_NO_CACHE=1; reads/writes skipped during dry runs.
|
|
97
97
|
this.healingCache = HealingCacheService_1.sharedHealingCache;
|
|
98
98
|
}
|
|
99
99
|
// ─────────────────────────────────────────────────────────────
|
|
100
|
-
// healArgument
|
|
100
|
+
// healArgument - repairs a wrong action argument (e.g. selectOption)
|
|
101
101
|
// ─────────────────────────────────────────────────────────────
|
|
102
102
|
async healArgument(page, selector, action, oldArgument, stableId, filePath, line) {
|
|
103
103
|
console.log(`[Sela] 🔧 HEALING ARGUMENT for action '${action}': "${oldArgument}"`);
|
|
@@ -130,7 +130,7 @@ CRITICAL: Do NOT return a CSS selector like "#my-select". Return plain text only
|
|
|
130
130
|
return null;
|
|
131
131
|
}
|
|
132
132
|
// ─────────────────────────────────────────────────────────────
|
|
133
|
-
// heal
|
|
133
|
+
// heal - repairs a broken selector
|
|
134
134
|
// ─────────────────────────────────────────────────────────────
|
|
135
135
|
async heal(page, fullSelector, elementSelectorOnly, stableId, filePath, line, healMode = "action") {
|
|
136
136
|
console.log(`\n[Sela] 🛠️ HEALING START for: ${stableId}`);
|
|
@@ -148,7 +148,7 @@ CRITICAL: Do NOT return a CSS selector like "#my-select". Return plain text only
|
|
|
148
148
|
catch (directiveErr) {
|
|
149
149
|
if ((0, SelaError_1.isSelaErrorFrom)(directiveErr, "ASTUpdater") &&
|
|
150
150
|
directiveErr.code === SelaError_1.AST_UPDATER_CODES.HEAL_DISABLED_BY_DIRECTIVE) {
|
|
151
|
-
console.warn(`[Sela] 🛑 Skipped by Developer
|
|
151
|
+
console.warn(`[Sela] 🛑 Skipped by Developer - ${directiveErr.reason}`);
|
|
152
152
|
const skipEvent = {
|
|
153
153
|
kind: "FAILED",
|
|
154
154
|
stableId,
|
|
@@ -218,10 +218,10 @@ If the text has NOT changed, omit the "contentChange" field entirely.`;
|
|
|
218
218
|
return "";
|
|
219
219
|
const lines = [];
|
|
220
220
|
if (anchors.closestLabel) {
|
|
221
|
-
lines.push(`- Label: "${anchors.closestLabel}"
|
|
221
|
+
lines.push(`- Label: "${anchors.closestLabel}" - the UI label or heading associated with this element.`);
|
|
222
222
|
}
|
|
223
223
|
if (anchors.rowAnchor) {
|
|
224
|
-
lines.push(`- Row key: "${anchors.rowAnchor}"
|
|
224
|
+
lines.push(`- Row key: "${anchors.rowAnchor}" - unique cell text identifying the table row this element belonged to.`);
|
|
225
225
|
}
|
|
226
226
|
if (lines.length === 0)
|
|
227
227
|
return "";
|
|
@@ -236,12 +236,12 @@ If the text has NOT changed, omit the "contentChange" field entirely.`;
|
|
|
236
236
|
// pair AND SafetyGuard cleared it, replay the cached fix instead
|
|
237
237
|
// of paying the LLM round-trip. Disabled when SELA_NO_CACHE=1.
|
|
238
238
|
// The synthetic aiFix is fed through SafetyGuard exactly like a
|
|
239
|
-
// fresh LLM response
|
|
239
|
+
// fresh LLM response - cache is a latency optimisation, never a
|
|
240
240
|
// safety bypass.
|
|
241
241
|
let aiFix = null;
|
|
242
242
|
let cacheHit = false;
|
|
243
243
|
// Fingerprint inputs the cache key depends on. Recomputed every
|
|
244
|
-
// heal so a DNA rewrite
|
|
244
|
+
// heal so a DNA rewrite - which changes `dnaContentHash` -
|
|
245
245
|
// automatically invalidates every prior entry without an explicit
|
|
246
246
|
// purge.
|
|
247
247
|
const dnaContentHash = (0, HealingCacheService_1.hashDnaContent)(lastKnownState ?? null);
|
|
@@ -255,13 +255,14 @@ If the text has NOT changed, omit the "contentChange" field entirely.`;
|
|
|
255
255
|
if (!this.config.noCache) {
|
|
256
256
|
const cached = this.healingCache.get(cacheKeyInput);
|
|
257
257
|
if (cached) {
|
|
258
|
-
console.log(`[Sela] 🧊 Cache hit (0ms LLM)
|
|
258
|
+
console.log(`[Sela] 🧊 Cache hit (0ms LLM) - replaying healed selector "${cached.healedSelector}"`);
|
|
259
259
|
aiFix = {
|
|
260
260
|
status: "FIXED",
|
|
261
261
|
new_selector: cached.healedSelector,
|
|
262
262
|
chainSegments: cached.chainSegments,
|
|
263
|
-
segments: cached.segments ??
|
|
264
|
-
|
|
263
|
+
segments: cached.segments ?? [
|
|
264
|
+
{ type: "element", selector: cached.healedSelector },
|
|
265
|
+
],
|
|
265
266
|
confidence: cached.confidence,
|
|
266
267
|
contentChange: cached.contentChange,
|
|
267
268
|
explanation: cached.explanation ?? "Replayed from local healing cache",
|
|
@@ -308,7 +309,7 @@ If the text has NOT changed, omit the "contentChange" field entirely.`;
|
|
|
308
309
|
console.log(`[Sela] 🔍 Step 3.5: Live text fetch skipped (element not yet rendered or non-text)`);
|
|
309
310
|
}
|
|
310
311
|
// ── Step 3.6: Atomic v2 capture of the candidate element ──
|
|
311
|
-
// Single page.evaluate() call
|
|
312
|
+
// Single page.evaluate() call - collects visibility, ancestry,
|
|
312
313
|
// and anchors of the healed element for SafetyGuard use.
|
|
313
314
|
// Returns null on any failure; never blocks the heal.
|
|
314
315
|
const liveSnapshot = await this.snapshotService.captureElement(page, elementOnlySelector, successfulPath);
|
|
@@ -321,7 +322,7 @@ If the text has NOT changed, omit the "contentChange" field entirely.`;
|
|
|
321
322
|
: "";
|
|
322
323
|
const occFlag = liveVisibility?.isOccluded ? " 🫥 OCCLUDED" : "";
|
|
323
324
|
console.log(`[Sela] 🔍 Step 3.6: Live v2 capture complete` +
|
|
324
|
-
`${ghostFlag}${occFlag}
|
|
325
|
+
`${ghostFlag}${occFlag} - ancestry depth: ${liveAncestry?.length ?? 0}`);
|
|
325
326
|
}
|
|
326
327
|
else {
|
|
327
328
|
console.log(`[Sela] 🔍 Step 3.6: Live v2 capture skipped`);
|
|
@@ -355,7 +356,7 @@ If the text has NOT changed, omit the "contentChange" field entirely.`;
|
|
|
355
356
|
}
|
|
356
357
|
// ── Report: PROTECTED (or BLOCKED non-INCONSISTENT) ─────────
|
|
357
358
|
// The auto-fix was blocked. If the auditor verdict is INCONSISTENT
|
|
358
|
-
// this is a "Security Win"
|
|
359
|
+
// this is a "Security Win" - a masked regression was caught.
|
|
359
360
|
const protectedEvent = {
|
|
360
361
|
kind: "PROTECTED",
|
|
361
362
|
stableId,
|
|
@@ -407,17 +408,69 @@ If the text has NOT changed, omit the "contentChange" field entirely.`;
|
|
|
407
408
|
parts.push(`${breakdown.penalty} ancestry drift`);
|
|
408
409
|
if (breakdown.bonus !== 0)
|
|
409
410
|
parts.push(`+${breakdown.bonus} anchor match`);
|
|
410
|
-
console.log(`[Sela] 📊 Score
|
|
411
|
+
console.log(`[Sela] 📊 Score - AI: ${fmtPctEngine(aiFix.confidence)} | ` +
|
|
411
412
|
`Auditor: ${fmtPctEngine(breakdown.adjustedConfidence)} ` +
|
|
412
413
|
`(raw ${fmtPctEngine(breakdown.rawConfidence)}, ${parts.join(", ")})`);
|
|
413
414
|
}
|
|
414
415
|
console.log(`[Sela] 🚀 Rebuilt Full Runtime Selector (Clean): ${newFullSelector}`);
|
|
415
|
-
|
|
416
|
+
// ── PRIMARY: composeHeal trace-back pipeline (Stages 1-4) ──────────
|
|
417
|
+
// Trace the failing call-site to its source declaration and apply a
|
|
418
|
+
// write-disciplined Method-Shift / in-place mutation through the
|
|
419
|
+
// type-safety gate. Restricted to plain in-place writes (commentOnly /
|
|
420
|
+
// branch / autoCommit / dry-run keep the legacy git-aware path).
|
|
421
|
+
// Falls back to the legacy regex/semantic engine on ABORT / PROMPT.
|
|
422
|
+
let updateResult;
|
|
423
|
+
const plainInPlace = (!this.config.updateStrategy ||
|
|
424
|
+
this.config.updateStrategy === "inPlace") &&
|
|
425
|
+
!this.config.autoCommit;
|
|
426
|
+
const resolvedForCompose = SourceUpdater_1.SourceUpdater.resolveFilePath(filePath);
|
|
427
|
+
if (plainInPlace && !DryRunGuard_1.DryRunGuard.active() && resolvedForCompose) {
|
|
428
|
+
try {
|
|
429
|
+
WorkspaceSnapshotService_1.sharedWorkspaceSnapshot.preWrite(resolvedForCompose);
|
|
430
|
+
const composed = ASTSourceUpdater_1.ASTSourceUpdater.composeHeal({
|
|
431
|
+
filePath: resolvedForCompose,
|
|
432
|
+
line,
|
|
433
|
+
newSelector: newFullSelector,
|
|
434
|
+
apply: true,
|
|
435
|
+
applyOptions: { respectDryRun: true },
|
|
436
|
+
});
|
|
437
|
+
const strat = composed.decision?.strategy;
|
|
438
|
+
if (composed.kind === "decision" &&
|
|
439
|
+
composed.outcome?.status === "applied" &&
|
|
440
|
+
(strat === "MUTATE_IN_PLACE" || strat === "FORK_AT_TEST")) {
|
|
441
|
+
WorkspaceSnapshotService_1.sharedWorkspaceSnapshot.postWrite(resolvedForCompose);
|
|
442
|
+
const wl = composed.writtenLine ?? line;
|
|
443
|
+
updateResult = {
|
|
444
|
+
success: true,
|
|
445
|
+
reason: `composeHeal[${strat}]: ${composed.decision.reason}`,
|
|
446
|
+
lineUpdated: wl - 1,
|
|
447
|
+
healedLocatorString: newFullSelector,
|
|
448
|
+
definitionSite: { file: resolvedForCompose, line: wl - 1 },
|
|
449
|
+
blastRadius: composed.decision.blastRadius,
|
|
450
|
+
};
|
|
451
|
+
console.log(`[Sela] 🧠 composeHeal primary path applied (${strat})`);
|
|
452
|
+
}
|
|
453
|
+
else {
|
|
454
|
+
const why = composed.kind === "abort"
|
|
455
|
+
? `abort:${composed.trace.code ?? composed.trace.status}`
|
|
456
|
+
: `${strat ?? "no-decision"}/${composed.outcome?.status ?? "not-applied"}`;
|
|
457
|
+
console.log(`[Sela] ↩️ composeHeal declined (${why}) - falling back to legacy heal`);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
catch (e) {
|
|
461
|
+
console.warn(`[Sela] composeHeal primary failed (${e?.message}) - falling back to legacy heal`);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
// ── FALLBACK: legacy regex/semantic heal (also handles dry-run /
|
|
465
|
+
// commentOnly / branch / autoCommit / cross-file). ──────────────
|
|
466
|
+
if (!updateResult) {
|
|
467
|
+
updateResult = SourceUpdater_1.SourceUpdater.update({ filePath, line }, elementSelectorOnly, newFullSelector, neighborhoodDom, undefined, fullSelector, aiFix.segments, aiFix.contentChange, aiFix.chainSegments, aiFix.originalChainHint, this.config.updateStrategy, this.config.autoCommit);
|
|
468
|
+
}
|
|
416
469
|
// Resolve the line that was actually mutated. Order of precedence:
|
|
417
470
|
// 1. updateResult.lineUpdated (the strategy's literal write location)
|
|
418
471
|
// 2. updateResult.definitionSite.line (semantic JumpToDef target,
|
|
419
472
|
// used when the mutation crossed into a const/variable declaration)
|
|
420
|
-
// 3. line (the original failure line
|
|
473
|
+
// 3. line (the original failure line - last-resort fallback)
|
|
421
474
|
// For cross-file definitionSite.file, we still read the new line from
|
|
422
475
|
// the SAME file that was mutated.
|
|
423
476
|
const defSite = updateResult.definitionSite;
|
|
@@ -430,7 +483,7 @@ If the text has NOT changed, omit the "contentChange" field entirely.`;
|
|
|
430
483
|
line;
|
|
431
484
|
const mutatedFilePath = !sameFileDef && defSite ? defSite.file : filePath;
|
|
432
485
|
// Clean-Room isolated diff. We diff the in-memory snapshot recorded
|
|
433
|
-
// by WorkspaceSnapshotService (contentBefore
|
|
486
|
+
// by WorkspaceSnapshotService (contentBefore - disk state right
|
|
434
487
|
// before Sela's first write to this file) against the post-write
|
|
435
488
|
// disk state. The result contains EXCLUSIVELY Sela's selector swap;
|
|
436
489
|
// any developer dirty edits sit identically in both buffers and
|
|
@@ -441,12 +494,14 @@ If the text has NOT changed, omit the "contentChange" field entirely.`;
|
|
|
441
494
|
// Backward-compat: keep oldCodeLine/newCodeLine populated from the
|
|
442
495
|
// first -/+ pair inside the diff. When git produced no diff (file
|
|
443
496
|
// untracked, git unavailable, …) fall back to a direct disk read of
|
|
444
|
-
// the mutated line
|
|
445
|
-
const oldCodeLine = firstChange.oldLine ??
|
|
446
|
-
|
|
497
|
+
// the mutated line - strictly inferior but preserves old reports.
|
|
498
|
+
const oldCodeLine = firstChange.oldLine ??
|
|
499
|
+
this._safeReadLine(mutatedFilePath, writtenLine);
|
|
500
|
+
const newCodeLine = firstChange.newLine ??
|
|
501
|
+
this._safeReadLine(mutatedFilePath, writtenLine);
|
|
447
502
|
const canonicalSelector = updateResult.healedLocatorString ?? newFullSelector;
|
|
448
503
|
console.log(`[Sela] ✅ Canonical selector (disk == runtime): "${canonicalSelector}"`);
|
|
449
|
-
// Buffer CLI-ready metadata
|
|
504
|
+
// Buffer CLI-ready metadata - merged into DNA at commitUpdates() time.
|
|
450
505
|
// Sanitize filePath: strip any residual "at " stack-trace prefix + whitespace,
|
|
451
506
|
// then resolve to an absolute path before converting to project-relative form.
|
|
452
507
|
const match = filePath.match(/at\s+(.*)/i);
|
|
@@ -515,7 +570,7 @@ If the text has NOT changed, omit the "contentChange" field entirely.`;
|
|
|
515
570
|
contentChange: aiFix.contentChange,
|
|
516
571
|
};
|
|
517
572
|
HealReportService_1.sharedHealReport.record(healedEvent);
|
|
518
|
-
// ── Cache write
|
|
573
|
+
// ── Cache write - persist this safe heal for future sessions ──
|
|
519
574
|
// Best-effort: failures are logged inside the service and never
|
|
520
575
|
// block the heal return. Skipped automatically during dry runs
|
|
521
576
|
// and when SELA_NO_CACHE=1.
|
|
@@ -536,7 +591,7 @@ If the text has NOT changed, omit the "contentChange" field entirely.`;
|
|
|
536
591
|
}
|
|
537
592
|
return canonicalSelector;
|
|
538
593
|
}
|
|
539
|
-
// AI did not return FIXED
|
|
594
|
+
// AI did not return FIXED - record as FAILED.
|
|
540
595
|
const failedEvent = {
|
|
541
596
|
kind: "FAILED",
|
|
542
597
|
stableId,
|
|
@@ -562,9 +617,12 @@ If the text has NOT changed, omit the "contentChange" field entirely.`;
|
|
|
562
617
|
// Prefer the structured instanceof check; fall back to message-substring
|
|
563
618
|
// detection for any legacy throw sites that have not yet been migrated.
|
|
564
619
|
const isSafetyBlock = (0, SelaError_1.isSelaErrorFrom)(error, "SafetyGuard") ||
|
|
565
|
-
(typeof error?.message === "string" &&
|
|
566
|
-
|
|
567
|
-
|
|
620
|
+
(typeof error?.message === "string" &&
|
|
621
|
+
error.message.includes("[Sela-SafetyGuard]")) ||
|
|
622
|
+
(typeof error?.message === "string" &&
|
|
623
|
+
error.message.includes("[SafetyGuard]"));
|
|
624
|
+
const isNotFoundAlreadyRecorded = typeof error?.message === "string" &&
|
|
625
|
+
error.message === "AI could not provide a fix";
|
|
568
626
|
if (!isSafetyBlock && !isNotFoundAlreadyRecorded) {
|
|
569
627
|
const failedEvent = {
|
|
570
628
|
kind: "FAILED",
|
|
@@ -594,14 +652,20 @@ If the text has NOT changed, omit the "contentChange" field entirely.`;
|
|
|
594
652
|
if (!filePath)
|
|
595
653
|
return "";
|
|
596
654
|
const match = filePath.match(/at\s+(.*)/i);
|
|
597
|
-
const cleaned = (match ? match[1] : filePath)
|
|
598
|
-
|
|
655
|
+
const cleaned = (match ? match[1] : filePath)
|
|
656
|
+
.replace(/^\(|\)$/g, "")
|
|
657
|
+
.trim();
|
|
658
|
+
const abs = path.isAbsolute(cleaned)
|
|
659
|
+
? cleaned
|
|
660
|
+
: path.resolve(process.cwd(), cleaned);
|
|
599
661
|
return path.relative(process.cwd(), abs) || abs;
|
|
600
662
|
}
|
|
601
663
|
_safeReadLine(filePath, lineNumber) {
|
|
602
664
|
try {
|
|
603
665
|
const cleaned = this._toReportFile(filePath);
|
|
604
|
-
const abs = path.isAbsolute(cleaned)
|
|
666
|
+
const abs = path.isAbsolute(cleaned)
|
|
667
|
+
? cleaned
|
|
668
|
+
: path.resolve(process.cwd(), cleaned);
|
|
605
669
|
if (!fs.existsSync(abs))
|
|
606
670
|
return "";
|
|
607
671
|
const lines = fs.readFileSync(abs, "utf8").split(/\r?\n/);
|
|
@@ -654,7 +718,7 @@ If the text has NOT changed, omit the "contentChange" field entirely.`;
|
|
|
654
718
|
* `gitUnifiedDiff` payload yet.
|
|
655
719
|
*
|
|
656
720
|
* Carefully skips the diff preamble (--- a/file, +++ b/file, "diff ",
|
|
657
|
-
* "index ", rename headers)
|
|
721
|
+
* "index ", rename headers) - those lines would otherwise be mistaken for
|
|
658
722
|
* a -/+ change pair and produce nonsense like the original "line replaced
|
|
659
723
|
* by itself" output. Real change lines only count once the parser has seen
|
|
660
724
|
* a "@@" hunk header.
|
|
@@ -694,7 +758,7 @@ If the text has NOT changed, omit the "contentChange" field entirely.`;
|
|
|
694
758
|
if (!meta || !meta.auditorVerdict)
|
|
695
759
|
return null;
|
|
696
760
|
// Keep auditor confidence as `undefined` when SafetyGuard did not produce
|
|
697
|
-
// one
|
|
761
|
+
// one - the renderer fmtPct helper turns it into "n/a" rather than 0%.
|
|
698
762
|
const conf = typeof meta.auditorConfidence === "number" &&
|
|
699
763
|
Number.isFinite(meta.auditorConfidence)
|
|
700
764
|
? meta.auditorConfidence
|
|
@@ -766,15 +830,16 @@ If the text has NOT changed, omit the "contentChange" field entirely.`;
|
|
|
766
830
|
_buildReasoningSteps(ctx) {
|
|
767
831
|
const steps = [];
|
|
768
832
|
if (ctx.dnaTag || ctx.dnaText) {
|
|
769
|
-
steps.push(`Recovered DNA baseline
|
|
770
|
-
(ctx.dnaText ? ` with text "${ctx.dnaText}"` : "") +
|
|
833
|
+
steps.push(`Recovered DNA baseline - previously a <${ctx.dnaTag ?? "?"}>` +
|
|
834
|
+
(ctx.dnaText ? ` with text "${ctx.dnaText}"` : "") +
|
|
835
|
+
".");
|
|
771
836
|
}
|
|
772
837
|
if (ctx.successfulPath.length > 0) {
|
|
773
838
|
steps.push(`Resolved DOM context inside ${ctx.successfulPath.length} nested frame(s): ${ctx.successfulPath.join(" >> ")}.`);
|
|
774
839
|
}
|
|
775
840
|
const firstSeg = ctx.aiFix.chainSegments?.[0];
|
|
776
841
|
if (firstSeg) {
|
|
777
|
-
steps.push(`AI preserved the ${firstSeg.type} pattern
|
|
842
|
+
steps.push(`AI preserved the ${firstSeg.type} pattern - semantic locator over raw CSS path.`);
|
|
778
843
|
}
|
|
779
844
|
if (ctx.aiFix.explanation) {
|
|
780
845
|
steps.push(`AI reasoning: ${ctx.aiFix.explanation}`);
|
|
@@ -783,7 +848,7 @@ If the text has NOT changed, omit the "contentChange" field entirely.`;
|
|
|
783
848
|
if (ctx.aiFix.contentChange) {
|
|
784
849
|
steps.push(`Detected content drift: "${ctx.aiFix.contentChange.oldText}" → "${ctx.aiFix.contentChange.newText}".`);
|
|
785
850
|
}
|
|
786
|
-
steps.push(`SafetyGuard verdict: ${ctx.safetyLevel}
|
|
851
|
+
steps.push(`SafetyGuard verdict: ${ctx.safetyLevel} - change cleared for write.`);
|
|
787
852
|
steps.push(`Source updated via ${ctx.strategy} at line ${ctx.writtenLine}.`);
|
|
788
853
|
if (ctx.blastRadius != null && ctx.blastRadius > 0) {
|
|
789
854
|
steps.push(`Blast radius: ${ctx.blastRadius} dependent test reference(s) inherited the fix.`);
|
|
@@ -808,7 +873,7 @@ If the text has NOT changed, omit the "contentChange" field entirely.`;
|
|
|
808
873
|
this.testTitleBuffer.clear();
|
|
809
874
|
SourceUpdater_1.SourceUpdater.flushAdvisories();
|
|
810
875
|
}
|
|
811
|
-
// ── Sela Insights
|
|
876
|
+
// ── Sela Insights - render the transparent healing report ───────
|
|
812
877
|
let reportHtmlPath = null;
|
|
813
878
|
const healedEvents = HealReportService_1.sharedHealReport.getHealedEvents();
|
|
814
879
|
const protectedEvents = HealReportService_1.sharedHealReport.getProtectedEvents();
|
|
@@ -827,11 +892,12 @@ If the text has NOT changed, omit the "contentChange" field entirely.`;
|
|
|
827
892
|
// ── Dual-Mode post-run lifecycle ─────────────────────────────────
|
|
828
893
|
//
|
|
829
894
|
// CI (process.env.CI truthy) → Clean-Room PR automation (existing flow).
|
|
830
|
-
// Local DX (CI falsy) →
|
|
831
|
-
//
|
|
832
|
-
//
|
|
895
|
+
// Local DX (CI falsy) → Write-and-notify: AST mutations are
|
|
896
|
+
// already on disk; the reporter prints a
|
|
897
|
+
// diff + vscode:// deeplink + report link
|
|
898
|
+
// via printHealReview() (no Y/N prompt).
|
|
833
899
|
//
|
|
834
|
-
// The
|
|
900
|
+
// The local branch deliberately bypasses PR automation because:
|
|
835
901
|
// • AST fixes are ALREADY persisted to disk (the test passed on them).
|
|
836
902
|
// • Local devs want to inspect + cherry-pick, not auto-commit.
|
|
837
903
|
if (isCiEnvironment()) {
|
|
@@ -858,11 +924,11 @@ If the text has NOT changed, omit the "contentChange" field entirely.`;
|
|
|
858
924
|
}
|
|
859
925
|
}
|
|
860
926
|
else if (healedEvents.length > 0) {
|
|
861
|
-
// Local DX:
|
|
862
|
-
//
|
|
863
|
-
//
|
|
864
|
-
//
|
|
865
|
-
//
|
|
927
|
+
// Local DX: serialise the mutated snapshots to a cross-process
|
|
928
|
+
// ledger. The SelaReporter drains it from `onEnd()` in the main
|
|
929
|
+
// process and prints a non-blocking summary (diff + vscode://
|
|
930
|
+
// deeplink). Mutations are already on disk - the ledger only
|
|
931
|
+
// exists so the reporter knows what to narrate.
|
|
866
932
|
try {
|
|
867
933
|
(0, PendingPromptLedger_1.writePendingPromptsLedger)({
|
|
868
934
|
cwd: process.cwd(),
|
|
@@ -887,12 +953,12 @@ If the text has NOT changed, omit the "contentChange" field entirely.`;
|
|
|
887
953
|
WorkspaceSnapshotService_1.sharedWorkspaceSnapshot.clear();
|
|
888
954
|
}
|
|
889
955
|
// ─────────────────────────────────────────────────────────────
|
|
890
|
-
// fetchLiveText
|
|
956
|
+
// fetchLiveText - frame-aware innerText fetch (zero-trust probe)
|
|
891
957
|
//
|
|
892
958
|
// Navigates the frame chain encoded in successfulPath, then calls
|
|
893
959
|
// .first().innerText() on the element-only selector. Uses a short
|
|
894
960
|
// 1.5 s timeout so it never adds meaningful latency. Returns null
|
|
895
|
-
// on ANY failure
|
|
961
|
+
// on ANY failure - the caller must treat null as "no data" and skip
|
|
896
962
|
// live verification rather than blocking the heal.
|
|
897
963
|
// ─────────────────────────────────────────────────────────────
|
|
898
964
|
async fetchLiveText(page, elementOnlySelector, framePath) {
|
|
@@ -923,7 +989,7 @@ If the text has NOT changed, omit the "contentChange" field entirely.`;
|
|
|
923
989
|
p === "frame" ||
|
|
924
990
|
p.includes("frame-"));
|
|
925
991
|
if (aiHasFrameSegments) {
|
|
926
|
-
console.log(`[Sela] 🔗 AI returned full frame path
|
|
992
|
+
console.log(`[Sela] 🔗 AI returned full frame path - using directly: ${aiNewSelector}`);
|
|
927
993
|
return aiNewSelector;
|
|
928
994
|
}
|
|
929
995
|
return [...frameParts, ...aiParts].join(" >> ");
|
|
@@ -933,7 +999,7 @@ If the text has NOT changed, omit the "contentChange" field entirely.`;
|
|
|
933
999
|
const ignoreList = [
|
|
934
1000
|
"node_modules",
|
|
935
1001
|
".git",
|
|
936
|
-
"
|
|
1002
|
+
"sela-snapshots",
|
|
937
1003
|
"dist",
|
|
938
1004
|
"test-results",
|
|
939
1005
|
"tests/reset-demo.ts",
|
|
@@ -951,7 +1017,7 @@ If the text has NOT changed, omit the "contentChange" field entirely.`;
|
|
|
951
1017
|
return arrayOfFiles;
|
|
952
1018
|
}
|
|
953
1019
|
// ─────────────────────────────────────────────────────────────
|
|
954
|
-
// captureSuccessfulElement
|
|
1020
|
+
// captureSuccessfulElement - capture and buffer a v2 DNA snapshot.
|
|
955
1021
|
//
|
|
956
1022
|
// Replaces the previous DOMUtils.getElementDNA() path with a single
|
|
957
1023
|
// captureElement() call that collects all v2 metrics atomically.
|
|
@@ -63,7 +63,6 @@ export declare const SAFETY_GUARD_CODES: {
|
|
|
63
63
|
readonly BLOCKED_ROLE_SWAP_ASSERTION: "SG_BLOCKED_ROLE_SWAP_ASSERTION";
|
|
64
64
|
readonly FAIL_HARD_AUDITOR_INCONSISTENT: "SG_FAIL_HARD_AUDITOR_INCONSISTENT";
|
|
65
65
|
readonly BLOCKED_AUDITOR_SUSPICIOUS: "SG_BLOCKED_AUDITOR_SUSPICIOUS";
|
|
66
|
-
readonly BLOCKED_SIMILARITY_FLOOR: "SG_BLOCKED_SIMILARITY_FLOOR";
|
|
67
66
|
/** Generic dispatcher when SafetyDecision arrives without an explicit code. */
|
|
68
67
|
readonly GENERIC_REJECTION: "SG_GENERIC_REJECTION";
|
|
69
68
|
};
|
|
@@ -120,6 +119,14 @@ export declare const REPORT_CODES: {
|
|
|
120
119
|
readonly INVALID_FIELD_TYPE: "REP_INVALID_FIELD_TYPE";
|
|
121
120
|
/** Disk write failed during flushToDisk(). */
|
|
122
121
|
readonly FLUSH_FAILED: "REP_FLUSH_FAILED";
|
|
122
|
+
/**
|
|
123
|
+
* A prior `.sela-history.json` existed on disk but could not be parsed as
|
|
124
|
+
* a JSON array. The merge path refuses to silently overwrite it with a
|
|
125
|
+
* fresh window (which would destroy the rolling history), so it aborts.
|
|
126
|
+
*/
|
|
127
|
+
readonly HISTORY_CORRUPT: "REP_HISTORY_CORRUPT";
|
|
128
|
+
/** A shard report file existed but was not valid JSON / had no events array. */
|
|
129
|
+
readonly SHARD_UNREADABLE: "REP_SHARD_UNREADABLE";
|
|
123
130
|
};
|
|
124
131
|
export type ReportCode = (typeof REPORT_CODES)[keyof typeof REPORT_CODES];
|
|
125
132
|
export declare const CHAIN_VALIDATOR_CODES: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SelaError.d.ts","sourceRoot":"","sources":["../../src/errors/SelaError.ts"],"names":[],"mappings":"AAkBA,MAAM,MAAM,aAAa,GACrB,aAAa,GACb,eAAe,GACf,YAAY,GACZ,OAAO,GACP,YAAY,GACZ,UAAU,GACV,gBAAgB,GAChB,QAAQ,GACR,QAAQ,CAAC;AAMb,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0EAA0E;IAC1E,WAAW,CAAC,EAAE,SAAS,GAAG,WAAW,GAAG,iBAAiB,GAAG,MAAM,CAAC;IACnE,0EAA0E;IAC1E,cAAc,CAAC,EAAE,YAAY,GAAG,cAAc,GAAG,YAAY,CAAC;IAC9D,4EAA4E;IAC5E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,iFAAiF;IACjF,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAMD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,aAAa,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,gBAAgB,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAMD,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,aAAa,CAAC;IACzB;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,6EAA6E;IAC7E,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,gBAAgB,CAAC;IAC3B,wDAAwD;IACxD,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAYD,qBAAa,SAAU,SAAQ,KAAK;IAClC,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,gBAAgB,CAAC;gBAEvB,IAAI,EAAE,aAAa;
|
|
1
|
+
{"version":3,"file":"SelaError.d.ts","sourceRoot":"","sources":["../../src/errors/SelaError.ts"],"names":[],"mappings":"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"}
|