@ory/argus 0.9.1 → 0.11.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/assets/skills/auth-setup/SKILL.md +3 -1
- package/assets/skills/login-flow/SKILL.md +118 -4
- package/dist/adapters.d.ts +104 -0
- package/dist/adapters.js +216 -0
- package/dist/agent-auth.js +33 -0
- package/dist/config.js +35 -3
- package/dist/contract-suite.d.ts +87 -0
- package/dist/contract-suite.js +239 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +9 -1
- package/dist/lifecycle.js +130 -0
- package/dist/local/configs.d.ts +2 -2
- package/dist/local/configs.js +10 -1
- package/dist/local/manager.d.ts +6 -4
- package/dist/local/manager.js +91 -16
- package/dist/permissions.d.ts +36 -8
- package/dist/permissions.js +62 -7
- package/dist/subject.d.ts +13 -1
- package/dist/subject.js +29 -2
- package/dist/testing.d.ts +214 -0
- package/dist/testing.js +372 -0
- package/dist/tool-catalog.d.ts +7 -0
- package/dist/tool-catalog.js +73 -0
- package/dist/user-login.js +7 -2
- package/package.json +1 -1
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared contract suite for harness plugin tests.
|
|
3
|
+
*
|
|
4
|
+
* Every harness plugin translates the same core decisions (session gates,
|
|
5
|
+
* `gateToolCall`, audit-only) onto its own native signals (exit codes,
|
|
6
|
+
* `{ decision: "block" }`, `{ block: true }`, thrown errors, …). The
|
|
7
|
+
* *decision semantics* are core-owned and tested once in `packages/core`;
|
|
8
|
+
* what varies per harness is only the translation. This suite re-runs the
|
|
9
|
+
* canonical scenario tables through a harness's real entry point via a
|
|
10
|
+
* small adapter, so a plugin cannot ship with a divergent understanding of
|
|
11
|
+
* observe / enforce / fail-open / audit-only — without each plugin
|
|
12
|
+
* re-transcribing the tables.
|
|
13
|
+
*
|
|
14
|
+
* A harness test file calls:
|
|
15
|
+
*
|
|
16
|
+
* ```ts
|
|
17
|
+
* runHarnessContractSuite({
|
|
18
|
+
* harness: "claude-code",
|
|
19
|
+
* tool: "Bash",
|
|
20
|
+
* sessionCanBlock: true,
|
|
21
|
+
* sessionStart: async ({ client, gates }) => {
|
|
22
|
+
* const out = await handleHookEvent(sessionInput(), client, gates);
|
|
23
|
+
* return { blocked: out.decision === "block", reason: out.reason };
|
|
24
|
+
* },
|
|
25
|
+
* toolBefore: async ({ client, gates }, tool) => { ... },
|
|
26
|
+
* });
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* and then adds only its genuinely harness-specific tests: export shape,
|
|
30
|
+
* event-name dispatch, block-signal details, input parsing (MCP names,
|
|
31
|
+
* `subagent_type`), unique span enrichment, and the install CLI.
|
|
32
|
+
*/
|
|
33
|
+
import { OryAgentClient } from "./client.js";
|
|
34
|
+
import type { ensureUserAuthenticated } from "./user-login.js";
|
|
35
|
+
import type { ensureAgentIdentity, ensureSubAgentIdentity } from "./agent-auth.js";
|
|
36
|
+
/** Normalized outcome of driving one lifecycle phase through the plugin. */
|
|
37
|
+
export interface ContractOutcome {
|
|
38
|
+
/** True when the plugin emitted its native block signal. */
|
|
39
|
+
blocked: boolean;
|
|
40
|
+
/** The human-readable reason carried by the block signal, if any. */
|
|
41
|
+
reason?: string;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Injectable auth gates handed to the plugin under test. The suite swaps
|
|
45
|
+
* their behavior per scenario; the adapter forwards them into the plugin's
|
|
46
|
+
* `deps` parameter (every plugin exposes `userLogin` / `agentGate` /
|
|
47
|
+
* `subAgentGate` injection points per AGENTS.md Step 5).
|
|
48
|
+
*/
|
|
49
|
+
export interface ContractGates {
|
|
50
|
+
userLogin: typeof ensureUserAuthenticated;
|
|
51
|
+
agentGate: typeof ensureAgentIdentity;
|
|
52
|
+
subAgentGate: typeof ensureSubAgentIdentity;
|
|
53
|
+
}
|
|
54
|
+
export interface ContractContext {
|
|
55
|
+
client: OryAgentClient;
|
|
56
|
+
gates: ContractGates;
|
|
57
|
+
}
|
|
58
|
+
export interface HarnessContractAdapter {
|
|
59
|
+
/** Harness name — used for the describe label and the client. */
|
|
60
|
+
harness: string;
|
|
61
|
+
/** A real, non-interactive tool from this harness's catalog. */
|
|
62
|
+
tool: string;
|
|
63
|
+
/**
|
|
64
|
+
* Whether the harness's session-start primitive can carry a hard block
|
|
65
|
+
* (subprocess exit-code harnesses: true; in-process advisory: false).
|
|
66
|
+
*/
|
|
67
|
+
sessionCanBlock: boolean;
|
|
68
|
+
/** Whether the tool gate can hard-block. Defaults to true. */
|
|
69
|
+
toolCanBlock?: boolean;
|
|
70
|
+
/** Set false when the plugin has no legacy OAuth2-token session path. */
|
|
71
|
+
legacyOAuth2?: boolean;
|
|
72
|
+
/** Drive the session-start phase through the plugin's real entry point. */
|
|
73
|
+
sessionStart(ctx: ContractContext): Promise<ContractOutcome>;
|
|
74
|
+
/** Drive the pre-tool gate for `tool` through the plugin's real entry point. */
|
|
75
|
+
toolBefore(ctx: ContractContext, tool: string): Promise<ContractOutcome>;
|
|
76
|
+
/** Drive the post-tool phase (must record `tool.complete`). Optional. */
|
|
77
|
+
toolAfter?(ctx: ContractContext, tool: string): Promise<void>;
|
|
78
|
+
/** Feed an event the plugin does not model; return the raw response. Optional. */
|
|
79
|
+
unknownEvent?(ctx: ContractContext): Promise<unknown>;
|
|
80
|
+
/** Expected raw response for `unknownEvent` (deep-equal). */
|
|
81
|
+
unknownEventOutput?: unknown;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Run the canonical harness contract scenarios through the adapter.
|
|
85
|
+
* Call once per harness test file, then add harness-specific tests.
|
|
86
|
+
*/
|
|
87
|
+
export declare function runHarnessContractSuite(adapter: HarnessContractAdapter): void;
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Shared contract suite for harness plugin tests.
|
|
4
|
+
*
|
|
5
|
+
* Every harness plugin translates the same core decisions (session gates,
|
|
6
|
+
* `gateToolCall`, audit-only) onto its own native signals (exit codes,
|
|
7
|
+
* `{ decision: "block" }`, `{ block: true }`, thrown errors, …). The
|
|
8
|
+
* *decision semantics* are core-owned and tested once in `packages/core`;
|
|
9
|
+
* what varies per harness is only the translation. This suite re-runs the
|
|
10
|
+
* canonical scenario tables through a harness's real entry point via a
|
|
11
|
+
* small adapter, so a plugin cannot ship with a divergent understanding of
|
|
12
|
+
* observe / enforce / fail-open / audit-only — without each plugin
|
|
13
|
+
* re-transcribing the tables.
|
|
14
|
+
*
|
|
15
|
+
* A harness test file calls:
|
|
16
|
+
*
|
|
17
|
+
* ```ts
|
|
18
|
+
* runHarnessContractSuite({
|
|
19
|
+
* harness: "claude-code",
|
|
20
|
+
* tool: "Bash",
|
|
21
|
+
* sessionCanBlock: true,
|
|
22
|
+
* sessionStart: async ({ client, gates }) => {
|
|
23
|
+
* const out = await handleHookEvent(sessionInput(), client, gates);
|
|
24
|
+
* return { blocked: out.decision === "block", reason: out.reason };
|
|
25
|
+
* },
|
|
26
|
+
* toolBefore: async ({ client, gates }, tool) => { ... },
|
|
27
|
+
* });
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* and then adds only its genuinely harness-specific tests: export shape,
|
|
31
|
+
* event-name dispatch, block-signal details, input parsing (MCP names,
|
|
32
|
+
* `subagent_type`), unique span enrichment, and the install CLI.
|
|
33
|
+
*/
|
|
34
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
35
|
+
exports.runHarnessContractSuite = runHarnessContractSuite;
|
|
36
|
+
const vitest_1 = require("vitest");
|
|
37
|
+
const config_js_1 = require("./config.js");
|
|
38
|
+
const testing_js_1 = require("./testing.js");
|
|
39
|
+
const DISABLED_DECISION = {
|
|
40
|
+
proceed: true,
|
|
41
|
+
mode: "disabled",
|
|
42
|
+
reason: "ORY_USER_LOGIN not enabled",
|
|
43
|
+
};
|
|
44
|
+
function makeGates(userDecision = DISABLED_DECISION) {
|
|
45
|
+
return {
|
|
46
|
+
userLogin: vitest_1.vi.fn(async () => userDecision),
|
|
47
|
+
agentGate: vitest_1.vi.fn(async () => ({
|
|
48
|
+
kind: "none",
|
|
49
|
+
reason: "not configured in contract suite",
|
|
50
|
+
warnings: [],
|
|
51
|
+
})),
|
|
52
|
+
subAgentGate: vitest_1.vi.fn(async () => ({
|
|
53
|
+
kind: "none",
|
|
54
|
+
subAgentType: "none",
|
|
55
|
+
reason: "not configured in contract suite",
|
|
56
|
+
warnings: [],
|
|
57
|
+
})),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
const ENV_KEYS = [
|
|
61
|
+
"ORY_PROJECT_URL",
|
|
62
|
+
"ORY_API_KEY",
|
|
63
|
+
"ORY_SESSION_TOKEN",
|
|
64
|
+
"ORY_OAUTH2_TOKEN",
|
|
65
|
+
"ORY_USER_SESSION_TOKEN",
|
|
66
|
+
"ORY_USER_OAUTH2_TOKEN",
|
|
67
|
+
"ORY_USER_LOGIN",
|
|
68
|
+
"ORY_USER_SUBJECT_ID",
|
|
69
|
+
"ORY_USER_SUBJECT_NAMESPACE",
|
|
70
|
+
"ORY_AGENT_SUBJECT_ID",
|
|
71
|
+
"ORY_PERMISSION_MODE",
|
|
72
|
+
"ORY_PERMISSION_NAMESPACE",
|
|
73
|
+
"ORY_INTERACTIVE_TOOLS",
|
|
74
|
+
];
|
|
75
|
+
/**
|
|
76
|
+
* Run the canonical harness contract scenarios through the adapter.
|
|
77
|
+
* Call once per harness test file, then add harness-specific tests.
|
|
78
|
+
*/
|
|
79
|
+
function runHarnessContractSuite(adapter) {
|
|
80
|
+
const toolCanBlock = adapter.toolCanBlock ?? true;
|
|
81
|
+
(0, vitest_1.describe)(`${adapter.harness} — Ory harness contract`, () => {
|
|
82
|
+
let client;
|
|
83
|
+
let restoreConfigDir;
|
|
84
|
+
const savedEnv = {};
|
|
85
|
+
(0, vitest_1.beforeEach)(() => {
|
|
86
|
+
for (const key of ENV_KEYS) {
|
|
87
|
+
savedEnv[key] = process.env[key];
|
|
88
|
+
delete process.env[key];
|
|
89
|
+
}
|
|
90
|
+
restoreConfigDir = (0, testing_js_1.useTempConfigDir)();
|
|
91
|
+
process.env.ORY_PROJECT_URL = "https://test.projects.oryapis.com";
|
|
92
|
+
client = (0, testing_js_1.createMockClient)({ harness: adapter.harness });
|
|
93
|
+
});
|
|
94
|
+
(0, vitest_1.afterEach)(() => {
|
|
95
|
+
restoreConfigDir();
|
|
96
|
+
for (const key of ENV_KEYS) {
|
|
97
|
+
if (savedEnv[key] === undefined)
|
|
98
|
+
delete process.env[key];
|
|
99
|
+
else
|
|
100
|
+
process.env[key] = savedEnv[key];
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
const ctx = (gates = makeGates()) => ({ client, gates });
|
|
104
|
+
// ── Session start never blocks on auth/infra failures ─────────
|
|
105
|
+
(0, vitest_1.describe)("session start is fail-open", () => {
|
|
106
|
+
vitest_1.it.each([
|
|
107
|
+
{ name: "valid session token", env: "session", stub: testing_js_1.stubSessionSuccess },
|
|
108
|
+
{ name: "inactive session", env: "session", stub: testing_js_1.stubSessionInactive },
|
|
109
|
+
{ name: "session network error", env: "session", stub: testing_js_1.stubSessionNetworkError },
|
|
110
|
+
{ name: "MFA required", env: "session", stub: testing_js_1.stubSessionMfaRequired },
|
|
111
|
+
{ name: "valid OAuth2 token", env: "oauth2", stub: testing_js_1.stubOAuth2Success },
|
|
112
|
+
{ name: "inactive OAuth2 token", env: "oauth2", stub: testing_js_1.stubOAuth2Inactive },
|
|
113
|
+
{ name: "unconfigured (no project URL)", env: "none", stub: undefined },
|
|
114
|
+
])("proceeds with $name", async ({ env, stub, name }) => {
|
|
115
|
+
if (name.includes("OAuth2") && adapter.legacyOAuth2 === false)
|
|
116
|
+
return;
|
|
117
|
+
if (env === "session")
|
|
118
|
+
process.env.ORY_SESSION_TOKEN = "token-under-test";
|
|
119
|
+
if (env === "oauth2")
|
|
120
|
+
process.env.ORY_OAUTH2_TOKEN = "token-under-test";
|
|
121
|
+
if (env === "none")
|
|
122
|
+
delete process.env.ORY_PROJECT_URL;
|
|
123
|
+
stub?.(client);
|
|
124
|
+
const outcome = await adapter.sessionStart(ctx());
|
|
125
|
+
(0, vitest_1.expect)(outcome.blocked).toBe(false);
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
// ── User-login gate translation ────────────────────────────────
|
|
129
|
+
(0, vitest_1.describe)("user-login gate", () => {
|
|
130
|
+
const declined = {
|
|
131
|
+
proceed: false,
|
|
132
|
+
mode: "declined",
|
|
133
|
+
reason: "User declined the OAuth2 consent screen",
|
|
134
|
+
};
|
|
135
|
+
(0, vitest_1.it)(adapter.sessionCanBlock
|
|
136
|
+
? "translates a declined login into the native session block"
|
|
137
|
+
: "treats a declined login as advisory (no session block channel)", async () => {
|
|
138
|
+
const gates = makeGates(declined);
|
|
139
|
+
const outcome = await adapter.sessionStart(ctx(gates));
|
|
140
|
+
(0, vitest_1.expect)(outcome.blocked).toBe(adapter.sessionCanBlock);
|
|
141
|
+
if (adapter.sessionCanBlock) {
|
|
142
|
+
(0, vitest_1.expect)(outcome.reason).toBeTruthy();
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
(0, vitest_1.it)("runs the agent gate even when the user gate declines", async () => {
|
|
146
|
+
const gates = makeGates(declined);
|
|
147
|
+
await adapter.sessionStart(ctx(gates));
|
|
148
|
+
(0, vitest_1.expect)(gates.agentGate).toHaveBeenCalledOnce();
|
|
149
|
+
});
|
|
150
|
+
(0, vitest_1.it)("proceeds without legacy verification when the gate handled auth", async () => {
|
|
151
|
+
process.env.ORY_SESSION_TOKEN = "should-not-be-verified";
|
|
152
|
+
const verify = (0, testing_js_1.stubSessionSuccess)(client);
|
|
153
|
+
const gates = makeGates({ proceed: true, mode: "ok", reason: "logged in" });
|
|
154
|
+
const outcome = await adapter.sessionStart(ctx(gates));
|
|
155
|
+
(0, vitest_1.expect)(outcome.blocked).toBe(false);
|
|
156
|
+
(0, vitest_1.expect)(verify).not.toHaveBeenCalled();
|
|
157
|
+
});
|
|
158
|
+
(0, vitest_1.it)("falls through to legacy verification when the gate is disabled", async () => {
|
|
159
|
+
process.env.ORY_SESSION_TOKEN = "legacy-token";
|
|
160
|
+
const verify = (0, testing_js_1.stubSessionSuccess)(client);
|
|
161
|
+
const outcome = await adapter.sessionStart(ctx());
|
|
162
|
+
(0, vitest_1.expect)(outcome.blocked).toBe(false);
|
|
163
|
+
(0, vitest_1.expect)(verify).toHaveBeenCalled();
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
// ── Tool gate translation ──────────────────────────────────────
|
|
167
|
+
(0, vitest_1.describe)("tool gate", () => {
|
|
168
|
+
vitest_1.it.each([
|
|
169
|
+
{ check: "allowed", mode: "enforce", blocked: false, invoke: true, block: false },
|
|
170
|
+
{ check: "denied", mode: "enforce", blocked: toolCanBlock, invoke: false, block: true },
|
|
171
|
+
{ check: "denied", mode: "observe", blocked: false, invoke: true, block: true },
|
|
172
|
+
{ check: "network_error", mode: "enforce", blocked: false, invoke: false, block: false },
|
|
173
|
+
{ check: "rate_limited", mode: "enforce", blocked: false, invoke: false, block: false },
|
|
174
|
+
])("check=$check × $mode → blocked=$blocked", async ({ check, mode, blocked, invoke, block }) => {
|
|
175
|
+
const stubs = {
|
|
176
|
+
allowed: testing_js_1.stubPermissionAllowed,
|
|
177
|
+
denied: testing_js_1.stubPermissionDenied,
|
|
178
|
+
network_error: testing_js_1.stubPermissionNetworkError,
|
|
179
|
+
rate_limited: testing_js_1.stubPermissionRateLimited,
|
|
180
|
+
};
|
|
181
|
+
stubs[check](client);
|
|
182
|
+
process.env.ORY_PERMISSION_MODE = mode;
|
|
183
|
+
const outcome = await adapter.toolBefore(ctx(), adapter.tool);
|
|
184
|
+
(0, vitest_1.expect)(outcome.blocked).toBe(blocked);
|
|
185
|
+
if (blocked)
|
|
186
|
+
(0, vitest_1.expect)(outcome.reason).toBeTruthy();
|
|
187
|
+
const invokeSpans = (0, testing_js_1.getTraceSpans)(client, "tool.invoke");
|
|
188
|
+
const blockSpans = (0, testing_js_1.getTraceSpans)(client, "tool.block");
|
|
189
|
+
if (invoke) {
|
|
190
|
+
(0, vitest_1.expect)(invokeSpans.length).toBeGreaterThanOrEqual(1);
|
|
191
|
+
(0, vitest_1.expect)(invokeSpans[0].attributes?.toolName).toBe(adapter.tool);
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
(0, vitest_1.expect)(invokeSpans).toHaveLength(0);
|
|
195
|
+
}
|
|
196
|
+
if (block) {
|
|
197
|
+
(0, vitest_1.expect)(blockSpans.length).toBeGreaterThanOrEqual(1);
|
|
198
|
+
(0, vitest_1.expect)(blockSpans[0].attributes?.blocked).toBe(mode === "enforce" && toolCanBlock);
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
(0, vitest_1.expect)(blockSpans).toHaveLength(0);
|
|
202
|
+
}
|
|
203
|
+
// Observe-mode denies also emit the audit span; enforce must not.
|
|
204
|
+
const observeSpans = (0, testing_js_1.getTraceSpans)(client, "permission.observe_deny");
|
|
205
|
+
(0, vitest_1.expect)(observeSpans).toHaveLength(check === "denied" && mode === "observe" ? 1 : 0);
|
|
206
|
+
});
|
|
207
|
+
(0, vitest_1.it)("skips the permission check entirely in audit-only mode", async () => {
|
|
208
|
+
(0, config_js_1.saveConfig)({ auditOnly: true });
|
|
209
|
+
const checkSpy = vitest_1.vi.spyOn(client, "checkPermission");
|
|
210
|
+
process.env.ORY_PERMISSION_MODE = "enforce";
|
|
211
|
+
const outcome = await adapter.toolBefore(ctx(), adapter.tool);
|
|
212
|
+
(0, vitest_1.expect)(outcome.blocked).toBe(false);
|
|
213
|
+
(0, vitest_1.expect)(checkSpy).not.toHaveBeenCalled();
|
|
214
|
+
});
|
|
215
|
+
(0, vitest_1.it)("addresses the check to ORY_USER_SUBJECT_ID when set", async () => {
|
|
216
|
+
process.env.ORY_USER_SUBJECT_ID = "user:custom-override";
|
|
217
|
+
(0, testing_js_1.stubPermissionAllowed)(client);
|
|
218
|
+
await adapter.toolBefore(ctx(), adapter.tool);
|
|
219
|
+
const [span] = (0, testing_js_1.getTraceSpans)(client, "tool.invoke");
|
|
220
|
+
(0, vitest_1.expect)(span.attributes?.subjectId).toBe("user:custom-override");
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
// ── Post-tool + passthrough ────────────────────────────────────
|
|
224
|
+
if (adapter.toolAfter) {
|
|
225
|
+
(0, vitest_1.it)("records tool.complete after execution", async () => {
|
|
226
|
+
await adapter.toolAfter(ctx(), adapter.tool);
|
|
227
|
+
const spans = (0, testing_js_1.getTraceSpans)(client, "tool.complete");
|
|
228
|
+
(0, vitest_1.expect)(spans.length).toBeGreaterThanOrEqual(1);
|
|
229
|
+
(0, vitest_1.expect)(spans[0].attributes?.toolName).toBe(adapter.tool);
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
if (adapter.unknownEvent) {
|
|
233
|
+
(0, vitest_1.it)("passes unknown events through with the native empty response", async () => {
|
|
234
|
+
const raw = await adapter.unknownEvent(ctx());
|
|
235
|
+
(0, vitest_1.expect)(raw).toEqual(adapter.unknownEventOutput);
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -19,7 +19,8 @@ export { checkAndDecide, applyPermissionMode, gateToolCall, type PermissionDecis
|
|
|
19
19
|
export { HARNESS_TOOL_CATALOG, KNOWN_HARNESSES, ALL_TOOLS, getToolCatalog, INTERACTIVE_TOOL_CATALOG, getInteractiveToolCatalog, isInteractiveTool, type KnownHarness, } from "./tool-catalog.js";
|
|
20
20
|
export { classifyLifecycle, isUserFacingPhase, isToolExecutionPhase, HARNESS_LIFECYCLE_MAP, USER_FACING_PHASES, TOOL_EXECUTION_PHASES, type LifecyclePhase, } from "./lifecycle.js";
|
|
21
21
|
export { parseClaudeCodeMcpTool, parseGeminiMcpTool, parseMcpToolGeneric, checkMcpPermission, type McpToolIdentifier, type McpPermissionCheckOptions, type McpPermissionResult, } from "./mcp.js";
|
|
22
|
-
export { resolveUserSubject, subjectLabel, type UserSubjectRef, } from "./subject.js";
|
|
22
|
+
export { resolveUserSubject, runWithUserSubject, subjectLabel, type UserSubjectRef, } from "./subject.js";
|
|
23
23
|
export { formatDenialMessage, formatDenialSummary, formatAlertMessage, formatAlertSummary, alertAttributes, OryDenialError, type DenialContext, type AlertAttributes, } from "./denial.js";
|
|
24
24
|
export { summarizeToolInput, summarizeToolOutput, type ToolInputSummary, type ToolOutputSummary, } from "./tool-metadata.js";
|
|
25
25
|
export { OtlpExporter, otlpExporterFromEnv, parseKeyValueList, type SpanExporter, type OtlpExporterOptions, type OtlpProtocol, } from "./otel/index.js";
|
|
26
|
+
export { resolveNamespace, sessionStart, gate, complete, registerSubagent, wrapTool, type SessionStartResult, type SessionStartOptions, type RegisterSubagentOptions, type GateResult, type GateOptions, type WrapToolOptions, } from "./adapters.js";
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.printOryConfig = exports.runAgentCommand = exports.runConfigureCommand = exports.AGENT_TOKEN_EXPIRY_SKEW_SEC = exports.clearSubAgentDynamicCredentials = exports.saveSubAgentDynamicCredentials = exports.loadSubAgentDynamicCredentials = exports.clearAgentDynamicCredentials = exports.saveAgentDynamicCredentials = exports.loadAgentDynamicCredentials = exports.registerAgentClient = exports.fetchClientCredentialsToken = exports.ensureSubAgentIdentity = exports.ensureAgentIdentity = exports.resolveAgentCredentials = exports.ensureAuthenticated = exports.ensureUserAuthenticated = exports.TOKEN_EXPIRY_SKEW_SEC = exports.waitForPeerTokensSync = exports.waitForPeerTokens = exports.clearPkceFlightLock = exports.tryAcquirePkceFlightLock = exports.refreshAndSave = exports.isExpired = exports.clearTokens = exports.saveTokens = exports.loadTokens = exports.DEFAULT_LOGIN_TIMEOUT_MS = exports.LOOPBACK_PORTS = exports.buildAuthorizeUrl = exports.sha256Base64Url = exports.generateCodeVerifier = exports.detectHeadless = exports.refreshAccessToken = exports.pkceLogin = exports.getHarnessDataDir = exports.getDataDir = exports.getConfigPath = exports.mutateConfig = exports.resolveConfig = exports.saveConfig = exports.loadConfig = exports.watchTraceFile = exports.formatSpan = exports.deriveTraceId = exports.ActiveSpan = exports.Tracer = exports.redactLogData = exports.DebugLogger = exports.OryAgentClient = void 0;
|
|
4
4
|
exports.runLocalCommand = exports.ORY_COMMAND_SLUGS = exports.ORY_COMMAND_SKILL_NAMES = exports.ORY_SKILL_NAMES = exports.removeSkillDirs = exports.writeSkillTree = exports.toSkillMarkdown = exports.commandToPlainMarkdown = exports.commandToFrontmatterMarkdown = exports.commandToToml = exports.commandToSkill = exports.renderOryCommands = exports.renderOrySkills = exports.runDevLauncher = exports.unregisterPlugin = exports.registerPlugin = exports.removeMcpServer = exports.mergeMcpServer = exports.mcpServerEntry = exports.resolveMcpServerCommand = exports.printNextSteps = exports.printSetupHelp = exports.removeFlatHooks = exports.mergeFlatHooks = exports.flatHookEntry = exports.removeMatcherHooks = exports.mergeMatcherHooks = exports.matcherHookEntry = exports.resolveHookCommand = exports.isOryHookCommand = exports.writeJsonFile = exports.readJsonFile = exports.parseSetupArgs = exports.printPermissionsSection = exports.printAgentIdentitySection = exports.printUserIdentitySection = exports.runStatusCommand = exports.printPermissionsOnboardingHelp = exports.maybeAutoBootstrap = exports.isUserIdentityCached = exports.runPermissionsCommand = exports.interactiveConfigPrompt = exports.promptForProjectUrl = exports.promptOnTty = exports.isTtyAvailable = exports.runWatchCommand = exports.printTraceTail = exports.printEnvHelp = exports.printLogTail = exports.printEnvironment = void 0;
|
|
5
|
-
exports.parseKeyValueList = exports.otlpExporterFromEnv = exports.OtlpExporter = exports.summarizeToolOutput = exports.summarizeToolInput = exports.OryDenialError = exports.alertAttributes = exports.formatAlertSummary = exports.formatAlertMessage = exports.formatDenialSummary = exports.formatDenialMessage = exports.subjectLabel = exports.resolveUserSubject = exports.checkMcpPermission = exports.parseMcpToolGeneric = exports.parseGeminiMcpTool = exports.parseClaudeCodeMcpTool = exports.TOOL_EXECUTION_PHASES = exports.USER_FACING_PHASES = exports.HARNESS_LIFECYCLE_MAP = exports.isToolExecutionPhase = exports.isUserFacingPhase = exports.classifyLifecycle = exports.isInteractiveTool = exports.getInteractiveToolCatalog = exports.INTERACTIVE_TOOL_CATALOG = exports.getToolCatalog = exports.ALL_TOOLS = exports.KNOWN_HARNESSES = exports.HARNESS_TOOL_CATALOG = exports.gateToolCall = exports.applyPermissionMode = exports.checkAndDecide = exports.runRegistryCommand = exports.DEV_JAEGER_CONTAINER = exports.stopDevJaeger = exports.ensureDevJaeger = void 0;
|
|
5
|
+
exports.wrapTool = exports.registerSubagent = exports.complete = exports.gate = exports.sessionStart = exports.resolveNamespace = exports.parseKeyValueList = exports.otlpExporterFromEnv = exports.OtlpExporter = exports.summarizeToolOutput = exports.summarizeToolInput = exports.OryDenialError = exports.alertAttributes = exports.formatAlertSummary = exports.formatAlertMessage = exports.formatDenialSummary = exports.formatDenialMessage = exports.subjectLabel = exports.runWithUserSubject = exports.resolveUserSubject = exports.checkMcpPermission = exports.parseMcpToolGeneric = exports.parseGeminiMcpTool = exports.parseClaudeCodeMcpTool = exports.TOOL_EXECUTION_PHASES = exports.USER_FACING_PHASES = exports.HARNESS_LIFECYCLE_MAP = exports.isToolExecutionPhase = exports.isUserFacingPhase = exports.classifyLifecycle = exports.isInteractiveTool = exports.getInteractiveToolCatalog = exports.INTERACTIVE_TOOL_CATALOG = exports.getToolCatalog = exports.ALL_TOOLS = exports.KNOWN_HARNESSES = exports.HARNESS_TOOL_CATALOG = exports.gateToolCall = exports.applyPermissionMode = exports.checkAndDecide = exports.runRegistryCommand = exports.DEV_JAEGER_CONTAINER = exports.stopDevJaeger = exports.ensureDevJaeger = void 0;
|
|
6
6
|
var client_js_1 = require("./client.js");
|
|
7
7
|
Object.defineProperty(exports, "OryAgentClient", { enumerable: true, get: function () { return client_js_1.OryAgentClient; } });
|
|
8
8
|
var logger_js_1 = require("./logger.js");
|
|
@@ -149,6 +149,7 @@ Object.defineProperty(exports, "parseMcpToolGeneric", { enumerable: true, get: f
|
|
|
149
149
|
Object.defineProperty(exports, "checkMcpPermission", { enumerable: true, get: function () { return mcp_js_1.checkMcpPermission; } });
|
|
150
150
|
var subject_js_1 = require("./subject.js");
|
|
151
151
|
Object.defineProperty(exports, "resolveUserSubject", { enumerable: true, get: function () { return subject_js_1.resolveUserSubject; } });
|
|
152
|
+
Object.defineProperty(exports, "runWithUserSubject", { enumerable: true, get: function () { return subject_js_1.runWithUserSubject; } });
|
|
152
153
|
Object.defineProperty(exports, "subjectLabel", { enumerable: true, get: function () { return subject_js_1.subjectLabel; } });
|
|
153
154
|
var denial_js_1 = require("./denial.js");
|
|
154
155
|
Object.defineProperty(exports, "formatDenialMessage", { enumerable: true, get: function () { return denial_js_1.formatDenialMessage; } });
|
|
@@ -164,3 +165,10 @@ var index_js_3 = require("./otel/index.js");
|
|
|
164
165
|
Object.defineProperty(exports, "OtlpExporter", { enumerable: true, get: function () { return index_js_3.OtlpExporter; } });
|
|
165
166
|
Object.defineProperty(exports, "otlpExporterFromEnv", { enumerable: true, get: function () { return index_js_3.otlpExporterFromEnv; } });
|
|
166
167
|
Object.defineProperty(exports, "parseKeyValueList", { enumerable: true, get: function () { return index_js_3.parseKeyValueList; } });
|
|
168
|
+
var adapters_js_1 = require("./adapters.js");
|
|
169
|
+
Object.defineProperty(exports, "resolveNamespace", { enumerable: true, get: function () { return adapters_js_1.resolveNamespace; } });
|
|
170
|
+
Object.defineProperty(exports, "sessionStart", { enumerable: true, get: function () { return adapters_js_1.sessionStart; } });
|
|
171
|
+
Object.defineProperty(exports, "gate", { enumerable: true, get: function () { return adapters_js_1.gate; } });
|
|
172
|
+
Object.defineProperty(exports, "complete", { enumerable: true, get: function () { return adapters_js_1.complete; } });
|
|
173
|
+
Object.defineProperty(exports, "registerSubagent", { enumerable: true, get: function () { return adapters_js_1.registerSubagent; } });
|
|
174
|
+
Object.defineProperty(exports, "wrapTool", { enumerable: true, get: function () { return adapters_js_1.wrapTool; } });
|
package/dist/lifecycle.js
CHANGED
|
@@ -44,6 +44,7 @@ exports.isToolExecutionPhase = isToolExecutionPhase;
|
|
|
44
44
|
* `passthrough` via {@link classifyLifecycle}.
|
|
45
45
|
*/
|
|
46
46
|
exports.HARNESS_LIFECYCLE_MAP = {
|
|
47
|
+
// ── Coding-agent harnesses ──
|
|
47
48
|
"claude-code": {
|
|
48
49
|
SessionStart: "session.start",
|
|
49
50
|
SessionEnd: "session.stop",
|
|
@@ -88,6 +89,135 @@ exports.HARNESS_LIFECYCLE_MAP = {
|
|
|
88
89
|
"tool.execute.before": "tool.before",
|
|
89
90
|
"tool.execute.after": "tool.after",
|
|
90
91
|
},
|
|
92
|
+
// Continue's `Stop` brackets a turn (not the session) and `ConfigChange`
|
|
93
|
+
// carries no lifecycle meaning — both fall through to passthrough and are
|
|
94
|
+
// traced as audit spans by the handler.
|
|
95
|
+
continue: {
|
|
96
|
+
SessionStart: "session.start",
|
|
97
|
+
SessionEnd: "session.stop",
|
|
98
|
+
PreToolUse: "tool.before",
|
|
99
|
+
PostToolUse: "tool.after",
|
|
100
|
+
PostToolUseFailure: "tool.failure",
|
|
101
|
+
PermissionRequest: "permission.ask",
|
|
102
|
+
UserPromptSubmit: "user.prompt",
|
|
103
|
+
SubagentStart: "subagent.start",
|
|
104
|
+
SubagentStop: "subagent.stop",
|
|
105
|
+
PreCompact: "compaction",
|
|
106
|
+
},
|
|
107
|
+
// Goose's file/shell notification hooks surface activity to the user and
|
|
108
|
+
// are trace-only — user-facing, never gated.
|
|
109
|
+
goose: {
|
|
110
|
+
SessionStart: "session.start",
|
|
111
|
+
SessionEnd: "session.stop",
|
|
112
|
+
Stop: "session.stop",
|
|
113
|
+
PreToolUse: "tool.before",
|
|
114
|
+
PostToolUse: "tool.after",
|
|
115
|
+
PostToolUseFailure: "tool.failure",
|
|
116
|
+
UserPromptSubmit: "user.prompt",
|
|
117
|
+
BeforeReadFile: "user.interaction",
|
|
118
|
+
AfterFileEdit: "user.interaction",
|
|
119
|
+
BeforeShellExecution: "user.interaction",
|
|
120
|
+
AfterShellExecution: "user.interaction",
|
|
121
|
+
},
|
|
122
|
+
cline: {
|
|
123
|
+
agent_start: "session.start",
|
|
124
|
+
agent_resume: "session.start",
|
|
125
|
+
agent_abort: "session.stop",
|
|
126
|
+
agent_end: "session.stop",
|
|
127
|
+
agent_error: "session.stop",
|
|
128
|
+
session_shutdown: "session.stop",
|
|
129
|
+
tool_call: "tool.before",
|
|
130
|
+
tool_result: "tool.after",
|
|
131
|
+
prompt_submit: "user.prompt",
|
|
132
|
+
pre_compact: "compaction",
|
|
133
|
+
},
|
|
134
|
+
// Amp is hybrid: session.start / tool.result are in-process plugin events;
|
|
135
|
+
// tool.call is gated by the subprocess permission delegate. agent.start /
|
|
136
|
+
// agent.end bracket a turn and fall through to passthrough.
|
|
137
|
+
amp: {
|
|
138
|
+
"session.start": "session.start",
|
|
139
|
+
"tool.call": "tool.before",
|
|
140
|
+
"tool.result": "tool.after",
|
|
141
|
+
},
|
|
142
|
+
// Pi runs session start in the extension factory body (awaited before
|
|
143
|
+
// startup); `session_start` is the harness's equivalent event name.
|
|
144
|
+
pi: {
|
|
145
|
+
session_start: "session.start",
|
|
146
|
+
tool_call: "tool.before",
|
|
147
|
+
tool_result: "tool.after",
|
|
148
|
+
},
|
|
149
|
+
// Google Antigravity (`agy` CLI / IDE). Subprocess hooks with
|
|
150
|
+
// Claude-Code-style snake_case payloads. PreInvocation/PostInvocation
|
|
151
|
+
// bracket each agent turn (not user prompts), so they fall through to
|
|
152
|
+
// passthrough and are traced as audit spans by the handler.
|
|
153
|
+
antigravity: {
|
|
154
|
+
SessionStart: "session.start",
|
|
155
|
+
SessionEnd: "session.stop",
|
|
156
|
+
Stop: "session.stop",
|
|
157
|
+
PreToolUse: "tool.before",
|
|
158
|
+
PostToolUse: "tool.after",
|
|
159
|
+
Notification: "user.interaction",
|
|
160
|
+
},
|
|
161
|
+
// ── Agent SDK integrations ── (parity with `ory_argus.lifecycle`; each
|
|
162
|
+
// entry names the canonical extension point the integration hooks)
|
|
163
|
+
langchain: {
|
|
164
|
+
"wrap_tool_call.before": "tool.before",
|
|
165
|
+
"wrap_tool_call.after": "tool.after",
|
|
166
|
+
on_tool_start: "tool.before",
|
|
167
|
+
on_tool_end: "tool.after",
|
|
168
|
+
on_tool_error: "tool.failure",
|
|
169
|
+
},
|
|
170
|
+
"openai-agents": {
|
|
171
|
+
tool_input_guardrail: "tool.before",
|
|
172
|
+
tool_output_guardrail: "tool.after",
|
|
173
|
+
on_tool_start: "tool.before",
|
|
174
|
+
on_tool_end: "tool.after",
|
|
175
|
+
on_handoff: "subagent.start",
|
|
176
|
+
},
|
|
177
|
+
crewai: {
|
|
178
|
+
ToolUsageStartedEvent: "tool.before",
|
|
179
|
+
ToolUsageFinishedEvent: "tool.after",
|
|
180
|
+
ToolUsageErrorEvent: "tool.failure",
|
|
181
|
+
},
|
|
182
|
+
llamaindex: {
|
|
183
|
+
AgentToolCallEvent: "tool.before",
|
|
184
|
+
function_call: "tool.before",
|
|
185
|
+
function_call_result: "tool.after",
|
|
186
|
+
},
|
|
187
|
+
"pydantic-ai": {
|
|
188
|
+
"call_tool.before": "tool.before",
|
|
189
|
+
"call_tool.after": "tool.after",
|
|
190
|
+
},
|
|
191
|
+
"agent-framework": {
|
|
192
|
+
"function_middleware.before": "tool.before",
|
|
193
|
+
"function_middleware.after": "tool.after",
|
|
194
|
+
},
|
|
195
|
+
"vercel-ai": {
|
|
196
|
+
"execute.before": "tool.before",
|
|
197
|
+
"execute.after": "tool.after",
|
|
198
|
+
},
|
|
199
|
+
"claude-agent-sdk": {
|
|
200
|
+
SessionStart: "session.start",
|
|
201
|
+
PreToolUse: "tool.before",
|
|
202
|
+
PostToolUse: "tool.after",
|
|
203
|
+
},
|
|
204
|
+
"google-adk": {
|
|
205
|
+
before_tool_callback: "tool.before",
|
|
206
|
+
after_tool_callback: "tool.after",
|
|
207
|
+
transfer_to_agent: "subagent.start",
|
|
208
|
+
},
|
|
209
|
+
mastra: {
|
|
210
|
+
beforeToolCall: "tool.before",
|
|
211
|
+
afterToolCall: "tool.after",
|
|
212
|
+
},
|
|
213
|
+
strands: {
|
|
214
|
+
BeforeToolCallEvent: "tool.before",
|
|
215
|
+
AfterToolCallEvent: "tool.after",
|
|
216
|
+
},
|
|
217
|
+
"cloudflare-agents": {
|
|
218
|
+
"execute.before": "tool.before",
|
|
219
|
+
"execute.after": "tool.after",
|
|
220
|
+
},
|
|
91
221
|
};
|
|
92
222
|
/**
|
|
93
223
|
* Resolve a harness event name to its canonical {@link LifecyclePhase}.
|
package/dist/local/configs.d.ts
CHANGED
|
@@ -73,8 +73,8 @@ export declare const GATEWAY_PORT = 4000;
|
|
|
73
73
|
export declare const GATEWAY_URL = "http://localhost:4000";
|
|
74
74
|
export declare const CONSOLE_PORT = 4100;
|
|
75
75
|
export declare const CONSOLE_URL = "http://localhost:4100";
|
|
76
|
-
export declare const LOGIN_UI_PORT =
|
|
77
|
-
export declare const LOGIN_UI_URL = "http://localhost:
|
|
76
|
+
export declare const LOGIN_UI_PORT = 4455;
|
|
77
|
+
export declare const LOGIN_UI_URL = "http://localhost:4455";
|
|
78
78
|
export declare const KRATOS_PUBLIC_PORT = 4433;
|
|
79
79
|
export declare const KRATOS_ADMIN_PORT = 4434;
|
|
80
80
|
export declare const KETO_READ_PORT = 4466;
|
package/dist/local/configs.js
CHANGED
|
@@ -189,6 +189,14 @@ function nginxGatewayService() {
|
|
|
189
189
|
* `COOKIE_SECRET` and `CSRF_COOKIE_SECRET` are required by the image —
|
|
190
190
|
* the container exits at startup if they're missing. Both must be at
|
|
191
191
|
* least 32 chars. Fixed dev-only values so launches are deterministic.
|
|
192
|
+
*
|
|
193
|
+
* `DANGEROUSLY_DISABLE_SECURE_CSRF_COOKIES=true` is required for local
|
|
194
|
+
* dev (HTTP, not HTTPS). Without it the csrf-csrf library sets `Secure`
|
|
195
|
+
* cookies, which the browser silently drops over plain HTTP. This causes
|
|
196
|
+
* every self-service flow (login, registration, settings) to fail with a
|
|
197
|
+
* 403 CSRF error, and the registration handler's redirectOnSoftError
|
|
198
|
+
* enters an infinite redirect loop — pegging CPU and making the UI
|
|
199
|
+
* unusable. Only safe for loopback-local dev.
|
|
192
200
|
*/
|
|
193
201
|
function loginUiService() {
|
|
194
202
|
return ` login-ui:
|
|
@@ -207,6 +215,7 @@ function loginUiService() {
|
|
|
207
215
|
COOKIE_SECRET: "ory-agent-plugins-local-cookie-secret-32+"
|
|
208
216
|
CSRF_COOKIE_NAME: "ory_agent_plugins_local_csrf"
|
|
209
217
|
CSRF_COOKIE_SECRET: "ory-agent-plugins-local-csrf-secret-32+!"
|
|
218
|
+
DANGEROUSLY_DISABLE_SECURE_CSRF_COOKIES: "true"
|
|
210
219
|
depends_on:
|
|
211
220
|
- kratos
|
|
212
221
|
- hydra
|
|
@@ -616,7 +625,7 @@ exports.GATEWAY_PORT = 4000;
|
|
|
616
625
|
exports.GATEWAY_URL = `http://localhost:${exports.GATEWAY_PORT}`;
|
|
617
626
|
exports.CONSOLE_PORT = 4100;
|
|
618
627
|
exports.CONSOLE_URL = `http://localhost:${exports.CONSOLE_PORT}`;
|
|
619
|
-
exports.LOGIN_UI_PORT =
|
|
628
|
+
exports.LOGIN_UI_PORT = 4455;
|
|
620
629
|
exports.LOGIN_UI_URL = `http://localhost:${exports.LOGIN_UI_PORT}`;
|
|
621
630
|
exports.KRATOS_PUBLIC_PORT = 4433;
|
|
622
631
|
exports.KRATOS_ADMIN_PORT = 4434;
|
package/dist/local/manager.d.ts
CHANGED
|
@@ -24,10 +24,12 @@ export interface EnsureLocalOryStackResult {
|
|
|
24
24
|
* Bring up the local Ory stack and wait for the gateway to be healthy.
|
|
25
25
|
*
|
|
26
26
|
* Idempotent and fail-graceful: returns a typed result instead of exiting.
|
|
27
|
-
* If the gateway is already healthy the
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
27
|
+
* If the gateway is already healthy AND the running stack's config matches
|
|
28
|
+
* what the current code generates (verified by a config fingerprint), the
|
|
29
|
+
* function short-circuits without touching Docker; otherwise it regenerates
|
|
30
|
+
* the config and runs `docker compose up -d --wait` to reconcile. When
|
|
31
|
+
* `quiet` is set, progress is logged to stderr only (suitable for the dev
|
|
32
|
+
* launcher); otherwise it prints to stdout in the format `localUp` historically used.
|
|
31
33
|
*/
|
|
32
34
|
export declare function ensureLocalOryStack(opts?: {
|
|
33
35
|
quiet?: boolean;
|