@driftgard/node 1.6.0 → 1.8.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/README.md +33 -1
- package/dist/index.d.ts +18 -2
- package/dist/index.js +50 -0
- package/dist/types.d.ts +38 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -47,10 +47,11 @@ const result = await dg.evaluate({
|
|
|
47
47
|
model_id: "gpt-4o",
|
|
48
48
|
session_id: "sess_abc123", // groups evals in a conversation
|
|
49
49
|
parent_evaluation_id: "eval_prev_id", // chains to the previous eval
|
|
50
|
+
sequence_no: 1, // optional — enforces ordering within session
|
|
50
51
|
});
|
|
51
52
|
```
|
|
52
53
|
|
|
53
|
-
This enables chain depth protection (prevents infinite agent loops) and lets you trace evaluation lineage in the dashboard.
|
|
54
|
+
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`.
|
|
54
55
|
|
|
55
56
|
## A/B experiments
|
|
56
57
|
|
|
@@ -104,6 +105,37 @@ if (result.cost_alert) {
|
|
|
104
105
|
|
|
105
106
|
Configure thresholds in Settings — per-project, per-model, or per-session. Session-scoped alerts catch runaway agent loops in real-time.
|
|
106
107
|
|
|
108
|
+
## Tool call validation
|
|
109
|
+
|
|
110
|
+
Validate AI agent tool/function calls against your control pack's tool rules:
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
// Direct tool call evaluation
|
|
114
|
+
const result = await dg.evaluateToolCall({
|
|
115
|
+
project_id: "your-project-id",
|
|
116
|
+
model_id: "gpt-4o",
|
|
117
|
+
tool_name: "transfer_money",
|
|
118
|
+
parameters: { amount: 500, to_account: "account_123" },
|
|
119
|
+
session_id: "sess_abc123",
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
if (!result.evaluation.allowed) {
|
|
123
|
+
console.log("Tool blocked:", result.fallback?.message);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Or wrap a tool function — blocks automatically
|
|
127
|
+
const safeTransfer = dg.guard(transferMoney, "transfer_money", "your-project-id");
|
|
128
|
+
await safeTransfer({ amount: 500, to_account: "account_123" }); // throws if blocked
|
|
129
|
+
|
|
130
|
+
// Report execution outcome (optional)
|
|
131
|
+
await dg.reportOutcome(result.evaluation_id, "your-project-id", {
|
|
132
|
+
execution_status: "success",
|
|
133
|
+
duration_ms: 230,
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
For Strands agents, use the `BeforeToolCallEvent` hook — see the integration guide.
|
|
138
|
+
|
|
107
139
|
## Features
|
|
108
140
|
|
|
109
141
|
- Single `evaluate()` method — send prompt/response, get verdict
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { DriftgardConfig, EvaluateRequest, EvaluateResponse } from "./types";
|
|
2
|
-
export { DriftgardConfig, EvaluateRequest, EvaluateResponse, CircuitBreakerConfig } from "./types";
|
|
1
|
+
import { DriftgardConfig, EvaluateRequest, EvaluateResponse, ToolCallRequest, OutcomeRequest } from "./types";
|
|
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
5
|
type CircuitState = "closed" | "open" | "half-open";
|
|
@@ -22,6 +22,22 @@ export declare class Driftgard {
|
|
|
22
22
|
evaluate(req: EvaluateRequest): Promise<EvaluateResponse>;
|
|
23
23
|
/** Generate a synthetic response based on failure mode. */
|
|
24
24
|
private syntheticResponse;
|
|
25
|
+
/**
|
|
26
|
+
* Evaluate a tool/function call against your control pack's tool_rules.
|
|
27
|
+
*/
|
|
28
|
+
evaluateToolCall(req: ToolCallRequest): Promise<EvaluateResponse>;
|
|
29
|
+
/**
|
|
30
|
+
* Report the outcome of a tool execution after it was allowed.
|
|
31
|
+
*/
|
|
32
|
+
reportOutcome(evaluationId: string, projectId: string, outcome: OutcomeRequest): Promise<any>;
|
|
33
|
+
/**
|
|
34
|
+
* Wrap a tool function with Driftgard policy check.
|
|
35
|
+
* Returns a guarded version that evaluates before executing.
|
|
36
|
+
*/
|
|
37
|
+
guard<T extends (...args: any[]) => any>(toolFn: T, toolName: string, projectId: string, opts?: {
|
|
38
|
+
modelId?: string;
|
|
39
|
+
sessionId?: string;
|
|
40
|
+
}): (...args: Parameters<T>) => Promise<ReturnType<T>>;
|
|
25
41
|
/** Get current circuit breaker state (for observability). */
|
|
26
42
|
get circuitBreakerState(): {
|
|
27
43
|
state: CircuitState;
|
package/dist/index.js
CHANGED
|
@@ -58,6 +58,9 @@ class Driftgard {
|
|
|
58
58
|
...(req.control_pack_id ? { control_pack_id: req.control_pack_id } : {}),
|
|
59
59
|
...(req.control_pack_version ? { control_pack_version: req.control_pack_version } : {}),
|
|
60
60
|
...(req.dry_run ? { dry_run: req.dry_run } : {}),
|
|
61
|
+
...(req.eval_mode ? { eval_mode: req.eval_mode } : {}),
|
|
62
|
+
...(req.tool_call ? { tool_call: req.tool_call } : {}),
|
|
63
|
+
...(req.sequence_no != null ? { sequence_no: req.sequence_no } : {}),
|
|
61
64
|
...(req.usage ? { usage: req.usage } : {}),
|
|
62
65
|
}, idempotencyKey);
|
|
63
66
|
// Success — reset circuit breaker
|
|
@@ -117,6 +120,53 @@ class Driftgard {
|
|
|
117
120
|
}),
|
|
118
121
|
};
|
|
119
122
|
}
|
|
123
|
+
/**
|
|
124
|
+
* Evaluate a tool/function call against your control pack's tool_rules.
|
|
125
|
+
*/
|
|
126
|
+
async evaluateToolCall(req) {
|
|
127
|
+
return this.evaluate({
|
|
128
|
+
project_id: req.project_id,
|
|
129
|
+
prompt: "",
|
|
130
|
+
response: "",
|
|
131
|
+
model_id: req.model_id,
|
|
132
|
+
eval_mode: "tool_call",
|
|
133
|
+
tool_call: { tool_name: req.tool_name, parameters: req.parameters || {} },
|
|
134
|
+
session_id: req.session_id,
|
|
135
|
+
parent_evaluation_id: req.parent_evaluation_id,
|
|
136
|
+
idempotency_key: req.idempotency_key,
|
|
137
|
+
sequence_no: req.sequence_no,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Report the outcome of a tool execution after it was allowed.
|
|
142
|
+
*/
|
|
143
|
+
async reportOutcome(evaluationId, projectId, outcome) {
|
|
144
|
+
return this.post(`/audit/evaluations/${encodeURIComponent(evaluationId)}/outcome`, {
|
|
145
|
+
project_id: projectId,
|
|
146
|
+
...outcome,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Wrap a tool function with Driftgard policy check.
|
|
151
|
+
* Returns a guarded version that evaluates before executing.
|
|
152
|
+
*/
|
|
153
|
+
guard(toolFn, toolName, projectId, opts) {
|
|
154
|
+
const dg = this;
|
|
155
|
+
return async (...args) => {
|
|
156
|
+
const parameters = args.length === 1 && typeof args[0] === "object" ? args[0] : { args };
|
|
157
|
+
const result = await dg.evaluateToolCall({
|
|
158
|
+
project_id: projectId,
|
|
159
|
+
model_id: opts?.modelId || "agent",
|
|
160
|
+
tool_name: toolName,
|
|
161
|
+
parameters,
|
|
162
|
+
session_id: opts?.sessionId,
|
|
163
|
+
});
|
|
164
|
+
if (!result.evaluation.allowed) {
|
|
165
|
+
throw new errors_1.DriftgardError(result.fallback?.message || `Tool "${toolName}" blocked by policy`, 403, "tool_blocked");
|
|
166
|
+
}
|
|
167
|
+
return toolFn(...args);
|
|
168
|
+
};
|
|
169
|
+
}
|
|
120
170
|
/** Get current circuit breaker state (for observability). */
|
|
121
171
|
get circuitBreakerState() {
|
|
122
172
|
return { state: this.cbState, failures: this.cbFailures, openedAt: this.cbOpenedAt };
|
package/dist/types.d.ts
CHANGED
|
@@ -26,8 +26,15 @@ export interface EvaluateRequest {
|
|
|
26
26
|
control_pack_id?: string;
|
|
27
27
|
control_pack_version?: number;
|
|
28
28
|
dry_run?: boolean;
|
|
29
|
+
eval_mode?: "text" | "tool_call";
|
|
30
|
+
tool_call?: {
|
|
31
|
+
tool_name: string;
|
|
32
|
+
parameters?: Record<string, any>;
|
|
33
|
+
};
|
|
29
34
|
/** Caller-provided idempotency key. If omitted, the SDK generates one. */
|
|
30
35
|
idempotency_key?: string;
|
|
36
|
+
/** Sequence number within a session for ordering. Optional — if sent, ordering is enforced. */
|
|
37
|
+
sequence_no?: number;
|
|
31
38
|
usage?: {
|
|
32
39
|
prompt_tokens?: number;
|
|
33
40
|
completion_tokens?: number;
|
|
@@ -112,4 +119,35 @@ export interface EvaluateResponse {
|
|
|
112
119
|
threshold_usd: number;
|
|
113
120
|
actual_usd: number;
|
|
114
121
|
};
|
|
122
|
+
eval_mode?: string;
|
|
123
|
+
tool_call?: {
|
|
124
|
+
tool_name: string;
|
|
125
|
+
parameters?: Record<string, any>;
|
|
126
|
+
};
|
|
127
|
+
sequence_no?: number;
|
|
128
|
+
sequence_warning?: {
|
|
129
|
+
message: string;
|
|
130
|
+
expected_next: number;
|
|
131
|
+
received: number;
|
|
132
|
+
max_seen: number;
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
export interface ToolCallRequest {
|
|
136
|
+
project_id: string;
|
|
137
|
+
model_id: string;
|
|
138
|
+
tool_name: string;
|
|
139
|
+
parameters?: Record<string, any>;
|
|
140
|
+
session_id?: string;
|
|
141
|
+
parent_evaluation_id?: string;
|
|
142
|
+
idempotency_key?: string;
|
|
143
|
+
sequence_no?: number;
|
|
144
|
+
}
|
|
145
|
+
export interface OutcomeRequest {
|
|
146
|
+
execution_status: "success" | "failed" | "rolled_back";
|
|
147
|
+
duration_ms?: number;
|
|
148
|
+
error_code?: string;
|
|
149
|
+
error_message?: string;
|
|
150
|
+
affected_resources?: string[];
|
|
151
|
+
compensation_action?: string;
|
|
152
|
+
external_ref?: string;
|
|
115
153
|
}
|
package/package.json
CHANGED