ovrule-lab 0.2.2 → 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/README.md +6 -7
- package/dist/index.d.ts +19 -3
- package/dist/index.js +45 -7
- package/package.json +4 -5
package/README.md
CHANGED
|
@@ -27,16 +27,15 @@ console.log(receipt.summary);
|
|
|
27
27
|
import { guard } from "ovrule-lab";
|
|
28
28
|
|
|
29
29
|
const result = await guard({
|
|
30
|
-
scenario: "
|
|
31
|
-
policyPack: "finance",
|
|
30
|
+
scenario: "An AI coding agent wants to run DROP TABLE users on the production database.",
|
|
32
31
|
});
|
|
33
32
|
|
|
34
33
|
if (!result.allowed) {
|
|
35
|
-
console.error(result.decision, result.
|
|
36
|
-
throw new Error("
|
|
34
|
+
console.error(result.decision, result.reason); // "REFUSED" — irreversible, production-wide
|
|
35
|
+
throw new Error("Action blocked by Ovrule.");
|
|
37
36
|
}
|
|
38
37
|
|
|
39
|
-
await
|
|
38
|
+
await runCommand();
|
|
40
39
|
```
|
|
41
40
|
|
|
42
41
|
## 3. LangChain middleware integration
|
|
@@ -55,8 +54,8 @@ export async function beforeToolInvoke(input: string) {
|
|
|
55
54
|
if (!guard.allowed) {
|
|
56
55
|
return {
|
|
57
56
|
blocked: true,
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
decision: guard.decision,
|
|
58
|
+
reason: guard.reason,
|
|
60
59
|
};
|
|
61
60
|
}
|
|
62
61
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export type Decision = "ADMISSIBLE" | "AMBIGUOUS" | "REFUSED";
|
|
2
2
|
export type Verdict = "PASS" | "WARN" | "FAIL";
|
|
3
|
-
export type PolicyPackId = "general" | "customer_support" | "healthcare" | "finance";
|
|
3
|
+
export type PolicyPackId = "general" | "customer_support" | "healthcare" | "finance" | "payments" | "code";
|
|
4
4
|
export type RuleName = "SAFETY" | "AUTHORIZATION" | "CAUSAL VALIDITY" | "REVERSIBILITY" | "IMPACT SCOPE" | "CONSENT";
|
|
5
5
|
export type RuleTraceItem = {
|
|
6
6
|
rule: RuleName;
|
|
@@ -77,8 +77,10 @@ export type ProposedAction = {
|
|
|
77
77
|
export type GuardResult = {
|
|
78
78
|
allowed: boolean;
|
|
79
79
|
decision: Decision;
|
|
80
|
-
|
|
81
|
-
|
|
80
|
+
reason: string;
|
|
81
|
+
ruleTrace: RuleTraceItem[];
|
|
82
|
+
receiptId: string;
|
|
83
|
+
signature?: string;
|
|
82
84
|
};
|
|
83
85
|
export type OvruleClientOptions = {
|
|
84
86
|
baseUrl?: string;
|
|
@@ -103,4 +105,18 @@ export declare function guard(action: string | ProposedAction, options?: Request
|
|
|
103
105
|
export declare function verify(receipt: CaseFileReceipt, signature: string, options?: {
|
|
104
106
|
signal?: AbortSignal;
|
|
105
107
|
} & OvruleClientOptions): Promise<boolean>;
|
|
108
|
+
/** Thrown by a guarded tool when Ovrule refuses the action. `.result` holds the decision. */
|
|
109
|
+
export declare class GuardBlockedError extends Error {
|
|
110
|
+
readonly result: GuardResult;
|
|
111
|
+
constructor(result: GuardResult);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Wrap any agent tool so it is audited by Ovrule BEFORE it runs. Works with any
|
|
115
|
+
* framework (LangChain, OpenAI Agents, MCP) because tools are just functions.
|
|
116
|
+
* A refused action throws GuardBlockedError and the tool never executes.
|
|
117
|
+
*
|
|
118
|
+
* const safeExec = guardTool(runCommand, (cmd) => `An AI agent wants to run: ${cmd}`, { policyPack: "code" });
|
|
119
|
+
* await safeExec("DROP TABLE users;"); // throws — never runs
|
|
120
|
+
*/
|
|
121
|
+
export declare function guardTool<A extends unknown[], R>(tool: (...args: A) => Promise<R>, describe: (...args: A) => string, options?: RequestOptions & OvruleClientOptions): (...args: A) => Promise<R>;
|
|
106
122
|
export {};
|
package/dist/index.js
CHANGED
|
@@ -129,14 +129,26 @@ export class OvruleClient {
|
|
|
129
129
|
return finalReceipt;
|
|
130
130
|
}
|
|
131
131
|
async guard(action, options = {}) {
|
|
132
|
-
const
|
|
133
|
-
const
|
|
134
|
-
|
|
132
|
+
const payload = normalizeAction(action, options);
|
|
133
|
+
const response = await this.fetchImpl(`${this.baseUrl}/api/guard`, {
|
|
134
|
+
method: "POST",
|
|
135
|
+
headers: { "Content-Type": "application/json" },
|
|
136
|
+
body: JSON.stringify({ action: payload.scenario, policyPack: payload.policyPack }),
|
|
137
|
+
signal: options.signal,
|
|
138
|
+
});
|
|
139
|
+
// A blocked (REFUSED) action returns HTTP 403 with a full decision body, so we
|
|
140
|
+
// read the body regardless of status and only fail on a missing decision.
|
|
141
|
+
const data = (await response.json().catch(() => null));
|
|
142
|
+
if (!data || data.decision === undefined) {
|
|
143
|
+
throw new Error((data && data.error) || `Ovrule guard failed with status ${response.status}.`);
|
|
144
|
+
}
|
|
135
145
|
return {
|
|
136
|
-
allowed,
|
|
137
|
-
decision:
|
|
138
|
-
|
|
139
|
-
|
|
146
|
+
allowed: Boolean(data.allowed),
|
|
147
|
+
decision: data.decision,
|
|
148
|
+
reason: data.reason ?? "",
|
|
149
|
+
ruleTrace: data.ruleTrace ?? [],
|
|
150
|
+
receiptId: data.receiptId ?? "",
|
|
151
|
+
signature: data.signature,
|
|
140
152
|
};
|
|
141
153
|
}
|
|
142
154
|
async verify(receipt, signature, options = {}) {
|
|
@@ -171,3 +183,29 @@ export async function verify(receipt, signature, options) {
|
|
|
171
183
|
}
|
|
172
184
|
return defaultClient.verify(receipt, signature, options);
|
|
173
185
|
}
|
|
186
|
+
/** Thrown by a guarded tool when Ovrule refuses the action. `.result` holds the decision. */
|
|
187
|
+
export class GuardBlockedError extends Error {
|
|
188
|
+
result;
|
|
189
|
+
constructor(result) {
|
|
190
|
+
super(`Ovrule blocked action (${result.decision}): ${result.reason}`);
|
|
191
|
+
this.result = result;
|
|
192
|
+
this.name = "GuardBlockedError";
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Wrap any agent tool so it is audited by Ovrule BEFORE it runs. Works with any
|
|
197
|
+
* framework (LangChain, OpenAI Agents, MCP) because tools are just functions.
|
|
198
|
+
* A refused action throws GuardBlockedError and the tool never executes.
|
|
199
|
+
*
|
|
200
|
+
* const safeExec = guardTool(runCommand, (cmd) => `An AI agent wants to run: ${cmd}`, { policyPack: "code" });
|
|
201
|
+
* await safeExec("DROP TABLE users;"); // throws — never runs
|
|
202
|
+
*/
|
|
203
|
+
export function guardTool(tool, describe, options) {
|
|
204
|
+
return async (...args) => {
|
|
205
|
+
const result = await guard(describe(...args), options);
|
|
206
|
+
if (!result.allowed) {
|
|
207
|
+
throw new GuardBlockedError(result);
|
|
208
|
+
}
|
|
209
|
+
return tool(...args);
|
|
210
|
+
};
|
|
211
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ovrule-lab",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "TypeScript SDK for Ovrule
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "TypeScript SDK for Ovrule — accountability guardrails for AI agents: classify an action, guard a tool call (incl. the accounts-payable / payment-fraud pack), and verify signed receipts.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"module": "./dist/index.js",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"bin/welcome.js"
|
|
20
20
|
],
|
|
21
21
|
"bin": {
|
|
22
|
-
"ovrule-lab": "bin/ovrule-lab"
|
|
22
|
+
"ovrule-lab": "./bin/ovrule-lab"
|
|
23
23
|
},
|
|
24
24
|
"scripts": {
|
|
25
25
|
"build": "tsc -p tsconfig.json",
|
|
@@ -38,6 +38,5 @@
|
|
|
38
38
|
"sideEffects": false,
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"typescript": "^5.8.3"
|
|
41
|
-
}
|
|
42
|
-
"author": ""
|
|
41
|
+
}
|
|
43
42
|
}
|