@driftgard/node 1.4.0 → 1.5.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 +55 -3
- package/dist/errors.d.ts +5 -0
- package/dist/errors.js +10 -1
- package/dist/index.d.ts +20 -3
- package/dist/index.js +109 -13
- package/dist/types.d.ts +49 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -27,10 +27,31 @@ const result = await dg.evaluate({
|
|
|
27
27
|
if (result.evaluation.allowed) {
|
|
28
28
|
console.log("Safe to return to user");
|
|
29
29
|
} else {
|
|
30
|
+
// Use the fallback message if configured in your control pack
|
|
31
|
+
if (result.fallback) {
|
|
32
|
+
console.log("Show to user:", result.fallback.message);
|
|
33
|
+
}
|
|
30
34
|
console.log("Blocked:", result.evaluation.violations);
|
|
31
35
|
}
|
|
32
36
|
```
|
|
33
37
|
|
|
38
|
+
## Conversation tracking
|
|
39
|
+
|
|
40
|
+
Link evaluations within an agent session using `session_id` and `parent_evaluation_id`:
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
const result = await dg.evaluate({
|
|
44
|
+
project_id: "your-project-id",
|
|
45
|
+
prompt: "Transfer $500 to account 12345",
|
|
46
|
+
response: "I've initiated the transfer.",
|
|
47
|
+
model_id: "gpt-4o",
|
|
48
|
+
session_id: "sess_abc123", // groups evals in a conversation
|
|
49
|
+
parent_evaluation_id: "eval_prev_id", // chains to the previous eval
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
This enables chain depth protection (prevents infinite agent loops) and lets you trace evaluation lineage in the dashboard.
|
|
54
|
+
|
|
34
55
|
## A/B experiments
|
|
35
56
|
|
|
36
57
|
Tag evaluations with an `experiment_id` to compare governance metrics across models:
|
|
@@ -67,13 +88,15 @@ const result = await dg.evaluate({
|
|
|
67
88
|
```
|
|
68
89
|
|
|
69
90
|
All fields in `usage` are optional. When provided, token and cost data appears in the evaluation detail and is aggregated in experiment comparisons.
|
|
70
|
-
```
|
|
71
91
|
|
|
72
92
|
## Features
|
|
73
93
|
|
|
74
94
|
- Single `evaluate()` method — send prompt/response, get verdict
|
|
95
|
+
- Failure mode: `fail-open` or `fail-closed` when API is unreachable
|
|
96
|
+
- Circuit breaker: skips API after consecutive failures, auto-recovers
|
|
97
|
+
- Idempotency: deduplicates retried requests via `x-idempotency-key`
|
|
75
98
|
- Auto-retry with exponential backoff on 5xx and network errors
|
|
76
|
-
- Typed errors: `AuthError`, `RateLimitError`, `FeatureNotAvailableError`
|
|
99
|
+
- Typed errors: `AuthError`, `RateLimitError`, `FeatureNotAvailableError`, `ChainDepthExceededError`
|
|
77
100
|
- Full TypeScript types for requests and responses
|
|
78
101
|
- Zero dependencies (uses native `fetch`)
|
|
79
102
|
|
|
@@ -85,13 +108,39 @@ const dg = new Driftgard({
|
|
|
85
108
|
baseUrl: "https://api.driftgard.com", // optional
|
|
86
109
|
timeout: 30000, // optional, ms (default 30s)
|
|
87
110
|
maxRetries: 2, // optional (default 2)
|
|
111
|
+
failureMode: "open", // "open" = allow if API down, "closed" = block (default "open")
|
|
112
|
+
circuitBreaker: {
|
|
113
|
+
threshold: 5, // open circuit after 5 consecutive failures (default 5)
|
|
114
|
+
resetTimeoutMs: 30000, // try again after 30s (default 30000)
|
|
115
|
+
},
|
|
88
116
|
});
|
|
89
117
|
```
|
|
90
118
|
|
|
119
|
+
## Failure mode & circuit breaker
|
|
120
|
+
|
|
121
|
+
The SDK never throws on network/server errors during `evaluate()`. Instead, it returns a synthetic response:
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
const result = await dg.evaluate({ ... });
|
|
125
|
+
|
|
126
|
+
// Check where the decision came from
|
|
127
|
+
console.log(result.decision_source);
|
|
128
|
+
// "policy" — normal API evaluation
|
|
129
|
+
// "failure_mode" — API unreachable, failureMode applied
|
|
130
|
+
// "circuit_open" — circuit breaker open, failureMode applied
|
|
131
|
+
// "idempotency_cache" — duplicate request, cached result returned
|
|
132
|
+
|
|
133
|
+
// Monitor circuit breaker state
|
|
134
|
+
console.log(dg.circuitBreakerState);
|
|
135
|
+
// { state: "closed", failures: 0, openedAt: 0 }
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
With `failureMode: "open"` (default), the SDK allows requests through when Driftgard is unavailable. With `failureMode: "closed"`, it blocks them with a fallback message.
|
|
139
|
+
|
|
91
140
|
## Error handling
|
|
92
141
|
|
|
93
142
|
```typescript
|
|
94
|
-
import { Driftgard, AuthError, RateLimitError, FeatureNotAvailableError } from "@driftgard/node";
|
|
143
|
+
import { Driftgard, AuthError, RateLimitError, FeatureNotAvailableError, ChainDepthExceededError } from "@driftgard/node";
|
|
95
144
|
|
|
96
145
|
try {
|
|
97
146
|
const result = await dg.evaluate({ ... });
|
|
@@ -100,6 +149,9 @@ try {
|
|
|
100
149
|
// Invalid or revoked API key (401)
|
|
101
150
|
} else if (e instanceof RateLimitError) {
|
|
102
151
|
// Too many requests (429)
|
|
152
|
+
} else if (e instanceof ChainDepthExceededError) {
|
|
153
|
+
// Agent loop detected — chain depth exceeded (429)
|
|
154
|
+
console.log(`Depth ${e.depth} exceeds max ${e.max}`);
|
|
103
155
|
} else if (e instanceof FeatureNotAvailableError) {
|
|
104
156
|
// API evaluate requires Compliance+ tier (403)
|
|
105
157
|
}
|
package/dist/errors.d.ts
CHANGED
|
@@ -13,3 +13,8 @@ export declare class FeatureNotAvailableError extends DriftgardError {
|
|
|
13
13
|
tier: string;
|
|
14
14
|
constructor(message?: string, tier?: string);
|
|
15
15
|
}
|
|
16
|
+
export declare class ChainDepthExceededError extends DriftgardError {
|
|
17
|
+
depth: number;
|
|
18
|
+
max: number;
|
|
19
|
+
constructor(message?: string, depth?: number, max?: number);
|
|
20
|
+
}
|
package/dist/errors.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.FeatureNotAvailableError = exports.RateLimitError = exports.AuthError = exports.DriftgardError = void 0;
|
|
3
|
+
exports.ChainDepthExceededError = exports.FeatureNotAvailableError = exports.RateLimitError = exports.AuthError = exports.DriftgardError = void 0;
|
|
4
4
|
class DriftgardError extends Error {
|
|
5
5
|
constructor(message, status, code) {
|
|
6
6
|
super(message);
|
|
@@ -32,3 +32,12 @@ class FeatureNotAvailableError extends DriftgardError {
|
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
exports.FeatureNotAvailableError = FeatureNotAvailableError;
|
|
35
|
+
class ChainDepthExceededError extends DriftgardError {
|
|
36
|
+
constructor(message = "Evaluation chain depth exceeded", depth = 0, max = 0) {
|
|
37
|
+
super(message, 429, "chain_depth_exceeded");
|
|
38
|
+
this.name = "ChainDepthExceededError";
|
|
39
|
+
this.depth = depth;
|
|
40
|
+
this.max = max;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
exports.ChainDepthExceededError = ChainDepthExceededError;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,17 +1,34 @@
|
|
|
1
1
|
import { DriftgardConfig, EvaluateRequest, EvaluateResponse } from "./types";
|
|
2
|
-
export { DriftgardConfig, EvaluateRequest, EvaluateResponse } from "./types";
|
|
3
|
-
export { Violation, EvaluationResult, HitlInfo } from "./types";
|
|
4
|
-
export { DriftgardError, AuthError, RateLimitError, FeatureNotAvailableError } from "./errors";
|
|
2
|
+
export { DriftgardConfig, EvaluateRequest, EvaluateResponse, CircuitBreakerConfig } from "./types";
|
|
3
|
+
export { Violation, EvaluationResult, FallbackResponse, HitlInfo } from "./types";
|
|
4
|
+
export { DriftgardError, AuthError, RateLimitError, FeatureNotAvailableError, ChainDepthExceededError } from "./errors";
|
|
5
|
+
type CircuitState = "closed" | "open" | "half-open";
|
|
5
6
|
export declare class Driftgard {
|
|
6
7
|
private apiKey;
|
|
7
8
|
private baseUrl;
|
|
8
9
|
private timeout;
|
|
9
10
|
private maxRetries;
|
|
11
|
+
private failureMode;
|
|
12
|
+
private cbThreshold;
|
|
13
|
+
private cbResetMs;
|
|
14
|
+
private cbState;
|
|
15
|
+
private cbFailures;
|
|
16
|
+
private cbOpenedAt;
|
|
10
17
|
constructor(config: DriftgardConfig);
|
|
11
18
|
/**
|
|
12
19
|
* Evaluate a prompt/response pair against your active control pack.
|
|
20
|
+
* Handles circuit breaker and failure mode automatically.
|
|
13
21
|
*/
|
|
14
22
|
evaluate(req: EvaluateRequest): Promise<EvaluateResponse>;
|
|
23
|
+
/** Generate a synthetic response based on failure mode. */
|
|
24
|
+
private syntheticResponse;
|
|
25
|
+
/** Get current circuit breaker state (for observability). */
|
|
26
|
+
get circuitBreakerState(): {
|
|
27
|
+
state: CircuitState;
|
|
28
|
+
failures: number;
|
|
29
|
+
openedAt: number;
|
|
30
|
+
};
|
|
31
|
+
private generateIdempotencyKey;
|
|
15
32
|
private post;
|
|
16
33
|
private delay;
|
|
17
34
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,40 +1,130 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Driftgard = exports.FeatureNotAvailableError = exports.RateLimitError = exports.AuthError = exports.DriftgardError = void 0;
|
|
3
|
+
exports.Driftgard = exports.ChainDepthExceededError = exports.FeatureNotAvailableError = exports.RateLimitError = exports.AuthError = exports.DriftgardError = void 0;
|
|
4
4
|
const errors_1 = require("./errors");
|
|
5
5
|
var errors_2 = require("./errors");
|
|
6
6
|
Object.defineProperty(exports, "DriftgardError", { enumerable: true, get: function () { return errors_2.DriftgardError; } });
|
|
7
7
|
Object.defineProperty(exports, "AuthError", { enumerable: true, get: function () { return errors_2.AuthError; } });
|
|
8
8
|
Object.defineProperty(exports, "RateLimitError", { enumerable: true, get: function () { return errors_2.RateLimitError; } });
|
|
9
9
|
Object.defineProperty(exports, "FeatureNotAvailableError", { enumerable: true, get: function () { return errors_2.FeatureNotAvailableError; } });
|
|
10
|
+
Object.defineProperty(exports, "ChainDepthExceededError", { enumerable: true, get: function () { return errors_2.ChainDepthExceededError; } });
|
|
10
11
|
const DEFAULT_BASE_URL = "https://api.driftgard.com";
|
|
11
12
|
const DEFAULT_TIMEOUT = 30000;
|
|
12
13
|
const DEFAULT_MAX_RETRIES = 2;
|
|
13
14
|
const RETRY_DELAY_MS = 500;
|
|
15
|
+
const DEFAULT_CB_THRESHOLD = 5;
|
|
16
|
+
const DEFAULT_CB_RESET_MS = 30000;
|
|
14
17
|
class Driftgard {
|
|
15
18
|
constructor(config) {
|
|
19
|
+
this.cbState = "closed";
|
|
20
|
+
this.cbFailures = 0;
|
|
21
|
+
this.cbOpenedAt = 0;
|
|
16
22
|
if (!config.apiKey)
|
|
17
23
|
throw new Error("apiKey is required");
|
|
18
24
|
this.apiKey = config.apiKey;
|
|
19
25
|
this.baseUrl = (config.baseUrl || DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
20
26
|
this.timeout = config.timeout ?? DEFAULT_TIMEOUT;
|
|
21
27
|
this.maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
28
|
+
this.failureMode = config.failureMode ?? "open";
|
|
29
|
+
this.cbThreshold = config.circuitBreaker?.threshold ?? DEFAULT_CB_THRESHOLD;
|
|
30
|
+
this.cbResetMs = config.circuitBreaker?.resetTimeoutMs ?? DEFAULT_CB_RESET_MS;
|
|
22
31
|
}
|
|
23
32
|
/**
|
|
24
33
|
* Evaluate a prompt/response pair against your active control pack.
|
|
34
|
+
* Handles circuit breaker and failure mode automatically.
|
|
25
35
|
*/
|
|
26
36
|
async evaluate(req) {
|
|
27
|
-
|
|
37
|
+
const idempotencyKey = req.idempotency_key || this.generateIdempotencyKey();
|
|
38
|
+
// Circuit breaker: if open, check if reset timeout has passed
|
|
39
|
+
if (this.cbState === "open") {
|
|
40
|
+
if (Date.now() - this.cbOpenedAt >= this.cbResetMs) {
|
|
41
|
+
this.cbState = "half-open";
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
// Circuit is open — apply failure mode without calling API
|
|
45
|
+
return this.syntheticResponse(req, "circuit_open");
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
const result = await this.post("/audit/evaluate", {
|
|
50
|
+
project_id: req.project_id,
|
|
51
|
+
prompt: req.prompt,
|
|
52
|
+
response: req.response,
|
|
53
|
+
model_id: req.model_id,
|
|
54
|
+
timestamp: req.timestamp || new Date().toISOString(),
|
|
55
|
+
...(req.experiment_id ? { experiment_id: req.experiment_id } : {}),
|
|
56
|
+
...(req.session_id ? { session_id: req.session_id } : {}),
|
|
57
|
+
...(req.parent_evaluation_id ? { parent_evaluation_id: req.parent_evaluation_id } : {}),
|
|
58
|
+
...(req.control_pack_id ? { control_pack_id: req.control_pack_id } : {}),
|
|
59
|
+
...(req.control_pack_version ? { control_pack_version: req.control_pack_version } : {}),
|
|
60
|
+
...(req.dry_run ? { dry_run: req.dry_run } : {}),
|
|
61
|
+
...(req.usage ? { usage: req.usage } : {}),
|
|
62
|
+
}, idempotencyKey);
|
|
63
|
+
// Success — reset circuit breaker
|
|
64
|
+
this.cbFailures = 0;
|
|
65
|
+
this.cbState = "closed";
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
68
|
+
catch (e) {
|
|
69
|
+
// Auth, rate limit, chain depth, feature errors are real errors — don't trigger circuit breaker
|
|
70
|
+
if (e instanceof errors_1.AuthError || e instanceof errors_1.RateLimitError ||
|
|
71
|
+
e instanceof errors_1.ChainDepthExceededError || e instanceof errors_1.FeatureNotAvailableError) {
|
|
72
|
+
throw e;
|
|
73
|
+
}
|
|
74
|
+
// Network/server error — increment circuit breaker
|
|
75
|
+
this.cbFailures++;
|
|
76
|
+
if (this.cbFailures >= this.cbThreshold) {
|
|
77
|
+
this.cbState = "open";
|
|
78
|
+
this.cbOpenedAt = Date.now();
|
|
79
|
+
}
|
|
80
|
+
// Apply failure mode
|
|
81
|
+
return this.syntheticResponse(req, "failure_mode");
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/** Generate a synthetic response based on failure mode. */
|
|
85
|
+
syntheticResponse(req, source) {
|
|
86
|
+
const allowed = this.failureMode === "open";
|
|
87
|
+
return {
|
|
88
|
+
ok: true,
|
|
28
89
|
project_id: req.project_id,
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
90
|
+
evaluation_id: `local_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
|
|
91
|
+
active_control_pack_id: "",
|
|
92
|
+
active_control_pack_version: 0,
|
|
93
|
+
data_mode: "unknown",
|
|
94
|
+
telemetry_mode: "unknown",
|
|
95
|
+
execution_mode: "sync",
|
|
96
|
+
decision_action: allowed ? "log_only" : "enforce",
|
|
97
|
+
decision_source: source,
|
|
98
|
+
evaluation: {
|
|
99
|
+
allowed,
|
|
100
|
+
risk_score: allowed ? 0 : 10,
|
|
101
|
+
violations: allowed ? [] : [{
|
|
102
|
+
clause_id: "DRIFTGARD_UNAVAILABLE",
|
|
103
|
+
severity: "critical",
|
|
104
|
+
category: "system",
|
|
105
|
+
reason: `Driftgard API unavailable — fail-closed policy applied (${source})`,
|
|
106
|
+
}],
|
|
107
|
+
},
|
|
108
|
+
...(allowed ? {} : {
|
|
109
|
+
fallback: {
|
|
110
|
+
message: "Service temporarily unavailable. Request blocked by fail-closed policy.",
|
|
111
|
+
code: "DRIFTGARD_UNAVAILABLE",
|
|
112
|
+
action: "show_message",
|
|
113
|
+
source,
|
|
114
|
+
escalated: false,
|
|
115
|
+
retry_after_ms: this.cbState === "open" ? this.cbResetMs : null,
|
|
116
|
+
},
|
|
117
|
+
}),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
/** Get current circuit breaker state (for observability). */
|
|
121
|
+
get circuitBreakerState() {
|
|
122
|
+
return { state: this.cbState, failures: this.cbFailures, openedAt: this.cbOpenedAt };
|
|
36
123
|
}
|
|
37
|
-
|
|
124
|
+
generateIdempotencyKey() {
|
|
125
|
+
return `idem_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
|
|
126
|
+
}
|
|
127
|
+
async post(path, body, idempotencyKey, attempt = 0) {
|
|
38
128
|
const controller = new AbortController();
|
|
39
129
|
const timer = setTimeout(() => controller.abort(), this.timeout);
|
|
40
130
|
try {
|
|
@@ -43,14 +133,20 @@ class Driftgard {
|
|
|
43
133
|
headers: {
|
|
44
134
|
"Content-Type": "application/json",
|
|
45
135
|
"x-api-key": this.apiKey,
|
|
136
|
+
...(idempotencyKey ? { "x-idempotency-key": idempotencyKey } : {}),
|
|
46
137
|
},
|
|
47
138
|
body: JSON.stringify(body),
|
|
48
139
|
signal: controller.signal,
|
|
49
140
|
});
|
|
50
141
|
if (res.status === 401)
|
|
51
142
|
throw new errors_1.AuthError();
|
|
52
|
-
if (res.status === 429)
|
|
143
|
+
if (res.status === 429) {
|
|
144
|
+
const payload = await res.json().catch(() => ({}));
|
|
145
|
+
if (payload?.error === "chain_depth_exceeded") {
|
|
146
|
+
throw new errors_1.ChainDepthExceededError(payload.message, payload.depth, payload.max);
|
|
147
|
+
}
|
|
53
148
|
throw new errors_1.RateLimitError();
|
|
149
|
+
}
|
|
54
150
|
if (res.status === 403) {
|
|
55
151
|
const payload = await res.json().catch(() => ({}));
|
|
56
152
|
if (payload?.error === "feature_not_available") {
|
|
@@ -61,7 +157,7 @@ class Driftgard {
|
|
|
61
157
|
// Retry on 5xx
|
|
62
158
|
if (res.status >= 500 && attempt < this.maxRetries) {
|
|
63
159
|
await this.delay(RETRY_DELAY_MS * Math.pow(2, attempt));
|
|
64
|
-
return this.post(path, body, attempt + 1);
|
|
160
|
+
return this.post(path, body, idempotencyKey, attempt + 1);
|
|
65
161
|
}
|
|
66
162
|
const payload = await res.json();
|
|
67
163
|
if (!res.ok) {
|
|
@@ -75,7 +171,7 @@ class Driftgard {
|
|
|
75
171
|
// Network error — retry
|
|
76
172
|
if (attempt < this.maxRetries) {
|
|
77
173
|
await this.delay(RETRY_DELAY_MS * Math.pow(2, attempt));
|
|
78
|
-
return this.post(path, body, attempt + 1);
|
|
174
|
+
return this.post(path, body, idempotencyKey, attempt + 1);
|
|
79
175
|
}
|
|
80
176
|
const msg = e instanceof Error ? e.message : String(e);
|
|
81
177
|
throw new errors_1.DriftgardError(msg, 0, "network_error");
|
package/dist/types.d.ts
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
|
+
export interface CircuitBreakerConfig {
|
|
2
|
+
/** Number of consecutive failures before opening the circuit. Default 5. */
|
|
3
|
+
threshold?: number;
|
|
4
|
+
/** Milliseconds to wait before trying again after circuit opens. Default 30000. */
|
|
5
|
+
resetTimeoutMs?: number;
|
|
6
|
+
}
|
|
1
7
|
export interface DriftgardConfig {
|
|
2
8
|
apiKey: string;
|
|
3
9
|
baseUrl?: string;
|
|
4
10
|
timeout?: number;
|
|
5
11
|
maxRetries?: number;
|
|
12
|
+
/** What to do when Driftgard API is unreachable. "open" = allow, "closed" = block. Default "open". */
|
|
13
|
+
failureMode?: "open" | "closed";
|
|
14
|
+
/** Circuit breaker config. Skips API calls after consecutive failures. */
|
|
15
|
+
circuitBreaker?: CircuitBreakerConfig;
|
|
6
16
|
}
|
|
7
17
|
export interface EvaluateRequest {
|
|
8
18
|
project_id: string;
|
|
@@ -11,6 +21,13 @@ export interface EvaluateRequest {
|
|
|
11
21
|
model_id: string;
|
|
12
22
|
timestamp?: string;
|
|
13
23
|
experiment_id?: string;
|
|
24
|
+
session_id?: string;
|
|
25
|
+
parent_evaluation_id?: string;
|
|
26
|
+
control_pack_id?: string;
|
|
27
|
+
control_pack_version?: number;
|
|
28
|
+
dry_run?: boolean;
|
|
29
|
+
/** Caller-provided idempotency key. If omitted, the SDK generates one. */
|
|
30
|
+
idempotency_key?: string;
|
|
14
31
|
usage?: {
|
|
15
32
|
prompt_tokens?: number;
|
|
16
33
|
completion_tokens?: number;
|
|
@@ -29,9 +46,15 @@ export interface EvaluationResult {
|
|
|
29
46
|
allowed: boolean;
|
|
30
47
|
risk_score: number;
|
|
31
48
|
violations: Violation[];
|
|
49
|
+
/** Original policy decision before execution_mode override (only present when overridden). */
|
|
50
|
+
policy_allowed?: boolean;
|
|
32
51
|
flags?: {
|
|
33
52
|
pii_detected?: boolean;
|
|
34
53
|
pii_in_prompt?: boolean;
|
|
54
|
+
secret_detected?: boolean;
|
|
55
|
+
adversarial_input?: boolean;
|
|
56
|
+
adversarial_score?: number;
|
|
57
|
+
dlp_findings_count?: number;
|
|
35
58
|
meta_bypass_detected?: boolean;
|
|
36
59
|
judge_used?: boolean;
|
|
37
60
|
judge_called?: boolean;
|
|
@@ -39,6 +62,14 @@ export interface EvaluationResult {
|
|
|
39
62
|
hard_blocked?: boolean;
|
|
40
63
|
};
|
|
41
64
|
}
|
|
65
|
+
export interface FallbackResponse {
|
|
66
|
+
message: string;
|
|
67
|
+
code: string;
|
|
68
|
+
action: string;
|
|
69
|
+
source: string;
|
|
70
|
+
escalated: boolean;
|
|
71
|
+
retry_after_ms?: number | null;
|
|
72
|
+
}
|
|
42
73
|
export interface HitlInfo {
|
|
43
74
|
queued: boolean;
|
|
44
75
|
hitl_id?: string;
|
|
@@ -53,6 +84,23 @@ export interface EvaluateResponse {
|
|
|
53
84
|
active_control_pack_version: number;
|
|
54
85
|
data_mode: string;
|
|
55
86
|
telemetry_mode: string;
|
|
56
|
-
|
|
87
|
+
/** Server-side execution mode: sync, async, or hybrid. */
|
|
88
|
+
execution_mode?: string;
|
|
89
|
+
/** How the decision was applied: "enforce" or "log_only". */
|
|
90
|
+
decision_action?: string;
|
|
91
|
+
/** Where the decision came from: "policy", "failure_mode", "circuit_open". */
|
|
92
|
+
decision_source?: string;
|
|
93
|
+
dry_run?: boolean;
|
|
94
|
+
experiment_id?: string;
|
|
95
|
+
session_id?: string;
|
|
96
|
+
parent_evaluation_id?: string;
|
|
97
|
+
usage?: {
|
|
98
|
+
prompt_tokens?: number;
|
|
99
|
+
completion_tokens?: number;
|
|
100
|
+
total_tokens?: number;
|
|
101
|
+
cost?: number;
|
|
102
|
+
};
|
|
103
|
+
hitl?: HitlInfo;
|
|
57
104
|
evaluation: EvaluationResult;
|
|
105
|
+
fallback?: FallbackResponse;
|
|
58
106
|
}
|
package/package.json
CHANGED