@ory/mastra 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 +42 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.js +116 -0
- package/dist/types.d.ts +34 -0
- package/dist/types.js +7 -0
- package/package.json +40 -0
package/README.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# @ory/mastra
|
|
2
|
+
|
|
3
|
+
Ory Agent Security for [Mastra](https://mastra.ai).
|
|
4
|
+
|
|
5
|
+
Authorizes every tool call against Ory Permissions, traces invocations, and propagates
|
|
6
|
+
user → agent → sub-agent identity — built on [`@ory/argus`](../core).
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
npm install @ory/mastra
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Recommended — agent tool-call hooks (gates every tool, including agent-/workflow-as-tools):
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
import { Agent } from "@mastra/core/agent";
|
|
16
|
+
import { oryAgentHooks } from "@ory/mastra";
|
|
17
|
+
|
|
18
|
+
const agent = new Agent({
|
|
19
|
+
name: "assistant",
|
|
20
|
+
instructions: "…",
|
|
21
|
+
model,
|
|
22
|
+
tools: { search, sendEmail },
|
|
23
|
+
hooks: oryAgentHooks(),
|
|
24
|
+
});
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`beforeToolCall` authorizes each call; in **enforce** mode (`ORY_PERMISSION_MODE=enforce`) a
|
|
28
|
+
denied tool is skipped via `{ proceed: false, output }` and the model receives the denial. In
|
|
29
|
+
**observe** mode (default) the tool runs and a `permission.observe_deny` span is recorded.
|
|
30
|
+
`afterToolCall` records `tool.complete`.
|
|
31
|
+
|
|
32
|
+
Alternatively, wrap tool `execute` directly (deny → throw):
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
import { withOry } from "@ory/mastra";
|
|
36
|
+
const tools = withOry({ search, sendEmail });
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
This package depends only on `@ory/argus` — not `@mastra/core`; tools and hook contexts are
|
|
40
|
+
duck-typed so any Mastra v1 version works. Credentials come from the shared
|
|
41
|
+
`~/.config/ory-agent-plugins/config.json`. See
|
|
42
|
+
[docs/sdk-integrations.md](../../docs/sdk-integrations.md).
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ory Agent Security for Mastra.
|
|
3
|
+
*
|
|
4
|
+
* Two interchangeable wiring points, both on the shared `@ory/argus` adapters:
|
|
5
|
+
*
|
|
6
|
+
* - {@link oryAgentHooks} (recommended) — agent tool-call hooks for `new Agent({ hooks })`.
|
|
7
|
+
* `beforeToolCall` authorizes every tool (incl. agent-/workflow-as-tools); a deny returns
|
|
8
|
+
* `{ proceed: false, output }` so Mastra skips the tool. `afterToolCall` traces completion.
|
|
9
|
+
* - {@link withOry} — wraps each tool's `execute` directly (deny → throw), for callers who
|
|
10
|
+
* prefer per-tool wrapping or are on a Mastra version without the hooks API.
|
|
11
|
+
*
|
|
12
|
+
* The package has no `@mastra/core` dependency — tools/hook contexts are duck-typed.
|
|
13
|
+
*/
|
|
14
|
+
import { OryAgentClient } from "@ory/argus";
|
|
15
|
+
import type { OryMastraHooks, ToolSet } from "./types.js";
|
|
16
|
+
export interface OryMastraOptions {
|
|
17
|
+
/** Client to use. Defaults to `OryAgentClient.fromEnv("mastra")` (reads the shared config). */
|
|
18
|
+
client?: OryAgentClient;
|
|
19
|
+
/** Override the Ory project URL for the session gates. */
|
|
20
|
+
projectUrl?: string;
|
|
21
|
+
/** Whether a denied tool is hard-blocked. Default true. */
|
|
22
|
+
canBlock?: boolean;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Build the `{ beforeToolCall, afterToolCall }` hooks object for `new Agent({ hooks })`.
|
|
26
|
+
*/
|
|
27
|
+
export declare function oryAgentHooks(options?: OryMastraOptions): OryMastraHooks;
|
|
28
|
+
/**
|
|
29
|
+
* Wrap each tool's `execute` with the Ory gate. On a hard deny it throws (Mastra surfaces
|
|
30
|
+
* the error to the model); otherwise the tool runs and `tool.complete` is recorded. Tools
|
|
31
|
+
* without an `execute` are passed through untouched.
|
|
32
|
+
*/
|
|
33
|
+
export declare function withOry(tools: ToolSet, options?: OryMastraOptions): ToolSet;
|
|
34
|
+
export { OryAgentClient, OryDenialError } from "@ory/argus";
|
|
35
|
+
export type { OryMastraHooks, ToolSet, MastraTool, ToolHookBeforeContext, ToolHookAfterContext } from "./types.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Ory Agent Security for Mastra.
|
|
4
|
+
*
|
|
5
|
+
* Two interchangeable wiring points, both on the shared `@ory/argus` adapters:
|
|
6
|
+
*
|
|
7
|
+
* - {@link oryAgentHooks} (recommended) — agent tool-call hooks for `new Agent({ hooks })`.
|
|
8
|
+
* `beforeToolCall` authorizes every tool (incl. agent-/workflow-as-tools); a deny returns
|
|
9
|
+
* `{ proceed: false, output }` so Mastra skips the tool. `afterToolCall` traces completion.
|
|
10
|
+
* - {@link withOry} — wraps each tool's `execute` directly (deny → throw), for callers who
|
|
11
|
+
* prefer per-tool wrapping or are on a Mastra version without the hooks API.
|
|
12
|
+
*
|
|
13
|
+
* The package has no `@mastra/core` dependency — tools/hook contexts are duck-typed.
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.OryDenialError = exports.OryAgentClient = void 0;
|
|
17
|
+
exports.oryAgentHooks = oryAgentHooks;
|
|
18
|
+
exports.withOry = withOry;
|
|
19
|
+
const argus_1 = require("@ory/argus");
|
|
20
|
+
const HARNESS = "mastra";
|
|
21
|
+
function lazySession(client, projectUrl) {
|
|
22
|
+
let started = false;
|
|
23
|
+
return async () => {
|
|
24
|
+
if (started)
|
|
25
|
+
return;
|
|
26
|
+
started = true;
|
|
27
|
+
try {
|
|
28
|
+
await (0, argus_1.sessionStart)(client, { harness: HARNESS, projectUrl });
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
client.logger.warn("session_start.failed", {
|
|
32
|
+
message: err instanceof Error ? err.message : String(err),
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Build the `{ beforeToolCall, afterToolCall }` hooks object for `new Agent({ hooks })`.
|
|
39
|
+
*/
|
|
40
|
+
function oryAgentHooks(options = {}) {
|
|
41
|
+
const client = options.client ?? argus_1.OryAgentClient.fromEnv(HARNESS);
|
|
42
|
+
const canBlock = options.canBlock ?? true;
|
|
43
|
+
const ensureSession = lazySession(client, options.projectUrl);
|
|
44
|
+
return {
|
|
45
|
+
beforeToolCall: async (ctx) => {
|
|
46
|
+
await ensureSession();
|
|
47
|
+
const toolName = ctx.toolName;
|
|
48
|
+
// Sub-agent delegation: agent-/workflow-as-tools surface as `agent-*` / `workflow-*`.
|
|
49
|
+
if (toolName.startsWith("agent-") || toolName.startsWith("workflow-")) {
|
|
50
|
+
await (0, argus_1.registerSubagent)(client, {
|
|
51
|
+
harness: HARNESS,
|
|
52
|
+
subAgentType: toolName,
|
|
53
|
+
projectUrl: options.projectUrl,
|
|
54
|
+
}).catch(() => undefined);
|
|
55
|
+
}
|
|
56
|
+
const result = await (0, argus_1.gate)(client, {
|
|
57
|
+
harness: HARNESS,
|
|
58
|
+
toolName,
|
|
59
|
+
toolArgs: ctx.input,
|
|
60
|
+
canBlock,
|
|
61
|
+
});
|
|
62
|
+
if (result.kind === "deny" && result.blocked) {
|
|
63
|
+
return { proceed: false, output: result.denialMessage ?? "Ory: permission denied" };
|
|
64
|
+
}
|
|
65
|
+
return undefined; // allow / observe / fail-open / interactive → proceed
|
|
66
|
+
},
|
|
67
|
+
afterToolCall: async (ctx) => {
|
|
68
|
+
(0, argus_1.complete)(client, { toolName: ctx.toolName, output: ctx.output });
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Wrap each tool's `execute` with the Ory gate. On a hard deny it throws (Mastra surfaces
|
|
74
|
+
* the error to the model); otherwise the tool runs and `tool.complete` is recorded. Tools
|
|
75
|
+
* without an `execute` are passed through untouched.
|
|
76
|
+
*/
|
|
77
|
+
function withOry(tools, options = {}) {
|
|
78
|
+
const client = options.client ?? argus_1.OryAgentClient.fromEnv(HARNESS);
|
|
79
|
+
const canBlock = options.canBlock ?? true;
|
|
80
|
+
const ensureSession = lazySession(client, options.projectUrl);
|
|
81
|
+
const wrapped = {};
|
|
82
|
+
for (const [key, tool] of Object.entries(tools)) {
|
|
83
|
+
if (!tool || typeof tool.execute !== "function") {
|
|
84
|
+
wrapped[key] = tool;
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
const toolName = tool.id ?? key;
|
|
88
|
+
const originalExecute = tool.execute.bind(tool);
|
|
89
|
+
wrapped[key] = {
|
|
90
|
+
...tool,
|
|
91
|
+
execute: async (inputData, context) => {
|
|
92
|
+
await ensureSession();
|
|
93
|
+
const result = await (0, argus_1.gate)(client, {
|
|
94
|
+
harness: HARNESS,
|
|
95
|
+
toolName,
|
|
96
|
+
toolArgs: inputData,
|
|
97
|
+
canBlock,
|
|
98
|
+
});
|
|
99
|
+
if (result.blocked) {
|
|
100
|
+
throw new argus_1.OryDenialError({
|
|
101
|
+
tool: toolName,
|
|
102
|
+
subjectId: result.subject,
|
|
103
|
+
namespace: result.namespace,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
const output = await originalExecute(inputData, context);
|
|
107
|
+
(0, argus_1.complete)(client, { toolName, output });
|
|
108
|
+
return output;
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
return wrapped;
|
|
113
|
+
}
|
|
114
|
+
var argus_2 = require("@ory/argus");
|
|
115
|
+
Object.defineProperty(exports, "OryAgentClient", { enumerable: true, get: function () { return argus_2.OryAgentClient; } });
|
|
116
|
+
Object.defineProperty(exports, "OryDenialError", { enumerable: true, get: function () { return argus_2.OryDenialError; } });
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal Mastra shapes. Defined here (not imported from `@mastra/core`) so the package has
|
|
3
|
+
* no SDK dependency — same policy as the other integrations. These mirror Mastra v1's
|
|
4
|
+
* agent tool-call hooks and tool `execute` contract closely enough to wire the gate.
|
|
5
|
+
*/
|
|
6
|
+
/** Context passed to `beforeToolCall`. */
|
|
7
|
+
export interface ToolHookBeforeContext {
|
|
8
|
+
toolName: string;
|
|
9
|
+
input?: unknown;
|
|
10
|
+
context?: unknown;
|
|
11
|
+
metadata?: unknown;
|
|
12
|
+
}
|
|
13
|
+
/** Context passed to `afterToolCall`. */
|
|
14
|
+
export interface ToolHookAfterContext extends ToolHookBeforeContext {
|
|
15
|
+
output?: unknown;
|
|
16
|
+
error?: unknown;
|
|
17
|
+
}
|
|
18
|
+
/** `beforeToolCall` returns this to skip the tool; the `output` becomes the tool result. */
|
|
19
|
+
export interface ToolBeforeHookResult {
|
|
20
|
+
proceed: false;
|
|
21
|
+
output: unknown;
|
|
22
|
+
}
|
|
23
|
+
/** The object passed to `new Agent({ hooks })`. */
|
|
24
|
+
export interface OryMastraHooks {
|
|
25
|
+
beforeToolCall: (ctx: ToolHookBeforeContext) => Promise<void | ToolBeforeHookResult> | void | ToolBeforeHookResult;
|
|
26
|
+
afterToolCall: (ctx: ToolHookAfterContext) => Promise<void> | void;
|
|
27
|
+
}
|
|
28
|
+
/** A Mastra tool — duck-typed. `execute` takes `(inputData, context)` in v1. */
|
|
29
|
+
export interface MastraTool {
|
|
30
|
+
id?: string;
|
|
31
|
+
execute?: (inputData: unknown, context?: unknown) => unknown;
|
|
32
|
+
[key: string]: unknown;
|
|
33
|
+
}
|
|
34
|
+
export type ToolSet = Record<string, MastraTool>;
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Minimal Mastra shapes. Defined here (not imported from `@mastra/core`) so the package has
|
|
4
|
+
* no SDK dependency — same policy as the other integrations. These mirror Mastra v1's
|
|
5
|
+
* agent tool-call hooks and tool `execute` contract closely enough to wire the gate.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ory/mastra",
|
|
3
|
+
"version": "0.10.0",
|
|
4
|
+
"description": "Ory Agent Security for Mastra — per-tool authorization, tracing, and identity propagation via agent tool-call hooks (or 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
|
+
"mastra",
|
|
21
|
+
"authorization",
|
|
22
|
+
"agent",
|
|
23
|
+
"ai",
|
|
24
|
+
"permissions"
|
|
25
|
+
],
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@ory/argus": "0.10.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"typescript": "^6.0.2",
|
|
31
|
+
"vitest": "4.1.4"
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "tsc",
|
|
35
|
+
"clean": "rm -rf dist *.tsbuildinfo",
|
|
36
|
+
"test": "vitest run",
|
|
37
|
+
"test:watch": "vitest",
|
|
38
|
+
"typecheck": "tsc --noEmit"
|
|
39
|
+
}
|
|
40
|
+
}
|