@ory/argus 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.
Files changed (150) hide show
  1. package/README.md +31 -46
  2. package/assets/commands/temporal-up.md +1 -1
  3. package/assets/skills/auth-setup/SKILL.md +1 -1
  4. package/assets/skills/local-dev/SKILL.md +17 -7
  5. package/assets/skills/ory-build-agent/SKILL.md +39 -91
  6. package/assets/skills/ory-e2b-sandbox/SKILL.md +18 -17
  7. package/assets/skills/ory-temporal-worker/SKILL.md +31 -33
  8. package/assets/skills/permissions-onboarding/SKILL.md +131 -104
  9. package/dist/adapters.d.ts +88 -32
  10. package/dist/adapters.js +443 -164
  11. package/dist/agent-auth.d.ts +226 -67
  12. package/dist/agent-auth.js +951 -205
  13. package/dist/auth-store.d.ts +37 -2
  14. package/dist/auth-store.js +37 -3
  15. package/dist/auth.d.ts +33 -4
  16. package/dist/auth.js +163 -20
  17. package/dist/bash-parser.d.ts +98 -0
  18. package/dist/bash-parser.js +396 -0
  19. package/dist/branding.d.ts +77 -16
  20. package/dist/branding.js +93 -23
  21. package/dist/build-info.json +4 -4
  22. package/dist/cli-invocation.d.ts +1 -1
  23. package/dist/cli-invocation.js +2 -1
  24. package/dist/cli.d.ts +20 -29
  25. package/dist/cli.js +271 -239
  26. package/dist/client.d.ts +175 -138
  27. package/dist/client.js +672 -391
  28. package/dist/config.d.ts +217 -54
  29. package/dist/config.js +461 -42
  30. package/dist/context.d.ts +10 -0
  31. package/dist/context.js +21 -0
  32. package/dist/contract-suite.d.ts +3 -5
  33. package/dist/contract-suite.js +75 -47
  34. package/dist/denial.d.ts +36 -3
  35. package/dist/denial.js +79 -10
  36. package/dist/event-reporter.d.ts +77 -0
  37. package/dist/event-reporter.js +776 -0
  38. package/dist/external-registrations-main.d.ts +10 -0
  39. package/dist/external-registrations-main.js +38 -0
  40. package/dist/external-registrations.d.ts +79 -0
  41. package/dist/external-registrations.js +188 -0
  42. package/dist/help-cli.d.ts +39 -0
  43. package/dist/help-cli.js +55 -0
  44. package/dist/hook-timeout.d.ts +64 -0
  45. package/dist/hook-timeout.js +88 -0
  46. package/dist/index.d.ts +28 -23
  47. package/dist/index.js +173 -58
  48. package/dist/lifecycle.d.ts +3 -3
  49. package/dist/lifecycle.js +38 -6
  50. package/dist/local/cli.js +11 -6
  51. package/dist/local/configs.d.ts +74 -18
  52. package/dist/local/configs.js +291 -84
  53. package/dist/local/health.js +7 -7
  54. package/dist/local/index.d.ts +2 -2
  55. package/dist/local/index.js +24 -10
  56. package/dist/local/manager.d.ts +20 -1
  57. package/dist/local/manager.js +159 -36
  58. package/dist/local/ports.d.ts +158 -0
  59. package/dist/local/ports.js +443 -0
  60. package/dist/local/seed.d.ts +13 -5
  61. package/dist/local/seed.js +62 -38
  62. package/dist/logger.d.ts +54 -25
  63. package/dist/logger.js +329 -63
  64. package/dist/mcp.d.ts +2 -2
  65. package/dist/mcp.js +10 -5
  66. package/dist/mirror-bootstrap.d.ts +48 -0
  67. package/dist/mirror-bootstrap.js +254 -0
  68. package/dist/opl.d.ts +289 -0
  69. package/dist/opl.js +446 -0
  70. package/dist/permission-mode.d.ts +87 -0
  71. package/dist/permission-mode.js +307 -0
  72. package/dist/permissions-cli.d.ts +13 -49
  73. package/dist/permissions-cli.js +154 -348
  74. package/dist/permissions.d.ts +148 -38
  75. package/dist/permissions.js +591 -45
  76. package/dist/post-install.d.ts +33 -0
  77. package/dist/post-install.js +127 -0
  78. package/dist/read-credential.d.ts +65 -0
  79. package/dist/read-credential.js +86 -0
  80. package/dist/registry/cli.js +5 -2
  81. package/dist/registry/config.d.ts +0 -17
  82. package/dist/registry/config.js +0 -23
  83. package/dist/registry/index.d.ts +1 -1
  84. package/dist/registry/index.js +2 -2
  85. package/dist/registry/manager.d.ts +4 -21
  86. package/dist/registry/manager.js +21 -37
  87. package/dist/runtime-credential.d.ts +140 -0
  88. package/dist/runtime-credential.js +572 -0
  89. package/dist/runtime.d.ts +408 -0
  90. package/dist/runtime.js +748 -0
  91. package/dist/setup.d.ts +23 -47
  92. package/dist/setup.js +59 -116
  93. package/dist/skills.js +0 -7
  94. package/dist/status-cli.d.ts +27 -11
  95. package/dist/status-cli.js +121 -115
  96. package/dist/status-data.d.ts +116 -17
  97. package/dist/status-data.js +121 -38
  98. package/dist/subject.d.ts +126 -20
  99. package/dist/subject.js +215 -30
  100. package/dist/testing.d.ts +74 -38
  101. package/dist/testing.js +185 -68
  102. package/dist/tool-catalog.d.ts +53 -11
  103. package/dist/tool-catalog.js +164 -13
  104. package/dist/tool-metadata.d.ts +7 -6
  105. package/dist/tool-metadata.js +6 -5
  106. package/dist/types.d.ts +11 -1
  107. package/dist/uninstall.d.ts +71 -34
  108. package/dist/uninstall.js +217 -85
  109. package/dist/user-login.d.ts +9 -10
  110. package/dist/user-login.js +56 -75
  111. package/dist/watch-cli.d.ts +6 -0
  112. package/dist/watch-cli.js +217 -0
  113. package/package.json +4 -27
  114. package/assets/commands/dashboard.md +0 -34
  115. package/dist/dashboard-cli.d.ts +0 -8
  116. package/dist/dashboard-cli.js +0 -70
  117. package/dist/dev.d.ts +0 -103
  118. package/dist/dev.js +0 -583
  119. package/dist/interactive-setup.d.ts +0 -286
  120. package/dist/interactive-setup.js +0 -1734
  121. package/dist/local/jaeger-main.d.ts +0 -13
  122. package/dist/local/jaeger-main.js +0 -85
  123. package/dist/local/jaeger.d.ts +0 -50
  124. package/dist/local/jaeger.js +0 -162
  125. package/dist/otel/exporter.d.ts +0 -17
  126. package/dist/otel/exporter.js +0 -12
  127. package/dist/otel/index.d.ts +0 -2
  128. package/dist/otel/index.js +0 -8
  129. package/dist/otel/otlp.d.ts +0 -103
  130. package/dist/otel/otlp.js +0 -385
  131. package/dist/project-api-key.d.ts +0 -69
  132. package/dist/project-api-key.js +0 -147
  133. package/dist/setup-actions.d.ts +0 -232
  134. package/dist/setup-actions.js +0 -507
  135. package/dist/tracer.d.ts +0 -190
  136. package/dist/tracer.js +0 -481
  137. package/dist/watch-sandbox.d.ts +0 -9
  138. package/dist/watch-sandbox.js +0 -81
  139. package/dist/web/api.d.ts +0 -33
  140. package/dist/web/api.js +0 -294
  141. package/dist/web/launch.d.ts +0 -11
  142. package/dist/web/launch.js +0 -96
  143. package/dist/web/server.d.ts +0 -20
  144. package/dist/web/server.js +0 -233
  145. package/dist/web/types.d.ts +0 -65
  146. package/dist/web/types.js +0 -2
  147. package/dist/webapp/assets/index-Wucl4SZs.css +0 -1
  148. package/dist/webapp/assets/index-m-GtEdq0.js +0 -49
  149. package/dist/webapp/favicon.ico +0 -0
  150. package/dist/webapp/index.html +0 -15
