sela-core 0.1.0-alpha.1
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 +103 -0
- package/bin/sela.js +3 -0
- package/dist/cli/ErrorHandler.d.ts +10 -0
- package/dist/cli/ErrorHandler.d.ts.map +1 -0
- package/dist/cli/ErrorHandler.js +70 -0
- package/dist/cli/commands/bulk.d.ts +3 -0
- package/dist/cli/commands/bulk.d.ts.map +1 -0
- package/dist/cli/commands/bulk.js +158 -0
- package/dist/cli/commands/find.d.ts +3 -0
- package/dist/cli/commands/find.d.ts.map +1 -0
- package/dist/cli/commands/find.js +51 -0
- package/dist/cli/commands/init.d.ts +3 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +166 -0
- package/dist/cli/commands/list.d.ts +3 -0
- package/dist/cli/commands/list.d.ts.map +1 -0
- package/dist/cli/commands/list.js +56 -0
- package/dist/cli/commands/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/refactor.d.ts +3 -0
- package/dist/cli/commands/refactor.d.ts.map +1 -0
- package/dist/cli/commands/refactor.js +30 -0
- package/dist/cli/commands/showReport.d.ts +3 -0
- package/dist/cli/commands/showReport.d.ts.map +1 -0
- package/dist/cli/commands/showReport.js +82 -0
- package/dist/cli/commands/status.d.ts +3 -0
- package/dist/cli/commands/status.d.ts.map +1 -0
- package/dist/cli/commands/status.js +51 -0
- package/dist/cli/commands/sync.d.ts +3 -0
- package/dist/cli/commands/sync.d.ts.map +1 -0
- package/dist/cli/commands/sync.js +131 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +94 -0
- package/dist/cli/ui/DnaTable.d.ts +3 -0
- package/dist/cli/ui/DnaTable.d.ts.map +1 -0
- package/dist/cli/ui/DnaTable.js +77 -0
- package/dist/cli/ui/LocatorPicker.d.ts +8 -0
- package/dist/cli/ui/LocatorPicker.d.ts.map +1 -0
- package/dist/cli/ui/LocatorPicker.js +33 -0
- package/dist/cli/ui/ProgressReporter.d.ts +11 -0
- package/dist/cli/ui/ProgressReporter.d.ts.map +1 -0
- package/dist/cli/ui/ProgressReporter.js +33 -0
- package/dist/cli/ui/RefactorWizard.d.ts +26 -0
- package/dist/cli/ui/RefactorWizard.d.ts.map +1 -0
- package/dist/cli/ui/RefactorWizard.js +295 -0
- package/dist/config/ConfigLoader.d.ts +16 -0
- package/dist/config/ConfigLoader.d.ts.map +1 -0
- package/dist/config/ConfigLoader.js +185 -0
- package/dist/config/DryRunGuard.d.ts +14 -0
- package/dist/config/DryRunGuard.d.ts.map +1 -0
- package/dist/config/DryRunGuard.js +80 -0
- package/dist/config/PathScopeGuard.d.ts +42 -0
- package/dist/config/PathScopeGuard.d.ts.map +1 -0
- package/dist/config/PathScopeGuard.js +192 -0
- package/dist/config/SelaConfig.d.ts +178 -0
- package/dist/config/SelaConfig.d.ts.map +1 -0
- package/dist/config/SelaConfig.js +141 -0
- package/dist/config/defineConfig.d.ts +3 -0
- package/dist/config/defineConfig.d.ts.map +1 -0
- package/dist/config/defineConfig.js +9 -0
- package/dist/engine/FixwrightEngine.d.ts +24 -0
- package/dist/engine/FixwrightEngine.d.ts.map +1 -0
- package/dist/engine/FixwrightEngine.js +403 -0
- package/dist/engine/HealingRegistry.d.ts +40 -0
- package/dist/engine/HealingRegistry.d.ts.map +1 -0
- package/dist/engine/HealingRegistry.js +99 -0
- package/dist/engine/SelaEngine.d.ts +57 -0
- package/dist/engine/SelaEngine.d.ts.map +1 -0
- package/dist/engine/SelaEngine.js +1146 -0
- package/dist/engine/singleton.d.ts +3 -0
- package/dist/engine/singleton.d.ts.map +1 -0
- package/dist/engine/singleton.js +5 -0
- package/dist/errors/SelaError.d.ts +146 -0
- package/dist/errors/SelaError.d.ts.map +1 -0
- package/dist/errors/SelaError.js +168 -0
- package/dist/fixtures/expectProxy.d.ts +12 -0
- package/dist/fixtures/expectProxy.d.ts.map +1 -0
- package/dist/fixtures/expectProxy.js +246 -0
- package/dist/fixtures/index.d.ts +30 -0
- package/dist/fixtures/index.d.ts.map +1 -0
- package/dist/fixtures/index.js +765 -0
- package/dist/fixtures/moduleExpect.d.ts +2 -0
- package/dist/fixtures/moduleExpect.d.ts.map +1 -0
- package/dist/fixtures/moduleExpect.js +54 -0
- package/dist/fixtures/proxyTag.d.ts +12 -0
- package/dist/fixtures/proxyTag.d.ts.map +1 -0
- package/dist/fixtures/proxyTag.js +45 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/reporter/SelaReporter.d.ts +165 -0
- package/dist/reporter/SelaReporter.d.ts.map +1 -0
- package/dist/reporter/SelaReporter.js +517 -0
- package/dist/services/ASTSourceUpdater.d.ts +143 -0
- package/dist/services/ASTSourceUpdater.d.ts.map +1 -0
- package/dist/services/ASTSourceUpdater.js +3514 -0
- 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.d.ts +26 -0
- package/dist/services/ArgumentTypeAnalyzer.d.ts.map +1 -0
- package/dist/services/ArgumentTypeAnalyzer.js +92 -0
- package/dist/services/BlastRadiusAnalyzer.d.ts +15 -0
- package/dist/services/BlastRadiusAnalyzer.d.ts.map +1 -0
- package/dist/services/BlastRadiusAnalyzer.js +107 -0
- package/dist/services/ChainValidator.d.ts +76 -0
- package/dist/services/ChainValidator.d.ts.map +1 -0
- package/dist/services/ChainValidator.js +570 -0
- package/dist/services/CrossFileHealer.d.ts +6 -0
- package/dist/services/CrossFileHealer.d.ts.map +1 -0
- package/dist/services/CrossFileHealer.js +135 -0
- 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 +41 -0
- package/dist/services/DefinitionTracer.d.ts.map +1 -0
- package/dist/services/DefinitionTracer.js +366 -0
- package/dist/services/DnaEditorService.d.ts +31 -0
- package/dist/services/DnaEditorService.d.ts.map +1 -0
- package/dist/services/DnaEditorService.js +217 -0
- package/dist/services/DnaIndexService.d.ts +24 -0
- package/dist/services/DnaIndexService.d.ts.map +1 -0
- package/dist/services/DnaIndexService.js +131 -0
- package/dist/services/HealBudget.d.ts +32 -0
- package/dist/services/HealBudget.d.ts.map +1 -0
- package/dist/services/HealBudget.js +85 -0
- package/dist/services/HealReportService.d.ts +161 -0
- package/dist/services/HealReportService.d.ts.map +1 -0
- package/dist/services/HealReportService.js +1039 -0
- package/dist/services/HealingAdvisory.d.ts +30 -0
- package/dist/services/HealingAdvisory.d.ts.map +1 -0
- package/dist/services/HealingAdvisory.js +51 -0
- package/dist/services/HealingCacheService.d.ts +135 -0
- package/dist/services/HealingCacheService.d.ts.map +1 -0
- package/dist/services/HealingCacheService.js +404 -0
- package/dist/services/HealthReportService.d.ts +10 -0
- package/dist/services/HealthReportService.d.ts.map +1 -0
- package/dist/services/HealthReportService.js +84 -0
- package/dist/services/InitializerUpdater.d.ts +16 -0
- package/dist/services/InitializerUpdater.d.ts.map +1 -0
- package/dist/services/InitializerUpdater.js +73 -0
- package/dist/services/IntentAuditor.d.ts +56 -0
- package/dist/services/IntentAuditor.d.ts.map +1 -0
- package/dist/services/IntentAuditor.js +326 -0
- package/dist/services/InteractiveReview.d.ts +15 -0
- package/dist/services/InteractiveReview.d.ts.map +1 -0
- package/dist/services/InteractiveReview.js +128 -0
- package/dist/services/LLMService.d.ts +121 -0
- package/dist/services/LLMService.d.ts.map +1 -0
- package/dist/services/LLMService.js +475 -0
- package/dist/services/MutationApplier.d.ts +69 -0
- package/dist/services/MutationApplier.d.ts.map +1 -0
- package/dist/services/MutationApplier.js +360 -0
- package/dist/services/PRAutomationService.d.ts +50 -0
- package/dist/services/PRAutomationService.d.ts.map +1 -0
- package/dist/services/PRAutomationService.js +760 -0
- package/dist/services/PendingPromptLedger.d.ts +51 -0
- package/dist/services/PendingPromptLedger.d.ts.map +1 -0
- package/dist/services/PendingPromptLedger.js +205 -0
- package/dist/services/ReportGenerator.d.ts +156 -0
- package/dist/services/ReportGenerator.d.ts.map +1 -0
- package/dist/services/ReportGenerator.js +278 -0
- 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 +112 -0
- package/dist/services/SafetyGuard.d.ts.map +1 -0
- package/dist/services/SafetyGuard.js +87 -0
- package/dist/services/Sanitizer.d.ts +28 -0
- package/dist/services/Sanitizer.d.ts.map +1 -0
- package/dist/services/Sanitizer.js +142 -0
- package/dist/services/SanitizingAnthropic.d.ts +19 -0
- package/dist/services/SanitizingAnthropic.d.ts.map +1 -0
- package/dist/services/SanitizingAnthropic.js +84 -0
- 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.d.ts +11 -0
- package/dist/services/SnapshotService.d.ts.map +1 -0
- package/dist/services/SnapshotService.js +352 -0
- package/dist/services/SourceLinkService.d.ts +26 -0
- package/dist/services/SourceLinkService.d.ts.map +1 -0
- package/dist/services/SourceLinkService.js +165 -0
- package/dist/services/SourceUpdater.d.ts +45 -0
- package/dist/services/SourceUpdater.d.ts.map +1 -0
- package/dist/services/SourceUpdater.js +1091 -0
- package/dist/services/TemplateDiffService.d.ts +25 -0
- package/dist/services/TemplateDiffService.d.ts.map +1 -0
- package/dist/services/TemplateDiffService.js +334 -0
- 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 +71 -0
- package/dist/services/WorkspaceSnapshotService.d.ts.map +1 -0
- package/dist/services/WorkspaceSnapshotService.js +200 -0
- package/dist/services/safety/PipelineContext.d.ts +63 -0
- package/dist/services/safety/PipelineContext.d.ts.map +1 -0
- package/dist/services/safety/PipelineContext.js +34 -0
- package/dist/services/safety/roleClassifier.d.ts +4 -0
- package/dist/services/safety/roleClassifier.d.ts.map +1 -0
- package/dist/services/safety/roleClassifier.js +233 -0
- package/dist/services/safety/rules/ConfidenceTierRule.d.ts +3 -0
- package/dist/services/safety/rules/ConfidenceTierRule.d.ts.map +1 -0
- package/dist/services/safety/rules/ConfidenceTierRule.js +32 -0
- package/dist/services/safety/rules/DangerousSinkRule.d.ts +10 -0
- package/dist/services/safety/rules/DangerousSinkRule.d.ts.map +1 -0
- package/dist/services/safety/rules/DangerousSinkRule.js +53 -0
- package/dist/services/safety/rules/FunctionalRoleRule.d.ts +3 -0
- package/dist/services/safety/rules/FunctionalRoleRule.d.ts.map +1 -0
- package/dist/services/safety/rules/FunctionalRoleRule.js +64 -0
- package/dist/services/safety/rules/HardConfidenceRule.d.ts +3 -0
- package/dist/services/safety/rules/HardConfidenceRule.d.ts.map +1 -0
- package/dist/services/safety/rules/HardConfidenceRule.js +22 -0
- package/dist/services/safety/rules/IntentAuditorRule.d.ts +3 -0
- package/dist/services/safety/rules/IntentAuditorRule.d.ts.map +1 -0
- package/dist/services/safety/rules/IntentAuditorRule.js +97 -0
- package/dist/services/safety/rules/SemanticOppositeRule.d.ts +3 -0
- package/dist/services/safety/rules/SemanticOppositeRule.d.ts.map +1 -0
- package/dist/services/safety/rules/SemanticOppositeRule.js +30 -0
- package/dist/services/safety/rules/StructuralOnlyRule.d.ts +3 -0
- package/dist/services/safety/rules/StructuralOnlyRule.d.ts.map +1 -0
- package/dist/services/safety/rules/StructuralOnlyRule.js +38 -0
- package/dist/services/safety/rules/VisibilityRule.d.ts +3 -0
- package/dist/services/safety/rules/VisibilityRule.d.ts.map +1 -0
- package/dist/services/safety/rules/VisibilityRule.js +53 -0
- package/dist/services/safety/rules/ZeroTrustRule.d.ts +3 -0
- package/dist/services/safety/rules/ZeroTrustRule.d.ts.map +1 -0
- package/dist/services/safety/rules/ZeroTrustRule.js +37 -0
- package/dist/services/safety/rules/index.d.ts +3 -0
- package/dist/services/safety/rules/index.d.ts.map +1 -0
- package/dist/services/safety/rules/index.js +34 -0
- package/dist/services/types.d.ts +59 -0
- package/dist/services/types.d.ts.map +1 -0
- package/dist/services/types.js +2 -0
- package/dist/storage/SnapshotManager.d.ts +5 -0
- package/dist/storage/SnapshotManager.d.ts.map +1 -0
- package/dist/storage/SnapshotManager.js +32 -0
- package/dist/types/index.d.ts +95 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +7 -0
- package/dist/utils/DOMUtils.d.ts +49 -0
- package/dist/utils/DOMUtils.d.ts.map +1 -0
- package/dist/utils/DOMUtils.js +466 -0
- package/dist/utils/FileLock.d.ts +44 -0
- package/dist/utils/FileLock.d.ts.map +1 -0
- package/dist/utils/FileLock.js +189 -0
- package/dist/utils/IsolatedDiff.d.ts +45 -0
- package/dist/utils/IsolatedDiff.d.ts.map +1 -0
- package/dist/utils/IsolatedDiff.js +382 -0
- package/dist/utils/StackUtils.d.ts +11 -0
- package/dist/utils/StackUtils.d.ts.map +1 -0
- package/dist/utils/StackUtils.js +120 -0
- package/dist/utils/SummaryTable.d.ts +17 -0
- package/dist/utils/SummaryTable.d.ts.map +1 -0
- package/dist/utils/SummaryTable.js +37 -0
- package/dist/utils/atomicWrite.d.ts +9 -0
- package/dist/utils/atomicWrite.d.ts.map +1 -0
- package/dist/utils/atomicWrite.js +83 -0
- package/dist/utils/logger.d.ts +59 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +123 -0
- package/dist/vendor/enquirer.d.ts +33 -0
- package/dist/vendor/enquirer.d.ts.map +1 -0
- package/dist/vendor/enquirer.js +11 -0
- package/package.json +71 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
export interface FileSnapshot {
|
|
2
|
+
/** Absolute, normalised path of the tracked file. */
|
|
3
|
+
absolutePath: string;
|
|
4
|
+
/** Project-relative path (posix slashes) - for diff headers + PR commits. */
|
|
5
|
+
relativePath: string;
|
|
6
|
+
/**
|
|
7
|
+
* Disk content captured BEFORE Sela's first write to this file.
|
|
8
|
+
* May contain the developer's uncommitted edits - that's the point.
|
|
9
|
+
*/
|
|
10
|
+
contentBefore: string;
|
|
11
|
+
/** Disk content captured AFTER Sela's latest write. `null` until first postWrite(). */
|
|
12
|
+
contentAfter: string | null;
|
|
13
|
+
/** Content of the file at `HEAD` (last committed) - `null` when untracked / no git. */
|
|
14
|
+
headContent: string | null;
|
|
15
|
+
/** True when `contentBefore !== headContent`, i.e. developer had dirty edits. */
|
|
16
|
+
hadUncommittedChanges: boolean;
|
|
17
|
+
/** True when the file did not exist on disk before Sela touched it. */
|
|
18
|
+
createdBySela: boolean;
|
|
19
|
+
}
|
|
20
|
+
export declare class WorkspaceSnapshotService {
|
|
21
|
+
private snapshots;
|
|
22
|
+
private cwd;
|
|
23
|
+
constructor(cwd?: string);
|
|
24
|
+
/** Override the cwd used for relative-path + HEAD lookups (tests). */
|
|
25
|
+
setCwd(cwd: string): void;
|
|
26
|
+
/**
|
|
27
|
+
* Record the pre-mutation state of `filePath` if not already tracked.
|
|
28
|
+
* Idempotent - only the FIRST call per file matters.
|
|
29
|
+
*/
|
|
30
|
+
preWrite(filePath: string): void;
|
|
31
|
+
/**
|
|
32
|
+
* Refresh the post-mutation state. Safe to call multiple times - each
|
|
33
|
+
* call overwrites `contentAfter` with the latest disk state.
|
|
34
|
+
*/
|
|
35
|
+
postWrite(filePath: string): void;
|
|
36
|
+
/** Lookup by absolute or relative path. Returns `null` when untracked. */
|
|
37
|
+
getSnapshot(filePath: string): FileSnapshot | null;
|
|
38
|
+
/** Every tracked snapshot. Insertion order. */
|
|
39
|
+
getAllSnapshots(): FileSnapshot[];
|
|
40
|
+
/**
|
|
41
|
+
* Did Sela actually mutate this file? True when contentAfter exists and
|
|
42
|
+
* differs from contentBefore.
|
|
43
|
+
*/
|
|
44
|
+
isMutated(filePath: string): boolean;
|
|
45
|
+
/** All snapshots with a real mutation recorded. */
|
|
46
|
+
getMutatedSnapshots(): FileSnapshot[];
|
|
47
|
+
/**
|
|
48
|
+
* Rewind every tracked file on disk to `contentBefore` - restoring the
|
|
49
|
+
* developer's pre-Sela workspace state byte-for-byte.
|
|
50
|
+
*/
|
|
51
|
+
restoreOriginal(): void;
|
|
52
|
+
/**
|
|
53
|
+
* Rewind a SINGLE tracked file on disk to `contentBefore`. Used by the
|
|
54
|
+
* Local DX interactive review flow when the developer rejects a fix.
|
|
55
|
+
* Returns true when the rewind happened, false when the file is untracked.
|
|
56
|
+
*/
|
|
57
|
+
restoreOriginalByPath(filePath: string): boolean;
|
|
58
|
+
private rewindSnapshot;
|
|
59
|
+
/**
|
|
60
|
+
* Forward every tracked file on disk to `contentAfter` (no-op for files
|
|
61
|
+
* that were never written). Used by PRAutomationService to reapply
|
|
62
|
+
* Sela's mutations after a `git stash pop` round-trip.
|
|
63
|
+
*/
|
|
64
|
+
restoreMutated(): void;
|
|
65
|
+
clear(): void;
|
|
66
|
+
private normalise;
|
|
67
|
+
private toRelative;
|
|
68
|
+
private readHeadContent;
|
|
69
|
+
}
|
|
70
|
+
export declare const sharedWorkspaceSnapshot: WorkspaceSnapshotService;
|
|
71
|
+
//# sourceMappingURL=WorkspaceSnapshotService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WorkspaceSnapshotService.d.ts","sourceRoot":"","sources":["../../src/services/WorkspaceSnapshotService.ts"],"names":[],"mappings":"AAyBA,MAAM,WAAW,YAAY;IAC3B,qDAAqD;IACrD,YAAY,EAAE,MAAM,CAAC;IACrB,6EAA6E;IAC7E,YAAY,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,aAAa,EAAE,MAAM,CAAC;IACtB,uFAAuF;IACvF,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,uFAAuF;IACvF,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,iFAAiF;IACjF,qBAAqB,EAAE,OAAO,CAAC;IAC/B,uEAAuE;IACvE,aAAa,EAAE,OAAO,CAAC;CACxB;AAED,qBAAa,wBAAwB;IACnC,OAAO,CAAC,SAAS,CAAmC;IACpD,OAAO,CAAC,GAAG,CAAS;gBAER,GAAG,GAAE,MAAsB;IAIvC,sEAAsE;IACtE,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAIzB;;;OAGG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAqBhC;;;OAGG;IACH,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAOjC,0EAA0E;IAC1E,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI;IAIlD,+CAA+C;IAC/C,eAAe,IAAI,YAAY,EAAE;IAIjC;;;OAGG;IACH,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAMpC,mDAAmD;IACnD,mBAAmB,IAAI,YAAY,EAAE;IAMrC;;;OAGG;IACH,eAAe,IAAI,IAAI;IAMvB;;;;OAIG;IACH,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAOhD,OAAO,CAAC,cAAc;IAUtB;;;;OAIG;IACH,cAAc,IAAI,IAAI;IAOtB,KAAK,IAAI,IAAI;IAQb,OAAO,CAAC,SAAS;IAMjB,OAAO,CAAC,UAAU;IAKlB,OAAO,CAAC,eAAe;CAcxB;AAED,eAAO,MAAM,uBAAuB,0BAAiC,CAAC"}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/services/WorkspaceSnapshotService.ts
|
|
3
|
+
//
|
|
4
|
+
// Clean-Room Architecture - file 1 of 2.
|
|
5
|
+
//
|
|
6
|
+
// Tracks the EXACT content of every file Sela mutates during a session, so:
|
|
7
|
+
// • The HTML report diff can be generated from an isolated in-memory diff
|
|
8
|
+
// (developer's dirty pre-existing edits NEVER bleed in).
|
|
9
|
+
// • PRAutomationService can restore the developer's pre-Sela workspace
|
|
10
|
+
// byte-for-byte after a PR is opened on an isolated branch.
|
|
11
|
+
//
|
|
12
|
+
// Lifecycle:
|
|
13
|
+
// preWrite(path) - first call per file records `contentBefore` (disk
|
|
14
|
+
// snapshot right before Sela touches it) + HEAD blob.
|
|
15
|
+
// postWrite(path) - refreshes `contentAfter` (latest disk state).
|
|
16
|
+
// restoreOriginal()- writes contentBefore back to every tracked file.
|
|
17
|
+
// restoreMutated() - writes contentAfter back to every tracked file.
|
|
18
|
+
// clear() - empties the ledger.
|
|
19
|
+
//
|
|
20
|
+
// Singleton `sharedWorkspaceSnapshot` is the canonical instance the
|
|
21
|
+
// SourceUpdater / ASTSourceUpdater write hooks talk to.
|
|
22
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
23
|
+
if (k2 === undefined) k2 = k;
|
|
24
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
25
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
26
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
27
|
+
}
|
|
28
|
+
Object.defineProperty(o, k2, desc);
|
|
29
|
+
}) : (function(o, m, k, k2) {
|
|
30
|
+
if (k2 === undefined) k2 = k;
|
|
31
|
+
o[k2] = m[k];
|
|
32
|
+
}));
|
|
33
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
34
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
35
|
+
}) : function(o, v) {
|
|
36
|
+
o["default"] = v;
|
|
37
|
+
});
|
|
38
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
39
|
+
var ownKeys = function(o) {
|
|
40
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
41
|
+
var ar = [];
|
|
42
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
43
|
+
return ar;
|
|
44
|
+
};
|
|
45
|
+
return ownKeys(o);
|
|
46
|
+
};
|
|
47
|
+
return function (mod) {
|
|
48
|
+
if (mod && mod.__esModule) return mod;
|
|
49
|
+
var result = {};
|
|
50
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
51
|
+
__setModuleDefault(result, mod);
|
|
52
|
+
return result;
|
|
53
|
+
};
|
|
54
|
+
})();
|
|
55
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
56
|
+
exports.sharedWorkspaceSnapshot = exports.WorkspaceSnapshotService = void 0;
|
|
57
|
+
const fs = __importStar(require("fs"));
|
|
58
|
+
const path = __importStar(require("path"));
|
|
59
|
+
const child_process_1 = require("child_process");
|
|
60
|
+
class WorkspaceSnapshotService {
|
|
61
|
+
snapshots = new Map();
|
|
62
|
+
cwd;
|
|
63
|
+
constructor(cwd = process.cwd()) {
|
|
64
|
+
this.cwd = cwd;
|
|
65
|
+
}
|
|
66
|
+
/** Override the cwd used for relative-path + HEAD lookups (tests). */
|
|
67
|
+
setCwd(cwd) {
|
|
68
|
+
this.cwd = cwd;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Record the pre-mutation state of `filePath` if not already tracked.
|
|
72
|
+
* Idempotent - only the FIRST call per file matters.
|
|
73
|
+
*/
|
|
74
|
+
preWrite(filePath) {
|
|
75
|
+
const abs = this.normalise(filePath);
|
|
76
|
+
if (this.snapshots.has(abs))
|
|
77
|
+
return;
|
|
78
|
+
const exists = fs.existsSync(abs);
|
|
79
|
+
const contentBefore = exists ? fs.readFileSync(abs, "utf8") : "";
|
|
80
|
+
const headContent = this.readHeadContent(abs);
|
|
81
|
+
const hadUncommittedChanges = headContent !== null && headContent !== contentBefore;
|
|
82
|
+
this.snapshots.set(abs, {
|
|
83
|
+
absolutePath: abs,
|
|
84
|
+
relativePath: this.toRelative(abs),
|
|
85
|
+
contentBefore,
|
|
86
|
+
contentAfter: null,
|
|
87
|
+
headContent,
|
|
88
|
+
hadUncommittedChanges,
|
|
89
|
+
createdBySela: !exists,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Refresh the post-mutation state. Safe to call multiple times - each
|
|
94
|
+
* call overwrites `contentAfter` with the latest disk state.
|
|
95
|
+
*/
|
|
96
|
+
postWrite(filePath) {
|
|
97
|
+
const abs = this.normalise(filePath);
|
|
98
|
+
const snap = this.snapshots.get(abs);
|
|
99
|
+
if (!snap)
|
|
100
|
+
return;
|
|
101
|
+
snap.contentAfter = fs.existsSync(abs) ? fs.readFileSync(abs, "utf8") : "";
|
|
102
|
+
}
|
|
103
|
+
/** Lookup by absolute or relative path. Returns `null` when untracked. */
|
|
104
|
+
getSnapshot(filePath) {
|
|
105
|
+
return this.snapshots.get(this.normalise(filePath)) ?? null;
|
|
106
|
+
}
|
|
107
|
+
/** Every tracked snapshot. Insertion order. */
|
|
108
|
+
getAllSnapshots() {
|
|
109
|
+
return Array.from(this.snapshots.values());
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Did Sela actually mutate this file? True when contentAfter exists and
|
|
113
|
+
* differs from contentBefore.
|
|
114
|
+
*/
|
|
115
|
+
isMutated(filePath) {
|
|
116
|
+
const snap = this.getSnapshot(filePath);
|
|
117
|
+
if (!snap || snap.contentAfter === null)
|
|
118
|
+
return false;
|
|
119
|
+
return snap.contentAfter !== snap.contentBefore;
|
|
120
|
+
}
|
|
121
|
+
/** All snapshots with a real mutation recorded. */
|
|
122
|
+
getMutatedSnapshots() {
|
|
123
|
+
return this.getAllSnapshots().filter((s) => s.contentAfter !== null && s.contentAfter !== s.contentBefore);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Rewind every tracked file on disk to `contentBefore` - restoring the
|
|
127
|
+
* developer's pre-Sela workspace state byte-for-byte.
|
|
128
|
+
*/
|
|
129
|
+
restoreOriginal() {
|
|
130
|
+
for (const snap of this.snapshots.values()) {
|
|
131
|
+
this.rewindSnapshot(snap);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Rewind a SINGLE tracked file on disk to `contentBefore`. Used by the
|
|
136
|
+
* Local DX interactive review flow when the developer rejects a fix.
|
|
137
|
+
* Returns true when the rewind happened, false when the file is untracked.
|
|
138
|
+
*/
|
|
139
|
+
restoreOriginalByPath(filePath) {
|
|
140
|
+
const snap = this.getSnapshot(filePath);
|
|
141
|
+
if (!snap)
|
|
142
|
+
return false;
|
|
143
|
+
this.rewindSnapshot(snap);
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
rewindSnapshot(snap) {
|
|
147
|
+
if (snap.createdBySela) {
|
|
148
|
+
if (fs.existsSync(snap.absolutePath)) {
|
|
149
|
+
fs.unlinkSync(snap.absolutePath);
|
|
150
|
+
}
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
fs.writeFileSync(snap.absolutePath, snap.contentBefore, "utf8");
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Forward every tracked file on disk to `contentAfter` (no-op for files
|
|
157
|
+
* that were never written). Used by PRAutomationService to reapply
|
|
158
|
+
* Sela's mutations after a `git stash pop` round-trip.
|
|
159
|
+
*/
|
|
160
|
+
restoreMutated() {
|
|
161
|
+
for (const snap of this.snapshots.values()) {
|
|
162
|
+
if (snap.contentAfter === null)
|
|
163
|
+
continue;
|
|
164
|
+
fs.writeFileSync(snap.absolutePath, snap.contentAfter, "utf8");
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
clear() {
|
|
168
|
+
this.snapshots.clear();
|
|
169
|
+
}
|
|
170
|
+
// ────────────────────────────────────────────────────────────────
|
|
171
|
+
// INTERNAL
|
|
172
|
+
// ────────────────────────────────────────────────────────────────
|
|
173
|
+
normalise(raw) {
|
|
174
|
+
if (!raw)
|
|
175
|
+
return raw;
|
|
176
|
+
const abs = path.isAbsolute(raw) ? raw : path.resolve(this.cwd, raw);
|
|
177
|
+
return path.normalize(abs);
|
|
178
|
+
}
|
|
179
|
+
toRelative(abs) {
|
|
180
|
+
const rel = path.relative(this.cwd, abs);
|
|
181
|
+
return rel.split(path.sep).join("/");
|
|
182
|
+
}
|
|
183
|
+
readHeadContent(abs) {
|
|
184
|
+
try {
|
|
185
|
+
const rel = this.toRelative(abs);
|
|
186
|
+
const out = (0, child_process_1.execSync)(`git show HEAD:"${rel}"`, {
|
|
187
|
+
cwd: this.cwd,
|
|
188
|
+
encoding: "utf8",
|
|
189
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
190
|
+
windowsHide: true,
|
|
191
|
+
});
|
|
192
|
+
return out.toString();
|
|
193
|
+
}
|
|
194
|
+
catch {
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
exports.WorkspaceSnapshotService = WorkspaceSnapshotService;
|
|
200
|
+
exports.sharedWorkspaceSnapshot = new WorkspaceSnapshotService();
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { IntentAuditor } from "../IntentAuditor";
|
|
2
|
+
import type { ResolvedThresholds } from "../../config/SelaConfig";
|
|
3
|
+
import type { AIFixSummary, HealMode, SafetyDecision } from "../SafetyGuard";
|
|
4
|
+
import type { FunctionalRole } from "./roleClassifier";
|
|
5
|
+
/**
|
|
6
|
+
* Mutable bag of state shared across the rule pipeline for a single
|
|
7
|
+
* evaluate() call. Immutable inputs are readonly; fields derived
|
|
8
|
+
* mid-pipeline by context-mutator rules are writable.
|
|
9
|
+
*/
|
|
10
|
+
export interface PipelineContext {
|
|
11
|
+
readonly aiFix: AIFixSummary;
|
|
12
|
+
readonly mode: HealMode;
|
|
13
|
+
readonly branch?: string;
|
|
14
|
+
/**
|
|
15
|
+
* AI confidence coerced to a finite number. Missing / NaN / Infinity
|
|
16
|
+
* collapses to 0 so the hard-stop and review-threshold rules still gate
|
|
17
|
+
* the heal (conservative worst case).
|
|
18
|
+
*/
|
|
19
|
+
readonly confidence: number;
|
|
20
|
+
readonly thresholds: ResolvedThresholds;
|
|
21
|
+
readonly auditor: IntentAuditor;
|
|
22
|
+
/**
|
|
23
|
+
* The content change the pipeline actually reasons about: the
|
|
24
|
+
* AI-reported pair when present, otherwise a zero-trust synthesized
|
|
25
|
+
* pair from live vs DNA text. Undefined ⇒ structural-only heal.
|
|
26
|
+
*/
|
|
27
|
+
effectiveContentChange?: {
|
|
28
|
+
oldText: string;
|
|
29
|
+
newText: string;
|
|
30
|
+
};
|
|
31
|
+
/** Provenance of effectiveContentChange for logging + auditor gating. */
|
|
32
|
+
source?: "AI-reported" | "zero-trust";
|
|
33
|
+
oldText?: string;
|
|
34
|
+
newText?: string;
|
|
35
|
+
oldRole?: FunctionalRole;
|
|
36
|
+
newRole?: FunctionalRole;
|
|
37
|
+
/**
|
|
38
|
+
* Set by DangerousSinkRule when the heal transitions INTO a destructive /
|
|
39
|
+
* auth role. Forces the Intent Auditor gate to fire regardless of source
|
|
40
|
+
* or mode (decision #2), closing the UNKNOWN→DELETE blind spot.
|
|
41
|
+
*/
|
|
42
|
+
forceAudit?: boolean;
|
|
43
|
+
/** Auditor score breakdown forwarded to the final confidence-tier decision. */
|
|
44
|
+
localAuditMeta?: SafetyDecision["meta"];
|
|
45
|
+
}
|
|
46
|
+
/** A rule returns a decision to short-circuit, or null to continue. */
|
|
47
|
+
export type SafetyRuleResult = SafetyDecision | null;
|
|
48
|
+
export interface SafetyRule {
|
|
49
|
+
/** Stable identifier for logging / debugging. */
|
|
50
|
+
readonly id: string;
|
|
51
|
+
run(ctx: PipelineContext): SafetyRuleResult | Promise<SafetyRuleResult>;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Builds the initial pipeline context. Mirrors the head of the
|
|
55
|
+
* pre-refactor evaluate() exactly (confidence coercion + input
|
|
56
|
+
* passthrough). Derived fields are populated later by the rules.
|
|
57
|
+
*/
|
|
58
|
+
export declare function buildContext(context: {
|
|
59
|
+
mode: HealMode;
|
|
60
|
+
action?: string;
|
|
61
|
+
branch?: string;
|
|
62
|
+
}, aiFix: AIFixSummary, thresholds: ResolvedThresholds, auditor: IntentAuditor): PipelineContext;
|
|
63
|
+
//# sourceMappingURL=PipelineContext.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PipelineContext.d.ts","sourceRoot":"","sources":["../../../src/services/safety/PipelineContext.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,KAAK,EACV,YAAY,EACZ,QAAQ,EACR,cAAc,EACf,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAE9B,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,kBAAkB,CAAC;IACxC,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;IAGhC;;;;OAIG;IACH,sBAAsB,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9D,yEAAyE;IACzE,MAAM,CAAC,EAAE,aAAa,GAAG,YAAY,CAAC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,+EAA+E;IAC/E,cAAc,CAAC,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;CACzC;AAED,uEAAuE;AACvE,MAAM,MAAM,gBAAgB,GAAG,cAAc,GAAG,IAAI,CAAC;AAErD,MAAM,WAAW,UAAU;IACzB,iDAAiD;IACjD,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,GAAG,EAAE,eAAe,GAAG,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;CACzE;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,EAC7D,KAAK,EAAE,YAAY,EACnB,UAAU,EAAE,kBAAkB,EAC9B,OAAO,EAAE,aAAa,GACrB,eAAe,CAcjB"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/services/safety/PipelineContext.ts
|
|
3
|
+
//
|
|
4
|
+
// Shared state threaded through the SafetyGuard rule pipeline, plus the
|
|
5
|
+
// SafetyRule contract every rule implements.
|
|
6
|
+
//
|
|
7
|
+
// Phase 1 refactor: the monolithic SafetyGuard.evaluate() became an
|
|
8
|
+
// ordered array of rules (chain-of-responsibility). A rule returns a
|
|
9
|
+
// SafetyDecision to short-circuit the pipeline, or null to fall through
|
|
10
|
+
// to the next rule. Some rules are pure deciders; a few are
|
|
11
|
+
// *context-mutators* that derive shared fields (effectiveContentChange,
|
|
12
|
+
// roles) and always return null so later rules can consume them without
|
|
13
|
+
// re-deriving. This keeps the exact priority order and the exact
|
|
14
|
+
// derived values of the pre-refactor implementation.
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.buildContext = buildContext;
|
|
17
|
+
/**
|
|
18
|
+
* Builds the initial pipeline context. Mirrors the head of the
|
|
19
|
+
* pre-refactor evaluate() exactly (confidence coercion + input
|
|
20
|
+
* passthrough). Derived fields are populated later by the rules.
|
|
21
|
+
*/
|
|
22
|
+
function buildContext(context, aiFix, thresholds, auditor) {
|
|
23
|
+
const confidence = typeof aiFix.confidence === "number" && Number.isFinite(aiFix.confidence)
|
|
24
|
+
? aiFix.confidence
|
|
25
|
+
: 0;
|
|
26
|
+
return {
|
|
27
|
+
aiFix,
|
|
28
|
+
mode: context.mode,
|
|
29
|
+
branch: context.branch,
|
|
30
|
+
confidence,
|
|
31
|
+
thresholds,
|
|
32
|
+
auditor,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export type FunctionalRole = "SUBMIT" | "CANCEL" | "DELETE" | "NAVIGATE" | "AUTH" | "STATUS_POSITIVE" | "STATUS_NEGATIVE" | "UNKNOWN";
|
|
2
|
+
export declare function classifyFunctionalRole(text: string): FunctionalRole;
|
|
3
|
+
export declare function isSemanticOpposite(oldText: string, newText: string): boolean;
|
|
4
|
+
//# sourceMappingURL=roleClassifier.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"roleClassifier.d.ts","sourceRoot":"","sources":["../../../src/services/safety/roleClassifier.ts"],"names":[],"mappings":"AAgBA,MAAM,MAAM,cAAc,GACtB,QAAQ,GACR,QAAQ,GACR,QAAQ,GACR,UAAU,GACV,MAAM,GACN,iBAAiB,GACjB,iBAAiB,GACjB,SAAS,CAAC;AA4Kd,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CASnE;AAgDD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAG5E"}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/services/safety/roleClassifier.ts
|
|
3
|
+
//
|
|
4
|
+
// Pure functional-role classification + binary semantic-opposite lookup.
|
|
5
|
+
//
|
|
6
|
+
// Extracted verbatim from SafetyGuard.ts during the Phase 1 pipeline
|
|
7
|
+
// refactor. Behavior is byte-for-byte identical to the pre-refactor
|
|
8
|
+
// implementation - the role taxonomy, keyword sets, word-boundary regex
|
|
9
|
+
// compilation, and the 36-entry opposite-pair set are unchanged. Keeping
|
|
10
|
+
// this module dependency-free (no SafetyGuard import) avoids a runtime
|
|
11
|
+
// import cycle: SafetyGuard and the rule modules consume it, never the
|
|
12
|
+
// reverse.
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.classifyFunctionalRole = classifyFunctionalRole;
|
|
15
|
+
exports.isSemanticOpposite = isSemanticOpposite;
|
|
16
|
+
function escapeRegExp(s) {
|
|
17
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
18
|
+
}
|
|
19
|
+
const ROLE_KEYWORDS = new Map([
|
|
20
|
+
[
|
|
21
|
+
"SUBMIT",
|
|
22
|
+
new Set([
|
|
23
|
+
"submit",
|
|
24
|
+
"send",
|
|
25
|
+
"confirm",
|
|
26
|
+
"save",
|
|
27
|
+
"ok",
|
|
28
|
+
"done",
|
|
29
|
+
"apply",
|
|
30
|
+
"proceed",
|
|
31
|
+
"continue",
|
|
32
|
+
"execute",
|
|
33
|
+
"finish",
|
|
34
|
+
"complete",
|
|
35
|
+
"publish",
|
|
36
|
+
"post",
|
|
37
|
+
]),
|
|
38
|
+
],
|
|
39
|
+
[
|
|
40
|
+
"CANCEL",
|
|
41
|
+
new Set([
|
|
42
|
+
"cancel",
|
|
43
|
+
"abort",
|
|
44
|
+
"dismiss",
|
|
45
|
+
"close",
|
|
46
|
+
"back",
|
|
47
|
+
"exit",
|
|
48
|
+
"skip",
|
|
49
|
+
"no",
|
|
50
|
+
"discard",
|
|
51
|
+
"reject",
|
|
52
|
+
"decline",
|
|
53
|
+
]),
|
|
54
|
+
],
|
|
55
|
+
[
|
|
56
|
+
"DELETE",
|
|
57
|
+
new Set([
|
|
58
|
+
"delete",
|
|
59
|
+
"remove",
|
|
60
|
+
"clear",
|
|
61
|
+
"erase",
|
|
62
|
+
"destroy",
|
|
63
|
+
"trash",
|
|
64
|
+
"purge",
|
|
65
|
+
"drop",
|
|
66
|
+
"wipe",
|
|
67
|
+
]),
|
|
68
|
+
],
|
|
69
|
+
[
|
|
70
|
+
"NAVIGATE",
|
|
71
|
+
new Set([
|
|
72
|
+
"next",
|
|
73
|
+
"previous",
|
|
74
|
+
"prev",
|
|
75
|
+
"forward",
|
|
76
|
+
"backward",
|
|
77
|
+
"go",
|
|
78
|
+
"navigate",
|
|
79
|
+
"open",
|
|
80
|
+
"view",
|
|
81
|
+
"home",
|
|
82
|
+
"return",
|
|
83
|
+
"redirect",
|
|
84
|
+
]),
|
|
85
|
+
],
|
|
86
|
+
[
|
|
87
|
+
"AUTH",
|
|
88
|
+
new Set([
|
|
89
|
+
"login",
|
|
90
|
+
"logout",
|
|
91
|
+
"sign in",
|
|
92
|
+
"sign out",
|
|
93
|
+
"register",
|
|
94
|
+
"sign up",
|
|
95
|
+
"log in",
|
|
96
|
+
"log out",
|
|
97
|
+
"authenticate",
|
|
98
|
+
"authorize",
|
|
99
|
+
]),
|
|
100
|
+
],
|
|
101
|
+
[
|
|
102
|
+
"STATUS_POSITIVE",
|
|
103
|
+
new Set([
|
|
104
|
+
"success",
|
|
105
|
+
"approved",
|
|
106
|
+
"active",
|
|
107
|
+
"enabled",
|
|
108
|
+
"visible",
|
|
109
|
+
"on",
|
|
110
|
+
"open",
|
|
111
|
+
"allowed",
|
|
112
|
+
"welcome",
|
|
113
|
+
"pass",
|
|
114
|
+
"passed",
|
|
115
|
+
"valid",
|
|
116
|
+
"granted",
|
|
117
|
+
"connected",
|
|
118
|
+
"running",
|
|
119
|
+
"unlocked",
|
|
120
|
+
"online",
|
|
121
|
+
"available",
|
|
122
|
+
]),
|
|
123
|
+
],
|
|
124
|
+
[
|
|
125
|
+
"STATUS_NEGATIVE",
|
|
126
|
+
new Set([
|
|
127
|
+
"error",
|
|
128
|
+
"failure",
|
|
129
|
+
"failed",
|
|
130
|
+
"denied",
|
|
131
|
+
"inactive",
|
|
132
|
+
"disabled",
|
|
133
|
+
"hidden",
|
|
134
|
+
"off",
|
|
135
|
+
"closed",
|
|
136
|
+
"blocked",
|
|
137
|
+
"rejected",
|
|
138
|
+
"invalid",
|
|
139
|
+
"locked",
|
|
140
|
+
"disconnected",
|
|
141
|
+
"stopped",
|
|
142
|
+
"offline",
|
|
143
|
+
"unavailable",
|
|
144
|
+
"forbidden",
|
|
145
|
+
"unauthorized",
|
|
146
|
+
]),
|
|
147
|
+
],
|
|
148
|
+
]);
|
|
149
|
+
// Pre-compiled word-boundary regexes for the role classifier. Built
|
|
150
|
+
// once at module load - classifyFunctionalRole() is on the hot heal
|
|
151
|
+
// path, so we don't pay for regex construction per call.
|
|
152
|
+
const ROLE_KEYWORD_REGEXES = Array.from(ROLE_KEYWORDS, ([role, keywords]) => ({
|
|
153
|
+
role,
|
|
154
|
+
regexes: Array.from(keywords, (kw) => new RegExp(`\\b${escapeRegExp(kw)}\\b`, "i")),
|
|
155
|
+
}));
|
|
156
|
+
// ─────────────────────────────────────────────────────────────
|
|
157
|
+
// FUNCTIONAL ROLE CLASSIFICATION
|
|
158
|
+
//
|
|
159
|
+
// Returns the first role whose keyword matches the text as a whole
|
|
160
|
+
// word (delimited by \b boundaries). Returns UNKNOWN when no role
|
|
161
|
+
// matches.
|
|
162
|
+
//
|
|
163
|
+
// Why \b instead of `.includes(kw)`:
|
|
164
|
+
// - "unauthorized".includes("authorize") → true ← bug
|
|
165
|
+
// - "offline".includes("on") → true ← bug
|
|
166
|
+
// - "disconnected".includes("connected") → true ← bug
|
|
167
|
+
// - "inactive".includes("active") → true ← bug
|
|
168
|
+
// The substring approach mis-classified negations into the
|
|
169
|
+
// positive role of their root word, silently bypassing
|
|
170
|
+
// FAIL_HARD_STATUS_INVERSION. Word-boundary regex eliminates
|
|
171
|
+
// these collisions while keeping multi-word keywords ("sign in")
|
|
172
|
+
// and punctuation-flanked matches ("Submit!") working.
|
|
173
|
+
// ─────────────────────────────────────────────────────────────
|
|
174
|
+
function classifyFunctionalRole(text) {
|
|
175
|
+
const normalized = text.toLowerCase().trim();
|
|
176
|
+
if (normalized.length === 0)
|
|
177
|
+
return "UNKNOWN";
|
|
178
|
+
for (const { role, regexes } of ROLE_KEYWORD_REGEXES) {
|
|
179
|
+
for (const re of regexes) {
|
|
180
|
+
if (re.test(normalized))
|
|
181
|
+
return role;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return "UNKNOWN";
|
|
185
|
+
}
|
|
186
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
187
|
+
// BINARY SEMANTIC OPPOSITES
|
|
188
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
189
|
+
// Binary semantic opposites - always trigger FAIL_HARD regardless of mode,
|
|
190
|
+
// because they indicate a state inversion (pass→fail, success→error, etc.)
|
|
191
|
+
// that almost certainly masks a real logical regression.
|
|
192
|
+
const SEMANTIC_OPPOSITE_PAIRS = new Set([
|
|
193
|
+
"denied|welcome",
|
|
194
|
+
"welcome|denied",
|
|
195
|
+
"login|logout",
|
|
196
|
+
"logout|login",
|
|
197
|
+
"success|failure",
|
|
198
|
+
"failure|success",
|
|
199
|
+
"success|error",
|
|
200
|
+
"error|success",
|
|
201
|
+
"pass|fail",
|
|
202
|
+
"fail|pass",
|
|
203
|
+
"approved|rejected",
|
|
204
|
+
"rejected|approved",
|
|
205
|
+
"active|inactive",
|
|
206
|
+
"inactive|active",
|
|
207
|
+
"enabled|disabled",
|
|
208
|
+
"disabled|enabled",
|
|
209
|
+
"open|closed",
|
|
210
|
+
"closed|open",
|
|
211
|
+
"visible|hidden",
|
|
212
|
+
"hidden|visible",
|
|
213
|
+
"on|off",
|
|
214
|
+
"off|on",
|
|
215
|
+
"true|false",
|
|
216
|
+
"false|true",
|
|
217
|
+
"allowed|denied",
|
|
218
|
+
"denied|allowed",
|
|
219
|
+
"granted|denied",
|
|
220
|
+
"denied|granted",
|
|
221
|
+
"locked|unlocked",
|
|
222
|
+
"unlocked|locked",
|
|
223
|
+
"connected|disconnected",
|
|
224
|
+
"disconnected|connected",
|
|
225
|
+
"running|stopped",
|
|
226
|
+
"stopped|running",
|
|
227
|
+
"valid|invalid",
|
|
228
|
+
"invalid|valid",
|
|
229
|
+
]);
|
|
230
|
+
function isSemanticOpposite(oldText, newText) {
|
|
231
|
+
const key = `${oldText.toLowerCase().trim()}|${newText.toLowerCase().trim()}`;
|
|
232
|
+
return SEMANTIC_OPPOSITE_PAIRS.has(key);
|
|
233
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConfidenceTierRule.d.ts","sourceRoot":"","sources":["../../../../src/services/safety/rules/ConfidenceTierRule.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,UAAU,EAAqC,MAAM,oBAAoB,CAAC;AAExF,eAAO,MAAM,kBAAkB,EAAE,UAqBhC,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/services/safety/rules/ConfidenceTierRule.ts
|
|
3
|
+
//
|
|
4
|
+
// Step 8 - Confidence tiers (terminal rule). All inversion checks have
|
|
5
|
+
// passed; the only remaining question is whether confidence clears the
|
|
6
|
+
// review threshold. Below it → REQUIRES_REVIEW, at/above → SAFE. This
|
|
7
|
+
// rule always returns a decision, so the pipeline never falls past it.
|
|
8
|
+
//
|
|
9
|
+
// Extracted verbatim - identical reasons and the auditor score-breakdown
|
|
10
|
+
// meta forwarded from the IntentAuditor gate.
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.ConfidenceTierRule = void 0;
|
|
13
|
+
exports.ConfidenceTierRule = {
|
|
14
|
+
id: "confidence-tiers",
|
|
15
|
+
run(ctx) {
|
|
16
|
+
if (ctx.confidence < ctx.thresholds.confidenceReviewThreshold) {
|
|
17
|
+
return {
|
|
18
|
+
level: "REQUIRES_REVIEW",
|
|
19
|
+
reason: `Low-confidence fix (${ctx.confidence}/100) - tagging for manual review`,
|
|
20
|
+
canProceed: true,
|
|
21
|
+
meta: ctx.localAuditMeta,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
level: "SAFE",
|
|
26
|
+
reason: `All safety checks passed (confidence=${ctx.confidence}, source=${ctx.source}, ` +
|
|
27
|
+
`oldRole=${ctx.oldRole}, newRole=${ctx.newRole})`,
|
|
28
|
+
canProceed: true,
|
|
29
|
+
meta: ctx.localAuditMeta,
|
|
30
|
+
};
|
|
31
|
+
},
|
|
32
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { FunctionalRole } from "../roleClassifier";
|
|
2
|
+
import type { SafetyRule } from "../PipelineContext";
|
|
3
|
+
/**
|
|
4
|
+
* Roles whose ARRIVAL is treated as high-risk: a control that did not
|
|
5
|
+
* obviously mean "delete" or "authenticate" before, but now does.
|
|
6
|
+
*/
|
|
7
|
+
export declare const DANGEROUS_SINK_ROLES: ReadonlySet<FunctionalRole>;
|
|
8
|
+
export declare function isDangerousSinkTransition(oldRole: FunctionalRole, newRole: FunctionalRole): boolean;
|
|
9
|
+
export declare const DangerousSinkRule: SafetyRule;
|
|
10
|
+
//# sourceMappingURL=DangerousSinkRule.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DangerousSinkRule.d.ts","sourceRoot":"","sources":["../../../../src/services/safety/rules/DangerousSinkRule.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD,OAAO,KAAK,EAAE,UAAU,EAAqC,MAAM,oBAAoB,CAAC;AAExF;;;GAGG;AACH,eAAO,MAAM,oBAAoB,EAAE,WAAW,CAAC,cAAc,CAG3D,CAAC;AAEH,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE,cAAc,GACtB,OAAO,CAET;AAED,eAAO,MAAM,iBAAiB,EAAE,UAmB/B,CAAC"}
|