@ory/claude-agent-sdk 0.14.0 → 1.0.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 +8 -7
- package/dist/index.d.ts +2 -2
- package/dist/index.js +98 -62
- package/dist/types.d.ts +8 -0
- package/package.json +8 -4
package/README.md
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Ory Agent Security for the [Anthropic Claude Agent SDK](https://docs.claude.com/en/docs/claude-code/sdk).
|
|
4
4
|
|
|
5
|
-
Authorizes every tool call against Ory Permissions,
|
|
6
|
-
user → agent → sub-agent identity — built on [`@ory/argus`](
|
|
5
|
+
Authorizes every tool call against Ory Permissions, records invocation activity, and propagates
|
|
6
|
+
user → agent → sub-agent identity — built on [`@ory/argus`](https://www.npmjs.com/package/@ory/argus).
|
|
7
7
|
|
|
8
8
|
```bash
|
|
9
9
|
npm install @ory/claude-agent-sdk
|
|
@@ -15,7 +15,7 @@ Two interchangeable wiring points:
|
|
|
15
15
|
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
16
16
|
import { createOryHooks, oryCanUseTool } from "@ory/claude-agent-sdk";
|
|
17
17
|
|
|
18
|
-
// (a) hooks: SessionStart (auth
|
|
18
|
+
// (a) hooks: SessionStart (root auth) + SubagentStart (child auth) + tool authorization/activity
|
|
19
19
|
query({ prompt, options: { hooks: createOryHooks() } });
|
|
20
20
|
|
|
21
21
|
// (b) or the permission callback
|
|
@@ -24,10 +24,11 @@ query({ prompt, options: { canUseTool: oryCanUseTool() } });
|
|
|
24
24
|
|
|
25
25
|
`createOryHooks` runs the Ory session gates on `SessionStart`, authorizes on `PreToolUse`
|
|
26
26
|
(a deny returns `permissionDecision: "deny"`, blocking the tool), records `tool.complete` on
|
|
27
|
-
`PostToolUse`, and
|
|
28
|
-
|
|
29
|
-
mode (
|
|
27
|
+
`PostToolUse`, and enrolls each typed child from the authoritative `SubagentStart` event using
|
|
28
|
+
its `session_id`, `agent_type`, and `agent_id`. Later hooks executing inside that child reload
|
|
29
|
+
its scoped credential before permission and event requests. In **observe** mode (default) denies
|
|
30
|
+
are logged but tools run; in **enforce** mode they're blocked.
|
|
30
31
|
|
|
31
32
|
This package depends only on `@ory/argus` — the hook and permission types are defined locally
|
|
32
33
|
(no SDK dependency). Credentials come from the shared
|
|
33
|
-
|
|
34
|
+
the operating system credential store; plaintext configuration contains no runtime secrets.
|
package/dist/index.d.ts
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* Two interchangeable wiring points, both built on the shared `@ory/argus` adapters:
|
|
5
5
|
*
|
|
6
6
|
* - {@link createOryHooks} — a `hooks` config with `SessionStart` (auth gates),
|
|
7
|
-
* `
|
|
8
|
-
*
|
|
7
|
+
* `SubagentStart` (child identity enrollment), `PreToolUse` (authorize;
|
|
8
|
+
* `permissionDecision: "deny"` blocks), and `PostToolUse` (`tool.complete`).
|
|
9
9
|
* - {@link oryCanUseTool} — a `canUseTool` callback returning allow/deny, for callers who
|
|
10
10
|
* prefer the single permission-callback surface.
|
|
11
11
|
*
|
package/dist/index.js
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* Two interchangeable wiring points, both built on the shared `@ory/argus` adapters:
|
|
6
6
|
*
|
|
7
7
|
* - {@link createOryHooks} — a `hooks` config with `SessionStart` (auth gates),
|
|
8
|
-
* `
|
|
9
|
-
*
|
|
8
|
+
* `SubagentStart` (child identity enrollment), `PreToolUse` (authorize;
|
|
9
|
+
* `permissionDecision: "deny"` blocks), and `PostToolUse` (`tool.complete`).
|
|
10
10
|
* - {@link oryCanUseTool} — a `canUseTool` callback returning allow/deny, for callers who
|
|
11
11
|
* prefer the single permission-callback surface.
|
|
12
12
|
*
|
|
@@ -18,91 +18,124 @@ exports.createOryHooks = createOryHooks;
|
|
|
18
18
|
exports.oryCanUseTool = oryCanUseTool;
|
|
19
19
|
const argus_1 = require("@ory/argus");
|
|
20
20
|
const HARNESS = "claude-agent-sdk";
|
|
21
|
-
/**
|
|
22
|
-
* Extract the sub-agent type from a `Task`/`Agent` PreToolUse payload.
|
|
23
|
-
*
|
|
24
|
-
* In the real Claude PreToolUse contract `subagent_type` is a field of the
|
|
25
|
-
* Task tool's `tool_input`, so that is the primary location. A top-level
|
|
26
|
-
* `subagent_type` is kept as a harmless fallback for older payload shapes.
|
|
27
|
-
*/
|
|
28
|
-
function resolveSubAgentType(input) {
|
|
29
|
-
const toolInput = input.tool_input;
|
|
30
|
-
if (typeof toolInput === "object" &&
|
|
31
|
-
toolInput !== null &&
|
|
32
|
-
typeof toolInput.subagent_type === "string") {
|
|
33
|
-
return toolInput.subagent_type;
|
|
34
|
-
}
|
|
35
|
-
return typeof input.subagent_type === "string" ? input.subagent_type : undefined;
|
|
36
|
-
}
|
|
37
|
-
function lazySession(client, projectUrl) {
|
|
38
|
-
let sessionPromise;
|
|
39
|
-
// Cache the in-flight promise (not a boolean) so concurrent first calls all
|
|
40
|
-
// await the same session start instead of racing past a still-running gate.
|
|
41
|
-
// Fail-open: a rejection is logged, never rethrown, and clears the cache so
|
|
42
|
-
// a later call can retry.
|
|
43
|
-
return () => {
|
|
44
|
-
sessionPromise ??= (0, argus_1.sessionStart)(client, { harness: HARNESS, projectUrl }).then(() => undefined, (err) => {
|
|
45
|
-
sessionPromise = undefined;
|
|
46
|
-
client.logger.warn("session_start.failed", {
|
|
47
|
-
message: err instanceof Error ? err.message : String(err),
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
return sessionPromise;
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
21
|
/**
|
|
54
22
|
* Build a `hooks` config object for the Claude Agent SDK's `options.hooks`.
|
|
55
23
|
*/
|
|
56
24
|
function createOryHooks(options = {}) {
|
|
57
25
|
const client = options.client ?? argus_1.OryAgentClient.fromEnv(HARNESS);
|
|
58
26
|
const canBlock = options.canBlock ?? true;
|
|
59
|
-
const ensureSession =
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
27
|
+
const ensureSession = (0, argus_1.createSessionStarter)(client, {
|
|
28
|
+
harness: HARNESS,
|
|
29
|
+
projectUrl: options.projectUrl,
|
|
30
|
+
});
|
|
31
|
+
const sessionStartHook = async (raw) => {
|
|
32
|
+
const sessionId = typeof raw.session_id === "string" ? raw.session_id : undefined;
|
|
33
|
+
return (0, argus_1.withHookContext)(client, { sessionId }, async () => {
|
|
34
|
+
await ensureSession();
|
|
35
|
+
return {};
|
|
36
|
+
});
|
|
63
37
|
};
|
|
64
38
|
const preToolUse = async (raw) => {
|
|
65
|
-
await ensureSession();
|
|
66
39
|
const input = raw;
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const
|
|
70
|
-
|
|
40
|
+
return (0, argus_1.withHookContext)(client, { sessionId: input.session_id }, async () => {
|
|
41
|
+
await ensureSession();
|
|
42
|
+
const toolName = input.tool_name;
|
|
43
|
+
const active = await activateSubagent(client, input);
|
|
44
|
+
const result = await (0, argus_1.withHookContext)(client, { runtimeCredential: active.runtimeCredential }, () => (0, argus_1.gate)(client, {
|
|
45
|
+
harness: HARNESS,
|
|
46
|
+
toolName,
|
|
47
|
+
toolArgs: input.tool_input,
|
|
48
|
+
canBlock,
|
|
49
|
+
principals: active.principals,
|
|
50
|
+
}));
|
|
51
|
+
if (result.kind === "deny" && result.blocked) {
|
|
52
|
+
return {
|
|
53
|
+
hookSpecificOutput: {
|
|
54
|
+
hookEventName: "PreToolUse",
|
|
55
|
+
permissionDecision: "deny",
|
|
56
|
+
permissionDecisionReason: result.denialMessage ?? "Ory: permission denied",
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
return {};
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
const subagentStart = async (raw, _toolUseId, context) => {
|
|
64
|
+
const input = raw;
|
|
65
|
+
return (0, argus_1.withHookContext)(client, { sessionId: input.session_id }, async () => {
|
|
66
|
+
await ensureSession();
|
|
67
|
+
const subAgentType = input.agent_type ?? input.subagent_type;
|
|
68
|
+
if (subAgentType && input.agent_id) {
|
|
71
69
|
await (0, argus_1.registerSubagent)(client, {
|
|
72
70
|
harness: HARNESS,
|
|
73
71
|
subAgentType,
|
|
74
72
|
projectUrl: options.projectUrl,
|
|
73
|
+
sessionId: input.session_id,
|
|
74
|
+
perSpawnId: input.agent_id,
|
|
75
|
+
signal: context.signal,
|
|
75
76
|
}).catch(() => undefined);
|
|
76
77
|
}
|
|
77
|
-
|
|
78
|
-
const result = await (0, argus_1.gate)(client, {
|
|
79
|
-
harness: HARNESS,
|
|
80
|
-
toolName,
|
|
81
|
-
toolArgs: input.tool_input,
|
|
82
|
-
canBlock,
|
|
78
|
+
return {};
|
|
83
79
|
});
|
|
84
|
-
if (result.kind === "deny" && result.blocked) {
|
|
85
|
-
return {
|
|
86
|
-
hookSpecificOutput: {
|
|
87
|
-
hookEventName: "PreToolUse",
|
|
88
|
-
permissionDecision: "deny",
|
|
89
|
-
permissionDecisionReason: result.denialMessage ?? "Ory: permission denied",
|
|
90
|
-
},
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
return {};
|
|
94
80
|
};
|
|
95
81
|
const postToolUse = async (raw) => {
|
|
96
82
|
const input = raw;
|
|
97
|
-
(0, argus_1.
|
|
98
|
-
|
|
83
|
+
return (0, argus_1.withHookContext)(client, { sessionId: input.session_id }, async () => {
|
|
84
|
+
const active = await activateSubagent(client, input);
|
|
85
|
+
return (0, argus_1.withHookContext)(client, { runtimeCredential: active.runtimeCredential }, () => {
|
|
86
|
+
(0, argus_1.complete)(client, { toolName: input.tool_name, output: input.tool_response });
|
|
87
|
+
return {};
|
|
88
|
+
});
|
|
89
|
+
});
|
|
99
90
|
};
|
|
100
91
|
return {
|
|
101
92
|
SessionStart: [{ hooks: [sessionStartHook] }],
|
|
93
|
+
SubagentStart: [{ hooks: [subagentStart] }],
|
|
102
94
|
PreToolUse: [{ hooks: [preToolUse] }],
|
|
103
95
|
PostToolUse: [{ hooks: [postToolUse] }],
|
|
104
96
|
};
|
|
105
97
|
}
|
|
98
|
+
async function activateSubagent(client, input) {
|
|
99
|
+
const type = input.agent_type ?? input.subagent_type;
|
|
100
|
+
if (!input.agent_id || !type || !(0, argus_1.isSecurityConnected)()) {
|
|
101
|
+
return { principals: actingSubagentPrincipals(input) };
|
|
102
|
+
}
|
|
103
|
+
const identity = await (0, argus_1.ensureSubAgentIdentity)(client, {
|
|
104
|
+
harness: HARNESS,
|
|
105
|
+
sessionId: input.session_id,
|
|
106
|
+
subAgentType: type,
|
|
107
|
+
perSpawnId: input.agent_id,
|
|
108
|
+
allowEnrollment: false,
|
|
109
|
+
emitActivity: false,
|
|
110
|
+
}).catch(() => undefined);
|
|
111
|
+
if (!identity?.runtimeCredential)
|
|
112
|
+
return { principals: actingSubagentPrincipals(input) };
|
|
113
|
+
return {
|
|
114
|
+
runtimeCredential: identity.runtimeCredential,
|
|
115
|
+
principals: {
|
|
116
|
+
subAgentClientId: identity.subject,
|
|
117
|
+
subAgentType: type,
|
|
118
|
+
perSpawnId: input.agent_id,
|
|
119
|
+
sessionId: input.session_id,
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
function actingSubagentPrincipals(input) {
|
|
124
|
+
const type = input.agent_type;
|
|
125
|
+
if (!input.agent_id || !type)
|
|
126
|
+
return undefined;
|
|
127
|
+
const credential = input.session_id
|
|
128
|
+
? (0, argus_1.loadSubAgentDynamicCredentials)(HARNESS, input.session_id, type)
|
|
129
|
+
: (0, argus_1.loadSubAgentDynamicCredentials)(HARNESS, type);
|
|
130
|
+
if (!credential)
|
|
131
|
+
return undefined;
|
|
132
|
+
return {
|
|
133
|
+
subAgentClientId: credential.clientId,
|
|
134
|
+
subAgentType: type,
|
|
135
|
+
perSpawnId: input.agent_id,
|
|
136
|
+
sessionId: input.session_id,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
106
139
|
/**
|
|
107
140
|
* Build a `canUseTool` callback gated through Ory. Returns `{ behavior: "deny" }` when the
|
|
108
141
|
* tool is hard-denied, otherwise `{ behavior: "allow" }`.
|
|
@@ -110,7 +143,10 @@ function createOryHooks(options = {}) {
|
|
|
110
143
|
function oryCanUseTool(options = {}) {
|
|
111
144
|
const client = options.client ?? argus_1.OryAgentClient.fromEnv(HARNESS);
|
|
112
145
|
const canBlock = options.canBlock ?? true;
|
|
113
|
-
const ensureSession =
|
|
146
|
+
const ensureSession = (0, argus_1.createSessionStarter)(client, {
|
|
147
|
+
harness: HARNESS,
|
|
148
|
+
projectUrl: options.projectUrl,
|
|
149
|
+
});
|
|
114
150
|
return async (toolName, input) => {
|
|
115
151
|
await ensureSession();
|
|
116
152
|
const result = await (0, argus_1.gate)(client, {
|
package/dist/types.d.ts
CHANGED
|
@@ -20,6 +20,10 @@ export interface PreToolUseHookInput {
|
|
|
20
20
|
*/
|
|
21
21
|
subagent_type?: string;
|
|
22
22
|
session_id?: string;
|
|
23
|
+
/** Present on hooks executing inside a spawned sub-agent. */
|
|
24
|
+
agent_id?: string;
|
|
25
|
+
/** Type of the sub-agent currently executing this hook. */
|
|
26
|
+
agent_type?: string;
|
|
23
27
|
[key: string]: unknown;
|
|
24
28
|
}
|
|
25
29
|
export interface PostToolUseHookInput {
|
|
@@ -27,6 +31,10 @@ export interface PostToolUseHookInput {
|
|
|
27
31
|
tool_name: string;
|
|
28
32
|
tool_input?: unknown;
|
|
29
33
|
tool_response?: unknown;
|
|
34
|
+
session_id?: string;
|
|
35
|
+
agent_id?: string;
|
|
36
|
+
agent_type?: string;
|
|
37
|
+
subagent_type?: string;
|
|
30
38
|
[key: string]: unknown;
|
|
31
39
|
}
|
|
32
40
|
export interface HookJSONOutput {
|
package/package.json
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ory/claude-agent-sdk",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Ory Agent Security for the Anthropic Claude Agent SDK — per-tool authorization,
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Ory Agent Security for the Anthropic Claude Agent SDK — per-tool authorization, activity auditing, and identity propagation via PreToolUse hooks and canUseTool. Built on @ory/argus.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"publishConfig": {
|
|
7
7
|
"access": "public",
|
|
8
8
|
"registry": "https://registry.npmjs.org/",
|
|
9
9
|
"provenance": true
|
|
10
10
|
},
|
|
11
|
+
"reova": {
|
|
12
|
+
"enabled": true,
|
|
13
|
+
"endpoint": "https://telemetry.reo.dev/data"
|
|
14
|
+
},
|
|
11
15
|
"main": "dist/index.js",
|
|
12
16
|
"types": "dist/index.d.ts",
|
|
13
17
|
"exports": {
|
|
@@ -30,8 +34,8 @@
|
|
|
30
34
|
"ai"
|
|
31
35
|
],
|
|
32
36
|
"dependencies": {
|
|
33
|
-
"
|
|
34
|
-
"@ory/argus": "0.
|
|
37
|
+
"reova": "^0.7.0",
|
|
38
|
+
"@ory/argus": "1.0.0"
|
|
35
39
|
},
|
|
36
40
|
"devDependencies": {
|
|
37
41
|
"typescript": "^6.0.2",
|