package/dist/subject.js CHANGED
@@ -2,28 +2,45 @@
2
2
  /**
3
3
  * User-subject resolution for permission checks.
4
4
  *
5
- * Permission checks need a subject reference that matches the form
6
- * stored in Keto. Two shapes are supported:
5
+ * Every principal user, agent, sub-agent is addressed as a **SubjectSet**
6
+ * `<namespace>:<object>` (e.g. `User:<UUID>`). This is the form the Ory Console's
7
+ * *Add relationship* dialog writes, the form the local-stack seed writes, and the
8
+ * Zanzibar-idiomatic composition point (grant `Role:developer` `use` on a tool and
9
+ * add principals to the role, rather than fanning out a tuple per subject). It is
10
+ * the only first-class subject form: Keto does no cross-form expansion, so a check
11
+ * must match the write shape exactly, and standardizing on one shape removes that
12
+ * silent-misfire footgun.
7
13
  *
8
- * - **SubjectSet** — `<namespace>:<object>` (e.g. `User:<UUID>`). This
9
- * is the form the Console's *Add relationship* dialog writes, so
10
- * dev/demo flows that grant tuples through the UI use it.
11
- *
12
- * - **Direct SubjectID** opaque string (e.g. `user:<UUID>` or
13
- * `session:<id>`). Backward-compatible default when no subject
14
- * namespace is configured.
15
- *
16
- * The selection is driven by the user-subject namespace: `ORY_USER_SUBJECT_NAMESPACE`
17
- * takes precedence, then the client's configured `userSubjectNamespace` (persisted
18
- * in the shared config, e.g. by the local-stack install path). When a namespace is
19
- * present the resolver builds a SubjectSet using it; otherwise it falls back to the
20
- * direct SubjectID chain.
14
+ * The namespace is resolved by precedence: `ORY_USER_SUBJECT_NAMESPACE` (env)
15
+ * the client's configured `userSubjectNamespace` (persisted in the shared config)
16
+ * the {@link DEFAULT_USER_SUBJECT_NAMESPACE} (`User`). The env/config values now
17
+ * *override* the default namespace; they are no longer an opt-in switch that
18
+ * toggles between SubjectSet and a legacy direct SubjectID.
21
19
  */
