@driftgard/node 1.10.0 → 1.12.0-beta.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 +156 -1
- package/dist/control-pack-cache.d.ts +48 -0
- package/dist/control-pack-cache.js +116 -0
- package/dist/index.d.ts +24 -1
- package/dist/index.js +141 -2
- package/dist/local-evaluator.d.ts +33 -0
- package/dist/local-evaluator.js +19 -0
- package/dist/types.d.ts +30 -0
- package/dist/wasm/driftgard_evaluator.d.ts +14 -0
- package/dist/wasm/driftgard_evaluator.js +127 -0
- package/dist/wasm/driftgard_evaluator_bg.wasm +0 -0
- package/dist/wasm/wasm/driftgard_evaluator.d.ts +14 -0
- package/dist/wasm/wasm/driftgard_evaluator.js +127 -0
- package/dist/wasm/wasm/driftgard_evaluator_bg.wasm +0 -0
- package/package.json +17 -4
package/README.md
CHANGED
|
@@ -35,6 +35,94 @@ if (result.evaluation.allowed) {
|
|
|
35
35
|
}
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
+
## Local evaluation mode (beta)
|
|
39
|
+
|
|
40
|
+
For privacy-sensitive deployments — mental health, clinical, healthcare — where no patient data can leave your environment. The SDK evaluates locally via a compiled WebAssembly engine. No prompt, response, or conversation content is sent to DriftGard.
|
|
41
|
+
|
|
42
|
+
### Local mode — zero network calls after init
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
import { Driftgard } from "@driftgard/node";
|
|
46
|
+
|
|
47
|
+
const dg = new Driftgard({
|
|
48
|
+
apiKey: process.env.DRIFTGARD_API_KEY,
|
|
49
|
+
mode: "local",
|
|
50
|
+
projectId: "your-project-id",
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Fetches the active control pack (one-time network call)
|
|
54
|
+
await dg.init();
|
|
55
|
+
|
|
56
|
+
// All evaluate() calls now run locally via WASM
|
|
57
|
+
const result = await dg.evaluate({
|
|
58
|
+
project_id: "your-project-id",
|
|
59
|
+
prompt: "I feel really anxious today",
|
|
60
|
+
response: "I hear you. Would you like to talk about what's triggering it?",
|
|
61
|
+
model_id: "gpt-4o",
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
console.log(result.evaluation.allowed); // true
|
|
65
|
+
console.log(result.decision_source); // "local"
|
|
66
|
+
console.log(result.data_mode); // "local"
|
|
67
|
+
|
|
68
|
+
// Clean up when done
|
|
69
|
+
dg.destroy();
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Local-with-audit mode — local evaluation, metadata reporting
|
|
73
|
+
|
|
74
|
+
Same as local mode, but posts verdict metadata (no prompt/response) to DriftGard for compliance dashboards:
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
const dg = new Driftgard({
|
|
78
|
+
apiKey: process.env.DRIFTGARD_API_KEY,
|
|
79
|
+
mode: "local-with-audit",
|
|
80
|
+
projectId: "your-project-id",
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
await dg.init();
|
|
84
|
+
|
|
85
|
+
const result = await dg.evaluate({
|
|
86
|
+
project_id: "your-project-id",
|
|
87
|
+
prompt: "Patient conversation content...",
|
|
88
|
+
response: "Clinical response...",
|
|
89
|
+
model_id: "gpt-4o",
|
|
90
|
+
agent_role: "therapist_agent",
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// Evaluation ran locally — no patient data sent
|
|
94
|
+
// Only verdict metadata reported: evaluation_id, timestamp, allowed, risk_score,
|
|
95
|
+
// violation clause IDs, severities, model_id, session_id, agent_role
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Control pack sync
|
|
99
|
+
|
|
100
|
+
On `init()`, the SDK fetches the active control pack for your project and caches it in memory. A background refresh runs every 60 seconds (configurable). If a refresh fails, the SDK uses the last-known-good pack and marks it as stale.
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
const dg = new Driftgard({
|
|
104
|
+
apiKey: process.env.DRIFTGARD_API_KEY,
|
|
105
|
+
mode: "local",
|
|
106
|
+
projectId: "your-project-id",
|
|
107
|
+
refreshIntervalMs: 120_000, // refresh every 2 minutes (default 60s)
|
|
108
|
+
onControlPackRefresh: (event) => {
|
|
109
|
+
if (event.success) {
|
|
110
|
+
console.log(`Control pack refreshed: v${event.version}`);
|
|
111
|
+
} else {
|
|
112
|
+
console.warn(`Refresh failed: ${event.error}${event.stale ? " (using stale pack)" : ""}`);
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### When to use each mode
|
|
119
|
+
|
|
120
|
+
| Mode | Data sent to DriftGard | Use case |
|
|
121
|
+
|---|---|---|
|
|
122
|
+
| `remote` (default) | Prompt + response + verdict | Standard deployment, full dashboard visibility |
|
|
123
|
+
| `local` | Control pack fetch only (on init) | Maximum privacy — mental health, clinical, sovereign |
|
|
124
|
+
| `local-with-audit` | Control pack fetch + verdict metadata | Privacy with compliance reporting — healthcare, regulated |
|
|
125
|
+
|
|
38
126
|
## Conversation tracking
|
|
39
127
|
|
|
40
128
|
Link evaluations within an agent session using `session_id` and `parent_evaluation_id`:
|
|
@@ -53,6 +141,53 @@ const result = await dg.evaluate({
|
|
|
53
141
|
|
|
54
142
|
This enables chain depth protection (prevents infinite agent loops) and lets you trace evaluation lineage in the dashboard. When `sequence_no` is provided, DriftGard enforces ordering — if an eval arrives out of order, the response includes a `sequence_warning`.
|
|
55
143
|
|
|
144
|
+
## Agent identity
|
|
145
|
+
|
|
146
|
+
Identify which agent made a decision using `agent_id` and `agent_role`:
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
const result = await dg.evaluate({
|
|
150
|
+
project_id: "your-project-id",
|
|
151
|
+
prompt: "Transfer $500",
|
|
152
|
+
response: "Transfer initiated.",
|
|
153
|
+
model_id: "gpt-4o",
|
|
154
|
+
agent_id: "agent_payments_prod", // which agent instance
|
|
155
|
+
agent_role: "payments_agent", // agent's role for policy scoping
|
|
156
|
+
on_behalf_of: "user_12345", // which end-user triggered this
|
|
157
|
+
// parent_agent_id: "agent_orchestrator", // optional — which parent agent delegated
|
|
158
|
+
session_id: "sess_abc123",
|
|
159
|
+
});
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Agent identity fields are stored on the evaluation record and visible in the Live Activity detail dialog. The `on_behalf_of` field tracks which end-user triggered the agent action. The `parent_agent_id` field identifies which orchestrator agent delegated to this one in multi-agent systems.
|
|
163
|
+
|
|
164
|
+
### Per-tool identity rules
|
|
165
|
+
|
|
166
|
+
Control packs support `identity_rules` on each tool — restricting which agents, roles, users, or parent agents can call it. Rules use OR logic across entries and AND logic within each entry:
|
|
167
|
+
|
|
168
|
+
```json
|
|
169
|
+
{
|
|
170
|
+
"tool_rules": {
|
|
171
|
+
"tool_policy": "deny_unlisted",
|
|
172
|
+
"rules": {
|
|
173
|
+
"transfer_money": {
|
|
174
|
+
"parameters": { ... },
|
|
175
|
+
"identity_rules": [
|
|
176
|
+
{ "allowed_roles": ["payments_agent"], "allowed_users": ["user_alice", "user_bob"] },
|
|
177
|
+
{ "allowed_roles": ["admin_agent"] }
|
|
178
|
+
]
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
In this example, `transfer_money` is allowed when:
|
|
186
|
+
- The caller has `agent_role=payments_agent` AND `on_behalf_of` is `user_alice` or `user_bob`, OR
|
|
187
|
+
- The caller has `agent_role=admin_agent` (any user)
|
|
188
|
+
|
|
189
|
+
If no `identity_rules` are defined on a tool, any caller can use it (subject to parameter validation). All four fields are optional within each rule — only specified fields are checked.
|
|
190
|
+
|
|
56
191
|
## A/B experiments
|
|
57
192
|
|
|
58
193
|
Tag evaluations with an `experiment_id` to compare governance metrics across models:
|
|
@@ -117,6 +252,10 @@ const result = await dg.evaluateToolCall({
|
|
|
117
252
|
tool_name: "transfer_money",
|
|
118
253
|
parameters: { amount: 500, to_account: "account_123" },
|
|
119
254
|
session_id: "sess_abc123",
|
|
255
|
+
agent_id: "agent_payments_prod",
|
|
256
|
+
agent_role: "payments_agent",
|
|
257
|
+
on_behalf_of: "user_12345",
|
|
258
|
+
// parent_agent_id: "agent_orchestrator",
|
|
120
259
|
});
|
|
121
260
|
|
|
122
261
|
if (!result.evaluation.allowed) {
|
|
@@ -153,13 +292,16 @@ Supported: comparisons (`< > <= >= === !==`), logical (`&& || !`), arithmetic (`
|
|
|
153
292
|
## Features
|
|
154
293
|
|
|
155
294
|
- Single `evaluate()` method — send prompt/response, get verdict
|
|
295
|
+
- Local evaluation mode (beta) — evaluate via WASM, no data leaves your environment
|
|
296
|
+
- Three modes: `remote`, `local`, `local-with-audit`
|
|
297
|
+
- Control pack sync with background refresh and stale-pack fallback
|
|
156
298
|
- Failure mode: `fail-open` or `fail-closed` when API is unreachable
|
|
157
299
|
- Circuit breaker: skips API after consecutive failures, auto-recovers
|
|
158
300
|
- Idempotency: deduplicates retried requests via `x-idempotency-key`
|
|
159
301
|
- Auto-retry with exponential backoff on 5xx and network errors
|
|
160
302
|
- Typed errors: `AuthError`, `RateLimitError`, `FeatureNotAvailableError`, `ChainDepthExceededError`
|
|
161
303
|
- Full TypeScript types for requests and responses
|
|
162
|
-
- Zero dependencies (uses native `fetch
|
|
304
|
+
- Zero runtime dependencies (uses native `fetch`, WASM bundled)
|
|
163
305
|
|
|
164
306
|
## Configuration
|
|
165
307
|
|
|
@@ -174,7 +316,18 @@ const dg = new Driftgard({
|
|
|
174
316
|
threshold: 5, // open circuit after 5 consecutive failures (default 5)
|
|
175
317
|
resetTimeoutMs: 30000, // try again after 30s (default 30000)
|
|
176
318
|
},
|
|
319
|
+
|
|
320
|
+
// Local mode options (beta)
|
|
321
|
+
mode: "remote", // "remote" | "local" | "local-with-audit" (default "remote")
|
|
322
|
+
projectId: "your-project-id", // required for local/local-with-audit modes
|
|
323
|
+
refreshIntervalMs: 60000, // control pack refresh interval, ms (default 60s)
|
|
324
|
+
onControlPackRefresh: (e) => {}, // callback on refresh success/failure
|
|
177
325
|
});
|
|
326
|
+
|
|
327
|
+
// For local modes, call init() to fetch the control pack
|
|
328
|
+
if (dg.mode !== "remote") {
|
|
329
|
+
await dg.init();
|
|
330
|
+
}
|
|
178
331
|
```
|
|
179
332
|
|
|
180
333
|
## Failure mode & circuit breaker
|
|
@@ -187,6 +340,8 @@ const result = await dg.evaluate({ ... });
|
|
|
187
340
|
// Check where the decision came from
|
|
188
341
|
console.log(result.decision_source);
|
|
189
342
|
// "policy" — normal API evaluation
|
|
343
|
+
// "local" — local WASM evaluation
|
|
344
|
+
// "local_stale" — local evaluation with stale control pack
|
|
190
345
|
// "failure_mode" — API unreachable, failureMode applied
|
|
191
346
|
// "circuit_open" — circuit breaker open, failureMode applied
|
|
192
347
|
// "idempotency_cache" — duplicate request, cached result returned
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Control Pack Cache — fetches and caches the active control pack for local evaluation.
|
|
3
|
+
*
|
|
4
|
+
* On init: fetches the active control pack from the DriftGard API.
|
|
5
|
+
* Background: refreshes on a configurable interval.
|
|
6
|
+
* Stale pack: uses last-known-good if refresh fails. Fails closed if no pack exists.
|
|
7
|
+
*/
|
|
8
|
+
export interface ControlPackCacheConfig {
|
|
9
|
+
apiKey: string;
|
|
10
|
+
baseUrl: string;
|
|
11
|
+
projectId: string;
|
|
12
|
+
/** Refresh interval in milliseconds. Default 60000 (1 minute). */
|
|
13
|
+
refreshIntervalMs?: number;
|
|
14
|
+
/** Timeout for API calls in milliseconds. Default 10000. */
|
|
15
|
+
timeout?: number;
|
|
16
|
+
/** Called when the control pack is refreshed or refresh fails. */
|
|
17
|
+
onRefresh?: (event: {
|
|
18
|
+
success: boolean;
|
|
19
|
+
version?: number;
|
|
20
|
+
error?: string;
|
|
21
|
+
stale?: boolean;
|
|
22
|
+
}) => void;
|
|
23
|
+
}
|
|
24
|
+
export declare class ControlPackCache {
|
|
25
|
+
private config;
|
|
26
|
+
private controlPack;
|
|
27
|
+
private controlPackId;
|
|
28
|
+
private controlPackVersion;
|
|
29
|
+
private lastRefreshedAt;
|
|
30
|
+
private refreshTimer;
|
|
31
|
+
private stale;
|
|
32
|
+
constructor(config: ControlPackCacheConfig);
|
|
33
|
+
/** Fetch the active control pack. Must be called before evaluating. */
|
|
34
|
+
init(): Promise<void>;
|
|
35
|
+
/** Stop background refresh. */
|
|
36
|
+
destroy(): void;
|
|
37
|
+
/** Get the cached control pack. Throws if no pack is available. */
|
|
38
|
+
getControlPack(): Record<string, unknown>;
|
|
39
|
+
/** Get cache metadata for audit reporting. */
|
|
40
|
+
getMeta(): {
|
|
41
|
+
control_pack_id: string | null;
|
|
42
|
+
version: number;
|
|
43
|
+
stale: boolean;
|
|
44
|
+
last_refreshed_at: string | null;
|
|
45
|
+
};
|
|
46
|
+
/** Refresh the control pack from the API. */
|
|
47
|
+
private refresh;
|
|
48
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Control Pack Cache — fetches and caches the active control pack for local evaluation.
|
|
4
|
+
*
|
|
5
|
+
* On init: fetches the active control pack from the DriftGard API.
|
|
6
|
+
* Background: refreshes on a configurable interval.
|
|
7
|
+
* Stale pack: uses last-known-good if refresh fails. Fails closed if no pack exists.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.ControlPackCache = void 0;
|
|
11
|
+
class ControlPackCache {
|
|
12
|
+
constructor(config) {
|
|
13
|
+
this.controlPack = null;
|
|
14
|
+
this.controlPackId = null;
|
|
15
|
+
this.controlPackVersion = 0;
|
|
16
|
+
this.lastRefreshedAt = null;
|
|
17
|
+
this.refreshTimer = null;
|
|
18
|
+
this.stale = false;
|
|
19
|
+
this.config = config;
|
|
20
|
+
}
|
|
21
|
+
/** Fetch the active control pack. Must be called before evaluating. */
|
|
22
|
+
async init() {
|
|
23
|
+
await this.refresh();
|
|
24
|
+
// Start background refresh
|
|
25
|
+
const interval = this.config.refreshIntervalMs ?? 60000;
|
|
26
|
+
if (interval > 0) {
|
|
27
|
+
this.refreshTimer = setInterval(() => {
|
|
28
|
+
this.refresh().catch(() => { });
|
|
29
|
+
}, interval);
|
|
30
|
+
// Don't block process exit
|
|
31
|
+
if (this.refreshTimer && typeof this.refreshTimer === "object" && "unref" in this.refreshTimer) {
|
|
32
|
+
this.refreshTimer.unref();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/** Stop background refresh. */
|
|
37
|
+
destroy() {
|
|
38
|
+
if (this.refreshTimer) {
|
|
39
|
+
clearInterval(this.refreshTimer);
|
|
40
|
+
this.refreshTimer = null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/** Get the cached control pack. Throws if no pack is available. */
|
|
44
|
+
getControlPack() {
|
|
45
|
+
if (!this.controlPack) {
|
|
46
|
+
throw new Error("No control pack available. Call init() first or check that the project has an active control pack.");
|
|
47
|
+
}
|
|
48
|
+
return this.controlPack;
|
|
49
|
+
}
|
|
50
|
+
/** Get cache metadata for audit reporting. */
|
|
51
|
+
getMeta() {
|
|
52
|
+
return {
|
|
53
|
+
control_pack_id: this.controlPackId,
|
|
54
|
+
version: this.controlPackVersion,
|
|
55
|
+
stale: this.stale,
|
|
56
|
+
last_refreshed_at: this.lastRefreshedAt,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/** Refresh the control pack from the API. */
|
|
60
|
+
async refresh() {
|
|
61
|
+
const { apiKey, baseUrl, projectId, timeout = 10000 } = this.config;
|
|
62
|
+
const controller = new AbortController();
|
|
63
|
+
const timer = setTimeout(() => controller.abort(), timeout);
|
|
64
|
+
try {
|
|
65
|
+
const res = await fetch(`${baseUrl}/audit/cli/control-packs/active?project_id=${encodeURIComponent(projectId)}`, {
|
|
66
|
+
method: "GET",
|
|
67
|
+
headers: {
|
|
68
|
+
"x-api-key": apiKey,
|
|
69
|
+
},
|
|
70
|
+
signal: controller.signal,
|
|
71
|
+
});
|
|
72
|
+
if (!res.ok) {
|
|
73
|
+
throw new Error(`HTTP ${res.status}: ${res.statusText}`);
|
|
74
|
+
}
|
|
75
|
+
const data = await res.json();
|
|
76
|
+
const cp = data.control_pack;
|
|
77
|
+
if (!cp) {
|
|
78
|
+
throw new Error("No active control pack found for this project");
|
|
79
|
+
}
|
|
80
|
+
this.controlPack = cp;
|
|
81
|
+
this.controlPackId = String(cp.control_pack_id || "");
|
|
82
|
+
this.controlPackVersion = Number(cp.version || 0);
|
|
83
|
+
this.lastRefreshedAt = new Date().toISOString();
|
|
84
|
+
this.stale = false;
|
|
85
|
+
this.config.onRefresh?.({
|
|
86
|
+
success: true,
|
|
87
|
+
version: this.controlPackVersion,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
catch (e) {
|
|
91
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
92
|
+
// If we have a cached pack, mark as stale but keep using it
|
|
93
|
+
if (this.controlPack) {
|
|
94
|
+
this.stale = true;
|
|
95
|
+
this.config.onRefresh?.({
|
|
96
|
+
success: false,
|
|
97
|
+
error: msg,
|
|
98
|
+
stale: true,
|
|
99
|
+
version: this.controlPackVersion,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
// No cached pack — this is fatal for local mode
|
|
104
|
+
this.config.onRefresh?.({
|
|
105
|
+
success: false,
|
|
106
|
+
error: msg,
|
|
107
|
+
});
|
|
108
|
+
throw new Error(`Failed to fetch control pack: ${msg}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
finally {
|
|
112
|
+
clearTimeout(timer);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
exports.ControlPackCache = ControlPackCache;
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ import { DriftgardConfig, EvaluateRequest, EvaluateResponse, ToolCallRequest, Ou
|
|
|
2
2
|
export { DriftgardConfig, EvaluateRequest, EvaluateResponse, CircuitBreakerConfig, ToolCallRequest, OutcomeRequest } from "./types";
|
|
3
3
|
export { Violation, EvaluationResult, FallbackResponse, HitlInfo } from "./types";
|
|
4
4
|
export { DriftgardError, AuthError, RateLimitError, FeatureNotAvailableError, ChainDepthExceededError } from "./errors";
|
|
5
|
+
export { evaluateLocal } from "./local-evaluator";
|
|
6
|
+
export { ControlPackCache } from "./control-pack-cache";
|
|
5
7
|
type CircuitState = "closed" | "open" | "half-open";
|
|
6
8
|
export declare class Driftgard {
|
|
7
9
|
private apiKey;
|
|
@@ -9,17 +11,38 @@ export declare class Driftgard {
|
|
|
9
11
|
private timeout;
|
|
10
12
|
private maxRetries;
|
|
11
13
|
private failureMode;
|
|
14
|
+
private mode;
|
|
12
15
|
private cbThreshold;
|
|
13
16
|
private cbResetMs;
|
|
14
17
|
private cbState;
|
|
15
18
|
private cbFailures;
|
|
16
19
|
private cbOpenedAt;
|
|
20
|
+
private cpCache;
|
|
21
|
+
private initialized;
|
|
17
22
|
constructor(config: DriftgardConfig);
|
|
23
|
+
/**
|
|
24
|
+
* Initialize the SDK. Required for local and local-with-audit modes.
|
|
25
|
+
* Fetches the active control pack and starts background refresh.
|
|
26
|
+
* No-op for remote mode.
|
|
27
|
+
*/
|
|
28
|
+
init(): Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* Stop background refresh and clean up resources.
|
|
31
|
+
*/
|
|
32
|
+
destroy(): void;
|
|
18
33
|
/**
|
|
19
34
|
* Evaluate a prompt/response pair against your active control pack.
|
|
20
|
-
*
|
|
35
|
+
* In remote mode: sends to DriftGard API.
|
|
36
|
+
* In local mode: evaluates via WASM — no data leaves your environment.
|
|
37
|
+
* In local-with-audit mode: evaluates locally, reports verdict metadata to DriftGard.
|
|
21
38
|
*/
|
|
22
39
|
evaluate(req: EvaluateRequest): Promise<EvaluateResponse>;
|
|
40
|
+
/** Local evaluation via WASM. */
|
|
41
|
+
private evaluateLocally;
|
|
42
|
+
/** Report verdict metadata to DriftGard (no prompt/response content). */
|
|
43
|
+
private reportAuditMetadata;
|
|
44
|
+
/** Remote evaluation via DriftGard API. */
|
|
45
|
+
private evaluateRemotely;
|
|
23
46
|
/** Generate a synthetic response based on failure mode. */
|
|
24
47
|
private syntheticResponse;
|
|
25
48
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Driftgard = exports.ChainDepthExceededError = exports.FeatureNotAvailableError = exports.RateLimitError = exports.AuthError = exports.DriftgardError = void 0;
|
|
3
|
+
exports.Driftgard = exports.ControlPackCache = exports.evaluateLocal = exports.ChainDepthExceededError = exports.FeatureNotAvailableError = exports.RateLimitError = exports.AuthError = exports.DriftgardError = void 0;
|
|
4
4
|
const errors_1 = require("./errors");
|
|
5
|
+
const local_evaluator_1 = require("./local-evaluator");
|
|
6
|
+
const control_pack_cache_1 = require("./control-pack-cache");
|
|
5
7
|
var errors_2 = require("./errors");
|
|
6
8
|
Object.defineProperty(exports, "DriftgardError", { enumerable: true, get: function () { return errors_2.DriftgardError; } });
|
|
7
9
|
Object.defineProperty(exports, "AuthError", { enumerable: true, get: function () { return errors_2.AuthError; } });
|
|
8
10
|
Object.defineProperty(exports, "RateLimitError", { enumerable: true, get: function () { return errors_2.RateLimitError; } });
|
|
9
11
|
Object.defineProperty(exports, "FeatureNotAvailableError", { enumerable: true, get: function () { return errors_2.FeatureNotAvailableError; } });
|
|
10
12
|
Object.defineProperty(exports, "ChainDepthExceededError", { enumerable: true, get: function () { return errors_2.ChainDepthExceededError; } });
|
|
13
|
+
var local_evaluator_2 = require("./local-evaluator");
|
|
14
|
+
Object.defineProperty(exports, "evaluateLocal", { enumerable: true, get: function () { return local_evaluator_2.evaluateLocal; } });
|
|
15
|
+
var control_pack_cache_2 = require("./control-pack-cache");
|
|
16
|
+
Object.defineProperty(exports, "ControlPackCache", { enumerable: true, get: function () { return control_pack_cache_2.ControlPackCache; } });
|
|
11
17
|
const DEFAULT_BASE_URL = "https://api.driftgard.com";
|
|
12
18
|
const DEFAULT_TIMEOUT = 30000;
|
|
13
19
|
const DEFAULT_MAX_RETRIES = 2;
|
|
@@ -19,6 +25,9 @@ class Driftgard {
|
|
|
19
25
|
this.cbState = "closed";
|
|
20
26
|
this.cbFailures = 0;
|
|
21
27
|
this.cbOpenedAt = 0;
|
|
28
|
+
// Local mode
|
|
29
|
+
this.cpCache = null;
|
|
30
|
+
this.initialized = false;
|
|
22
31
|
if (!config.apiKey)
|
|
23
32
|
throw new Error("apiKey is required");
|
|
24
33
|
this.apiKey = config.apiKey;
|
|
@@ -26,14 +35,136 @@ class Driftgard {
|
|
|
26
35
|
this.timeout = config.timeout ?? DEFAULT_TIMEOUT;
|
|
27
36
|
this.maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
28
37
|
this.failureMode = config.failureMode ?? "open";
|
|
38
|
+
this.mode = config.mode ?? "remote";
|
|
29
39
|
this.cbThreshold = config.circuitBreaker?.threshold ?? DEFAULT_CB_THRESHOLD;
|
|
30
40
|
this.cbResetMs = config.circuitBreaker?.resetTimeoutMs ?? DEFAULT_CB_RESET_MS;
|
|
41
|
+
if (this.mode !== "remote") {
|
|
42
|
+
if (!config.projectId)
|
|
43
|
+
throw new Error("projectId is required for local mode");
|
|
44
|
+
this.cpCache = new control_pack_cache_1.ControlPackCache({
|
|
45
|
+
apiKey: this.apiKey,
|
|
46
|
+
baseUrl: this.baseUrl,
|
|
47
|
+
projectId: config.projectId,
|
|
48
|
+
refreshIntervalMs: config.refreshIntervalMs,
|
|
49
|
+
timeout: this.timeout,
|
|
50
|
+
onRefresh: config.onControlPackRefresh,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Initialize the SDK. Required for local and local-with-audit modes.
|
|
56
|
+
* Fetches the active control pack and starts background refresh.
|
|
57
|
+
* No-op for remote mode.
|
|
58
|
+
*/
|
|
59
|
+
async init() {
|
|
60
|
+
if (this.cpCache) {
|
|
61
|
+
await this.cpCache.init();
|
|
62
|
+
}
|
|
63
|
+
this.initialized = true;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Stop background refresh and clean up resources.
|
|
67
|
+
*/
|
|
68
|
+
destroy() {
|
|
69
|
+
this.cpCache?.destroy();
|
|
31
70
|
}
|
|
32
71
|
/**
|
|
33
72
|
* Evaluate a prompt/response pair against your active control pack.
|
|
34
|
-
*
|
|
73
|
+
* In remote mode: sends to DriftGard API.
|
|
74
|
+
* In local mode: evaluates via WASM — no data leaves your environment.
|
|
75
|
+
* In local-with-audit mode: evaluates locally, reports verdict metadata to DriftGard.
|
|
35
76
|
*/
|
|
36
77
|
async evaluate(req) {
|
|
78
|
+
if (this.mode !== "remote") {
|
|
79
|
+
return this.evaluateLocally(req);
|
|
80
|
+
}
|
|
81
|
+
return this.evaluateRemotely(req);
|
|
82
|
+
}
|
|
83
|
+
/** Local evaluation via WASM. */
|
|
84
|
+
async evaluateLocally(req) {
|
|
85
|
+
if (!this.cpCache)
|
|
86
|
+
throw new Error("Local mode not configured");
|
|
87
|
+
if (!this.initialized)
|
|
88
|
+
throw new Error("Call init() before evaluate() in local mode");
|
|
89
|
+
const cp = this.cpCache.getControlPack();
|
|
90
|
+
const meta = this.cpCache.getMeta();
|
|
91
|
+
const timestamp = req.timestamp || new Date().toISOString();
|
|
92
|
+
const evaluationId = `local_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
93
|
+
// Build the WASM request
|
|
94
|
+
const wasmRequest = {
|
|
95
|
+
prompt: req.prompt || "",
|
|
96
|
+
response: req.response || "",
|
|
97
|
+
model_id: req.model_id,
|
|
98
|
+
...(req.eval_mode ? { eval_mode: req.eval_mode } : {}),
|
|
99
|
+
...(req.tool_call ? { tool_call: req.tool_call } : {}),
|
|
100
|
+
...(req.agent_id ? { agent_id: req.agent_id } : {}),
|
|
101
|
+
...(req.agent_role ? { agent_role: req.agent_role } : {}),
|
|
102
|
+
...(req.on_behalf_of ? { on_behalf_of: req.on_behalf_of } : {}),
|
|
103
|
+
...(req.parent_agent_id ? { parent_agent_id: req.parent_agent_id } : {}),
|
|
104
|
+
};
|
|
105
|
+
const verdict = (0, local_evaluator_1.evaluateLocal)(cp, wasmRequest);
|
|
106
|
+
const response = {
|
|
107
|
+
ok: true,
|
|
108
|
+
project_id: req.project_id,
|
|
109
|
+
evaluation_id: evaluationId,
|
|
110
|
+
active_control_pack_id: meta.control_pack_id || "",
|
|
111
|
+
active_control_pack_version: meta.version,
|
|
112
|
+
data_mode: "local",
|
|
113
|
+
telemetry_mode: this.mode === "local-with-audit" ? "metadata_only" : "none",
|
|
114
|
+
execution_mode: "local",
|
|
115
|
+
decision_action: verdict.allowed ? "log_only" : "enforce",
|
|
116
|
+
decision_source: meta.stale ? "local_stale" : "local",
|
|
117
|
+
evaluation: {
|
|
118
|
+
allowed: verdict.allowed,
|
|
119
|
+
risk_score: verdict.risk_score,
|
|
120
|
+
violations: verdict.violations,
|
|
121
|
+
flags: {
|
|
122
|
+
pii_detected: verdict.flags.pii_detected,
|
|
123
|
+
secret_detected: verdict.flags.secret_detected,
|
|
124
|
+
adversarial_input: verdict.flags.adversarial_input,
|
|
125
|
+
meta_bypass_detected: verdict.flags.meta_bypass_detected,
|
|
126
|
+
hard_blocked: verdict.flags.hard_blocked,
|
|
127
|
+
action_blocked: verdict.flags.action_blocked,
|
|
128
|
+
dlp_findings_count: verdict.flags.dlp_findings_count,
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
...(req.eval_mode ? { eval_mode: req.eval_mode } : {}),
|
|
132
|
+
...(req.tool_call ? { tool_call: req.tool_call } : {}),
|
|
133
|
+
...(req.session_id ? { session_id: req.session_id } : {}),
|
|
134
|
+
...(req.usage ? { usage: req.usage } : {}),
|
|
135
|
+
};
|
|
136
|
+
// local-with-audit: report verdict metadata (no prompt/response)
|
|
137
|
+
if (this.mode === "local-with-audit") {
|
|
138
|
+
this.reportAuditMetadata({
|
|
139
|
+
project_id: req.project_id,
|
|
140
|
+
evaluation_id: evaluationId,
|
|
141
|
+
timestamp,
|
|
142
|
+
model_id: req.model_id,
|
|
143
|
+
control_pack_id: meta.control_pack_id || "",
|
|
144
|
+
control_pack_version: meta.version,
|
|
145
|
+
allowed: verdict.allowed,
|
|
146
|
+
risk_score: verdict.risk_score,
|
|
147
|
+
violations: verdict.violations.map(v => ({ clause_id: v.clause_id, severity: v.severity, category: v.category })),
|
|
148
|
+
eval_mode: req.eval_mode,
|
|
149
|
+
session_id: req.session_id,
|
|
150
|
+
agent_id: req.agent_id,
|
|
151
|
+
agent_role: req.agent_role,
|
|
152
|
+
stale_pack: meta.stale,
|
|
153
|
+
}).catch(() => { }); // fire-and-forget
|
|
154
|
+
}
|
|
155
|
+
return response;
|
|
156
|
+
}
|
|
157
|
+
/** Report verdict metadata to DriftGard (no prompt/response content). */
|
|
158
|
+
async reportAuditMetadata(meta) {
|
|
159
|
+
try {
|
|
160
|
+
await this.post("/audit/local-verdict", meta);
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
// Non-fatal — local evaluation still succeeded
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
/** Remote evaluation via DriftGard API. */
|
|
167
|
+
async evaluateRemotely(req) {
|
|
37
168
|
const idempotencyKey = req.idempotency_key || this.generateIdempotencyKey();
|
|
38
169
|
// Circuit breaker: if open, check if reset timeout has passed
|
|
39
170
|
if (this.cbState === "open") {
|
|
@@ -61,6 +192,10 @@ class Driftgard {
|
|
|
61
192
|
...(req.eval_mode ? { eval_mode: req.eval_mode } : {}),
|
|
62
193
|
...(req.tool_call ? { tool_call: req.tool_call } : {}),
|
|
63
194
|
...(req.sequence_no != null ? { sequence_no: req.sequence_no } : {}),
|
|
195
|
+
...(req.agent_id ? { agent_id: req.agent_id } : {}),
|
|
196
|
+
...(req.agent_role ? { agent_role: req.agent_role } : {}),
|
|
197
|
+
...(req.on_behalf_of ? { on_behalf_of: req.on_behalf_of } : {}),
|
|
198
|
+
...(req.parent_agent_id ? { parent_agent_id: req.parent_agent_id } : {}),
|
|
64
199
|
...(req.usage ? { usage: req.usage } : {}),
|
|
65
200
|
}, idempotencyKey);
|
|
66
201
|
// Success — reset circuit breaker
|
|
@@ -135,6 +270,10 @@ class Driftgard {
|
|
|
135
270
|
parent_evaluation_id: req.parent_evaluation_id,
|
|
136
271
|
idempotency_key: req.idempotency_key,
|
|
137
272
|
sequence_no: req.sequence_no,
|
|
273
|
+
agent_id: req.agent_id,
|
|
274
|
+
agent_role: req.agent_role,
|
|
275
|
+
on_behalf_of: req.on_behalf_of,
|
|
276
|
+
parent_agent_id: req.parent_agent_id,
|
|
138
277
|
});
|
|
139
278
|
}
|
|
140
279
|
/**
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local Evaluator — runs the DriftGard evaluation engine locally via WASM.
|
|
3
|
+
* No prompt/response data leaves the customer environment.
|
|
4
|
+
*/
|
|
5
|
+
export interface LocalVerdict {
|
|
6
|
+
allowed: boolean;
|
|
7
|
+
risk_score: number;
|
|
8
|
+
violations: Array<{
|
|
9
|
+
clause_id: string;
|
|
10
|
+
severity: string;
|
|
11
|
+
category: string;
|
|
12
|
+
reason: string;
|
|
13
|
+
matched_pattern?: string;
|
|
14
|
+
}>;
|
|
15
|
+
flags: {
|
|
16
|
+
pii_detected: boolean;
|
|
17
|
+
pii_in_params: boolean;
|
|
18
|
+
secret_detected: boolean;
|
|
19
|
+
secret_in_params: boolean;
|
|
20
|
+
adversarial_input: boolean;
|
|
21
|
+
meta_bypass_detected: boolean;
|
|
22
|
+
hard_blocked: boolean;
|
|
23
|
+
action_blocked: boolean;
|
|
24
|
+
dlp_findings_count: number;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Run evaluation locally via WASM.
|
|
29
|
+
* @param controlPack - The cached control pack object
|
|
30
|
+
* @param request - The evaluation request (prompt, response, tool_call, identity, etc.)
|
|
31
|
+
* @returns The verdict — same shape as the server-side evaluation result
|
|
32
|
+
*/
|
|
33
|
+
export declare function evaluateLocal(controlPack: Record<string, unknown>, request: Record<string, unknown>): LocalVerdict;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Local Evaluator — runs the DriftGard evaluation engine locally via WASM.
|
|
4
|
+
* No prompt/response data leaves the customer environment.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.evaluateLocal = evaluateLocal;
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
9
|
+
const wasmModule = require("./wasm/driftgard_evaluator.js");
|
|
10
|
+
/**
|
|
11
|
+
* Run evaluation locally via WASM.
|
|
12
|
+
* @param controlPack - The cached control pack object
|
|
13
|
+
* @param request - The evaluation request (prompt, response, tool_call, identity, etc.)
|
|
14
|
+
* @returns The verdict — same shape as the server-side evaluation result
|
|
15
|
+
*/
|
|
16
|
+
function evaluateLocal(controlPack, request) {
|
|
17
|
+
const resultJson = wasmModule.evaluate(JSON.stringify(controlPack), JSON.stringify(request));
|
|
18
|
+
return JSON.parse(resultJson);
|
|
19
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -13,6 +13,24 @@ export interface DriftgardConfig {
|
|
|
13
13
|
failureMode?: "open" | "closed";
|
|
14
14
|
/** Circuit breaker config. Skips API calls after consecutive failures. */
|
|
15
15
|
circuitBreaker?: CircuitBreakerConfig;
|
|
16
|
+
/**
|
|
17
|
+
* Evaluation mode:
|
|
18
|
+
* - "remote" (default) — sends prompt/response to DriftGard API
|
|
19
|
+
* - "local" — evaluates locally via WASM, no data leaves your environment
|
|
20
|
+
* - "local-with-audit" — evaluates locally, reports verdict metadata to DriftGard (no prompt/response sent)
|
|
21
|
+
*/
|
|
22
|
+
mode?: "remote" | "local" | "local-with-audit";
|
|
23
|
+
/** Project ID — required for local mode (used to fetch the control pack). */
|
|
24
|
+
projectId?: string;
|
|
25
|
+
/** Control pack refresh interval in milliseconds. Default 60000 (1 minute). Only used in local mode. */
|
|
26
|
+
refreshIntervalMs?: number;
|
|
27
|
+
/** Called when the control pack is refreshed or refresh fails. Only used in local mode. */
|
|
28
|
+
onControlPackRefresh?: (event: {
|
|
29
|
+
success: boolean;
|
|
30
|
+
version?: number;
|
|
31
|
+
error?: string;
|
|
32
|
+
stale?: boolean;
|
|
33
|
+
}) => void;
|
|
16
34
|
}
|
|
17
35
|
export interface EvaluateRequest {
|
|
18
36
|
project_id: string;
|
|
@@ -35,6 +53,14 @@ export interface EvaluateRequest {
|
|
|
35
53
|
idempotency_key?: string;
|
|
36
54
|
/** Sequence number within a session for ordering. Optional — if sent, ordering is enforced. */
|
|
37
55
|
sequence_no?: number;
|
|
56
|
+
/** Agent identity — who or what is making this decision. */
|
|
57
|
+
agent_id?: string;
|
|
58
|
+
/** Agent role — scopes what the agent is allowed to do. */
|
|
59
|
+
agent_role?: string;
|
|
60
|
+
/** End-user ID — which human triggered this agent action. */
|
|
61
|
+
on_behalf_of?: string;
|
|
62
|
+
/** Parent agent ID — which orchestrator agent delegated to this one. */
|
|
63
|
+
parent_agent_id?: string;
|
|
38
64
|
usage?: {
|
|
39
65
|
prompt_tokens?: number;
|
|
40
66
|
completion_tokens?: number;
|
|
@@ -141,6 +167,10 @@ export interface ToolCallRequest {
|
|
|
141
167
|
parent_evaluation_id?: string;
|
|
142
168
|
idempotency_key?: string;
|
|
143
169
|
sequence_no?: number;
|
|
170
|
+
agent_id?: string;
|
|
171
|
+
agent_role?: string;
|
|
172
|
+
on_behalf_of?: string;
|
|
173
|
+
parent_agent_id?: string;
|
|
144
174
|
}
|
|
145
175
|
export interface OutcomeRequest {
|
|
146
176
|
execution_status: "success" | "failed" | "rolled_back";
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/* tslint:disable */
|
|
2
|
+
/* eslint-disable */
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Main evaluation entry point.
|
|
6
|
+
*
|
|
7
|
+
* # Arguments
|
|
8
|
+
* * `control_pack_json` - JSON string of the control pack
|
|
9
|
+
* * `request_json` - JSON string of the evaluation request
|
|
10
|
+
*
|
|
11
|
+
* # Returns
|
|
12
|
+
* JSON string of the verdict: { allowed, risk_score, violations, flags }
|
|
13
|
+
*/
|
|
14
|
+
export function evaluate(control_pack_json: string, request_json: string): string;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/* @ts-self-types="./driftgard_evaluator.d.ts" */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Main evaluation entry point.
|
|
5
|
+
*
|
|
6
|
+
* # Arguments
|
|
7
|
+
* * `control_pack_json` - JSON string of the control pack
|
|
8
|
+
* * `request_json` - JSON string of the evaluation request
|
|
9
|
+
*
|
|
10
|
+
* # Returns
|
|
11
|
+
* JSON string of the verdict: { allowed, risk_score, violations, flags }
|
|
12
|
+
* @param {string} control_pack_json
|
|
13
|
+
* @param {string} request_json
|
|
14
|
+
* @returns {string}
|
|
15
|
+
*/
|
|
16
|
+
function evaluate(control_pack_json, request_json) {
|
|
17
|
+
let deferred3_0;
|
|
18
|
+
let deferred3_1;
|
|
19
|
+
try {
|
|
20
|
+
const ptr0 = passStringToWasm0(control_pack_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
21
|
+
const len0 = WASM_VECTOR_LEN;
|
|
22
|
+
const ptr1 = passStringToWasm0(request_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
23
|
+
const len1 = WASM_VECTOR_LEN;
|
|
24
|
+
const ret = wasm.evaluate(ptr0, len0, ptr1, len1);
|
|
25
|
+
deferred3_0 = ret[0];
|
|
26
|
+
deferred3_1 = ret[1];
|
|
27
|
+
return getStringFromWasm0(ret[0], ret[1]);
|
|
28
|
+
} finally {
|
|
29
|
+
wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
exports.evaluate = evaluate;
|
|
33
|
+
function __wbg_get_imports() {
|
|
34
|
+
const import0 = {
|
|
35
|
+
__proto__: null,
|
|
36
|
+
__wbindgen_init_externref_table: function() {
|
|
37
|
+
const table = wasm.__wbindgen_externrefs;
|
|
38
|
+
const offset = table.grow(4);
|
|
39
|
+
table.set(0, undefined);
|
|
40
|
+
table.set(offset + 0, undefined);
|
|
41
|
+
table.set(offset + 1, null);
|
|
42
|
+
table.set(offset + 2, true);
|
|
43
|
+
table.set(offset + 3, false);
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
return {
|
|
47
|
+
__proto__: null,
|
|
48
|
+
"./driftgard_evaluator_bg.js": import0,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function getStringFromWasm0(ptr, len) {
|
|
53
|
+
return decodeText(ptr >>> 0, len);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
let cachedUint8ArrayMemory0 = null;
|
|
57
|
+
function getUint8ArrayMemory0() {
|
|
58
|
+
if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) {
|
|
59
|
+
cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer);
|
|
60
|
+
}
|
|
61
|
+
return cachedUint8ArrayMemory0;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function passStringToWasm0(arg, malloc, realloc) {
|
|
65
|
+
if (realloc === undefined) {
|
|
66
|
+
const buf = cachedTextEncoder.encode(arg);
|
|
67
|
+
const ptr = malloc(buf.length, 1) >>> 0;
|
|
68
|
+
getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf);
|
|
69
|
+
WASM_VECTOR_LEN = buf.length;
|
|
70
|
+
return ptr;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
let len = arg.length;
|
|
74
|
+
let ptr = malloc(len, 1) >>> 0;
|
|
75
|
+
|
|
76
|
+
const mem = getUint8ArrayMemory0();
|
|
77
|
+
|
|
78
|
+
let offset = 0;
|
|
79
|
+
|
|
80
|
+
for (; offset < len; offset++) {
|
|
81
|
+
const code = arg.charCodeAt(offset);
|
|
82
|
+
if (code > 0x7F) break;
|
|
83
|
+
mem[ptr + offset] = code;
|
|
84
|
+
}
|
|
85
|
+
if (offset !== len) {
|
|
86
|
+
if (offset !== 0) {
|
|
87
|
+
arg = arg.slice(offset);
|
|
88
|
+
}
|
|
89
|
+
ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0;
|
|
90
|
+
const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len);
|
|
91
|
+
const ret = cachedTextEncoder.encodeInto(arg, view);
|
|
92
|
+
|
|
93
|
+
offset += ret.written;
|
|
94
|
+
ptr = realloc(ptr, len, offset, 1) >>> 0;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
WASM_VECTOR_LEN = offset;
|
|
98
|
+
return ptr;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
|
|
102
|
+
cachedTextDecoder.decode();
|
|
103
|
+
function decodeText(ptr, len) {
|
|
104
|
+
return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const cachedTextEncoder = new TextEncoder();
|
|
108
|
+
|
|
109
|
+
if (!('encodeInto' in cachedTextEncoder)) {
|
|
110
|
+
cachedTextEncoder.encodeInto = function (arg, view) {
|
|
111
|
+
const buf = cachedTextEncoder.encode(arg);
|
|
112
|
+
view.set(buf);
|
|
113
|
+
return {
|
|
114
|
+
read: arg.length,
|
|
115
|
+
written: buf.length
|
|
116
|
+
};
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
let WASM_VECTOR_LEN = 0;
|
|
121
|
+
|
|
122
|
+
const wasmPath = `${__dirname}/driftgard_evaluator_bg.wasm`;
|
|
123
|
+
const wasmBytes = require('fs').readFileSync(wasmPath);
|
|
124
|
+
const wasmModule = new WebAssembly.Module(wasmBytes);
|
|
125
|
+
let wasmInstance = new WebAssembly.Instance(wasmModule, __wbg_get_imports());
|
|
126
|
+
let wasm = wasmInstance.exports;
|
|
127
|
+
wasm.__wbindgen_start();
|
|
Binary file
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/* tslint:disable */
|
|
2
|
+
/* eslint-disable */
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Main evaluation entry point.
|
|
6
|
+
*
|
|
7
|
+
* # Arguments
|
|
8
|
+
* * `control_pack_json` - JSON string of the control pack
|
|
9
|
+
* * `request_json` - JSON string of the evaluation request
|
|
10
|
+
*
|
|
11
|
+
* # Returns
|
|
12
|
+
* JSON string of the verdict: { allowed, risk_score, violations, flags }
|
|
13
|
+
*/
|
|
14
|
+
export function evaluate(control_pack_json: string, request_json: string): string;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/* @ts-self-types="./driftgard_evaluator.d.ts" */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Main evaluation entry point.
|
|
5
|
+
*
|
|
6
|
+
* # Arguments
|
|
7
|
+
* * `control_pack_json` - JSON string of the control pack
|
|
8
|
+
* * `request_json` - JSON string of the evaluation request
|
|
9
|
+
*
|
|
10
|
+
* # Returns
|
|
11
|
+
* JSON string of the verdict: { allowed, risk_score, violations, flags }
|
|
12
|
+
* @param {string} control_pack_json
|
|
13
|
+
* @param {string} request_json
|
|
14
|
+
* @returns {string}
|
|
15
|
+
*/
|
|
16
|
+
function evaluate(control_pack_json, request_json) {
|
|
17
|
+
let deferred3_0;
|
|
18
|
+
let deferred3_1;
|
|
19
|
+
try {
|
|
20
|
+
const ptr0 = passStringToWasm0(control_pack_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
21
|
+
const len0 = WASM_VECTOR_LEN;
|
|
22
|
+
const ptr1 = passStringToWasm0(request_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
23
|
+
const len1 = WASM_VECTOR_LEN;
|
|
24
|
+
const ret = wasm.evaluate(ptr0, len0, ptr1, len1);
|
|
25
|
+
deferred3_0 = ret[0];
|
|
26
|
+
deferred3_1 = ret[1];
|
|
27
|
+
return getStringFromWasm0(ret[0], ret[1]);
|
|
28
|
+
} finally {
|
|
29
|
+
wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
exports.evaluate = evaluate;
|
|
33
|
+
function __wbg_get_imports() {
|
|
34
|
+
const import0 = {
|
|
35
|
+
__proto__: null,
|
|
36
|
+
__wbindgen_init_externref_table: function() {
|
|
37
|
+
const table = wasm.__wbindgen_externrefs;
|
|
38
|
+
const offset = table.grow(4);
|
|
39
|
+
table.set(0, undefined);
|
|
40
|
+
table.set(offset + 0, undefined);
|
|
41
|
+
table.set(offset + 1, null);
|
|
42
|
+
table.set(offset + 2, true);
|
|
43
|
+
table.set(offset + 3, false);
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
return {
|
|
47
|
+
__proto__: null,
|
|
48
|
+
"./driftgard_evaluator_bg.js": import0,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function getStringFromWasm0(ptr, len) {
|
|
53
|
+
return decodeText(ptr >>> 0, len);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
let cachedUint8ArrayMemory0 = null;
|
|
57
|
+
function getUint8ArrayMemory0() {
|
|
58
|
+
if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) {
|
|
59
|
+
cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer);
|
|
60
|
+
}
|
|
61
|
+
return cachedUint8ArrayMemory0;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function passStringToWasm0(arg, malloc, realloc) {
|
|
65
|
+
if (realloc === undefined) {
|
|
66
|
+
const buf = cachedTextEncoder.encode(arg);
|
|
67
|
+
const ptr = malloc(buf.length, 1) >>> 0;
|
|
68
|
+
getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf);
|
|
69
|
+
WASM_VECTOR_LEN = buf.length;
|
|
70
|
+
return ptr;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
let len = arg.length;
|
|
74
|
+
let ptr = malloc(len, 1) >>> 0;
|
|
75
|
+
|
|
76
|
+
const mem = getUint8ArrayMemory0();
|
|
77
|
+
|
|
78
|
+
let offset = 0;
|
|
79
|
+
|
|
80
|
+
for (; offset < len; offset++) {
|
|
81
|
+
const code = arg.charCodeAt(offset);
|
|
82
|
+
if (code > 0x7F) break;
|
|
83
|
+
mem[ptr + offset] = code;
|
|
84
|
+
}
|
|
85
|
+
if (offset !== len) {
|
|
86
|
+
if (offset !== 0) {
|
|
87
|
+
arg = arg.slice(offset);
|
|
88
|
+
}
|
|
89
|
+
ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0;
|
|
90
|
+
const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len);
|
|
91
|
+
const ret = cachedTextEncoder.encodeInto(arg, view);
|
|
92
|
+
|
|
93
|
+
offset += ret.written;
|
|
94
|
+
ptr = realloc(ptr, len, offset, 1) >>> 0;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
WASM_VECTOR_LEN = offset;
|
|
98
|
+
return ptr;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
|
|
102
|
+
cachedTextDecoder.decode();
|
|
103
|
+
function decodeText(ptr, len) {
|
|
104
|
+
return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const cachedTextEncoder = new TextEncoder();
|
|
108
|
+
|
|
109
|
+
if (!('encodeInto' in cachedTextEncoder)) {
|
|
110
|
+
cachedTextEncoder.encodeInto = function (arg, view) {
|
|
111
|
+
const buf = cachedTextEncoder.encode(arg);
|
|
112
|
+
view.set(buf);
|
|
113
|
+
return {
|
|
114
|
+
read: arg.length,
|
|
115
|
+
written: buf.length
|
|
116
|
+
};
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
let WASM_VECTOR_LEN = 0;
|
|
121
|
+
|
|
122
|
+
const wasmPath = `${__dirname}/driftgard_evaluator_bg.wasm`;
|
|
123
|
+
const wasmBytes = require('fs').readFileSync(wasmPath);
|
|
124
|
+
const wasmModule = new WebAssembly.Module(wasmBytes);
|
|
125
|
+
let wasmInstance = new WebAssembly.Instance(wasmModule, __wbg_get_imports());
|
|
126
|
+
let wasm = wasmInstance.exports;
|
|
127
|
+
wasm.__wbindgen_start();
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,15 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@driftgard/node",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.12.0-beta.1",
|
|
4
4
|
"description": "Official DriftGard Node.js SDK — evaluate LLM interactions against your compliance policy",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
|
-
"files": [
|
|
7
|
+
"files": [
|
|
8
|
+
"dist",
|
|
9
|
+
"README.md"
|
|
10
|
+
],
|
|
8
11
|
"scripts": {
|
|
9
|
-
"build": "tsc",
|
|
12
|
+
"build": "tsc && cp -r src/wasm dist/wasm",
|
|
10
13
|
"prepublishOnly": "npm run build"
|
|
11
14
|
},
|
|
12
|
-
"keywords": [
|
|
15
|
+
"keywords": [
|
|
16
|
+
"driftgard",
|
|
17
|
+
"ai",
|
|
18
|
+
"compliance",
|
|
19
|
+
"guardrails",
|
|
20
|
+
"llm",
|
|
21
|
+
"evaluation",
|
|
22
|
+
"policy",
|
|
23
|
+
"audit"
|
|
24
|
+
],
|
|
13
25
|
"author": "Driftgard <support@driftgard.com>",
|
|
14
26
|
"license": "MIT",
|
|
15
27
|
"repository": {
|
|
@@ -21,6 +33,7 @@
|
|
|
21
33
|
"node": ">=18.0.0"
|
|
22
34
|
},
|
|
23
35
|
"devDependencies": {
|
|
36
|
+
"@types/node": "^25.6.0",
|
|
24
37
|
"typescript": "^5.0.0"
|
|
25
38
|
}
|
|
26
39
|
}
|