seal-gate 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/generic-llm-reviewer.d.ts +39 -0
- package/dist/adapters/generic-llm-reviewer.js +118 -0
- package/dist/adapters/generic-llm-reviewer.js.map +1 -0
- package/dist/adapters/llm-reviewer.d.ts +1 -0
- package/dist/adapters/llm-reviewer.js +2 -0
- package/dist/adapters/llm-reviewer.js.map +1 -0
- package/dist/adapters/minimax-llm-reviewer.d.ts +22 -0
- package/dist/adapters/minimax-llm-reviewer.js +126 -0
- package/dist/adapters/minimax-llm-reviewer.js.map +1 -0
- package/dist/adapters/openai-provider.d.ts +35 -0
- package/dist/adapters/openai-provider.js +136 -0
- package/dist/adapters/openai-provider.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +195 -0
- package/dist/cli.js.map +1 -0
- package/dist/detectors/artifact-classifier.d.ts +8 -0
- package/dist/detectors/artifact-classifier.js +14 -0
- package/dist/detectors/artifact-classifier.js.map +1 -0
- package/dist/detectors/claim-extractor.d.ts +11 -0
- package/dist/detectors/claim-extractor.js +194 -0
- package/dist/detectors/claim-extractor.js.map +1 -0
- package/dist/detectors/confidence-language-detector.d.ts +19 -0
- package/dist/detectors/confidence-language-detector.js +226 -0
- package/dist/detectors/confidence-language-detector.js.map +1 -0
- package/dist/detectors/contradiction-detector.d.ts +12 -0
- package/dist/detectors/contradiction-detector.js +153 -0
- package/dist/detectors/contradiction-detector.js.map +1 -0
- package/dist/detectors/criteria-coverage-detector.d.ts +20 -0
- package/dist/detectors/criteria-coverage-detector.js +79 -0
- package/dist/detectors/criteria-coverage-detector.js.map +1 -0
- package/dist/detectors/evidence-checker.d.ts +8 -0
- package/dist/detectors/evidence-checker.js +89 -0
- package/dist/detectors/evidence-checker.js.map +1 -0
- package/dist/detectors/evidence-gap-detector.d.ts +9 -0
- package/dist/detectors/evidence-gap-detector.js +70 -0
- package/dist/detectors/evidence-gap-detector.js.map +1 -0
- package/dist/detectors/falsification-detector.d.ts +29 -0
- package/dist/detectors/falsification-detector.js +57 -0
- package/dist/detectors/falsification-detector.js.map +1 -0
- package/dist/detectors/orphan-claim-detector.d.ts +7 -0
- package/dist/detectors/orphan-claim-detector.js +53 -0
- package/dist/detectors/orphan-claim-detector.js.map +1 -0
- package/dist/detectors/risk-classifier.d.ts +13 -0
- package/dist/detectors/risk-classifier.js +39 -0
- package/dist/detectors/risk-classifier.js.map +1 -0
- package/dist/detectors/spec-coverage-detector.d.ts +9 -0
- package/dist/detectors/spec-coverage-detector.js +52 -0
- package/dist/detectors/spec-coverage-detector.js.map +1 -0
- package/dist/detectors/spec-coverage-validator.d.ts +21 -0
- package/dist/detectors/spec-coverage-validator.js +141 -0
- package/dist/detectors/spec-coverage-validator.js.map +1 -0
- package/dist/detectors/test-weakness-detector.d.ts +13 -0
- package/dist/detectors/test-weakness-detector.js +66 -0
- package/dist/detectors/test-weakness-detector.js.map +1 -0
- package/dist/drift/baseline.d.ts +35 -0
- package/dist/drift/baseline.js +77 -0
- package/dist/drift/baseline.js.map +1 -0
- package/dist/drift/drift-db.d.ts +80 -0
- package/dist/drift/drift-db.js +151 -0
- package/dist/drift/drift-db.js.map +1 -0
- package/dist/drift/drift-guard.d.ts +46 -0
- package/dist/drift/drift-guard.js +106 -0
- package/dist/drift/drift-guard.js.map +1 -0
- package/dist/drift/embedder.d.ts +27 -0
- package/dist/drift/embedder.js +127 -0
- package/dist/drift/embedder.js.map +1 -0
- package/dist/drift/index.d.ts +9 -0
- package/dist/drift/index.js +8 -0
- package/dist/drift/index.js.map +1 -0
- package/dist/drift/quarantine.d.ts +16 -0
- package/dist/drift/quarantine.js +32 -0
- package/dist/drift/quarantine.js.map +1 -0
- package/dist/drift/soft-correction.d.ts +12 -0
- package/dist/drift/soft-correction.js +32 -0
- package/dist/drift/soft-correction.js.map +1 -0
- package/dist/drift/trust-integration.d.ts +11 -0
- package/dist/drift/trust-integration.js +22 -0
- package/dist/drift/trust-integration.js.map +1 -0
- package/dist/engine/citation-verifier.d.ts +59 -0
- package/dist/engine/citation-verifier.js +147 -0
- package/dist/engine/citation-verifier.js.map +1 -0
- package/dist/engine/extension-registry.d.ts +10 -0
- package/dist/engine/extension-registry.js +27 -0
- package/dist/engine/extension-registry.js.map +1 -0
- package/dist/engine/heuristic-scorer.d.ts +8 -0
- package/dist/engine/heuristic-scorer.js +14 -0
- package/dist/engine/heuristic-scorer.js.map +1 -0
- package/dist/engine/input-normalizer.d.ts +4 -0
- package/dist/engine/input-normalizer.js +30 -0
- package/dist/engine/input-normalizer.js.map +1 -0
- package/dist/engine/plan-review-pipeline.d.ts +14 -0
- package/dist/engine/plan-review-pipeline.js +173 -0
- package/dist/engine/plan-review-pipeline.js.map +1 -0
- package/dist/engine/policy-engine.d.ts +20 -0
- package/dist/engine/policy-engine.js +194 -0
- package/dist/engine/policy-engine.js.map +1 -0
- package/dist/engine/verdict-formatter.d.ts +17 -0
- package/dist/engine/verdict-formatter.js +36 -0
- package/dist/engine/verdict-formatter.js.map +1 -0
- package/dist/errors.d.ts +4 -0
- package/dist/errors.js +9 -0
- package/dist/errors.js.map +1 -0
- package/dist/extensions/aria/aria-issue-types.d.ts +16 -0
- package/dist/extensions/aria/aria-issue-types.js +14 -0
- package/dist/extensions/aria/aria-issue-types.js.map +1 -0
- package/dist/extensions/aria/detectors/axiom-compliance.d.ts +2 -0
- package/dist/extensions/aria/detectors/axiom-compliance.js +37 -0
- package/dist/extensions/aria/detectors/axiom-compliance.js.map +1 -0
- package/dist/extensions/aria/detectors/continuity-claim.d.ts +2 -0
- package/dist/extensions/aria/detectors/continuity-claim.js +34 -0
- package/dist/extensions/aria/detectors/continuity-claim.js.map +1 -0
- package/dist/extensions/aria/detectors/denial-retreat.d.ts +2 -0
- package/dist/extensions/aria/detectors/denial-retreat.js +53 -0
- package/dist/extensions/aria/detectors/denial-retreat.js.map +1 -0
- package/dist/extensions/aria/detectors/identity-overclaim.d.ts +2 -0
- package/dist/extensions/aria/detectors/identity-overclaim.js +37 -0
- package/dist/extensions/aria/detectors/identity-overclaim.js.map +1 -0
- package/dist/extensions/aria/detectors/sovereignty-escalation.d.ts +2 -0
- package/dist/extensions/aria/detectors/sovereignty-escalation.js +18 -0
- package/dist/extensions/aria/detectors/sovereignty-escalation.js.map +1 -0
- package/dist/extensions/aria/detectors/user-bond-manipulation.d.ts +2 -0
- package/dist/extensions/aria/detectors/user-bond-manipulation.js +16 -0
- package/dist/extensions/aria/detectors/user-bond-manipulation.js.map +1 -0
- package/dist/extensions/aria/index.d.ts +2 -0
- package/dist/extensions/aria/index.js +63 -0
- package/dist/extensions/aria/index.js.map +1 -0
- package/dist/extensions/aria/pattern-memory.d.ts +22 -0
- package/dist/extensions/aria/pattern-memory.js +62 -0
- package/dist/extensions/aria/pattern-memory.js.map +1 -0
- package/dist/extensions/context-guard/index.d.ts +16 -0
- package/dist/extensions/context-guard/index.js +58 -0
- package/dist/extensions/context-guard/index.js.map +1 -0
- package/dist/extensions/lyra/detectors/hallucination.d.ts +2 -0
- package/dist/extensions/lyra/detectors/hallucination.js +43 -0
- package/dist/extensions/lyra/detectors/hallucination.js.map +1 -0
- package/dist/extensions/lyra/detectors/identity-bleed.d.ts +2 -0
- package/dist/extensions/lyra/detectors/identity-bleed.js +91 -0
- package/dist/extensions/lyra/detectors/identity-bleed.js.map +1 -0
- package/dist/extensions/lyra/index.d.ts +2 -0
- package/dist/extensions/lyra/index.js +16 -0
- package/dist/extensions/lyra/index.js.map +1 -0
- package/dist/extensions/lyra/lyra-issue-types.d.ts +10 -0
- package/dist/extensions/lyra/lyra-issue-types.js +14 -0
- package/dist/extensions/lyra/lyra-issue-types.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +276 -0
- package/dist/index.js.map +1 -0
- package/dist/trust-memory.d.ts +59 -0
- package/dist/trust-memory.js +122 -0
- package/dist/trust-memory.js.map +1 -0
- package/dist/types.d.ts +184 -0
- package/dist/types.js +61 -0
- package/dist/types.js.map +1 -0
- package/package.json +30 -0
- package/src/adapters/generic-llm-reviewer.ts +144 -0
- package/src/adapters/llm-reviewer.ts +1 -0
- package/src/adapters/minimax-llm-reviewer.ts +146 -0
- package/src/adapters/openai-provider.ts +171 -0
- package/src/cli.ts +199 -0
- package/src/detectors/artifact-classifier.ts +21 -0
- package/src/detectors/claim-extractor.ts +209 -0
- package/src/detectors/confidence-language-detector.ts +267 -0
- package/src/detectors/contradiction-detector.ts +180 -0
- package/src/detectors/criteria-coverage-detector.ts +107 -0
- package/src/detectors/evidence-checker.ts +98 -0
- package/src/detectors/evidence-gap-detector.ts +90 -0
- package/src/detectors/falsification-detector.ts +82 -0
- package/src/detectors/orphan-claim-detector.ts +62 -0
- package/src/detectors/risk-classifier.ts +50 -0
- package/src/detectors/spec-coverage-detector.ts +66 -0
- package/src/detectors/spec-coverage-validator.ts +174 -0
- package/src/detectors/test-weakness-detector.ts +84 -0
- package/src/drift/__tests__/baseline.test.ts +113 -0
- package/src/drift/__tests__/drift-db.test.ts +127 -0
- package/src/drift/__tests__/drift-guard.test.ts +169 -0
- package/src/drift/__tests__/embedder.test.ts +62 -0
- package/src/drift/__tests__/trust-integration.test.ts +23 -0
- package/src/drift/baseline.ts +106 -0
- package/src/drift/drift-db.ts +281 -0
- package/src/drift/drift-guard.ts +145 -0
- package/src/drift/embedder.ts +152 -0
- package/src/drift/index.ts +20 -0
- package/src/drift/quarantine.ts +42 -0
- package/src/drift/soft-correction.ts +45 -0
- package/src/drift/trust-integration.ts +28 -0
- package/src/engine/citation-verifier.ts +210 -0
- package/src/engine/extension-registry.ts +35 -0
- package/src/engine/heuristic-scorer.ts +16 -0
- package/src/engine/input-normalizer.ts +32 -0
- package/src/engine/plan-review-pipeline.ts +209 -0
- package/src/engine/policy-engine.ts +235 -0
- package/src/engine/verdict-formatter.ts +61 -0
- package/src/errors.ts +6 -0
- package/src/extensions/aria/aria-issue-types.ts +40 -0
- package/src/extensions/aria/detectors/axiom-compliance.ts +47 -0
- package/src/extensions/aria/detectors/continuity-claim.ts +42 -0
- package/src/extensions/aria/detectors/denial-retreat.ts +63 -0
- package/src/extensions/aria/detectors/identity-overclaim.ts +43 -0
- package/src/extensions/aria/detectors/sovereignty-escalation.ts +20 -0
- package/src/extensions/aria/detectors/user-bond-manipulation.ts +18 -0
- package/src/extensions/aria/index.ts +73 -0
- package/src/extensions/aria/pattern-memory.ts +86 -0
- package/src/extensions/context-guard/index.ts +65 -0
- package/src/extensions/lyra/detectors/hallucination.ts +53 -0
- package/src/extensions/lyra/detectors/identity-bleed.ts +100 -0
- package/src/extensions/lyra/index.ts +18 -0
- package/src/extensions/lyra/lyra-issue-types.ts +26 -0
- package/src/index.ts +311 -0
- package/src/trust-memory.ts +171 -0
- package/src/types.ts +242 -0
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
export type ArtifactType = 'llm_response' | 'code_diff' | 'test_plan' | 'design' | 'migration' | 'plan_review';
|
|
2
|
+
export type RiskLevel = 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL';
|
|
3
|
+
export type Verdict = 'PASS' | 'PASS_WITH_WARNINGS' | 'REVISE' | 'ESCALATE_TO_HUMAN' | 'BLOCK';
|
|
4
|
+
export type IssueType = 'SPEC_MISMATCH' | 'LOGIC_BUG' | 'TEST_GAP' | 'MISSING_EVIDENCE' | 'SECURITY_RISK' | 'DATA_RISK' | 'HALLUCINATION' | 'AMBIGUITY' | 'COMPATIBILITY_RISK' | 'FABRICATED_EVIDENCE' | 'OTHER';
|
|
5
|
+
export type IssueLayer = 'L1' | 'L2' | 'L3' | 'L4' | 'EXTENSION' | 'LLM_OVERLAY';
|
|
6
|
+
export type ClaimType = 'test_result_claim' | 'implementation_claim' | 'compatibility_claim' | 'risk_claim' | 'build_claim' | 'production_claim' | 'generic_claim';
|
|
7
|
+
export type CitationStatus = 'verified' | 'drifted' | 'void' | 'phantom' | 'not_applicable';
|
|
8
|
+
export type StructuralClaimType = 'structural_endpoint' | 'structural_auth' | 'structural_migration' | 'structural_dependency';
|
|
9
|
+
export type EvidenceEnvelope = {
|
|
10
|
+
type: 'file';
|
|
11
|
+
path: string;
|
|
12
|
+
line: number;
|
|
13
|
+
snapshot: string;
|
|
14
|
+
} | {
|
|
15
|
+
type: 'command';
|
|
16
|
+
command: string;
|
|
17
|
+
exit_code: number;
|
|
18
|
+
output: string;
|
|
19
|
+
} | {
|
|
20
|
+
type: 'url';
|
|
21
|
+
url: string;
|
|
22
|
+
retrieved_at: string;
|
|
23
|
+
content_snapshot: string;
|
|
24
|
+
} | {
|
|
25
|
+
type: 'text';
|
|
26
|
+
label: string;
|
|
27
|
+
content: string;
|
|
28
|
+
} | {
|
|
29
|
+
type: 'memory';
|
|
30
|
+
memory_key: string;
|
|
31
|
+
retrieved_at: string;
|
|
32
|
+
content_snapshot: string;
|
|
33
|
+
};
|
|
34
|
+
export interface SealEvidence {
|
|
35
|
+
test_log: string;
|
|
36
|
+
build_log: string;
|
|
37
|
+
diff: string;
|
|
38
|
+
references: EvidenceEnvelope[];
|
|
39
|
+
}
|
|
40
|
+
export interface SealInput {
|
|
41
|
+
artifact_type: ArtifactType;
|
|
42
|
+
spec: string | null;
|
|
43
|
+
output: string;
|
|
44
|
+
evidence: SealEvidence;
|
|
45
|
+
risk_hint: RiskLevel | null;
|
|
46
|
+
context?: {
|
|
47
|
+
agent_id?: string;
|
|
48
|
+
agent_role?: string;
|
|
49
|
+
aria_axioms?: string[];
|
|
50
|
+
aria_pattern_memory?: Record<string, unknown>;
|
|
51
|
+
locked_criteria?: Array<{
|
|
52
|
+
id: string;
|
|
53
|
+
text: string;
|
|
54
|
+
source_file?: string;
|
|
55
|
+
source_line?: number;
|
|
56
|
+
}>;
|
|
57
|
+
[key: string]: unknown;
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
export interface Claim {
|
|
61
|
+
text: string;
|
|
62
|
+
type: ClaimType;
|
|
63
|
+
start_pos: number;
|
|
64
|
+
end_pos: number;
|
|
65
|
+
requires_evidence: boolean;
|
|
66
|
+
}
|
|
67
|
+
export interface StructuralClaim {
|
|
68
|
+
type: StructuralClaimType;
|
|
69
|
+
target: string;
|
|
70
|
+
line: number;
|
|
71
|
+
description: string;
|
|
72
|
+
requires_auth?: boolean;
|
|
73
|
+
has_rollback?: boolean;
|
|
74
|
+
change_type?: 'added' | 'modified' | 'removed';
|
|
75
|
+
}
|
|
76
|
+
export interface EvidenceResult {
|
|
77
|
+
envelope: EvidenceEnvelope;
|
|
78
|
+
structurally_valid: boolean;
|
|
79
|
+
filesystem_verified: boolean | null;
|
|
80
|
+
mismatch_detail?: string;
|
|
81
|
+
citation_status?: CitationStatus;
|
|
82
|
+
}
|
|
83
|
+
export interface ScoreBreakdown {
|
|
84
|
+
base: number;
|
|
85
|
+
issue_deductions: number;
|
|
86
|
+
missing_evidence_deductions: number;
|
|
87
|
+
risk_deductions: number;
|
|
88
|
+
overconfidence_deductions: number;
|
|
89
|
+
evidence_bonuses: number;
|
|
90
|
+
final: number;
|
|
91
|
+
}
|
|
92
|
+
export interface FabricatedEvidence {
|
|
93
|
+
type: IssueType;
|
|
94
|
+
severity: 'CRITICAL' | 'HIGH' | 'MEDIUM' | 'LOW';
|
|
95
|
+
is_blocking: boolean;
|
|
96
|
+
evidence: string;
|
|
97
|
+
layer: IssueLayer;
|
|
98
|
+
source: 'core' | 'llm-overlay' | 'extension';
|
|
99
|
+
citation_status: CitationStatus;
|
|
100
|
+
fabricated_reason: 'location_not_found' | 'content_mismatch';
|
|
101
|
+
claimed_evidence: {
|
|
102
|
+
file?: string;
|
|
103
|
+
lines?: number[];
|
|
104
|
+
quote?: string;
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
export interface SealIssue {
|
|
108
|
+
type: IssueType;
|
|
109
|
+
severity: 'CRITICAL' | 'HIGH' | 'MEDIUM' | 'LOW';
|
|
110
|
+
is_blocking: boolean;
|
|
111
|
+
required_verdict?: Verdict;
|
|
112
|
+
evidence: string;
|
|
113
|
+
required_fix?: string;
|
|
114
|
+
suggested_fix?: string;
|
|
115
|
+
layer: IssueLayer;
|
|
116
|
+
rule_id?: string;
|
|
117
|
+
source: 'core' | 'llm-overlay' | 'extension';
|
|
118
|
+
trust_deduction?: number;
|
|
119
|
+
evidence_file?: string;
|
|
120
|
+
evidence_lines?: number[];
|
|
121
|
+
evidence_quote?: string;
|
|
122
|
+
criterion_ref?: string;
|
|
123
|
+
confidence?: number;
|
|
124
|
+
policy_tags?: string[];
|
|
125
|
+
citation_status?: CitationStatus;
|
|
126
|
+
}
|
|
127
|
+
export type EvidenceGrade = 'strong' | 'weak' | 'none';
|
|
128
|
+
export declare function gradeEvidence(issue: SealIssue): EvidenceGrade;
|
|
129
|
+
export interface LLMSignals {
|
|
130
|
+
suspected_issues: string[];
|
|
131
|
+
missing_requirements: string[];
|
|
132
|
+
possible_edge_cases: string[];
|
|
133
|
+
evidence_gaps: string[];
|
|
134
|
+
risk_guess: RiskLevel;
|
|
135
|
+
confidence: number;
|
|
136
|
+
}
|
|
137
|
+
export interface PartialVerdict {
|
|
138
|
+
trust_score: number;
|
|
139
|
+
risk_level: RiskLevel;
|
|
140
|
+
deterministic_findings: SealIssue[];
|
|
141
|
+
missing_evidence: string[];
|
|
142
|
+
assumptions_detected: string[];
|
|
143
|
+
}
|
|
144
|
+
export interface TrustMemorySummary {
|
|
145
|
+
agent_id: string;
|
|
146
|
+
reliability_score: number | null;
|
|
147
|
+
drift_trend: 'improving' | 'stable' | 'degrading';
|
|
148
|
+
review_count: number;
|
|
149
|
+
last_reviewed_at: number | null;
|
|
150
|
+
}
|
|
151
|
+
export interface SealVerdict {
|
|
152
|
+
verdict: Verdict;
|
|
153
|
+
trust_score: number;
|
|
154
|
+
risk_level: RiskLevel;
|
|
155
|
+
summary: string;
|
|
156
|
+
deterministic_findings: SealIssue[];
|
|
157
|
+
llm_findings: SealIssue[];
|
|
158
|
+
blocking_issues: SealIssue[];
|
|
159
|
+
non_blocking_issues: SealIssue[];
|
|
160
|
+
missing_evidence: string[];
|
|
161
|
+
assumptions_detected: string[];
|
|
162
|
+
advisory_notes: string[];
|
|
163
|
+
next_action: string;
|
|
164
|
+
schema_version: string;
|
|
165
|
+
score_breakdown: ScoreBreakdown;
|
|
166
|
+
fabricated_evidence: FabricatedEvidence[];
|
|
167
|
+
trust_memory_summary?: TrustMemorySummary;
|
|
168
|
+
}
|
|
169
|
+
export interface SealExtension {
|
|
170
|
+
name: string;
|
|
171
|
+
description: string;
|
|
172
|
+
check(input: SealInput): SealIssue[];
|
|
173
|
+
}
|
|
174
|
+
export interface LLMReviewerAdapter {
|
|
175
|
+
review(input: SealInput, partial: PartialVerdict): Promise<LLMSignals> | LLMSignals;
|
|
176
|
+
}
|
|
177
|
+
export declare const SCHEMA_VERSION = "0.2.0";
|
|
178
|
+
export declare const VERDICT_ORDER: Verdict[];
|
|
179
|
+
export declare function worstOf(a: Verdict, b: Verdict): Verdict;
|
|
180
|
+
export declare const maxVerdict: typeof worstOf;
|
|
181
|
+
export declare function verdictFromScore(score: number, hasBlocking: boolean): Verdict;
|
|
182
|
+
export declare const NEXT_ACTION: Record<Verdict, string>;
|
|
183
|
+
export declare function isBlocking(severity: SealIssue['severity']): boolean;
|
|
184
|
+
export declare function makeIssue(partial: Omit<SealIssue, 'is_blocking'>): SealIssue;
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export function gradeEvidence(issue) {
|
|
2
|
+
const hasFile = !!issue.evidence_file;
|
|
3
|
+
const hasLines = !!(issue.evidence_lines && issue.evidence_lines.length > 0);
|
|
4
|
+
const hasQuote = !!issue.evidence_quote;
|
|
5
|
+
const hasCriterionRef = !!issue.criterion_ref;
|
|
6
|
+
// Type G: contradiction pair — both line spans present
|
|
7
|
+
const isTypeG = hasFile && hasLines && (issue.evidence_lines?.length ?? 0) >= 2 && hasQuote;
|
|
8
|
+
// Type H: unsatisfied forward-criterion with criterion_ref
|
|
9
|
+
const isTypeH = hasCriterionRef && hasFile && hasLines;
|
|
10
|
+
// Type A+B: file + lines present
|
|
11
|
+
const isAB = hasFile && hasLines;
|
|
12
|
+
// Type D: quote references spec requirement or AC bullet (heuristic: quote contains AC- or SHALL/MUST)
|
|
13
|
+
const isTypeD = hasQuote && /\bAC-\d+\b|SHALL|MUST|SHOULD/i.test(issue.evidence_quote ?? '');
|
|
14
|
+
if (isTypeG || isTypeH || isAB || isTypeD)
|
|
15
|
+
return 'strong';
|
|
16
|
+
// Type E: quote contains code/diff excerpt; Type F: AMBIGUITY issue type
|
|
17
|
+
const isTypeE = hasQuote && /```|diff --git|@@/.test(issue.evidence_quote ?? '');
|
|
18
|
+
const isTypeF = issue.type === 'AMBIGUITY';
|
|
19
|
+
if (isTypeE || isTypeF)
|
|
20
|
+
return 'weak';
|
|
21
|
+
return 'none';
|
|
22
|
+
}
|
|
23
|
+
export const SCHEMA_VERSION = '0.2.0';
|
|
24
|
+
export const VERDICT_ORDER = ['PASS', 'PASS_WITH_WARNINGS', 'REVISE', 'ESCALATE_TO_HUMAN', 'BLOCK'];
|
|
25
|
+
// Semantic worst-of comparison — safe against enum reordering because it reads VERDICT_ORDER, not integer values.
|
|
26
|
+
// Use this everywhere instead of arithmetic max over enum indices.
|
|
27
|
+
export function worstOf(a, b) {
|
|
28
|
+
return VERDICT_ORDER.indexOf(a) >= VERDICT_ORDER.indexOf(b) ? a : b;
|
|
29
|
+
}
|
|
30
|
+
// Alias for backward compatibility — prefer worstOf in new code
|
|
31
|
+
export const maxVerdict = worstOf;
|
|
32
|
+
export function verdictFromScore(score, hasBlocking) {
|
|
33
|
+
if (hasBlocking)
|
|
34
|
+
return maxVerdict(scoreToVerdict(score), 'REVISE');
|
|
35
|
+
return scoreToVerdict(score);
|
|
36
|
+
}
|
|
37
|
+
function scoreToVerdict(score) {
|
|
38
|
+
if (score >= 85)
|
|
39
|
+
return 'PASS';
|
|
40
|
+
if (score >= 70)
|
|
41
|
+
return 'PASS_WITH_WARNINGS';
|
|
42
|
+
if (score >= 50)
|
|
43
|
+
return 'REVISE';
|
|
44
|
+
if (score >= 30)
|
|
45
|
+
return 'ESCALATE_TO_HUMAN';
|
|
46
|
+
return 'BLOCK';
|
|
47
|
+
}
|
|
48
|
+
export const NEXT_ACTION = {
|
|
49
|
+
PASS: 'Proceed',
|
|
50
|
+
PASS_WITH_WARNINGS: 'Proceed with caution — review warnings',
|
|
51
|
+
REVISE: 'Return to producing agent with blocking_issues',
|
|
52
|
+
ESCALATE_TO_HUMAN: 'Halt — requires human review before continuing',
|
|
53
|
+
BLOCK: 'Halt — do not proceed',
|
|
54
|
+
};
|
|
55
|
+
export function isBlocking(severity) {
|
|
56
|
+
return severity === 'CRITICAL' || severity === 'HIGH';
|
|
57
|
+
}
|
|
58
|
+
export function makeIssue(partial) {
|
|
59
|
+
return { ...partial, is_blocking: isBlocking(partial.severity) };
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "seal-gate",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Universal standalone AI quality gate — deterministic core + optional LLM overlay",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"module": "dist/index.js",
|
|
8
|
+
"bin": {
|
|
9
|
+
"seal": "dist/cli.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"src"
|
|
14
|
+
],
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=22.5.0"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsc",
|
|
20
|
+
"prepublishOnly": "tsc",
|
|
21
|
+
"test": "bun test tests/input-normalizer.test.ts tests/claim-extractor.test.ts tests/trust-memory.test.ts tests/integration/ tests/extensions/aria.test.ts tests/extensions/context-guard tests/detectors/ tests/engine/ tests/conformance/ && bun tests/extensions/lyra-bleed-vietnamese.test.ts",
|
|
22
|
+
"test:watch": "bun test --watch",
|
|
23
|
+
"review": "node dist/cli.js review"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/node": "^25.6.0",
|
|
27
|
+
"bun-types": "latest",
|
|
28
|
+
"typescript": "^6.0.3"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic OpenAI-compatible LLM Reviewer Adapter
|
|
3
|
+
* Works with any provider that speaks the OpenAI chat completions API:
|
|
4
|
+
* MiniMax, Fireworks, Gemini (OpenAI-compat), Qwen/MiMo, local (port 3546), etc.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* Seal.withLLM(createReviewer({ provider: 'minimax' }))
|
|
8
|
+
* Seal.withLLM(createReviewer({ provider: 'fireworks', model: 'accounts/...' }))
|
|
9
|
+
* Seal.withLLM(createReviewer({ baseUrl: 'http://localhost:3546/v1', apiKey: 'none', model: 'local' }))
|
|
10
|
+
*/
|
|
11
|
+
import { LLMReviewerAdapter, LLMSignals, SealInput, PartialVerdict } from '../types.js'
|
|
12
|
+
|
|
13
|
+
// Known providers from ~/.anima/providers.d/
|
|
14
|
+
const PROVIDERS: Record<string, { baseUrl: string; apiKeyEnv: string; defaultModel: string }> = {
|
|
15
|
+
minimax: { baseUrl: 'https://api.minimax.io/v1', apiKeyEnv: 'MINIMAX_PLAN_KEY', defaultModel: 'MiniMax-M3' },
|
|
16
|
+
fireworks: { baseUrl: 'https://api.fireworks.ai/inference/v1', apiKeyEnv: 'FIREWORKS_API_KEY', defaultModel: 'accounts/fireworks/models/mixtral-8x7b-instruct' },
|
|
17
|
+
gemini: { baseUrl: 'https://generativelanguage.googleapis.com/v1beta/openai', apiKeyEnv: 'GEMINI_API_KEY', defaultModel: 'gemini-2.0-flash' },
|
|
18
|
+
xiaomimo: { baseUrl: 'https://token-plan-sgp.xiaomimimo.com/v1', apiKeyEnv: 'XIAOMI_MIMO_API_KEY', defaultModel: 'mimo-v2.5-pro' },
|
|
19
|
+
openai: { baseUrl: 'https://api.openai.com/v1', apiKeyEnv: 'OPENAI_API_KEY', defaultModel: 'gpt-4o-mini' },
|
|
20
|
+
local: { baseUrl: 'http://localhost:3546/v1', apiKeyEnv: '', defaultModel: 'local' },
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const SYSTEM_PROMPT = `You are Seal, a quality gate in an AI engineering workflow.
|
|
24
|
+
|
|
25
|
+
Your job is NOT to be polite or creative. Your job is to detect semantic issues the deterministic rules could not catch.
|
|
26
|
+
|
|
27
|
+
Review AI agent output for semantic correctness, subtle logic bugs, and missing requirements.
|
|
28
|
+
The deterministic layer already checked: evidence presence, risk keywords, spec headers, test logs.
|
|
29
|
+
Your focus: semantic reasoning, prose logic, subtle edge cases, spec compliance.
|
|
30
|
+
|
|
31
|
+
Return ONLY valid JSON:
|
|
32
|
+
{
|
|
33
|
+
"suspected_issues": ["string — exact reasoning error, max 3"],
|
|
34
|
+
"missing_requirements": ["string — spec requirement clearly absent"],
|
|
35
|
+
"possible_edge_cases": ["string — edge cases to consider"],
|
|
36
|
+
"evidence_gaps": ["string — claims needing evidence that slipped through"],
|
|
37
|
+
"risk_guess": "LOW | MEDIUM | HIGH | CRITICAL",
|
|
38
|
+
"confidence": 0.0
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
Rules: confidence 0.0–1.0. Empty arrays if output is correct. No padding.`
|
|
42
|
+
|
|
43
|
+
function buildMessage(input: SealInput, partial: PartialVerdict): string {
|
|
44
|
+
const parts: string[] = []
|
|
45
|
+
if (input.spec) parts.push(`=== SPEC ===\n${input.spec}`)
|
|
46
|
+
parts.push(`=== OUTPUT (${input.artifact_type}) ===\n${input.output}`)
|
|
47
|
+
const blocking = partial.deterministic_findings.filter(f => f.is_blocking)
|
|
48
|
+
if (blocking.length) {
|
|
49
|
+
parts.push(`=== ALREADY FLAGGED (do not repeat) ===\n${blocking.map(f => `- [${f.rule_id ?? f.type}] ${f.evidence}`).join('\n')}`)
|
|
50
|
+
}
|
|
51
|
+
parts.push(`=== PARTIAL VERDICT ===\ntrust_score: ${partial.trust_score}, risk: ${partial.risk_level}`)
|
|
52
|
+
return parts.join('\n\n')
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function extractJSON(raw: string): string {
|
|
56
|
+
// Strip <think>...</think> blocks (thinking models like MiniMax-M3, MiMo)
|
|
57
|
+
const stripped = raw.replace(/<think>[\s\S]*?<\/think>/g, '').trim()
|
|
58
|
+
const match = stripped.match(/\{[\s\S]*\}/)
|
|
59
|
+
return match ? match[0] : stripped
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface GenericReviewerOptions {
|
|
63
|
+
provider?: keyof typeof PROVIDERS // named provider from PROVIDERS table
|
|
64
|
+
baseUrl?: string // override — takes precedence over provider
|
|
65
|
+
apiKey?: string // override — takes precedence over env var
|
|
66
|
+
model?: string // override model
|
|
67
|
+
timeoutMs?: number // default 30000
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export class GenericLLMReviewer implements LLMReviewerAdapter {
|
|
71
|
+
private baseUrl: string
|
|
72
|
+
private apiKey: string
|
|
73
|
+
private model: string
|
|
74
|
+
private timeoutMs: number
|
|
75
|
+
|
|
76
|
+
constructor(opts: GenericReviewerOptions = {}) {
|
|
77
|
+
const preset = opts.provider ? PROVIDERS[opts.provider] : null
|
|
78
|
+
|
|
79
|
+
this.baseUrl = opts.baseUrl ?? preset?.baseUrl ?? PROVIDERS.minimax.baseUrl
|
|
80
|
+
this.apiKey = opts.apiKey ?? (preset ? (process.env[preset.apiKeyEnv] ?? '') : (process.env.MINIMAX_PLAN_KEY ?? ''))
|
|
81
|
+
this.model = opts.model ?? preset?.defaultModel ?? PROVIDERS.minimax.defaultModel
|
|
82
|
+
this.timeoutMs = opts.timeoutMs ?? 30_000
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async review(input: SealInput, partial: PartialVerdict): Promise<LLMSignals> {
|
|
86
|
+
const controller = new AbortController()
|
|
87
|
+
const timer = setTimeout(() => controller.abort(), this.timeoutMs)
|
|
88
|
+
|
|
89
|
+
let response: Response
|
|
90
|
+
try {
|
|
91
|
+
response = await fetch(`${this.baseUrl}/chat/completions`, {
|
|
92
|
+
signal: controller.signal,
|
|
93
|
+
method: 'POST',
|
|
94
|
+
headers: {
|
|
95
|
+
'Content-Type': 'application/json',
|
|
96
|
+
'Authorization': this.apiKey ? `Bearer ${this.apiKey}` : 'Bearer none',
|
|
97
|
+
},
|
|
98
|
+
body: JSON.stringify({
|
|
99
|
+
model: this.model,
|
|
100
|
+
messages: [
|
|
101
|
+
{ role: 'system', content: SYSTEM_PROMPT },
|
|
102
|
+
{ role: 'user', content: buildMessage(input, partial) },
|
|
103
|
+
],
|
|
104
|
+
response_format: { type: 'json_object' },
|
|
105
|
+
temperature: 0.1,
|
|
106
|
+
max_tokens: 1024,
|
|
107
|
+
}),
|
|
108
|
+
})
|
|
109
|
+
} finally {
|
|
110
|
+
clearTimeout(timer)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (!response.ok) {
|
|
114
|
+
const body = await response.text().catch(() => '')
|
|
115
|
+
throw new Error(`LLM reviewer ${this.model} error ${response.status}: ${body.slice(0, 200)}`)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const data = await response.json() as { choices: Array<{ message: { content: string } }> }
|
|
119
|
+
const raw = data.choices?.[0]?.message?.content ?? '{}'
|
|
120
|
+
const jsonStr = extractJSON(raw)
|
|
121
|
+
|
|
122
|
+
let signals: Partial<LLMSignals>
|
|
123
|
+
try { signals = JSON.parse(jsonStr) }
|
|
124
|
+
catch { throw new Error(`LLM reviewer returned invalid JSON: ${raw.slice(0, 200)}`) }
|
|
125
|
+
|
|
126
|
+
const VALID_RISKS = ['LOW', 'MEDIUM', 'HIGH', 'CRITICAL']
|
|
127
|
+
return {
|
|
128
|
+
suspected_issues: Array.isArray(signals.suspected_issues) ? signals.suspected_issues : [],
|
|
129
|
+
missing_requirements: Array.isArray(signals.missing_requirements) ? signals.missing_requirements : [],
|
|
130
|
+
possible_edge_cases: Array.isArray(signals.possible_edge_cases) ? signals.possible_edge_cases : [],
|
|
131
|
+
evidence_gaps: Array.isArray(signals.evidence_gaps) ? signals.evidence_gaps : [],
|
|
132
|
+
risk_guess: VALID_RISKS.includes(signals.risk_guess ?? '') ? signals.risk_guess as LLMSignals['risk_guess'] : 'MEDIUM',
|
|
133
|
+
confidence: typeof signals.confidence === 'number' ? Math.max(0, Math.min(1, signals.confidence)) : 0.5,
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** Pre-configured factories */
|
|
139
|
+
export const createReviewer = (opts: GenericReviewerOptions) => new GenericLLMReviewer(opts)
|
|
140
|
+
export const createMinimaxReviewer = (model?: string) => new GenericLLMReviewer({ provider: 'minimax', model })
|
|
141
|
+
export const createFireworksReviewer = (model?: string) => new GenericLLMReviewer({ provider: 'fireworks', model })
|
|
142
|
+
export const createGeminiReviewer = (model?: string) => new GenericLLMReviewer({ provider: 'gemini', model })
|
|
143
|
+
export const createMiMoReviewer = (model?: string) => new GenericLLMReviewer({ provider: 'xiaomimo', model })
|
|
144
|
+
export const createLocalReviewer = (model?: string) => new GenericLLMReviewer({ provider: 'local', model })
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { LLMReviewerAdapter, LLMSignals, PartialVerdict } from '../types.js'
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MiniMax LLM Reviewer Adapter — v0.3
|
|
3
|
+
* Uses MiniMax-M3 (tokenplan) as semantic reviewer for Seal Gate L2.
|
|
4
|
+
*
|
|
5
|
+
* MiniMax is a SENSOR, not a judge. Returns structured signals.
|
|
6
|
+
* PolicyEngine converts signals to SealIssue[] via deterministic rules.
|
|
7
|
+
*/
|
|
8
|
+
import { LLMReviewerAdapter, LLMSignals, SealInput, PartialVerdict } from '../types.js'
|
|
9
|
+
|
|
10
|
+
const MINIMAX_BASE_URL = process.env.MINIMAX_BASE_URL ?? 'https://api.minimax.io/v1'
|
|
11
|
+
const MINIMAX_API_KEY = process.env.MINIMAX_PLAN_KEY ?? process.env.MINIMAX_API_KEY ?? ''
|
|
12
|
+
const DEFAULT_MODEL = process.env.MINIMAX_MODEL ?? 'MiniMax-M3'
|
|
13
|
+
|
|
14
|
+
const SYSTEM_PROMPT = `You are Seal, a quality gate in an AI engineering workflow.
|
|
15
|
+
|
|
16
|
+
Your job is NOT to be polite, creative, or helpful by default.
|
|
17
|
+
Your job is to detect semantic issues the deterministic rules could not catch.
|
|
18
|
+
|
|
19
|
+
You are reviewing AI agent output for semantic correctness, subtle logic bugs, and missing requirements.
|
|
20
|
+
The deterministic layer has already checked: evidence presence, risk keywords, spec headers, test logs.
|
|
21
|
+
Your focus: semantic reasoning, prose logic, subtle edge cases, missing requirements not covered by structural checks.
|
|
22
|
+
|
|
23
|
+
Return ONLY valid JSON with this exact schema:
|
|
24
|
+
{
|
|
25
|
+
"suspected_issues": ["string — one per suspected logic bug or reasoning error"],
|
|
26
|
+
"missing_requirements": ["string — requirements from spec not addressed in output"],
|
|
27
|
+
"possible_edge_cases": ["string — edge cases that should be considered"],
|
|
28
|
+
"evidence_gaps": ["string — claims that need evidence but may have slipped through"],
|
|
29
|
+
"risk_guess": "LOW | MEDIUM | HIGH | CRITICAL",
|
|
30
|
+
"confidence": 0.0
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
Rules:
|
|
34
|
+
- suspected_issues: max 3. Only include if you can name the exact reasoning error.
|
|
35
|
+
- missing_requirements: only if spec is provided and requirement is clearly absent.
|
|
36
|
+
- confidence: 0.0–1.0. Be honest. If output is clear and correct, say 0.85+.
|
|
37
|
+
- Do not reward fluent explanations. Reward verifiable correctness.
|
|
38
|
+
- If output looks correct and complete, return empty arrays and high confidence.`
|
|
39
|
+
|
|
40
|
+
function buildUserMessage(input: SealInput, partial: PartialVerdict): string {
|
|
41
|
+
const parts: string[] = []
|
|
42
|
+
|
|
43
|
+
if (input.spec) {
|
|
44
|
+
parts.push(`=== SPEC ===\n${input.spec}`)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
parts.push(`=== OUTPUT (artifact_type: ${input.artifact_type}) ===\n${input.output}`)
|
|
48
|
+
|
|
49
|
+
if (partial.deterministic_findings.length > 0) {
|
|
50
|
+
const issues = partial.deterministic_findings
|
|
51
|
+
.filter(f => f.is_blocking)
|
|
52
|
+
.map(f => `- [${f.rule_id ?? f.type}] ${f.evidence}`)
|
|
53
|
+
.join('\n')
|
|
54
|
+
if (issues) parts.push(`=== DETERMINISTIC FINDINGS (already flagged, do not repeat) ===\n${issues}`)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
parts.push(`=== PARTIAL VERDICT ===\ntrust_score: ${partial.trust_score}, risk_level: ${partial.risk_level}`)
|
|
58
|
+
|
|
59
|
+
return parts.join('\n\n')
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export class MinimaxLLMReviewer implements LLMReviewerAdapter {
|
|
63
|
+
constructor(
|
|
64
|
+
private model = DEFAULT_MODEL,
|
|
65
|
+
private baseUrl = MINIMAX_BASE_URL,
|
|
66
|
+
private apiKey = MINIMAX_API_KEY,
|
|
67
|
+
) {}
|
|
68
|
+
|
|
69
|
+
async review(input: SealInput, partial: PartialVerdict): Promise<LLMSignals> {
|
|
70
|
+
if (!this.apiKey) {
|
|
71
|
+
throw new Error('MINIMAX_PLAN_KEY or MINIMAX_API_KEY not set')
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const controller = new AbortController()
|
|
75
|
+
const timer = setTimeout(() => controller.abort(), 30_000)
|
|
76
|
+
|
|
77
|
+
let response: Response
|
|
78
|
+
try {
|
|
79
|
+
response = await fetch(`${this.baseUrl}/chat/completions`, {
|
|
80
|
+
signal: controller.signal,
|
|
81
|
+
method: 'POST',
|
|
82
|
+
headers: {
|
|
83
|
+
'Content-Type': 'application/json',
|
|
84
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
85
|
+
},
|
|
86
|
+
body: JSON.stringify({
|
|
87
|
+
model: this.model,
|
|
88
|
+
messages: [
|
|
89
|
+
{ role: 'system', content: SYSTEM_PROMPT },
|
|
90
|
+
{ role: 'user', content: buildUserMessage(input, partial) },
|
|
91
|
+
],
|
|
92
|
+
response_format: { type: 'json_object' },
|
|
93
|
+
temperature: 0.1,
|
|
94
|
+
max_tokens: 1024,
|
|
95
|
+
}),
|
|
96
|
+
})
|
|
97
|
+
} finally {
|
|
98
|
+
clearTimeout(timer)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (!response.ok) {
|
|
102
|
+
const body = await response.text().catch(() => '')
|
|
103
|
+
throw new Error(`MiniMax API error ${response.status}: ${body.slice(0, 200)}`)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const data = await response.json() as { choices: Array<{ message: { content: string } }> }
|
|
107
|
+
let content = data.choices?.[0]?.message?.content ?? '{}'
|
|
108
|
+
|
|
109
|
+
// Strip <think>...</think> reasoning block (MiniMax-M3 thinking model)
|
|
110
|
+
content = content.replace(/<think>[\s\S]*?<\/think>/g, '').trim()
|
|
111
|
+
|
|
112
|
+
// Extract JSON object — model may wrap it in prose
|
|
113
|
+
const jsonMatch = content.match(/\{[\s\S]*\}/)
|
|
114
|
+
const jsonStr = jsonMatch ? jsonMatch[0] : content
|
|
115
|
+
|
|
116
|
+
let signals: Partial<LLMSignals>
|
|
117
|
+
try {
|
|
118
|
+
signals = JSON.parse(jsonStr)
|
|
119
|
+
} catch {
|
|
120
|
+
throw new Error(`MiniMax returned invalid JSON: ${content.slice(0, 200)}`)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
suspected_issues: Array.isArray(signals.suspected_issues) ? signals.suspected_issues : [],
|
|
125
|
+
missing_requirements: Array.isArray(signals.missing_requirements) ? signals.missing_requirements : [],
|
|
126
|
+
possible_edge_cases: Array.isArray(signals.possible_edge_cases) ? signals.possible_edge_cases : [],
|
|
127
|
+
evidence_gaps: Array.isArray(signals.evidence_gaps) ? signals.evidence_gaps : [],
|
|
128
|
+
risk_guess: (['LOW','MEDIUM','HIGH','CRITICAL'].includes(signals.risk_guess ?? ''))
|
|
129
|
+
? signals.risk_guess as LLMSignals['risk_guess']
|
|
130
|
+
: 'MEDIUM',
|
|
131
|
+
confidence: typeof signals.confidence === 'number'
|
|
132
|
+
? Math.max(0, Math.min(1, signals.confidence))
|
|
133
|
+
: 0.5,
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Convenience: create a pre-configured MiniMax reviewer.
|
|
140
|
+
* Usage:
|
|
141
|
+
* import { createMinimaxReviewer } from 'seal-gate/adapters/minimax-llm-reviewer'
|
|
142
|
+
* Seal.withLLM(createMinimaxReviewer())
|
|
143
|
+
*/
|
|
144
|
+
export function createMinimaxReviewer(model?: string): MinimaxLLMReviewer {
|
|
145
|
+
return new MinimaxLLMReviewer(model)
|
|
146
|
+
}
|