22
20
  Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.AGENT_SUBJECT_ID_ENV_VAR = exports.SUBJECT_AXIS_SEPARATOR = exports.SESSION_NAMESPACE = exports.SUBAGENT_NAMESPACE = exports.AGENT_NAMESPACE = exports.DEFAULT_USER_SUBJECT_NAMESPACE = void 0;
23
22
  exports.runWithUserSubject = runWithUserSubject;
24
23
  exports.resolveUserSubject = resolveUserSubject;
25
24
  exports.subjectLabel = subjectLabel;
25
+ exports.validateSubjectOverride = validateSubjectOverride;
26
+ exports.readAgentSubjectOverride = readAgentSubjectOverride;
27
+ exports.resolveAgentSubject = resolveAgentSubject;
28
+ exports.resolveAgentSessionSubject = resolveAgentSessionSubject;
29
+ exports.resolveSubAgentSubject = resolveSubAgentSubject;
30
+ exports.resolveSubAgentSpawnSubject = resolveSubAgentSpawnSubject;
26
31
  const node_async_hooks_1 = require("node:async_hooks");
32
+ /**
33
+ * Default namespace a resolved user principal is addressed under. The
34
+ * local-stack seed, the Console *Add relationship* dialog, and the Network OPL
35
+ * provisioning all use `User`, so it is the out-of-the-box default.
36
+ */
37
+ exports.DEFAULT_USER_SUBJECT_NAMESPACE = "User";
38
+ /** Namespace the agent principal is addressed under (delegation subjects). */
39
+ exports.AGENT_NAMESPACE = "Agent";
40
+ /** Namespace a sub-agent principal is addressed under. */
41
+ exports.SUBAGENT_NAMESPACE = "SubAgent";
42
+ /** Namespace the no-user-identity `session:<id>` fallback is addressed under. */
43
+ exports.SESSION_NAMESPACE = "Session";
27
44
  /**
28
45
  * Per-async-context user-subject override. Populated by
29
46
  * {@link runWithUserSubject} so multi-user server integrations (Vercel AI
@@ -37,7 +54,7 @@ const perCallUserSubject = new node_async_hooks_1.AsyncLocalStorage();
37
54
  * client's user principal and the env overrides in
38
55
  * {@link resolveUserSubject}. The override only applies within `fn`'s
39
56
  * async context, so concurrent calls cannot observe each other's subject.
40
- * `ORY_USER_SUBJECT_NAMESPACE` SubjectSet shaping still applies.
57
+ * SubjectSet shaping still applies.
41
58
  *
42
59
  * A missing/empty `subject` is a no-op: `fn` runs with the normal
43
60
  * resolution chain.
@@ -47,6 +64,10 @@ function runWithUserSubject(subject, fn) {
47
64
  return fn();
48
65
  return perCallUserSubject.run(subject, fn);
49
66
  }
67
+ /** Build a SubjectSet ref with an empty relation (a plain subject, not a subject-set expansion). */
68
+ function subjectSet(namespace, object) {
69
+ return { subjectSet: { namespace, object, relation: "" } };
70
+ }
50
71
  /**
51
72
  * Resolve the user subject for permission checks. Prefers a per-call
52
73
  * override installed via {@link runWithUserSubject}, then the user
@@ -54,32 +75,196 @@ function runWithUserSubject(subject, fn) {
54
75
  * legacy `ORY_AGENT_SUBJECT_ID`, then the caller-supplied `fallback`
55
76
  * (typically `session:<id>`).
56
77
  *
57
- * Returns a SubjectSet when a user-subject namespace is configured (env or
58
- * client config) and a concrete subject is available; otherwise a direct
59
- * SubjectID.
78
+ * Always returns a SubjectSet. A resolved principal is addressed under the
79
+ * configured user namespace (`ORY_USER_SUBJECT_NAMESPACE` client config
80
+ * {@link DEFAULT_USER_SUBJECT_NAMESPACE}). A `session:`-prefixed fallback is
81
+ * addressed under {@link SESSION_NAMESPACE} (`Session:<id>`). Only when nothing
82
+ * resolves at all do we emit the direct `agent:unknown` sentinel — an error
83
+ * marker that must never match a stored tuple.
60
84
  */
