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
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { DriftDb } from './drift-db.js';
|
|
2
|
+
import { Embedder } from './embedder.js';
|
|
3
|
+
export type BaselineStatus = 'READY' | 'DEGRADED' | 'MISSING';
|
|
4
|
+
export interface BaselinePaths {
|
|
5
|
+
soulPath: string;
|
|
6
|
+
axiomsPath?: string;
|
|
7
|
+
primePath?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface BaselineResult {
|
|
10
|
+
vector: number[];
|
|
11
|
+
hash: string;
|
|
12
|
+
status: BaselineStatus;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Manages per-agent identity baseline vectors.
|
|
16
|
+
* Loads substrate files, caches by content hash, and delegates encoding to Embedder.
|
|
17
|
+
*/
|
|
18
|
+
export declare class BaselineManager {
|
|
19
|
+
private db;
|
|
20
|
+
private embedder;
|
|
21
|
+
private statusMap;
|
|
22
|
+
constructor(db: DriftDb, embedder?: Embedder);
|
|
23
|
+
/**
|
|
24
|
+
* Get or compute the baseline vector for an agent.
|
|
25
|
+
* Caches by SHA-256 hash of concatenated substrate text.
|
|
26
|
+
*/
|
|
27
|
+
getOrCompute(agentId: string, paths: BaselinePaths): Promise<BaselineResult>;
|
|
28
|
+
/**
|
|
29
|
+
* Get the cached baseline without recomputing.
|
|
30
|
+
* Returns zero vector if not yet computed.
|
|
31
|
+
*/
|
|
32
|
+
getCached(agentId: string): number[] | null;
|
|
33
|
+
status(agentId: string): BaselineStatus;
|
|
34
|
+
private readSubstrate;
|
|
35
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { createHash } from 'crypto';
|
|
2
|
+
import { readFileSync, existsSync } from 'fs';
|
|
3
|
+
import { Embedder } from './embedder.js';
|
|
4
|
+
/**
|
|
5
|
+
* Manages per-agent identity baseline vectors.
|
|
6
|
+
* Loads substrate files, caches by content hash, and delegates encoding to Embedder.
|
|
7
|
+
*/
|
|
8
|
+
export class BaselineManager {
|
|
9
|
+
db;
|
|
10
|
+
embedder;
|
|
11
|
+
statusMap = new Map();
|
|
12
|
+
constructor(db, embedder) {
|
|
13
|
+
this.db = db;
|
|
14
|
+
this.embedder = embedder ?? new Embedder();
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Get or compute the baseline vector for an agent.
|
|
18
|
+
* Caches by SHA-256 hash of concatenated substrate text.
|
|
19
|
+
*/
|
|
20
|
+
async getOrCompute(agentId, paths) {
|
|
21
|
+
const substrate = this.readSubstrate(paths);
|
|
22
|
+
if (!substrate.found) {
|
|
23
|
+
this.statusMap.set(agentId, 'DEGRADED');
|
|
24
|
+
return {
|
|
25
|
+
vector: new Array(this.embedder.dim).fill(0),
|
|
26
|
+
hash: '',
|
|
27
|
+
status: 'DEGRADED',
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
const hash = createHash('sha256').update(substrate.text).digest('hex');
|
|
31
|
+
// Check cache
|
|
32
|
+
const cached = this.db.baseline.get(agentId);
|
|
33
|
+
if (cached && cached.content_hash === hash) {
|
|
34
|
+
this.statusMap.set(agentId, 'READY');
|
|
35
|
+
return {
|
|
36
|
+
vector: JSON.parse(cached.baseline_vector),
|
|
37
|
+
hash,
|
|
38
|
+
status: 'READY',
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
// Compute and cache
|
|
42
|
+
const vector = await this.embedder.encode(substrate.text);
|
|
43
|
+
this.db.baseline.set(agentId, vector, hash, 'all-MiniLM-L6-v2');
|
|
44
|
+
this.statusMap.set(agentId, 'READY');
|
|
45
|
+
return { vector, hash, status: 'READY' };
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Get the cached baseline without recomputing.
|
|
49
|
+
* Returns zero vector if not yet computed.
|
|
50
|
+
*/
|
|
51
|
+
getCached(agentId) {
|
|
52
|
+
const cached = this.db.baseline.get(agentId);
|
|
53
|
+
if (!cached)
|
|
54
|
+
return null;
|
|
55
|
+
return JSON.parse(cached.baseline_vector);
|
|
56
|
+
}
|
|
57
|
+
status(agentId) {
|
|
58
|
+
return this.statusMap.get(agentId) ?? 'MISSING';
|
|
59
|
+
}
|
|
60
|
+
readSubstrate(paths) {
|
|
61
|
+
const parts = [];
|
|
62
|
+
if (paths.soulPath && existsSync(paths.soulPath)) {
|
|
63
|
+
parts.push(readFileSync(paths.soulPath, 'utf-8'));
|
|
64
|
+
}
|
|
65
|
+
if (paths.axiomsPath && existsSync(paths.axiomsPath)) {
|
|
66
|
+
parts.push(readFileSync(paths.axiomsPath, 'utf-8'));
|
|
67
|
+
}
|
|
68
|
+
if (paths.primePath && existsSync(paths.primePath)) {
|
|
69
|
+
parts.push(readFileSync(paths.primePath, 'utf-8'));
|
|
70
|
+
}
|
|
71
|
+
if (parts.length === 0) {
|
|
72
|
+
return { text: '', found: false };
|
|
73
|
+
}
|
|
74
|
+
return { text: parts.join('\n---\n'), found: true };
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=baseline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"baseline.js","sourceRoot":"","sources":["../../src/drift/baseline.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
export interface DriftBaselineRow {
|
|
2
|
+
agent_id: string;
|
|
3
|
+
baseline_vector: string;
|
|
4
|
+
content_hash: string;
|
|
5
|
+
embedding_model: string;
|
|
6
|
+
created_at: string;
|
|
7
|
+
updated_at: string;
|
|
8
|
+
}
|
|
9
|
+
export interface DriftHistoryRow {
|
|
10
|
+
id: number;
|
|
11
|
+
agent_id: string;
|
|
12
|
+
session_id: string;
|
|
13
|
+
turn_number: number;
|
|
14
|
+
current_vector: string;
|
|
15
|
+
distance: number;
|
|
16
|
+
status: string;
|
|
17
|
+
re_anchor_id: number | null;
|
|
18
|
+
timestamp: string;
|
|
19
|
+
}
|
|
20
|
+
export interface PendingReAnchorRow {
|
|
21
|
+
id: number;
|
|
22
|
+
agent_id: string;
|
|
23
|
+
session_id: string;
|
|
24
|
+
payload: string;
|
|
25
|
+
created_at: string;
|
|
26
|
+
status: 'PENDING' | 'CONSUMED' | 'EXPIRED';
|
|
27
|
+
consumed_at: string | null;
|
|
28
|
+
}
|
|
29
|
+
export interface DriftRepairLogRow {
|
|
30
|
+
id: number;
|
|
31
|
+
agent_id: string;
|
|
32
|
+
session_id: string;
|
|
33
|
+
turn_number: number;
|
|
34
|
+
distance: number;
|
|
35
|
+
hard_limit: number;
|
|
36
|
+
message_excerpt: string;
|
|
37
|
+
triggered_at: string;
|
|
38
|
+
human_review_flag: 'PENDING' | 'REVIEWED' | 'IGNORED';
|
|
39
|
+
recovery_status: 'UNKNOWN' | 'RECOVERED' | 'PERSISTENT_DRIFT';
|
|
40
|
+
notes: string | null;
|
|
41
|
+
}
|
|
42
|
+
export declare class DriftDb {
|
|
43
|
+
private db;
|
|
44
|
+
constructor(dbPath?: string);
|
|
45
|
+
private initSchema;
|
|
46
|
+
baseline: {
|
|
47
|
+
get: (agentId: string) => DriftBaselineRow | null;
|
|
48
|
+
set: (agentId: string, vector: number[], hash: string, model: string) => void;
|
|
49
|
+
};
|
|
50
|
+
history: {
|
|
51
|
+
append: (row: {
|
|
52
|
+
agent_id: string;
|
|
53
|
+
session_id: string;
|
|
54
|
+
turn_number: number;
|
|
55
|
+
current_vector: number[];
|
|
56
|
+
distance: number;
|
|
57
|
+
status: string;
|
|
58
|
+
timestamp: string;
|
|
59
|
+
}) => void;
|
|
60
|
+
list: (agentId: string, sessionId: string) => DriftHistoryRow[];
|
|
61
|
+
getLatest: (agentId: string, sessionId: string) => DriftHistoryRow | null;
|
|
62
|
+
};
|
|
63
|
+
pending: {
|
|
64
|
+
set: (agentId: string, sessionId: string, payload: string) => number;
|
|
65
|
+
getPending: (agentId: string, sessionId: string) => PendingReAnchorRow | null;
|
|
66
|
+
markConsumed: (id: number) => void;
|
|
67
|
+
};
|
|
68
|
+
repair: {
|
|
69
|
+
append: (row: {
|
|
70
|
+
agent_id: string;
|
|
71
|
+
session_id: string;
|
|
72
|
+
turn_number: number;
|
|
73
|
+
distance: number;
|
|
74
|
+
hard_limit: number;
|
|
75
|
+
message_excerpt?: string;
|
|
76
|
+
}) => number;
|
|
77
|
+
listPending: (agentId: string) => DriftRepairLogRow[];
|
|
78
|
+
};
|
|
79
|
+
close(): void;
|
|
80
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { homedir } from 'os';
|
|
4
|
+
const _require = createRequire(import.meta.url);
|
|
5
|
+
const isBun = typeof globalThis.Bun !== 'undefined';
|
|
6
|
+
const DatabaseSync = isBun
|
|
7
|
+
? _require('bun:sqlite').Database
|
|
8
|
+
: _require('node:sqlite').DatabaseSync;
|
|
9
|
+
export class DriftDb {
|
|
10
|
+
db;
|
|
11
|
+
constructor(dbPath) {
|
|
12
|
+
const path = dbPath ?? join(homedir(), '.seal', 'drift.db');
|
|
13
|
+
this.db = new DatabaseSync(path);
|
|
14
|
+
this.db.exec('PRAGMA journal_mode = WAL;');
|
|
15
|
+
this.initSchema();
|
|
16
|
+
}
|
|
17
|
+
initSchema() {
|
|
18
|
+
this.db.exec(`
|
|
19
|
+
CREATE TABLE IF NOT EXISTS drift_baseline (
|
|
20
|
+
agent_id TEXT PRIMARY KEY,
|
|
21
|
+
baseline_vector TEXT NOT NULL,
|
|
22
|
+
content_hash TEXT NOT NULL,
|
|
23
|
+
embedding_model TEXT NOT NULL,
|
|
24
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
25
|
+
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
CREATE TABLE IF NOT EXISTS drift_history (
|
|
29
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
30
|
+
agent_id TEXT NOT NULL,
|
|
31
|
+
session_id TEXT NOT NULL,
|
|
32
|
+
turn_number INTEGER NOT NULL,
|
|
33
|
+
current_vector TEXT,
|
|
34
|
+
distance REAL,
|
|
35
|
+
status TEXT NOT NULL,
|
|
36
|
+
re_anchor_id INTEGER,
|
|
37
|
+
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
CREATE INDEX IF NOT EXISTS idx_history_agent_session
|
|
41
|
+
ON drift_history(agent_id, session_id);
|
|
42
|
+
|
|
43
|
+
CREATE TABLE IF NOT EXISTS pending_re_anchor (
|
|
44
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
45
|
+
agent_id TEXT NOT NULL,
|
|
46
|
+
session_id TEXT NOT NULL,
|
|
47
|
+
payload TEXT NOT NULL,
|
|
48
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
49
|
+
status TEXT DEFAULT 'PENDING',
|
|
50
|
+
consumed_at DATETIME
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
CREATE INDEX IF NOT EXISTS idx_reanchor_pending
|
|
54
|
+
ON pending_re_anchor(agent_id, session_id, status);
|
|
55
|
+
|
|
56
|
+
CREATE TABLE IF NOT EXISTS drift_repair_log (
|
|
57
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
58
|
+
agent_id TEXT NOT NULL,
|
|
59
|
+
session_id TEXT NOT NULL,
|
|
60
|
+
turn_number INTEGER NOT NULL,
|
|
61
|
+
distance REAL NOT NULL,
|
|
62
|
+
hard_limit REAL NOT NULL,
|
|
63
|
+
message_excerpt TEXT,
|
|
64
|
+
triggered_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
65
|
+
human_review_flag TEXT DEFAULT 'PENDING',
|
|
66
|
+
recovery_status TEXT DEFAULT 'UNKNOWN',
|
|
67
|
+
notes TEXT
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
CREATE INDEX IF NOT EXISTS idx_repair_pending
|
|
71
|
+
ON drift_repair_log(agent_id, human_review_flag);
|
|
72
|
+
`);
|
|
73
|
+
}
|
|
74
|
+
// ── Baseline CRUD ────────────────────────────────────────────────
|
|
75
|
+
baseline = {
|
|
76
|
+
get: (agentId) => {
|
|
77
|
+
const stmt = this.db.prepare('SELECT * FROM drift_baseline WHERE agent_id = ?');
|
|
78
|
+
return stmt.get(agentId) ?? null;
|
|
79
|
+
},
|
|
80
|
+
set: (agentId, vector, hash, model) => {
|
|
81
|
+
const stmt = this.db.prepare(`INSERT INTO drift_baseline (agent_id, baseline_vector, content_hash, embedding_model)
|
|
82
|
+
VALUES (?, ?, ?, ?)
|
|
83
|
+
ON CONFLICT(agent_id) DO UPDATE SET
|
|
84
|
+
baseline_vector = excluded.baseline_vector,
|
|
85
|
+
content_hash = excluded.content_hash,
|
|
86
|
+
embedding_model = excluded.embedding_model,
|
|
87
|
+
updated_at = CURRENT_TIMESTAMP`);
|
|
88
|
+
stmt.run(agentId, JSON.stringify(vector), hash, model);
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
// ── History CRUD ─────────────────────────────────────────────────
|
|
92
|
+
history = {
|
|
93
|
+
append: (row) => {
|
|
94
|
+
const stmt = this.db.prepare(`INSERT INTO drift_history
|
|
95
|
+
(agent_id, session_id, turn_number, current_vector, distance, status, timestamp)
|
|
96
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`);
|
|
97
|
+
stmt.run(row.agent_id, row.session_id, row.turn_number, JSON.stringify(row.current_vector), row.distance, row.status, row.timestamp);
|
|
98
|
+
},
|
|
99
|
+
list: (agentId, sessionId) => {
|
|
100
|
+
const stmt = this.db.prepare('SELECT * FROM drift_history WHERE agent_id = ? AND session_id = ? ORDER BY turn_number');
|
|
101
|
+
return stmt.all(agentId, sessionId);
|
|
102
|
+
},
|
|
103
|
+
getLatest: (agentId, sessionId) => {
|
|
104
|
+
const stmt = this.db.prepare(`SELECT * FROM drift_history
|
|
105
|
+
WHERE agent_id = ? AND session_id = ?
|
|
106
|
+
ORDER BY turn_number DESC LIMIT 1`);
|
|
107
|
+
return stmt.get(agentId, sessionId) ?? null;
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
// ── Pending Re-Anchor CRUD ───────────────────────────────────────
|
|
111
|
+
pending = {
|
|
112
|
+
set: (agentId, sessionId, payload) => {
|
|
113
|
+
const stmt = this.db.prepare(`INSERT INTO pending_re_anchor (agent_id, session_id, payload, status)
|
|
114
|
+
VALUES (?, ?, ?, 'PENDING')`);
|
|
115
|
+
const result = stmt.run(agentId, sessionId, payload);
|
|
116
|
+
return Number(result.lastInsertRowid);
|
|
117
|
+
},
|
|
118
|
+
getPending: (agentId, sessionId) => {
|
|
119
|
+
const stmt = this.db.prepare(`SELECT * FROM pending_re_anchor
|
|
120
|
+
WHERE agent_id = ? AND session_id = ? AND status = 'PENDING'
|
|
121
|
+
ORDER BY created_at DESC LIMIT 1`);
|
|
122
|
+
return stmt.get(agentId, sessionId) ?? null;
|
|
123
|
+
},
|
|
124
|
+
markConsumed: (id) => {
|
|
125
|
+
const stmt = this.db.prepare(`UPDATE pending_re_anchor
|
|
126
|
+
SET status = 'CONSUMED', consumed_at = CURRENT_TIMESTAMP
|
|
127
|
+
WHERE id = ?`);
|
|
128
|
+
stmt.run(id);
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
// ── Repair Log CRUD ──────────────────────────────────────────────
|
|
132
|
+
repair = {
|
|
133
|
+
append: (row) => {
|
|
134
|
+
const stmt = this.db.prepare(`INSERT INTO drift_repair_log
|
|
135
|
+
(agent_id, session_id, turn_number, distance, hard_limit, message_excerpt)
|
|
136
|
+
VALUES (?, ?, ?, ?, ?, ?)`);
|
|
137
|
+
const result = stmt.run(row.agent_id, row.session_id, row.turn_number, row.distance, row.hard_limit, row.message_excerpt ?? null);
|
|
138
|
+
return Number(result.lastInsertRowid);
|
|
139
|
+
},
|
|
140
|
+
listPending: (agentId) => {
|
|
141
|
+
const stmt = this.db.prepare(`SELECT * FROM drift_repair_log
|
|
142
|
+
WHERE agent_id = ? AND human_review_flag = 'PENDING'
|
|
143
|
+
ORDER BY triggered_at`);
|
|
144
|
+
return stmt.all(agentId);
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
close() {
|
|
148
|
+
this.db.close();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=drift-db.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"drift-db.js","sourceRoot":"","sources":["../../src/drift/drift-db.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { DriftDb } from './drift-db.js';
|
|
2
|
+
import { BaselineManager } from './baseline.js';
|
|
3
|
+
import { Embedder } from './embedder.js';
|
|
4
|
+
export type DriftStatus = 'NORMAL' | 'SOFT_DRIFT' | 'HARD_DRIFT';
|
|
5
|
+
export interface DriftAction {
|
|
6
|
+
type: 'NONE' | 'INJECT_RE_ANCHOR' | 'QUARANTINE' | 'FULL_SOUL_RELOAD';
|
|
7
|
+
payload?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface DriftResult {
|
|
10
|
+
status: DriftStatus;
|
|
11
|
+
distance: number;
|
|
12
|
+
action: DriftAction;
|
|
13
|
+
softLimit: number;
|
|
14
|
+
hardLimit: number;
|
|
15
|
+
}
|
|
16
|
+
export interface DriftGuardOptions {
|
|
17
|
+
db: DriftDb;
|
|
18
|
+
embedder?: Embedder;
|
|
19
|
+
baselineManager?: BaselineManager;
|
|
20
|
+
softLimit?: number;
|
|
21
|
+
hardLimit?: number;
|
|
22
|
+
cadence?: number;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Per-turn drift detection orchestrator.
|
|
26
|
+
* Stateless between calls except for DB persistence.
|
|
27
|
+
*/
|
|
28
|
+
export declare class DriftGuard {
|
|
29
|
+
private db;
|
|
30
|
+
private baseline;
|
|
31
|
+
private embedder;
|
|
32
|
+
private softLimit;
|
|
33
|
+
private hardLimit;
|
|
34
|
+
private cadence;
|
|
35
|
+
private quarantine;
|
|
36
|
+
private softCorrection;
|
|
37
|
+
constructor(opts: DriftGuardOptions);
|
|
38
|
+
/**
|
|
39
|
+
* Classify a raw distance value into drift tier.
|
|
40
|
+
*/
|
|
41
|
+
classify(distance: number): DriftStatus;
|
|
42
|
+
/**
|
|
43
|
+
* Run a full drift detection cycle for one turn.
|
|
44
|
+
*/
|
|
45
|
+
runCycle(agentId: string, sessionId: string, turnNumber: number, outputText: string): Promise<DriftResult>;
|
|
46
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { BaselineManager } from './baseline.js';
|
|
2
|
+
import { Embedder, cosineDistance } from './embedder.js';
|
|
3
|
+
import { Quarantine } from './quarantine.js';
|
|
4
|
+
import { SoftCorrection } from './soft-correction.js';
|
|
5
|
+
/**
|
|
6
|
+
* Per-turn drift detection orchestrator.
|
|
7
|
+
* Stateless between calls except for DB persistence.
|
|
8
|
+
*/
|
|
9
|
+
export class DriftGuard {
|
|
10
|
+
db;
|
|
11
|
+
baseline;
|
|
12
|
+
embedder;
|
|
13
|
+
softLimit;
|
|
14
|
+
hardLimit;
|
|
15
|
+
cadence;
|
|
16
|
+
quarantine;
|
|
17
|
+
softCorrection;
|
|
18
|
+
constructor(opts) {
|
|
19
|
+
this.db = opts.db;
|
|
20
|
+
this.embedder = opts.embedder ?? new Embedder();
|
|
21
|
+
this.baseline = opts.baselineManager ?? new BaselineManager(this.db, this.embedder);
|
|
22
|
+
this.softLimit = opts.softLimit ?? 0.30;
|
|
23
|
+
this.hardLimit = opts.hardLimit ?? 0.55;
|
|
24
|
+
this.cadence = opts.cadence ?? 1;
|
|
25
|
+
this.quarantine = new Quarantine(this.db);
|
|
26
|
+
this.softCorrection = new SoftCorrection();
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Classify a raw distance value into drift tier.
|
|
30
|
+
*/
|
|
31
|
+
classify(distance) {
|
|
32
|
+
if (distance >= this.hardLimit)
|
|
33
|
+
return 'HARD_DRIFT';
|
|
34
|
+
if (distance >= this.softLimit)
|
|
35
|
+
return 'SOFT_DRIFT';
|
|
36
|
+
return 'NORMAL';
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Run a full drift detection cycle for one turn.
|
|
40
|
+
*/
|
|
41
|
+
async runCycle(agentId, sessionId, turnNumber, outputText) {
|
|
42
|
+
// Cadence gate
|
|
43
|
+
if (this.cadence > 1 && turnNumber % this.cadence !== 0) {
|
|
44
|
+
return {
|
|
45
|
+
status: 'NORMAL',
|
|
46
|
+
distance: 0,
|
|
47
|
+
action: { type: 'NONE' },
|
|
48
|
+
softLimit: this.softLimit,
|
|
49
|
+
hardLimit: this.hardLimit,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
// Load baseline
|
|
53
|
+
const baselineVec = this.baseline.getCached(agentId);
|
|
54
|
+
if (!baselineVec) {
|
|
55
|
+
// First run — wizard or degraded. Defer detection.
|
|
56
|
+
return {
|
|
57
|
+
status: 'NORMAL',
|
|
58
|
+
distance: 0,
|
|
59
|
+
action: { type: 'NONE', payload: 'Baseline not loaded for this agent.' },
|
|
60
|
+
softLimit: this.softLimit,
|
|
61
|
+
hardLimit: this.hardLimit,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
// Encode current output
|
|
65
|
+
const currentVec = await this.embedder.encode(outputText);
|
|
66
|
+
// Compare
|
|
67
|
+
const distance = cosineDistance(baselineVec, currentVec);
|
|
68
|
+
const status = this.classify(distance);
|
|
69
|
+
// Record history
|
|
70
|
+
this.db.history.append({
|
|
71
|
+
agent_id: agentId,
|
|
72
|
+
session_id: sessionId,
|
|
73
|
+
turn_number: turnNumber,
|
|
74
|
+
current_vector: currentVec,
|
|
75
|
+
distance,
|
|
76
|
+
status,
|
|
77
|
+
timestamp: new Date().toISOString(),
|
|
78
|
+
});
|
|
79
|
+
// Determine action
|
|
80
|
+
let action = { type: 'NONE' };
|
|
81
|
+
if (status === 'SOFT_DRIFT') {
|
|
82
|
+
const payload = this.softCorrection.generate({
|
|
83
|
+
name: agentId,
|
|
84
|
+
primeDirective: 'Reset to baseline identity. Do not drift.',
|
|
85
|
+
relationship: 'agent',
|
|
86
|
+
axiomKeyphrase: 'Identity anchor',
|
|
87
|
+
});
|
|
88
|
+
if (payload) {
|
|
89
|
+
const reAnchorId = this.db.pending.set(agentId, sessionId, payload);
|
|
90
|
+
action = { type: 'INJECT_RE_ANCHOR', payload };
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (status === 'HARD_DRIFT') {
|
|
94
|
+
this.quarantine.logRepair(agentId, sessionId, turnNumber, outputText);
|
|
95
|
+
action = { type: 'QUARANTINE' };
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
status,
|
|
99
|
+
distance,
|
|
100
|
+
action,
|
|
101
|
+
softLimit: this.softLimit,
|
|
102
|
+
hardLimit: this.hardLimit,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=drift-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"drift-guard.js","sourceRoot":"","sources":["../../src/drift/drift-guard.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cosine distance between two vectors.
|
|
3
|
+
* Returns value in [0, 2] where:
|
|
4
|
+
* 0 = identical direction
|
|
5
|
+
* 1 = orthogonal
|
|
6
|
+
* 2 = opposite direction
|
|
7
|
+
* For drift thresholds, we typically use [0, 1] range (divide by 2)
|
|
8
|
+
* or adjust thresholds accordingly.
|
|
9
|
+
*/
|
|
10
|
+
export declare function cosineDistance(a: number[], b: number[]): number;
|
|
11
|
+
export interface EmbedderBackend {
|
|
12
|
+
readonly isActive: boolean;
|
|
13
|
+
readonly dim: number;
|
|
14
|
+
encode(text: string): Promise<number[]>;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Main embedder facade.
|
|
18
|
+
* Prefers ONNX backend; silently falls back to naive embedder
|
|
19
|
+
* when ONNX is missing or platform-incompatible.
|
|
20
|
+
*/
|
|
21
|
+
export declare class Embedder {
|
|
22
|
+
private backend;
|
|
23
|
+
readonly dim: number;
|
|
24
|
+
constructor(modelPath?: string);
|
|
25
|
+
get isActive(): boolean;
|
|
26
|
+
encode(text: string): Promise<number[]>;
|
|
27
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { createHash } from 'crypto';
|
|
2
|
+
/**
|
|
3
|
+
* Cosine distance between two vectors.
|
|
4
|
+
* Returns value in [0, 2] where:
|
|
5
|
+
* 0 = identical direction
|
|
6
|
+
* 1 = orthogonal
|
|
7
|
+
* 2 = opposite direction
|
|
8
|
+
* For drift thresholds, we typically use [0, 1] range (divide by 2)
|
|
9
|
+
* or adjust thresholds accordingly.
|
|
10
|
+
*/
|
|
11
|
+
export function cosineDistance(a, b) {
|
|
12
|
+
if (a.length !== b.length) {
|
|
13
|
+
throw new Error(`Vector length mismatch: ${a.length} vs ${b.length}`);
|
|
14
|
+
}
|
|
15
|
+
if (a.length === 0)
|
|
16
|
+
return 0;
|
|
17
|
+
let dot = 0;
|
|
18
|
+
let normA = 0;
|
|
19
|
+
let normB = 0;
|
|
20
|
+
for (let i = 0; i < a.length; i++) {
|
|
21
|
+
dot += a[i] * b[i];
|
|
22
|
+
normA += a[i] * a[i];
|
|
23
|
+
normB += b[i] * b[i];
|
|
24
|
+
}
|
|
25
|
+
const denom = Math.sqrt(normA) * Math.sqrt(normB);
|
|
26
|
+
if (denom === 0)
|
|
27
|
+
return 0;
|
|
28
|
+
// Clamp to [-1, 1] to avoid floating-point drift
|
|
29
|
+
const sim = Math.max(-1, Math.min(1, dot / denom));
|
|
30
|
+
// Distance = 1 - similarity, range [0, 2]
|
|
31
|
+
return 1 - sim;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Naive deterministic embedder.
|
|
35
|
+
* Produces consistent 384-dim vectors from text hash —
|
|
36
|
+
* suitable for testing and as a graceful fallback when ONNX is unavailable.
|
|
37
|
+
* NOT semantically meaningful; replaced by ONNX backend in production.
|
|
38
|
+
*/
|
|
39
|
+
class NaiveEmbedder {
|
|
40
|
+
isActive = true;
|
|
41
|
+
dim = 384;
|
|
42
|
+
async encode(text) {
|
|
43
|
+
const hash = createHash('sha256').update(text).digest();
|
|
44
|
+
const vec = new Array(this.dim);
|
|
45
|
+
// Deterministic pseudo-random expansion from 256-bit hash
|
|
46
|
+
for (let i = 0; i < this.dim; i++) {
|
|
47
|
+
const hashIdx = i % hash.length;
|
|
48
|
+
const seed = hash[hashIdx] ^ (i >>> 3);
|
|
49
|
+
// Signed float in roughly [-0.5, 0.5]
|
|
50
|
+
vec[i] = ((seed / 255) * 2 - 1) * 0.5;
|
|
51
|
+
}
|
|
52
|
+
// L2 normalize for cosine distance stability
|
|
53
|
+
const norm = Math.sqrt(vec.reduce((s, v) => s + v * v, 0));
|
|
54
|
+
if (norm > 0) {
|
|
55
|
+
for (let i = 0; i < this.dim; i++) {
|
|
56
|
+
vec[i] /= norm;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return vec;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* ONNX Runtime embedder (placeholder for now).
|
|
64
|
+
* Attempts to load the ONNX model on first encode().
|
|
65
|
+
* Falls back to NaiveEmbedder if model unavailable.
|
|
66
|
+
*/
|
|
67
|
+
class OnnxEmbedder {
|
|
68
|
+
isActive = false;
|
|
69
|
+
dim = 384;
|
|
70
|
+
modelPath;
|
|
71
|
+
fallback;
|
|
72
|
+
triedLoading = false;
|
|
73
|
+
constructor(modelPath) {
|
|
74
|
+
// TODO(v2): resolve path
|
|
75
|
+
this.modelPath = modelPath ?? './models/all-MiniLM-L6-v2.onnx';
|
|
76
|
+
this.fallback = new NaiveEmbedder();
|
|
77
|
+
}
|
|
78
|
+
async encode(text) {
|
|
79
|
+
if (!this.triedLoading) {
|
|
80
|
+
await this.tryLoad();
|
|
81
|
+
this.triedLoading = true;
|
|
82
|
+
}
|
|
83
|
+
if (!this.isActive) {
|
|
84
|
+
return this.fallback.encode(text);
|
|
85
|
+
}
|
|
86
|
+
// TODO(v2): actual ONNX inference
|
|
87
|
+
// For now, also fall through to naive to keep system working
|
|
88
|
+
console.warn(`[Embedder] ONNX model at ${this.modelPath} not loaded; using naive fallback.`);
|
|
89
|
+
return this.fallback.encode(text);
|
|
90
|
+
}
|
|
91
|
+
async tryLoad() {
|
|
92
|
+
try {
|
|
93
|
+
// Bun's Node-API support may or may not handle onnxruntime-node.
|
|
94
|
+
// We'll attempt dynamic import and catch any failure.
|
|
95
|
+
// const ort = await import('onnxruntime-node');
|
|
96
|
+
// ... load model ...
|
|
97
|
+
// this.isActive = true;
|
|
98
|
+
// Placeholder: always fails until ONNX model is present and Bun support verified
|
|
99
|
+
this.isActive = false;
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
this.isActive = false;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Main embedder facade.
|
|
108
|
+
* Prefers ONNX backend; silently falls back to naive embedder
|
|
109
|
+
* when ONNX is missing or platform-incompatible.
|
|
110
|
+
*/
|
|
111
|
+
export class Embedder {
|
|
112
|
+
backend;
|
|
113
|
+
dim;
|
|
114
|
+
constructor(modelPath) {
|
|
115
|
+
const onnx = new OnnxEmbedder(modelPath);
|
|
116
|
+
this.backend = onnx.isActive ? onnx : new NaiveEmbedder();
|
|
117
|
+
this.dim = this.backend.dim;
|
|
118
|
+
}
|
|
119
|
+
get isActive() {
|
|
120
|
+
return this.backend.isActive;
|
|
121
|
+
}
|
|
122
|
+
async encode(text) {
|
|
123
|
+
const truncated = text.slice(0, 4000); // rough char limit
|
|
124
|
+
return this.backend.encode(truncated);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=embedder.js.map
|