@ory/argus 1.2.4 → 1.3.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 +25 -0
- package/assets/skills/ory-build-agent/SKILL.md +1 -1
- package/assets/skills/ory-deploy-agent/SKILL.md +315 -0
- package/assets/skills/ory-deploy-openai-e2b/SKILL.md +289 -0
- package/assets/skills/ory-e2b-sandbox/SKILL.md +10 -6
- package/assets/skills/ory-temporal-worker/SKILL.md +4 -4
- package/dist/adapters.d.ts +11 -3
- package/dist/adapters.js +55 -13
- package/dist/agent-auth.d.ts +18 -7
- package/dist/agent-auth.js +120 -10
- package/dist/auth-store.d.ts +3 -3
- package/dist/auth-store.js +56 -12
- package/dist/auth.d.ts +1 -1
- package/dist/auth.js +1 -1
- package/dist/build-info.json +4 -4
- package/dist/cli.js +5 -0
- package/dist/client.d.ts +15 -0
- package/dist/client.js +24 -7
- package/dist/config.d.ts +23 -3
- package/dist/config.js +202 -18
- package/dist/contract-suite.d.ts +0 -8
- package/dist/contract-suite.js +0 -1
- package/dist/event-reporter.d.ts +1 -0
- package/dist/event-reporter.js +19 -2
- package/dist/hook-timeout.d.ts +2 -2
- package/dist/hook-timeout.js +2 -2
- package/dist/index.d.ts +4 -4
- package/dist/index.js +13 -6
- package/dist/lifecycle.js +2 -1
- package/dist/local/configs.d.ts +2 -4
- package/dist/local/configs.js +2 -4
- package/dist/local/manager.js +4 -4
- package/dist/local/seed.d.ts +4 -4
- package/dist/local/seed.js +8 -10
- package/dist/runtime-credential.d.ts +35 -0
- package/dist/runtime-credential.js +252 -1
- package/dist/runtime.d.ts +19 -12
- package/dist/runtime.js +69 -21
- package/dist/setup.js +3 -3
- package/dist/skills.d.ts +3 -5
- package/dist/skills.js +14 -5
- package/dist/subject.d.ts +2 -3
- package/dist/subject.js +10 -11
- package/dist/testing.d.ts +17 -0
- package/dist/testing.js +53 -0
- package/dist/tool-catalog.d.ts +12 -12
- package/dist/tool-catalog.js +206 -50
- package/dist/user-login.js +1 -1
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ory-temporal-worker
|
|
3
|
-
description: Scaffold a [Temporal](https://temporal.io) TypeScript worker where every Activity execution is gated by Ory — the user is authenticated, the worker
|
|
3
|
+
description: Scaffold a [Temporal](https://temporal.io) TypeScript worker where every Activity execution is gated by Ory — the user is authenticated, the worker resolves an injected or Talos runtime identity, each Activity invocation runs an Ory Permission check, and the full lifecycle emits structured activity events. Use when the user asks to "add Ory to my Temporal worker", "wire Ory permissions into Temporal activities", "create a Temporal worker with Ory auth", "build a Temporal TypeScript project with the Ory agent client", or any close variant. The skill scaffolds the project in the user's repo following <https://docs.temporal.io/develop/typescript/set-up-your-local-typescript> — it does not run the worker.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Ory-authed Temporal TypeScript worker
|
|
@@ -262,9 +262,9 @@ Temporal Web UI.
|
|
|
262
262
|
|
|
263
263
|
This skill stops at the local dev server. For production:
|
|
264
264
|
|
|
265
|
-
-
|
|
266
|
-
|
|
267
|
-
|
|
265
|
+
- Inject a runtime request authenticator from the workload platform, or provide
|
|
266
|
+
`ORY_AGENT_API_KEY` through the deployment secret manager. Do not bake a
|
|
267
|
+
runtime credential into the worker image.
|
|
268
268
|
- Set retention and access controls for the worker's unified activity log.
|
|
269
269
|
Ory Agent Security owns service telemetry; the worker does not export traces.
|
|
270
270
|
- Use Temporal Cloud or a self-hosted Temporal cluster instead of
|
package/dist/adapters.d.ts
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* Every primitive is best-effort and fail-open.
|
|
22
22
|
*/
|
|
23
23
|
import type { OryAgentClient } from "./client.js";
|
|
24
|
-
import { ensureAgentIdentity, ensureSubAgentIdentity } from "./agent-auth.js";
|
|
24
|
+
import { ensureAgentIdentity, ensureSubAgentIdentity, type SubAgentIdentity } from "./agent-auth.js";
|
|
25
25
|
import { ensureUserAuthenticated } from "./user-login.js";
|
|
26
26
|
import { type ModeDecision, type PermissionDecision, type PrincipalBlockSubjects } from "./permissions.js";
|
|
27
27
|
import { type DenialContext } from "./denial.js";
|
|
@@ -42,12 +42,18 @@ export interface SessionStartResult {
|
|
|
42
42
|
}
|
|
43
43
|
export interface SessionStartOptions {
|
|
44
44
|
harness: string;
|
|
45
|
+
/** Root agent type supplied by the adapter/application. Defaults to `harness`. */
|
|
46
|
+
agentType?: string;
|
|
45
47
|
sessionId?: string;
|
|
46
48
|
projectUrl?: string;
|
|
47
49
|
binName?: string;
|
|
50
|
+
/** Environment override used by headless deployment packages and tests. */
|
|
51
|
+
env?: NodeJS.ProcessEnv;
|
|
48
52
|
/** Injectable gates (tests). */
|
|
49
53
|
userLogin?: typeof ensureUserAuthenticated;
|
|
50
54
|
agentGate?: typeof ensureAgentIdentity;
|
|
55
|
+
/** Injectable runtime-context transport (tests and custom runtimes). */
|
|
56
|
+
runtimeContextFetch?: typeof fetch;
|
|
51
57
|
/** Small, privacy-safe session metadata supported by the activity API. */
|
|
52
58
|
activityAttributes?: Record<string, unknown>;
|
|
53
59
|
}
|
|
@@ -77,7 +83,7 @@ export declare function createSessionStarter(client: OryAgentClient, opts: Sessi
|
|
|
77
83
|
* logged and swallowed. No-op when Agent Security isn't connected (there is no
|
|
78
84
|
* broker to record against) and when no user identity resolves.
|
|
79
85
|
*/
|
|
80
|
-
export declare function writeUserDelegatesAgent(client: OryAgentClient, harness?: string, sessionId?: string): Promise<void>;
|
|
86
|
+
export declare function writeUserDelegatesAgent(client: OryAgentClient, harness?: string, sessionId?: string, env?: NodeJS.ProcessEnv): Promise<void>;
|
|
81
87
|
export interface RegisterSubagentOptions {
|
|
82
88
|
harness: string;
|
|
83
89
|
subAgentType: string;
|
|
@@ -92,8 +98,9 @@ export interface RegisterSubagentOptions {
|
|
|
92
98
|
perSpawnId?: string;
|
|
93
99
|
sessionId?: string;
|
|
94
100
|
signal?: AbortSignal;
|
|
101
|
+
env?: NodeJS.ProcessEnv;
|
|
95
102
|
}
|
|
96
|
-
export declare function registerSubagent(client: OryAgentClient, opts: RegisterSubagentOptions): Promise<
|
|
103
|
+
export declare function registerSubagent(client: OryAgentClient, opts: RegisterSubagentOptions): Promise<SubAgentIdentity | undefined>;
|
|
97
104
|
/**
|
|
98
105
|
* Record the `agent → subagent` delegation edge through the agent-security
|
|
99
106
|
* broker. Exported so harness plugins that resolve the sub-agent identity
|
|
@@ -115,6 +122,7 @@ export declare function writeAgentDelegatesSubagent(client: OryAgentClient, subA
|
|
|
115
122
|
agentToken?: string;
|
|
116
123
|
runtimeCredential?: RuntimeCredential;
|
|
117
124
|
signal?: AbortSignal;
|
|
125
|
+
env?: NodeJS.ProcessEnv;
|
|
118
126
|
}): Promise<void>;
|
|
119
127
|
export interface GateResult {
|
|
120
128
|
/** False only when the tool was hard-denied in enforce mode. */
|
package/dist/adapters.js
CHANGED
|
@@ -104,19 +104,51 @@ async function sessionStart(client, opts) {
|
|
|
104
104
|
const projectUrl = opts.projectUrl ?? (0, config_js_1.resolveConfig)().projectUrl;
|
|
105
105
|
const userLogin = opts.userLogin ?? user_login_js_1.ensureUserAuthenticated;
|
|
106
106
|
const agentGate = opts.agentGate ?? agent_auth_js_1.ensureAgentIdentity;
|
|
107
|
+
const env = opts.env ?? process.env;
|
|
108
|
+
const packageWarnings = [];
|
|
109
|
+
let rootDelegation = (0, agent_auth_js_1.resolvePreprovisionedRootDelegation)(env, packageWarnings, client.runtimeCredential);
|
|
110
|
+
for (const message of packageWarnings) {
|
|
111
|
+
client.logger.warn("agent.package.invalid", { message });
|
|
112
|
+
}
|
|
113
|
+
if (!rootDelegation) {
|
|
114
|
+
try {
|
|
115
|
+
const context = await (0, agent_auth_js_1.resolvePreprovisionedRuntimeContext)(client, {
|
|
116
|
+
projectUrl,
|
|
117
|
+
expectedAgentType: opts.agentType ?? opts.harness,
|
|
118
|
+
env,
|
|
119
|
+
fetchImpl: opts.runtimeContextFetch,
|
|
120
|
+
});
|
|
121
|
+
rootDelegation = context?.rootDelegation;
|
|
122
|
+
}
|
|
123
|
+
catch (err) {
|
|
124
|
+
client.logger.warn("agent.runtime_context.failed", describeThrown(err));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
107
127
|
let decision = {
|
|
108
128
|
proceed: true,
|
|
109
129
|
mode: "skipped",
|
|
110
130
|
reason: "User authentication failed open",
|
|
111
131
|
};
|
|
112
|
-
|
|
113
|
-
decision =
|
|
114
|
-
|
|
115
|
-
|
|
132
|
+
if (rootDelegation) {
|
|
133
|
+
decision = {
|
|
134
|
+
proceed: true,
|
|
135
|
+
mode: "skipped",
|
|
136
|
+
reason: "Administrator-preprovisioned agent package does not require user login",
|
|
137
|
+
};
|
|
138
|
+
client.logger.activity("user.auth", "skipped", {
|
|
139
|
+
attributes: { mode: decision.mode, reason: decision.reason, proceed: true },
|
|
116
140
|
});
|
|
117
141
|
}
|
|
118
|
-
|
|
119
|
-
|
|
142
|
+
else {
|
|
143
|
+
try {
|
|
144
|
+
decision = await userLogin(client, {
|
|
145
|
+
binName: opts.binName ?? `ory-${opts.harness}`,
|
|
146
|
+
harness: opts.harness,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
catch (err) {
|
|
150
|
+
client.logger.warn("session_start.user_auth.failed", describeThrown(err));
|
|
151
|
+
}
|
|
120
152
|
}
|
|
121
153
|
let creds = {
|
|
122
154
|
kind: "none",
|
|
@@ -128,12 +160,13 @@ async function sessionStart(client, opts) {
|
|
|
128
160
|
projectUrl,
|
|
129
161
|
harness: opts.harness,
|
|
130
162
|
sessionId: opts.sessionId ?? client.sessionId,
|
|
163
|
+
env,
|
|
131
164
|
});
|
|
132
165
|
}
|
|
133
166
|
catch (err) {
|
|
134
167
|
client.logger.warn("session_start.agent_auth.failed", describeThrown(err));
|
|
135
168
|
}
|
|
136
|
-
await writeUserDelegatesAgent(client, opts.harness, opts.sessionId);
|
|
169
|
+
await writeUserDelegatesAgent(client, opts.harness, opts.sessionId, env);
|
|
137
170
|
// Warm the permission-mode cache from the server once per session, so the
|
|
138
171
|
// synchronous `resolveConfig().permissionMode` read (the MCP
|
|
139
172
|
// `applyPermissionMode` path, display surfaces) reflects the current project
|
|
@@ -183,8 +216,7 @@ function unqualify(subject, ns) {
|
|
|
183
216
|
}
|
|
184
217
|
/**
|
|
185
218
|
* Resolve one end of a delegation edge, preferring the live principal but
|
|
186
|
-
* falling back to the persisted delegation anchor
|
|
187
|
-
* on the DCR credentials in local config). This is what lets the edge be
|
|
219
|
+
* falling back to the persisted non-secret delegation anchor. This lets the edge be
|
|
188
220
|
* recorded from an authoritative source rather than from nothing when the
|
|
189
221
|
* principal isn't populated for the session. A mismatch between the two is
|
|
190
222
|
* logged for reconciliation visibility; the live principal wins.
|
|
@@ -211,9 +243,12 @@ function reconcileEdgeEnd(client, principal, anchor, role) {
|
|
|
211
243
|
* logged and swallowed. No-op when Agent Security isn't connected (there is no
|
|
212
244
|
* broker to record against) and when no user identity resolves.
|
|
213
245
|
*/
|
|
214
|
-
async function writeUserDelegatesAgent(client, harness, sessionId) {
|
|
246
|
+
async function writeUserDelegatesAgent(client, harness, sessionId, env = process.env) {
|
|
215
247
|
if (!(0, config_js_1.isSecurityConnected)())
|
|
216
248
|
return;
|
|
249
|
+
if (client.runtimeRootDelegation ||
|
|
250
|
+
(0, agent_auth_js_1.resolvePreprovisionedRootDelegation)(env, undefined, client.runtimeCredential))
|
|
251
|
+
return;
|
|
217
252
|
// Resolve the delegating user (the anchor of the edge). Prefer the live
|
|
218
253
|
// principal; fall back to the delegator persisted in the agent client's
|
|
219
254
|
// delegation anchor so the edge can still be recorded in a session where the
|
|
@@ -264,6 +299,7 @@ async function registerSubagent(client, opts) {
|
|
|
264
299
|
sessionId: opts.sessionId ?? client.sessionId,
|
|
265
300
|
perSpawnId: opts.perSpawnId,
|
|
266
301
|
signal: opts.signal,
|
|
302
|
+
env: opts.env,
|
|
267
303
|
});
|
|
268
304
|
}
|
|
269
305
|
catch (err) {
|
|
@@ -271,10 +307,12 @@ async function registerSubagent(client, opts) {
|
|
|
271
307
|
subAgentType: opts.subAgentType,
|
|
272
308
|
...describeThrown(err),
|
|
273
309
|
});
|
|
274
|
-
return;
|
|
310
|
+
return undefined;
|
|
275
311
|
}
|
|
276
312
|
if (identity.kind !== "runtime" && identity.kind !== "dynamic")
|
|
277
|
-
return;
|
|
313
|
+
return identity;
|
|
314
|
+
if (identity.runtimeCredential)
|
|
315
|
+
client.rememberRuntimeCredential(identity.runtimeCredential);
|
|
278
316
|
await writeAgentDelegatesSubagent(client, identity.subject ?? "", opts.subAgentType, {
|
|
279
317
|
harness: opts.harness,
|
|
280
318
|
sessionId: opts.sessionId,
|
|
@@ -282,7 +320,9 @@ async function registerSubagent(client, opts) {
|
|
|
282
320
|
signal: opts.signal,
|
|
283
321
|
runtimeCredential: identity.runtimeCredential,
|
|
284
322
|
agentToken: identity.token,
|
|
323
|
+
env: opts.env,
|
|
285
324
|
});
|
|
325
|
+
return identity;
|
|
286
326
|
}
|
|
287
327
|
/**
|
|
288
328
|
* Record the `agent → subagent` delegation edge through the agent-security
|
|
@@ -307,7 +347,8 @@ async function writeAgentDelegatesSubagent(client, subAgentSubject, subAgentType
|
|
|
307
347
|
// session*: the parent of this sub-agent is this run's session credential, so
|
|
308
348
|
// the harness alone would not identify it.
|
|
309
349
|
const sessionKey = (opts.sessionId ?? client.sessionId)?.trim() || config_js_1.SESSIONLESS_KEY;
|
|
310
|
-
const
|
|
350
|
+
const preprovisionedRoot = client.runtimeRootDelegation ?? (0, agent_auth_js_1.resolvePreprovisionedRootDelegation)(opts.env, undefined, client.runtimeCredential);
|
|
351
|
+
const delegatedBy = client.runtimeRootDelegation ?? (0, agent_auth_js_1.loadDelegationNodeId)(opts.harness ?? client.harness, sessionKey, opts.env, client.runtimeCredential);
|
|
311
352
|
if (!delegatedBy) {
|
|
312
353
|
client.logger.debug("delegation.skip", {
|
|
313
354
|
reason: "no persisted agent delegation node (user→agent edge not recorded yet)",
|
|
@@ -327,6 +368,7 @@ async function writeAgentDelegatesSubagent(client, subAgentSubject, subAgentType
|
|
|
327
368
|
signal: opts.signal,
|
|
328
369
|
agentToken: opts.agentToken,
|
|
329
370
|
runtimeCredential: opts.runtimeCredential,
|
|
371
|
+
preprovisionedRoot: preprovisionedRoot !== undefined,
|
|
330
372
|
activityAttributes: {
|
|
331
373
|
delegation: "agent-to-subagent",
|
|
332
374
|
subAgentType,
|
package/dist/agent-auth.d.ts
CHANGED
|
@@ -5,15 +5,26 @@
|
|
|
5
5
|
* user. Unlike the user, the agent never authenticates interactively.
|
|
6
6
|
* The normal path resolves a noninteractive runtime credential: an injected
|
|
7
7
|
* request authenticator, `ORY_AGENT_API_KEY`, or a Talos key enrolled with the
|
|
8
|
-
* already-authenticated user
|
|
8
|
+
* already-authenticated user and persisted in the native OS credential store.
|
|
9
9
|
*
|
|
10
10
|
* Legacy DCR and client-credentials helpers remain exported temporarily so the
|
|
11
11
|
* monorepo and existing consumers compile, but the normal gates do not select
|
|
12
12
|
* them.
|
|
13
13
|
*/
|
|
14
14
|
import { type OryAgentDynamicCredentials, type OryDelegationRecord } from "./config.js";
|
|
15
|
-
import { OryAgentClient } from "./client.js";
|
|
15
|
+
import { OryAgentClient, type RuntimeContext } from "./client.js";
|
|
16
16
|
import { enrollTalosChildRuntimeCredential, enrollTalosRuntimeCredential, type RuntimeCredential, type RuntimeRequestAuthenticator, type TalosCredentialStore } from "./runtime-credential.js";
|
|
17
|
+
/** Resolve the complete administrator-provisioned headless package, if present. */
|
|
18
|
+
export declare function resolvePreprovisionedRootDelegation(env?: NodeJS.ProcessEnv, warnings?: string[], runtimeCredential?: RuntimeCredential): string | undefined;
|
|
19
|
+
export interface ResolvePreprovisionedRuntimeContextOptions {
|
|
20
|
+
projectUrl?: string;
|
|
21
|
+
expectedAgentType: string;
|
|
22
|
+
env?: NodeJS.ProcessEnv;
|
|
23
|
+
fetchImpl?: typeof fetch;
|
|
24
|
+
signal?: AbortSignal;
|
|
25
|
+
}
|
|
26
|
+
/** Resolve missing headless deployment metadata using the existing runtime proof. */
|
|
27
|
+
export declare function resolvePreprovisionedRuntimeContext(client: OryAgentClient, options: ResolvePreprovisionedRuntimeContextOptions): Promise<RuntimeContext | undefined>;
|
|
17
28
|
/**
|
|
18
29
|
* Delegation context handed to {@link registerAgentClient} so the freshly
|
|
19
30
|
* minted OAuth2 client is stamped with its delegator at the identity layer.
|
|
@@ -174,7 +185,7 @@ export declare function countAgentSessions(harness: string): number;
|
|
|
174
185
|
* sessions but the node is not, so a sub-agent must hang off *its own* run's
|
|
175
186
|
* agent node.
|
|
176
187
|
*/
|
|
177
|
-
export declare function loadDelegationNodeId(harness: string, sessionKey: string): string | undefined;
|
|
188
|
+
export declare function loadDelegationNodeId(harness: string, sessionKey: string, env?: NodeJS.ProcessEnv, runtimeCredential?: RuntimeCredential): string | undefined;
|
|
178
189
|
/**
|
|
179
190
|
* Record the broker-assigned delegation node for `(harness, session)`, trimming
|
|
180
191
|
* the map to the most recent {@link DELEGATION_NODE_HISTORY} entries.
|
|
@@ -317,7 +328,7 @@ export interface EnsureAgentIdentityOptions {
|
|
|
317
328
|
/** Retained for source compatibility and activity attribution. */
|
|
318
329
|
harness?: string;
|
|
319
330
|
/**
|
|
320
|
-
* Session this identity acts in. Selects the
|
|
331
|
+
* Session this identity acts in. Selects the OS-store descriptor and defaults
|
|
321
332
|
* to the client's ambient session.
|
|
322
333
|
*/
|
|
323
334
|
sessionId?: string;
|
|
@@ -417,9 +428,9 @@ export interface EnsureSubAgentIdentityOptions {
|
|
|
417
428
|
allowEnrollment?: boolean;
|
|
418
429
|
}
|
|
419
430
|
/**
|
|
420
|
-
* Resolve a sub-agent
|
|
421
|
-
*
|
|
422
|
-
*
|
|
431
|
+
* Resolve a sub-agent to a child Talos credential. Stored credentials are
|
|
432
|
+
* loaded by spawn descriptor; missing credentials may be enrolled through the
|
|
433
|
+
* authenticated parent runtime.
|
|
423
434
|
*/
|
|
424
435
|
export declare function ensureSubAgentIdentity(client: OryAgentClient, options: EnsureSubAgentIdentityOptions): Promise<SubAgentIdentity>;
|
|
425
436
|
export {};
|
package/dist/agent-auth.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* user. Unlike the user, the agent never authenticates interactively.
|
|
7
7
|
* The normal path resolves a noninteractive runtime credential: an injected
|
|
8
8
|
* request authenticator, `ORY_AGENT_API_KEY`, or a Talos key enrolled with the
|
|
9
|
-
* already-authenticated user
|
|
9
|
+
* already-authenticated user and persisted in the native OS credential store.
|
|
10
10
|
*
|
|
11
11
|
* Legacy DCR and client-credentials helpers remain exported temporarily so the
|
|
12
12
|
* monorepo and existing consumers compile, but the normal gates do not select
|
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
*/
|
|
15
15
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
16
|
exports.DEFAULT_SESSION_RETENTION = exports.AGENT_TOKEN_EXPIRY_SKEW_SEC = void 0;
|
|
17
|
+
exports.resolvePreprovisionedRootDelegation = resolvePreprovisionedRootDelegation;
|
|
18
|
+
exports.resolvePreprovisionedRuntimeContext = resolvePreprovisionedRuntimeContext;
|
|
17
19
|
exports.resolveRuntimeAgentCredentials = resolveRuntimeAgentCredentials;
|
|
18
20
|
exports._resetAgentCredentialsCache = _resetAgentCredentialsCache;
|
|
19
21
|
exports.invalidateAgentAccessToken = invalidateAgentAccessToken;
|
|
@@ -55,6 +57,102 @@ const subject_js_1 = require("./subject.js");
|
|
|
55
57
|
const runtime_credential_js_1 = require("./runtime-credential.js");
|
|
56
58
|
const ENROLLMENT_LOCK_STALE_MS = 120_000;
|
|
57
59
|
const ENROLLMENT_LOCK_WAIT_MS = 35_000;
|
|
60
|
+
const validatedRuntimeSubjects = new WeakSet();
|
|
61
|
+
/** Resolve the complete administrator-provisioned headless package, if present. */
|
|
62
|
+
function resolvePreprovisionedRootDelegation(env = process.env, warnings, runtimeCredential) {
|
|
63
|
+
const root = (0, config_js_1.readAgentRootDelegation)(env, warnings);
|
|
64
|
+
if (!root)
|
|
65
|
+
return undefined;
|
|
66
|
+
if (!env.ORY_AGENT_API_KEY?.trim() && runtimeCredential?.kind !== "injected") {
|
|
67
|
+
warnings?.push("ORY_AGENT_ROOT_DELEGATION requires ORY_AGENT_API_KEY or an injected runtime authenticator; ignoring it.");
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
if (!(0, subject_js_1.readAgentSubjectOverride)(env, warnings)) {
|
|
71
|
+
warnings?.push("ORY_AGENT_ROOT_DELEGATION requires a valid ORY_AGENT_SUBJECT_ID; ignoring it.");
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
return root;
|
|
75
|
+
}
|
|
76
|
+
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
77
|
+
const RUNTIME_CONTEXT_KEYS = [
|
|
78
|
+
"agent_type",
|
|
79
|
+
"authenticated",
|
|
80
|
+
"deployment_id",
|
|
81
|
+
"root_delegation",
|
|
82
|
+
"subject_id",
|
|
83
|
+
];
|
|
84
|
+
function parseRuntimeContext(value) {
|
|
85
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
86
|
+
throw new Error("Runtime context response is malformed");
|
|
87
|
+
}
|
|
88
|
+
const raw = value;
|
|
89
|
+
if (Object.keys(raw).sort().join("\0") !== RUNTIME_CONTEXT_KEYS.join("\0")) {
|
|
90
|
+
throw new Error("Runtime context response is malformed");
|
|
91
|
+
}
|
|
92
|
+
const subjectId = raw.subject_id;
|
|
93
|
+
const rootDelegation = raw.root_delegation;
|
|
94
|
+
const deploymentId = raw.deployment_id;
|
|
95
|
+
const agentType = raw.agent_type;
|
|
96
|
+
if (raw.authenticated !== true ||
|
|
97
|
+
typeof subjectId !== "string" ||
|
|
98
|
+
!subjectId.startsWith("agent:") ||
|
|
99
|
+
!UUID_PATTERN.test(subjectId.slice("agent:".length)) ||
|
|
100
|
+
typeof rootDelegation !== "string" ||
|
|
101
|
+
!rootDelegation.trim() ||
|
|
102
|
+
typeof deploymentId !== "string" ||
|
|
103
|
+
!UUID_PATTERN.test(deploymentId) ||
|
|
104
|
+
typeof agentType !== "string" ||
|
|
105
|
+
!agentType.trim()) {
|
|
106
|
+
throw new Error("Runtime context response is malformed");
|
|
107
|
+
}
|
|
108
|
+
return { authenticated: true, subjectId, rootDelegation, deploymentId, agentType };
|
|
109
|
+
}
|
|
110
|
+
/** Resolve missing headless deployment metadata using the existing runtime proof. */
|
|
111
|
+
async function resolvePreprovisionedRuntimeContext(client, options) {
|
|
112
|
+
const env = options.env ?? process.env;
|
|
113
|
+
const credential = client.runtimeCredential ?? (0, runtime_credential_js_1.resolveRuntimeCredential)({
|
|
114
|
+
env,
|
|
115
|
+
fetch: options.fetchImpl,
|
|
116
|
+
});
|
|
117
|
+
if (!credential)
|
|
118
|
+
return undefined;
|
|
119
|
+
let response;
|
|
120
|
+
try {
|
|
121
|
+
response = await (0, runtime_credential_js_1.authenticatedRuntimeRequest)(credential, {
|
|
122
|
+
url: `${client.agentSecurityUrl.replace(/\/+$/, "")}/agent-security/v1/runtime:context`,
|
|
123
|
+
init: {
|
|
124
|
+
method: "POST",
|
|
125
|
+
headers: { "content-type": "application/json" },
|
|
126
|
+
body: JSON.stringify({ project_url: options.projectUrl ?? (0, config_js_1.resolveConfig)().projectUrl }),
|
|
127
|
+
signal: options.signal ?? AbortSignal.timeout(30_000),
|
|
128
|
+
},
|
|
129
|
+
authorizationHeader: "authorization",
|
|
130
|
+
fetch: options.fetchImpl,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
throw new Error("Runtime context request failed");
|
|
135
|
+
}
|
|
136
|
+
if (response.status !== 200) {
|
|
137
|
+
throw new Error(`Runtime context request failed (HTTP ${response.status})`);
|
|
138
|
+
}
|
|
139
|
+
let payload;
|
|
140
|
+
try {
|
|
141
|
+
payload = await response.json();
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
throw new Error("Runtime context response is malformed");
|
|
145
|
+
}
|
|
146
|
+
const context = parseRuntimeContext(payload);
|
|
147
|
+
if (context.agentType !== options.expectedAgentType) {
|
|
148
|
+
throw new Error("Runtime context agent type does not match the adapter type");
|
|
149
|
+
}
|
|
150
|
+
const contextualCredential = { ...credential, subject: context.subjectId };
|
|
151
|
+
validatedRuntimeSubjects.add(contextualCredential);
|
|
152
|
+
client.setRuntimeCredential(contextualCredential);
|
|
153
|
+
client.setRuntimeContext(context);
|
|
154
|
+
return context;
|
|
155
|
+
}
|
|
58
156
|
async function withEnrollmentLock(identity, work) {
|
|
59
157
|
const lockRoot = (0, node_path_1.join)((0, config_js_1.getDataDir)(), "runtime-credential-locks");
|
|
60
158
|
const lockPath = (0, node_path_1.join)(lockRoot, `${(0, runtime_credential_js_1.credentialAccount)(identity)}.lock`);
|
|
@@ -107,7 +205,17 @@ async function resolveRuntimeAgentCredentials(options = {}) {
|
|
|
107
205
|
subject,
|
|
108
206
|
fetch: options.fetchImpl,
|
|
109
207
|
});
|
|
110
|
-
|
|
208
|
+
let configured = (0, runtime_credential_js_1.resolveRuntimeCredential)({ env, subject, fetch: options.fetchImpl });
|
|
209
|
+
const current = options.runtimeCredential;
|
|
210
|
+
if (configured?.kind === "talos" &&
|
|
211
|
+
current?.kind === "talos" &&
|
|
212
|
+
current.subject &&
|
|
213
|
+
validatedRuntimeSubjects.has(current) &&
|
|
214
|
+
current.apiKeyFingerprint !== undefined &&
|
|
215
|
+
current.apiKeyFingerprint === configured.apiKeyFingerprint) {
|
|
216
|
+
configured = { ...configured, subject: current.subject };
|
|
217
|
+
validatedRuntimeSubjects.add(configured);
|
|
218
|
+
}
|
|
111
219
|
const existing = injected ?? configured
|
|
112
220
|
?? (options.runtimeCredential?.kind === "talos" ? options.runtimeCredential : undefined);
|
|
113
221
|
if (existing) {
|
|
@@ -342,7 +450,10 @@ function pruneAgentBlock(current, mutate) {
|
|
|
342
450
|
* sessions but the node is not, so a sub-agent must hang off *its own* run's
|
|
343
451
|
* agent node.
|
|
344
452
|
*/
|
|
345
|
-
function loadDelegationNodeId(harness, sessionKey) {
|
|
453
|
+
function loadDelegationNodeId(harness, sessionKey, env = process.env, runtimeCredential) {
|
|
454
|
+
const provisioned = resolvePreprovisionedRootDelegation(env, undefined, runtimeCredential);
|
|
455
|
+
if (provisioned)
|
|
456
|
+
return provisioned;
|
|
346
457
|
const config = (0, config_js_1.loadConfig)();
|
|
347
458
|
return config.delegationAnchors?.[harness]?.[sessionKey]
|
|
348
459
|
// Read the old location during the cutover; every subsequent save writes
|
|
@@ -996,8 +1107,8 @@ async function fetchClientCredentialsToken(args) {
|
|
|
996
1107
|
*/
|
|
997
1108
|
async function ensureAgentIdentity(client, options = {}) {
|
|
998
1109
|
const sessionId = options.sessionId ?? client.sessionId;
|
|
999
|
-
// Agent Security isn't connected
|
|
1000
|
-
//
|
|
1110
|
+
// Agent Security isn't connected: skip runtime credential resolution and
|
|
1111
|
+
// record the no-op, shaped like the user
|
|
1001
1112
|
// gate's not_connected result.
|
|
1002
1113
|
const resolvedConfig = (0, config_js_1.resolveConfig)();
|
|
1003
1114
|
const security = resolvedConfig.security;
|
|
@@ -1193,13 +1304,12 @@ async function drainRetiredCredentials(client, opts) {
|
|
|
1193
1304
|
});
|
|
1194
1305
|
}
|
|
1195
1306
|
/**
|
|
1196
|
-
* Resolve a sub-agent
|
|
1197
|
-
*
|
|
1198
|
-
*
|
|
1307
|
+
* Resolve a sub-agent to a child Talos credential. Stored credentials are
|
|
1308
|
+
* loaded by spawn descriptor; missing credentials may be enrolled through the
|
|
1309
|
+
* authenticated parent runtime.
|
|
1199
1310
|
*/
|
|
1200
1311
|
async function ensureSubAgentIdentity(client, options) {
|
|
1201
|
-
// Same no-op as ensureAgentIdentity
|
|
1202
|
-
// Security is not connected.
|
|
1312
|
+
// Same no-op as ensureAgentIdentity while Agent Security is disconnected.
|
|
1203
1313
|
const resolvedConfig = (0, config_js_1.resolveConfig)();
|
|
1204
1314
|
const subAgentSecurity = resolvedConfig.security;
|
|
1205
1315
|
if (!subAgentSecurity.connected) {
|
package/dist/auth-store.d.ts
CHANGED
|
@@ -33,9 +33,9 @@ export interface EnvUserCredential {
|
|
|
33
33
|
*
|
|
34
34
|
* **`ORY_USER_OAUTH2_TOKEN` is the only accepted variable**, because an OAuth2
|
|
35
35
|
* access token is the only thing the credential is ever used as. All three
|
|
36
|
-
* consumers of `userPrincipal.token` demand one: broker delegation
|
|
37
|
-
*
|
|
38
|
-
*
|
|
36
|
+
* consumers of `userPrincipal.token` demand one: broker delegation ownership
|
|
37
|
+
* and root Talos enrollment (which sends it as the user bearer). Nothing calls
|
|
38
|
+
* `verifySession`, so a Kratos
|
|
39
39
|
* session token is never consumed as a session token.
|
|
40
40
|
*
|
|
41
41
|
* Three variables used to be accepted alongside it and have been removed:
|
package/dist/auth-store.js
CHANGED
|
@@ -100,9 +100,9 @@ function clearTokens() {
|
|
|
100
100
|
*
|
|
101
101
|
* **`ORY_USER_OAUTH2_TOKEN` is the only accepted variable**, because an OAuth2
|
|
102
102
|
* access token is the only thing the credential is ever used as. All three
|
|
103
|
-
* consumers of `userPrincipal.token` demand one: broker delegation
|
|
104
|
-
*
|
|
105
|
-
*
|
|
103
|
+
* consumers of `userPrincipal.token` demand one: broker delegation ownership
|
|
104
|
+
* and root Talos enrollment (which sends it as the user bearer). Nothing calls
|
|
105
|
+
* `verifySession`, so a Kratos
|
|
106
106
|
* session token is never consumed as a session token.
|
|
107
107
|
*
|
|
108
108
|
* Three variables used to be accepted alongside it and have been removed:
|
|
@@ -142,16 +142,60 @@ function isExpired(tokens, now = Date.now()) {
|
|
|
142
142
|
* or if the refresh call fails.
|
|
143
143
|
*/
|
|
144
144
|
async function refreshAndSave(args) {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
145
|
+
return (0, config_js_1.mutateConfigAsync)(async (config) => {
|
|
146
|
+
const persistedProjectOrigin = canonicalProjectOrigin(config.projectUrl);
|
|
147
|
+
const requestedProjectOrigin = canonicalProjectOrigin(args.projectUrl);
|
|
148
|
+
if (persistedProjectOrigin !== requestedProjectOrigin) {
|
|
149
|
+
throw new Error("Persisted user OAuth2 tokens are bound to the current config projectUrl; sign in again for the selected project.");
|
|
150
|
+
}
|
|
151
|
+
const persisted = config.user?.oauth2;
|
|
152
|
+
const persistedClientId = persisted?.clientId?.trim() ||
|
|
153
|
+
config.oauth2ClientId?.trim() ||
|
|
154
|
+
config_js_1.DEFAULT_OAUTH2_CLIENT_ID;
|
|
155
|
+
if (persistedClientId !== args.clientId.trim()) {
|
|
156
|
+
throw new Error("Persisted user OAuth2 tokens are bound to a different OAuth2 client ID; sign in again with the selected client.");
|
|
157
|
+
}
|
|
158
|
+
if (persisted && !isExpired(persisted)) {
|
|
159
|
+
return { value: persisted };
|
|
160
|
+
}
|
|
161
|
+
const current = persisted ?? args.current;
|
|
162
|
+
if (!current.refreshToken) {
|
|
163
|
+
throw new Error("No refresh_token available; a fresh login is required.");
|
|
164
|
+
}
|
|
165
|
+
const next = await (0, auth_js_1.refreshAccessToken)({
|
|
166
|
+
projectUrl: args.projectUrl,
|
|
167
|
+
clientId: args.clientId,
|
|
168
|
+
refreshToken: current.refreshToken,
|
|
169
|
+
});
|
|
170
|
+
return {
|
|
171
|
+
config: {
|
|
172
|
+
...config,
|
|
173
|
+
user: { ...(config.user ?? {}), oauth2: next },
|
|
174
|
+
},
|
|
175
|
+
value: next,
|
|
176
|
+
};
|
|
152
177
|
});
|
|
153
|
-
|
|
154
|
-
|
|
178
|
+
}
|
|
179
|
+
function canonicalProjectOrigin(raw) {
|
|
180
|
+
if (!raw?.trim()) {
|
|
181
|
+
throw new Error("Persisted user OAuth2 tokens require projectUrl in the current config.");
|
|
182
|
+
}
|
|
183
|
+
let parsed;
|
|
184
|
+
try {
|
|
185
|
+
parsed = new URL(raw.trim());
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
throw new Error("Persisted user OAuth2 tokens require a valid projectUrl origin.");
|
|
189
|
+
}
|
|
190
|
+
if ((parsed.protocol !== "https:" && parsed.protocol !== "http:") ||
|
|
191
|
+
parsed.username ||
|
|
192
|
+
parsed.password ||
|
|
193
|
+
(parsed.pathname !== "/" && parsed.pathname !== "") ||
|
|
194
|
+
parsed.search ||
|
|
195
|
+
parsed.hash) {
|
|
196
|
+
throw new Error("Persisted user OAuth2 tokens require a valid projectUrl origin.");
|
|
197
|
+
}
|
|
198
|
+
return parsed.origin;
|
|
155
199
|
}
|
|
156
200
|
/**
|
|
157
201
|
* Try to acquire the pkce-flight lock. Returns the lock handle if this
|
package/dist/auth.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ export declare const LOOPBACK_PORTS: readonly [47823, 47824, 47825, 47826];
|
|
|
26
26
|
*
|
|
27
27
|
* The binding constraint is Antigravity, which validates its hook timeout to
|
|
28
28
|
* `5..120` seconds — so 120s is the ceiling, and this leaves headroom for the
|
|
29
|
-
*
|
|
29
|
+
* Talos enrollment and delegation recording that run after login inside
|
|
30
30
|
* the same hook invocation. See `hook-timeout.ts`, which derives every
|
|
31
31
|
* harness's declared window and asserts it outlasts this value.
|
|
32
32
|
*/
|
package/dist/auth.js
CHANGED
|
@@ -75,7 +75,7 @@ exports.LOOPBACK_PORTS = [47823, 47824, 47825, 47826];
|
|
|
75
75
|
*
|
|
76
76
|
* The binding constraint is Antigravity, which validates its hook timeout to
|
|
77
77
|
* `5..120` seconds — so 120s is the ceiling, and this leaves headroom for the
|
|
78
|
-
*
|
|
78
|
+
* Talos enrollment and delegation recording that run after login inside
|
|
79
79
|
* the same hook invocation. See `hook-timeout.ts`, which derives every
|
|
80
80
|
* harness's declared window and asserts it outlasts this value.
|
|
81
81
|
*/
|
package/dist/build-info.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"repo": "ory-agent-plugins",
|
|
3
|
-
"commit": "
|
|
4
|
-
"commitShort": "
|
|
3
|
+
"commit": "7f6431efd4116e683b164cf2c7300cb44d0c4dcb",
|
|
4
|
+
"commitShort": "7f6431e",
|
|
5
5
|
"branch": "main",
|
|
6
|
-
"commitDate": "2026-09-
|
|
6
|
+
"commitDate": "2026-09-28T11:06:31-07:00",
|
|
7
7
|
"dirty": false,
|
|
8
|
-
"builtAt": "2026-09-
|
|
8
|
+
"builtAt": "2026-09-28T18:10:11.171Z"
|
|
9
9
|
}
|
package/dist/cli.js
CHANGED
|
@@ -300,6 +300,11 @@ function printEnvironment() {
|
|
|
300
300
|
],
|
|
301
301
|
["ORY_USER_SUBJECT_ID", process.env.ORY_USER_SUBJECT_ID, false],
|
|
302
302
|
["ORY_AGENT_SUBJECT_ID", process.env.ORY_AGENT_SUBJECT_ID, false],
|
|
303
|
+
[
|
|
304
|
+
"ORY_AGENT_ROOT_DELEGATION",
|
|
305
|
+
process.env.ORY_AGENT_ROOT_DELEGATION ? "(set)" : undefined,
|
|
306
|
+
false,
|
|
307
|
+
],
|
|
303
308
|
["ORY_AGENT_DEBUG", process.env.ORY_AGENT_DEBUG, false],
|
|
304
309
|
["ORY_AGENT_LOG_FILE", process.env.ORY_AGENT_LOG_FILE, false],
|
|
305
310
|
];
|
package/dist/client.d.ts
CHANGED
|
@@ -93,6 +93,14 @@ export interface RecordDelegationResult {
|
|
|
93
93
|
/** Root-first chain, e.g. `["User:<id>", "agent:<join>"]`. */
|
|
94
94
|
delegationChain: string[];
|
|
95
95
|
}
|
|
96
|
+
/** Administrator-provisioned runtime metadata resolved from Agent Security. */
|
|
97
|
+
export interface RuntimeContext {
|
|
98
|
+
authenticated: true;
|
|
99
|
+
subjectId: string;
|
|
100
|
+
rootDelegation: string;
|
|
101
|
+
deploymentId: string;
|
|
102
|
+
agentType: string;
|
|
103
|
+
}
|
|
96
104
|
/**
|
|
97
105
|
* A principal known to the client. Either the human user (interactive
|
|
98
106
|
* PKCE login) or the agent (machine credentials).
|
|
@@ -111,6 +119,7 @@ export declare class OryAgentClient {
|
|
|
111
119
|
readonly logger: DebugLogger;
|
|
112
120
|
private readonly eventReporter?;
|
|
113
121
|
private _runtimeCredential?;
|
|
122
|
+
private _runtimeContext?;
|
|
114
123
|
private readonly _runtimeCredentialsBySession;
|
|
115
124
|
private readonly _runtimeCredentialsByPrincipalKey;
|
|
116
125
|
private sessionCache;
|
|
@@ -146,6 +155,10 @@ export declare class OryAgentClient {
|
|
|
146
155
|
/** Active noninteractive broker credential. It is process memory only. */
|
|
147
156
|
get runtimeCredential(): RuntimeCredential | undefined;
|
|
148
157
|
get hasRuntimeCredential(): boolean;
|
|
158
|
+
/** Process-local deployment context. It is never persisted or copied to the environment. */
|
|
159
|
+
get runtimeContext(): RuntimeContext | undefined;
|
|
160
|
+
get runtimeRootDelegation(): string | undefined;
|
|
161
|
+
setRuntimeContext(context: RuntimeContext): void;
|
|
149
162
|
setRuntimeCredential(credential: RuntimeCredential | undefined): void;
|
|
150
163
|
/** Retain an actor-scoped credential for deferred event delivery without activating it globally. */
|
|
151
164
|
rememberRuntimeCredential(credential: RuntimeCredential): void;
|
|
@@ -251,6 +264,8 @@ export declare class OryAgentClient {
|
|
|
251
264
|
agentToken?: string;
|
|
252
265
|
runtimeCredential?: RuntimeCredential;
|
|
253
266
|
signal?: AbortSignal;
|
|
267
|
+
/** Authenticate without a user bearer for a preprovisioned root lineage. */
|
|
268
|
+
preprovisionedRoot?: boolean;
|
|
254
269
|
}): Promise<RecordDelegationResult>;
|
|
255
270
|
/**
|
|
256
271
|
* Activity attributes describing which principals are attached to the
|