@secure-ai/guard 0.1.0 → 0.2.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 +1 -1
- package/dist/index.d.ts +17 -0
- package/dist/index.js +41 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -133,7 +133,7 @@ cause in a process already carrying too many.
|
|
|
133
133
|
## Also available over MCP
|
|
134
134
|
|
|
135
135
|
Agents that speak MCP can reach the same controls as tools at
|
|
136
|
-
`https://
|
|
136
|
+
`https://secureai.one/mcp` — `inspect_action`, `check_policy`,
|
|
137
137
|
`recent_activity`, plus `redact` and `restore`.
|
|
138
138
|
|
|
139
139
|
Full reference: <https://secureai.one/developers>
|
package/dist/index.d.ts
CHANGED
|
@@ -246,6 +246,23 @@ export declare class SecureAI {
|
|
|
246
246
|
* check would make protection opt-in at every site, which is the thing this
|
|
247
247
|
* exists to stop.
|
|
248
248
|
*/
|
|
249
|
+
/**
|
|
250
|
+
* What to hand the wrapped function.
|
|
251
|
+
*
|
|
252
|
+
* The API omits `input` only on a block, so on any other decision it is
|
|
253
|
+
* there. The question is what to do if it is not, and the answer is not
|
|
254
|
+
* "send what the caller had".
|
|
255
|
+
*
|
|
256
|
+
* On a redact, the caller's input is the one thing that must not go: it
|
|
257
|
+
* still contains the values the decision just said to replace. Falling
|
|
258
|
+
* back to it turns a missing field into the product doing the exact
|
|
259
|
+
* opposite of its purpose, silently, with the audit trail recording a
|
|
260
|
+
* redaction that did not happen. So a redact with nothing to send throws.
|
|
261
|
+
*
|
|
262
|
+
* On an allow, nothing was rewritten and the caller's own input is the
|
|
263
|
+
* correct thing to pass, so the fallback is right there and stays.
|
|
264
|
+
*/
|
|
265
|
+
private sendable;
|
|
249
266
|
guard<A, R>(tool: string, fn: (input: A) => Promise<R> | R, opts?: {
|
|
250
267
|
direction?: Direction;
|
|
251
268
|
agent?: string;
|
package/dist/index.js
CHANGED
|
@@ -82,7 +82,20 @@ export class SecureAIError extends Error {
|
|
|
82
82
|
this.code = code;
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
|
-
|
|
85
|
+
/**
|
|
86
|
+
* Where the client talks to, when nobody says otherwise.
|
|
87
|
+
*
|
|
88
|
+
* This was an api. subdomain in 0.2.0, and that host does not
|
|
89
|
+
* exist -- it was never created. Nothing caught it: the tests
|
|
90
|
+
* pass a baseUrl, the typecheck has no opinion about a string, and the
|
|
91
|
+
* package published cleanly. The first person to install the SDK and follow
|
|
92
|
+
* the README would have got ENOTFOUND on their first call.
|
|
93
|
+
*
|
|
94
|
+
* secureai.one/v1 is the address the docs have always given and it is
|
|
95
|
+
* proxied to the Worker, so this is the same endpoint the curl examples hit.
|
|
96
|
+
* Keep the two in step: docs/api.md in the web repo is the other half.
|
|
97
|
+
*/
|
|
98
|
+
const DEFAULT_BASE = "https://secureai.one";
|
|
86
99
|
export class SecureAI {
|
|
87
100
|
apiKey;
|
|
88
101
|
baseUrl;
|
|
@@ -231,6 +244,31 @@ export class SecureAI {
|
|
|
231
244
|
* check would make protection opt-in at every site, which is the thing this
|
|
232
245
|
* exists to stop.
|
|
233
246
|
*/
|
|
247
|
+
/**
|
|
248
|
+
* What to hand the wrapped function.
|
|
249
|
+
*
|
|
250
|
+
* The API omits `input` only on a block, so on any other decision it is
|
|
251
|
+
* there. The question is what to do if it is not, and the answer is not
|
|
252
|
+
* "send what the caller had".
|
|
253
|
+
*
|
|
254
|
+
* On a redact, the caller's input is the one thing that must not go: it
|
|
255
|
+
* still contains the values the decision just said to replace. Falling
|
|
256
|
+
* back to it turns a missing field into the product doing the exact
|
|
257
|
+
* opposite of its purpose, silently, with the audit trail recording a
|
|
258
|
+
* redaction that did not happen. So a redact with nothing to send throws.
|
|
259
|
+
*
|
|
260
|
+
* On an allow, nothing was rewritten and the caller's own input is the
|
|
261
|
+
* correct thing to pass, so the fallback is right there and stays.
|
|
262
|
+
*/
|
|
263
|
+
sendable(tool, verdict, original) {
|
|
264
|
+
if (verdict.input !== undefined)
|
|
265
|
+
return verdict.input;
|
|
266
|
+
if (verdict.decision === "redact") {
|
|
267
|
+
throw new SecureAIError(`Secure AI decided to redact ${tool} but returned nothing to send. ` +
|
|
268
|
+
`The original was not sent: it still holds the values that decision was about.`, 502, "missing_rewritten_input");
|
|
269
|
+
}
|
|
270
|
+
return original;
|
|
271
|
+
}
|
|
234
272
|
guard(tool, fn, opts) {
|
|
235
273
|
return async (input) => {
|
|
236
274
|
let verdict;
|
|
@@ -280,11 +318,9 @@ export class SecureAI {
|
|
|
280
318
|
});
|
|
281
319
|
if (after.decision === "block")
|
|
282
320
|
throw new ActionBlocked(tool, after);
|
|
283
|
-
return await fn((after
|
|
321
|
+
return await fn(this.sendable(tool, after, input));
|
|
284
322
|
}
|
|
285
|
-
|
|
286
|
-
// belt and braces against a future field being dropped.
|
|
287
|
-
return await fn((verdict.input ?? input));
|
|
323
|
+
return await fn(this.sendable(tool, verdict, input));
|
|
288
324
|
};
|
|
289
325
|
}
|
|
290
326
|
/**
|