61
85
  function resolveUserSubject(client, fallback) {
62
- const subject = perCallUserSubject.getStore()
86
+ const principal = perCallUserSubject.getStore()
63
87
  ?? client.userPrincipal.subject
64
88
  ?? process.env.ORY_USER_SUBJECT_ID
65
- ?? process.env.ORY_AGENT_SUBJECT_ID
66
- ?? fallback;
67
- const ns = process.env.ORY_USER_SUBJECT_NAMESPACE?.trim() || client.userSubjectNamespace;
68
- if (subject && ns) {
69
- return {
70
- subjectSet: { namespace: ns, object: subject, relation: "" },
71
- };
89
+ // Validated, not read raw: the same value feeds the agent subject, so a
90
+ // value rejected there must not slip in here (#241).
91
+ ?? readAgentSubjectOverride();
92
+ const ns = process.env.ORY_USER_SUBJECT_NAMESPACE?.trim()
93
+ || client.userSubjectNamespace
94
+ || exports.DEFAULT_USER_SUBJECT_NAMESPACE;
95
+ if (principal)
96
+ return subjectSet(ns, principal);
97
+ // No principal — use the caller's fallback. A `session:<id>` fallback is
98
+ // addressed under the Session namespace so it is a well-formed SubjectSet that
99
+ // could match a Session-scoped grant, rather than being wrapped under `User`.
100
+ if (fallback) {
101
+ const colon = fallback.indexOf(":");
102
+ if (colon > 0 && fallback.slice(0, colon).toLowerCase() === "session") {
103
+ return subjectSet(exports.SESSION_NAMESPACE, fallback.slice(colon + 1));
104
+ }
105
+ return subjectSet(ns, fallback);
72
106
  }
73
- if (subject)
74
- return { subjectId: subject };
75
107
  return { subjectId: "agent:unknown" };
76
108
  }
77
109
  /**
78
110
  * Printable label for a `UserSubjectRef`. Used in denial messages and
79
- * span attributes. SubjectSets render as `<namespace>:<object>`.
111
+ * activity attributes. SubjectSets render as `<namespace>:<object>`.
80
112
  */
81
113
  function subjectLabel(ref) {
82
114
  return "subjectSet" in ref
83
115
  ? `${ref.subjectSet.namespace}:${ref.subjectSet.object}`
84
116
  : ref.subjectId;
85
117
  }
118
+ // ─── Machine principals (agent / sub-agent) ──────────────────────────
119
+ //
120
+ // The agent's OAuth2 credential is session-scoped: one DCR client per
121
+ // `(projectUrl, harness, session)`, plus one client per typed sub-agent in that
122
+ // session. Sessionless integrations use a reserved persistent slot.
123
+ //
124
+ // The registered client id is the principal root. Session and spawn axes remain
125
+ // available as narrower broker subjects and are appended with `|`:
126
+ //
127
+ // Agent:<clientId> session credential
128
+ // Agent:<clientId>|<session> one run
129
+ // SubAgent:<clientId> typed sub-agent credential
130
+ // SubAgent:<clientId>|<session>|<type>[|<spawn>] one spawn
131
+ //
132
+ // Every one of these is computable locally, so a block can be written in the
133
+ // Console against the exact string the plugin will check. Nothing here depends
134
+ // on the broker's node id, which is opaque and server-assigned.
135
+ //
136
+ // See docs/session-identity.md.
137
+ /** Separator between the credential client id and narrower scoping axes. */
138
+ exports.SUBJECT_AXIS_SEPARATOR = "|";
139
+ /**
140
+ * Join a client id with the derived axes that narrow it. Empty and whitespace-
141
+ * only axes are dropped rather than producing a trailing separator — a subject
142
+ * ending in `|` would silently never match a stored relation.
143
+ */
144
+ function deriveSubject(clientId, ...axes) {
145
+ return [clientId, ...axes.map((a) => a?.trim()).filter((a) => !!a)].join(exports.SUBJECT_AXIS_SEPARATOR);
146
+ }
147
+ /** The env var that overrides the agent's subject. */
148
+ exports.AGENT_SUBJECT_ID_ENV_VAR = "ORY_AGENT_SUBJECT_ID";
149
+ /**
150
+ * Validate a subject override (issue #241).
151
+ *
152
+ * `deriveSubject` above already refuses to produce a trailing separator,
153
+ * because a subject ending in `|` silently never matches a stored relation.
154
+ * The same care is owed to the *base* it joins onto, which arrives from an
155
+ * operator-settable env var and was previously used verbatim.
156
+ *
157
+ * A value containing the axis separator is rejected for two independent
158
+ * reasons:
159
+ *
160
+ * 1. **It collapses the identity grains.** The credential, session, and spawn
161
+ * subjects are told apart only by separator position, so an override
162
+ * containing one makes them ambiguous: `override="a|b"` in session `c` and
163
+ * `override="a"` in session `b|c` produce the same string. A session-scoped
164
+ * block written to stop one runaway run could then match the base credential, or a
165
+ * different run — and all three grains are read together in one batched
166
+ * check, so this is a live semantics break rather than a theoretical one.
167
+ * 2. **It forges a delegation-node reference** (#225). A join key is exactly
168
+ * `<identity>|<harness>|<host>|<session>`, and the two string spaces stay
169
+ * disjoint today only because a Hydra client id is a UUID and contains no
170
+ * separator. This env var is the one supported knob that breaks that.
171
+ *
172
+ * Rejected rather than sanitized: silently stripping the separator would
173
+ * produce a *different* subject than the operator asked for, which fails just
174
+ * as confusingly and is harder to notice. Ignoring the value falls back to the
175
+ * client id — a real, working identity — and says so.
176
+ */
177
+ function validateSubjectOverride(raw, envVar = exports.AGENT_SUBJECT_ID_ENV_VAR) {
178
+ if (raw === undefined)
179
+ return {};
180
+ const trimmed = raw.trim();
181
+ if (!trimmed) {
182
+ return {
183
+ warning: `${envVar} is set but empty — ignoring it and using the client id as the subject.`,
184
+ };
185
+ }
186
+ if (trimmed.includes(exports.SUBJECT_AXIS_SEPARATOR)) {
187
+ return {
188
+ warning: `${envVar}="${trimmed}" contains "${exports.SUBJECT_AXIS_SEPARATOR}", which separates the derived `
189
+ + `session and spawn axes of a subject — a value containing it is ambiguous with a `
190
+ + `session-scoped subject and with a delegation-node reference. Ignoring it and using the `
191
+ + `client id as the subject.`,
192
+ };
193
+ }
194
+ return { value: trimmed };
195
+ }
196
+ /**
197
+ * Read and validate `ORY_AGENT_SUBJECT_ID`. Returns `undefined` when unset or
198
+ * rejected, pushing an explanation onto `warnings` in the latter case.
199
+ *
200
+ * One reader for every site that consumes the override, so the rule cannot hold
201
+ * in the resolver and not in what `status` reports.
202
+ */
203
+ function readAgentSubjectOverride(env = process.env, warnings) {
204
+ const { value, warning } = validateSubjectOverride(env[exports.AGENT_SUBJECT_ID_ENV_VAR]);
205
+ if (warning)
206
+ warnings?.push(warning);
207
+ return value;
208
+ }
209
+ /**
210
+ * The registered agent credential subject: `Agent:<clientId>`. For DCR this
211
+ * client belongs to one session; static credentials may be shared explicitly.
212
+ *
213
+ * `undefined` when no agent principal is populated — the agent gate never
214
+ * blocks, so a session with no resolved machine identity is normal and callers
215
+ * must treat a missing agent subject as "nothing to check", not as a deny.
216
+ */
217
+ function resolveAgentSubject(client) {
218
+ const subject = client.agentPrincipal.subject;
219
+ return subject ? subjectSet(exports.AGENT_NAMESPACE, subject) : undefined;
220
+ }
221
+ /**
222
+ * The **session-level** agent subject: `Agent:<clientId>|<session>`, unique to
223
+ * one run. Used for a block that stops the session's narrower subject without
224
+ * blocking its base credential subject.
225
+ *
226
+ * `undefined` when there is no agent principal, and — deliberately — also when
227
+ * there is no session. A caller with no session concept (an SDK integration in
228
+ * a long-running service) has exactly one sessionless credential subject;
229
+ * inventing a placeholder session would either collapse every run onto
230
+ * one string or produce a subject no admin could have written.
231
+ */
232
+ function resolveAgentSessionSubject(client, sessionId) {
233
+ const subject = client.agentPrincipal.subject;
234
+ const session = (sessionId ?? client.sessionId)?.trim();
235
+ if (!subject || !session)
236
+ return undefined;
237
+ return subjectSet(exports.AGENT_NAMESPACE, deriveSubject(subject, session));
238
+ }
239
+ /**
240
+ * The registered sub-agent credential subject: `SubAgent:<clientId>` for one
241
+ * typed sub-agent in one session.
242
+ *
243
+ * `subAgentClientId` is the client id from {@link ensureSubAgentIdentity};
244
+ * `undefined` in, and `undefined` out, since a sub-agent whose identity did not
245
+ * resolve has nothing to check.
246
+ */
247
+ function resolveSubAgentSubject(subAgentClientId) {
248
+ return subAgentClientId
249
+ ? subjectSet(exports.SUBAGENT_NAMESPACE, subAgentClientId)
250
+ : undefined;
251
+ }
252
+ /**
253
+ * The **spawn-level** sub-agent subject:
254
+ * `SubAgent:<clientId>|<session>|<type>[|<spawn>]`, the finest grain available.
255
+ *
256
+ * `perSpawnId` distinguishes two *concurrent* same-type sub-agents and is only
257
+ * present on harnesses that expose one (Cursor's `subagent_id`, Claude Code's
258
+ * `agent_id`, OpenClaw's `childRunId`, …). Where the harness exposes none the
259
+ * subject stops at the type and concurrent spawns share it — a limit of the
260
+ * harness, not of this model.
261
+ *
262
+ * As with the agent session subject, a missing session yields `undefined`
263
+ * rather than a placeholder.
264
+ */
265
+ function resolveSubAgentSpawnSubject(client, args) {
266
+ const session = (args.sessionId ?? client.sessionId)?.trim();
267
+ if (!args.subAgentClientId || !session)
268
+ return undefined;
269
+ return subjectSet(exports.SUBAGENT_NAMESPACE, deriveSubject(args.subAgentClientId, session, args.subAgentType, args.perSpawnId));
270
+ }
package/dist/testing.d.ts CHANGED
@@ -7,7 +7,8 @@
7
7
  */
8
8
  import { vi } from "vitest";
9
9
  import { OryAgentClient } from "./client.js";
10
- import type { TraceSpan, TraceEvent } from "./tracer.js";
10
+ import type { ActivityEntry } from "./logger.js";
11
+ import type { PermissionMode } from "./config.js";
11
12
  export { runHarnessContractSuite, type HarnessContractAdapter, type ContractContext, type ContractGates, type ContractOutcome, } from "./contract-suite.js";
12
13
  /**
13
14
  * Create an OryAgentClient with session caching disabled.
@@ -15,18 +16,26 @@ export { runHarnessContractSuite, type HarnessContractAdapter, type ContractCont
15
16
  */
16
17
  export declare function createMockClient(overrides?: Partial<ConstructorParameters<typeof OryAgentClient>[0]>): OryAgentClient;
17
18
  /**
18
- * Control what the exact-tuple existence probe ({@link OryAgentClient.relationshipExists},
19
- * used by `ensureRelationship`) reports. Pass `true` to model a tuple that is
20
- * already stored (the write is then skipped), `false` for absent (the write
21
- * proceeds). Returns the spy so callers can e.g. `.mockRejectedValue(...)` to
22
- * model a failed probe.
19
+ * Spy on {@link OryAgentClient.recordDelegation} (the agent-security broker
20
+ * call the delegation writers use). Resolves to `result` (a stub node by
21
+ * default). Returns the spy so callers assert the broker inputs or
22
+ * `.mockRejectedValue(...)` a broker failure.
23
23
  */
24
- export declare function stubRelationshipExists(client: OryAgentClient, exists: boolean): import("vitest").Mock<(check: import("./types.js").PermissionCheck) => Promise<boolean>>;
24
+ export declare function spyRecordDelegation(client: OryAgentClient, result?: {
25
+ nodeId: string;
26
+ delegationChain: string[];
27
+ }): import("vitest").Mock<(input: import("./client.js").RecordDelegationInput, options?: {
28
+ fetchImpl?: typeof fetch;
29
+ activityAttributes?: Record<string, unknown>;
30
+ agentToken?: string;
31
+ runtimeCredential?: import("./runtime-credential.js").RuntimeCredential;
32
+ signal?: AbortSignal;
33
+ }) => Promise<import("./client.js").RecordDelegationResult>>;
25
34
  /**
26
35
  * Stub an internal API instance method on the client.
27
36
  * Returns a vi.fn mock so callers can assert on calls.
28
37
  */
29
- export declare function stubApi<K extends "frontend" | "oauth2" | "permission" | "relationship">(client: OryAgentClient, api: K, method: string, impl: (...args: unknown[]) => unknown): ReturnType<typeof vi.fn>;
38
+ export declare function stubApi<K extends "frontend" | "oauth2" | "permission">(client: OryAgentClient, api: K, method: string, impl: (...args: unknown[]) => unknown): ReturnType<typeof vi.fn>;
30
39
  /** Successful session verification response (wraps Ory API shape). */
31
40
  export declare const MOCK_SESSION_RESPONSE: {
32
41
  data: {
@@ -132,28 +141,40 @@ export declare function makeMfaRequiredError(): Record<string, unknown>;
132
141
  /** Axios 401 with session_inactive error id. */
133
142
  export declare function makeSessionInactiveError(): Record<string, unknown>;
134
143
  /**
135
- * Get all recorded trace spans from a client, optionally filtered by event.
144
+ * Get all recorded activity events from a client, optionally filtered by event.
136
145
  */
137
- export declare function getTraceSpans(client: OryAgentClient, event?: TraceEvent): TraceSpan[];
146
+ export declare function getActivityEvents(client: OryAgentClient, event?: string): ActivityEntry[];
138
147
  /**
139
- * Assert the tracer recorded the expected tool names for a given event type.
140
- * Checks the `toolName` attribute on each span.
148
+ * Assert the activity log recorded the expected tool names for an event type.
141
149
  */
142
- export declare function expectTracedTools(client: OryAgentClient, event: TraceEvent, expectedTools: string[]): void;
150
+ export declare function expectActivityTools(client: OryAgentClient, event: string, expectedTools: string[]): void;
143
151
  /**
144
152
  * Set standard Ory env vars for a configured + authenticated test.
145
153
  * Returns a cleanup function that restores the previous state.
146
154
  */
147
155
  export declare function setOryEnv(overrides?: Partial<{
148
156
  projectUrl: string;
149
- sessionToken: string;
150
- oauth2Token: string;
157
+ agentSecurityUrl: string;
158
+ /**
159
+ * Public OAuth2 client id. Together with `projectUrl` this is what makes
160
+ * Agent Security connected — pass `undefined` for either to exercise the
161
+ * not-connected path.
162
+ */
163
+ oauth2ClientId: string;
164
+ /**
165
+ * Pre-supplied user OAuth2 access token (`ORY_USER_OAUTH2_TOKEN`) — the one
166
+ * accepted env credential. Pass `undefined` so the login gate does not
167
+ * short-circuit on it.
168
+ */
169
+ userToken: string;
151
170
  subjectId: string;
152
171
  namespace: string;
153
172
  /**
154
173
  * Permission mode for the test. Defaults to `"enforce"` so existing
155
- * test assertions that exercise the deny path still see a block.
156
- * Pass `"observe"` explicitly to exercise the new observe branch.
174
+ * test assertions that exercise the deny path still see a block. The mode
175
+ * is now a server-read Keto permission, so this sets the value the
176
+ * {@link stubPermissionAllowed}/`Denied` stubs report for the mode check —
177
+ * it is NOT an env var (there is no `ORY_PERMISSION_MODE` anymore).
157
178
  */
158
179
  permissionMode: "observe" | "enforce";
159
180
  }>): () => void;
@@ -164,9 +185,37 @@ export declare function setOryEnv(overrides?: Partial<{
164
185
  * that restores the env var and removes the temp directory.
165
186
  *
166
187
  * Use `saveConfig(...)` from `./config.js` inside the test to shape the
167
- * isolated config (e.g. `saveConfig({ auditOnly: true })`).
188
+ * isolated config (e.g. `saveConfig({ projectUrl, oauth2ClientId })`).
168
189
  */
169
190
  export declare function useTempConfigDir(): () => void;
191
+ /** Placeholder values `connectSecurity` / `disconnectSecurity` write. */
192
+ export declare const TEST_PROJECT_URL = "https://test.projects.oryapis.com";
193
+ export declare const TEST_AGENT_SECURITY_URL = "https://agents.console.ory.com";
194
+ export declare const TEST_OAUTH2_CLIENT_ID = "test-login-client";
195
+ /**
196
+ * Put the isolated config into the **connected** state so the auth gates and
197
+ * permission checks actually run.
198
+ *
199
+ * Agent Security is connected exactly when a project URL and an OAuth2 client
200
+ * id both resolve — there is no flag to set. Tests that exercise checks need
201
+ * both, and writing them through one named call keeps that requirement in one
202
+ * place instead of two literals in every `beforeEach`.
203
+ *
204
+ * Call after `useTempConfigDir()` so it writes to the temp config.
205
+ */
206
+ export declare function connectSecurity(overrides?: {
207
+ projectUrl?: string;
208
+ agentSecurityUrl?: string;
209
+ oauth2ClientId?: string;
210
+ }): void;
211
+ /**
212
+ * Put the isolated config into a partial connection state for contract tests.
213
+ *
214
+ * `which` selects which connection values are absent. The `no-client-id` case
215
+ * now exercises the reserved client default rather than a disconnected state.
216
+ * The `neither` and `no-project-url` forms remain disconnected.
217
+ */
218
+ export declare function disconnectSecurity(which?: "neither" | "no-client-id" | "no-project-url"): void;
170
219
  /**
171
220
  * Clear all Ory env vars (simulate unconfigured state).
172
221
  */
@@ -185,33 +234,20 @@ export declare function stubSessionExpired(client: OryAgentClient): import("vite
185
234
  export declare function stubOAuth2Success(client: OryAgentClient): import("vitest").Mock<import(".pnpm/@vitest+spy@4.1.7/node_modules/@vitest/spy", { with: { "resolution-mode": "import" } }).Procedure | import(".pnpm/@vitest+spy@4.1.7/node_modules/@vitest/spy", { with: { "resolution-mode": "import" } }).Constructable>;
186
235
  /** Stub introspectToken to return inactive. */
187
236
  export declare function stubOAuth2Inactive(client: OryAgentClient): import("vitest").Mock<import(".pnpm/@vitest+spy@4.1.7/node_modules/@vitest/spy", { with: { "resolution-mode": "import" } }).Procedure | import(".pnpm/@vitest+spy@4.1.7/node_modules/@vitest/spy", { with: { "resolution-mode": "import" } }).Constructable>;
237
+ /**
238
+ * Set the mode the server-read stub reports. Also clears the in-memory
239
+ * permission-mode cache so the new value takes effect immediately (tests run
240
+ * with `ORY_PERMISSION_MODE_TTL_MS=0`, but resetting is belt-and-suspenders).
241
+ */
242
+ export declare function setTestPermissionMode(mode: PermissionMode): void;
188
243
  /** Stub checkPermission to allow. */
189
244
  export declare function stubPermissionAllowed(client: OryAgentClient): import("vitest").Mock<import(".pnpm/@vitest+spy@4.1.7/node_modules/@vitest/spy", { with: { "resolution-mode": "import" } }).Procedure | import(".pnpm/@vitest+spy@4.1.7/node_modules/@vitest/spy", { with: { "resolution-mode": "import" } }).Constructable>;
190
- /** Stub checkPermission to deny. */
245
+ /** Stub checkPermission to deny (the tool check; the mode check still answers `currentTestMode`). */
191
246
  export declare function stubPermissionDenied(client: OryAgentClient): import("vitest").Mock<import(".pnpm/@vitest+spy@4.1.7/node_modules/@vitest/spy", { with: { "resolution-mode": "import" } }).Procedure | import(".pnpm/@vitest+spy@4.1.7/node_modules/@vitest/spy", { with: { "resolution-mode": "import" } }).Constructable>;
192
247
  /** Stub checkPermission to throw a network error. */
193
248
  export declare function stubPermissionNetworkError(client: OryAgentClient): import("vitest").Mock<import(".pnpm/@vitest+spy@4.1.7/node_modules/@vitest/spy", { with: { "resolution-mode": "import" } }).Procedure | import(".pnpm/@vitest+spy@4.1.7/node_modules/@vitest/spy", { with: { "resolution-mode": "import" } }).Constructable>;
194
249
  /** Stub checkPermission to throw a rate-limit error. */
195
250
  export declare function stubPermissionRateLimited(client: OryAgentClient): import("vitest").Mock<import(".pnpm/@vitest+spy@4.1.7/node_modules/@vitest/spy", { with: { "resolution-mode": "import" } }).Procedure | import(".pnpm/@vitest+spy@4.1.7/node_modules/@vitest/spy", { with: { "resolution-mode": "import" } }).Constructable>;
196
- /**
197
- * Spy on the public `createRelationship` method and resolve successfully.
198
- * Prefer this over stubbing the internal relationship API: `setAgentPrincipal`
199
- * rebuilds the API instances when the agent token changes (e.g. inside
200
- * `sessionStart`), which silently discards API-level stubs.
201
- */
202
- export declare function spyRelationshipCreated(client: OryAgentClient): import("vitest").Mock<(check: import("./types.js").PermissionCheck, options?: {
203
- spanAttributes?: Record<string, unknown>;
204
- }) => Promise<{
205
- created: boolean;
206
- alreadyExisted: boolean;
207
- }>>;
208
- /** Spy on the public `createRelationship` method and reject. */
209
- export declare function spyRelationshipCreateError(client: OryAgentClient): import("vitest").Mock<(check: import("./types.js").PermissionCheck, options?: {
210
- spanAttributes?: Record<string, unknown>;
211
- }) => Promise<{
212
- created: boolean;
213
- alreadyExisted: boolean;
214
- }>>;
215
251
  /** Stub both checkPermission (server allow) and batchCheckPermission (both allow). */
216
252
  export declare function stubMcpAllowed(client: OryAgentClient): import("vitest").Mock<import(".pnpm/@vitest+spy@4.1.7/node_modules/@vitest/spy", { with: { "resolution-mode": "import" } }).Procedure | import(".pnpm/@vitest+spy@4.1.7/node_modules/@vitest/spy", { with: { "resolution-mode": "import" } }).Constructable>;
217
253
  /** Stub checkPermission to deny (server-only MCP check). */