@ory/vercel-ai 0.10.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 +38 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.js +79 -0
- package/package.json +41 -0
package/README.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# @ory/vercel-ai
|
|
2
|
+
|
|
3
|
+
Ory Agent Security for the [Vercel AI SDK](https://ai-sdk.dev).
|
|
4
|
+
|
|
5
|
+
Wraps your tools so every call is authorized against Ory Permissions, traced, and tied to
|
|
6
|
+
the user → agent identity — built on [`@ory/argus`](../core).
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
npm install @ory/vercel-ai
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
import { generateText } from "ai";
|
|
14
|
+
import { withOry } from "@ory/vercel-ai";
|
|
15
|
+
|
|
16
|
+
const result = await generateText({
|
|
17
|
+
model,
|
|
18
|
+
tools: withOry({ search, sendEmail }), // gate every tool's execute()
|
|
19
|
+
prompt: "…",
|
|
20
|
+
});
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The Vercel AI SDK has no global before-tool hook — enforcement lives at the tool boundary, so
|
|
24
|
+
`withOry` wraps each tool's `execute`. On the first call it runs the Ory session gates; on
|
|
25
|
+
each call it authorizes the tool and records spans. In **enforce** mode
|
|
26
|
+
(`ORY_PERMISSION_MODE=enforce`) a denied tool throws, surfacing to the model as a tool error;
|
|
27
|
+
in **observe** mode (default) the tool runs and a `permission.observe_deny` span is recorded.
|
|
28
|
+
Tools without an `execute` (provider-defined) pass through untouched.
|
|
29
|
+
|
|
30
|
+
Derive the permission subject from per-call context with `subjectFromOptions`:
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
withOry(tools, { subjectFromOptions: (o) => o?.experimental_context?.userId });
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
This package depends only on `@ory/argus` — not the `ai` package; tools are duck-typed so any
|
|
37
|
+
`ai` version works. Credentials come from the shared `~/.config/ory-agent-plugins/config.json`.
|
|
38
|
+
See [docs/sdk-integrations.md](../../docs/sdk-integrations.md).
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ory Agent Security for the Vercel AI SDK.
|
|
3
|
+
*
|
|
4
|
+
* The Vercel AI SDK has no global before-tool hook — enforcement lives at the tool
|
|
5
|
+
* boundary. {@link withOry} wraps each tool's `execute` so every call is authorized against
|
|
6
|
+
* Ory Permissions, traced, and (on first call) preceded by the Ory session gates. A denied
|
|
7
|
+
* tool in enforce mode throws, surfacing to the model as a tool error; in observe mode the
|
|
8
|
+
* tool runs and a `permission.observe_deny` span is recorded.
|
|
9
|
+
*
|
|
10
|
+
* This package depends only on `@ory/argus` — the Vercel `ai` package is not a dependency;
|
|
11
|
+
* tools are duck-typed so any `ai` version works.
|
|
12
|
+
*
|
|
13
|
+
* import { withOry } from "@ory/vercel-ai";
|
|
14
|
+
* const result = await generateText({ model, tools: withOry({ search, sendEmail }) });
|
|
15
|
+
*/
|
|
16
|
+
import { OryAgentClient } from "@ory/argus";
|
|
17
|
+
/** A Vercel AI SDK tool — duck-typed to avoid a dependency on the `ai` package. */
|
|
18
|
+
export interface VercelTool {
|
|
19
|
+
execute?: (input: unknown, options?: unknown) => unknown;
|
|
20
|
+
[key: string]: unknown;
|
|
21
|
+
}
|
|
22
|
+
export type ToolSet = Record<string, VercelTool>;
|
|
23
|
+
export interface WithOryOptions {
|
|
24
|
+
/** Client to use. Defaults to `OryAgentClient.fromEnv("vercel-ai")` (reads shared config). */
|
|
25
|
+
client?: OryAgentClient;
|
|
26
|
+
/** Override the Ory project URL for the session gates. */
|
|
27
|
+
projectUrl?: string;
|
|
28
|
+
/** Whether a denied tool is hard-blocked (throws). Default true. */
|
|
29
|
+
canBlock?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Derive the permission subject from the AI SDK call options (e.g. read a thread/user id
|
|
32
|
+
* from `options.experimental_context`). Falls back to the client principal / env when the
|
|
33
|
+
* function returns undefined.
|
|
34
|
+
*/
|
|
35
|
+
subjectFromOptions?: (options: unknown) => string | undefined;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Wrap every tool in `tools` so its `execute` is gated through Ory. Tools without an
|
|
39
|
+
* `execute` (client-side / provider-defined tools) are passed through untouched.
|
|
40
|
+
*/
|
|
41
|
+
export declare function withOry(tools: ToolSet, options?: WithOryOptions): ToolSet;
|
|
42
|
+
export { OryAgentClient, OryDenialError } from "@ory/argus";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Ory Agent Security for the Vercel AI SDK.
|
|
4
|
+
*
|
|
5
|
+
* The Vercel AI SDK has no global before-tool hook — enforcement lives at the tool
|
|
6
|
+
* boundary. {@link withOry} wraps each tool's `execute` so every call is authorized against
|
|
7
|
+
* Ory Permissions, traced, and (on first call) preceded by the Ory session gates. A denied
|
|
8
|
+
* tool in enforce mode throws, surfacing to the model as a tool error; in observe mode the
|
|
9
|
+
* tool runs and a `permission.observe_deny` span is recorded.
|
|
10
|
+
*
|
|
11
|
+
* This package depends only on `@ory/argus` — the Vercel `ai` package is not a dependency;
|
|
12
|
+
* tools are duck-typed so any `ai` version works.
|
|
13
|
+
*
|
|
14
|
+
* import { withOry } from "@ory/vercel-ai";
|
|
15
|
+
* const result = await generateText({ model, tools: withOry({ search, sendEmail }) });
|
|
16
|
+
*/
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.OryDenialError = exports.OryAgentClient = void 0;
|
|
19
|
+
exports.withOry = withOry;
|
|
20
|
+
const argus_1 = require("@ory/argus");
|
|
21
|
+
const HARNESS = "vercel-ai";
|
|
22
|
+
/**
|
|
23
|
+
* Wrap every tool in `tools` so its `execute` is gated through Ory. Tools without an
|
|
24
|
+
* `execute` (client-side / provider-defined tools) are passed through untouched.
|
|
25
|
+
*/
|
|
26
|
+
function withOry(tools, options = {}) {
|
|
27
|
+
const client = options.client ?? argus_1.OryAgentClient.fromEnv(HARNESS);
|
|
28
|
+
const canBlock = options.canBlock ?? true;
|
|
29
|
+
let sessionStarted = false;
|
|
30
|
+
const ensureSession = async () => {
|
|
31
|
+
if (sessionStarted)
|
|
32
|
+
return;
|
|
33
|
+
sessionStarted = true;
|
|
34
|
+
try {
|
|
35
|
+
await (0, argus_1.sessionStart)(client, { harness: HARNESS, projectUrl: options.projectUrl });
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
client.logger.warn("session_start.failed", {
|
|
39
|
+
message: err instanceof Error ? err.message : String(err),
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
const wrapped = {};
|
|
44
|
+
for (const [name, tool] of Object.entries(tools)) {
|
|
45
|
+
if (!tool || typeof tool.execute !== "function") {
|
|
46
|
+
wrapped[name] = tool;
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
const originalExecute = tool.execute.bind(tool);
|
|
50
|
+
wrapped[name] = {
|
|
51
|
+
...tool,
|
|
52
|
+
execute: async (input, callOptions) => {
|
|
53
|
+
await ensureSession();
|
|
54
|
+
const subjectFallback = options.subjectFromOptions?.(callOptions);
|
|
55
|
+
const result = await (0, argus_1.gate)(client, {
|
|
56
|
+
harness: HARNESS,
|
|
57
|
+
toolName: name,
|
|
58
|
+
toolArgs: input,
|
|
59
|
+
subjectFallback,
|
|
60
|
+
canBlock,
|
|
61
|
+
});
|
|
62
|
+
if (result.blocked) {
|
|
63
|
+
throw new argus_1.OryDenialError({
|
|
64
|
+
tool: name,
|
|
65
|
+
subjectId: result.subject,
|
|
66
|
+
namespace: result.namespace,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
const output = await originalExecute(input, callOptions);
|
|
70
|
+
(0, argus_1.complete)(client, { toolName: name, output });
|
|
71
|
+
return output;
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
return wrapped;
|
|
76
|
+
}
|
|
77
|
+
var argus_2 = require("@ory/argus");
|
|
78
|
+
Object.defineProperty(exports, "OryAgentClient", { enumerable: true, get: function () { return argus_2.OryAgentClient; } });
|
|
79
|
+
Object.defineProperty(exports, "OryDenialError", { enumerable: true, get: function () { return argus_2.OryDenialError; } });
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ory/vercel-ai",
|
|
3
|
+
"version": "0.10.0",
|
|
4
|
+
"description": "Ory Agent Security for the Vercel AI SDK — per-tool authorization, tracing, and identity propagation by wrapping tool execute(). Built on @ory/argus.",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"!dist/**/*.tsbuildinfo"
|
|
17
|
+
],
|
|
18
|
+
"keywords": [
|
|
19
|
+
"ory",
|
|
20
|
+
"vercel",
|
|
21
|
+
"ai-sdk",
|
|
22
|
+
"authorization",
|
|
23
|
+
"agent",
|
|
24
|
+
"ai",
|
|
25
|
+
"permissions"
|
|
26
|
+
],
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@ory/argus": "0.10.0"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"typescript": "^6.0.2",
|
|
32
|
+
"vitest": "4.1.4"
|
|
33
|
+
},
|
|
34
|
+
"scripts": {
|
|
35
|
+
"build": "tsc",
|
|
36
|
+
"clean": "rm -rf dist *.tsbuildinfo",
|
|
37
|
+
"test": "vitest run",
|
|
38
|
+
"test:watch": "vitest",
|
|
39
|
+
"typecheck": "tsc --noEmit"
|
|
40
|
+
}
|
|
41
|
+
}
|