@ory/argus 0.14.0 → 1.0.1

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 +413 -0
  90. package/dist/runtime.js +825 -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
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.probePermissionsCoverage = probePermissionsCoverage;
37
+ exports.resolveDynamicName = resolveDynamicName;
37
38
  exports.collectStatusReport = collectStatusReport;
38
39
  /**
39
40
  * Structured status data — the data-gathering half of the `status` command,
@@ -48,23 +49,34 @@ exports.collectStatusReport = collectStatusReport;
48
49
  * implementation.
49
50
  */
50
51
  const config_js_1 = require("./config.js");
52
+ const permission_mode_js_1 = require("./permission-mode.js");
53
+ const permissions_js_1 = require("./permissions.js");
51
54
  const auth_store_js_1 = require("./auth-store.js");
52
55
  const agent_auth_js_1 = require("./agent-auth.js");
53
- const client_js_1 = require("./client.js");
54
56
  const subject_js_1 = require("./subject.js");
57
+ const client_js_1 = require("./client.js");
58
+ const subject_js_2 = require("./subject.js");
55
59
  const branding_js_1 = require("./branding.js");
60
+ const auth_js_1 = require("./auth.js");
61
+ const node_os_1 = require("node:os");
56
62
  const tool_catalog_js_1 = require("./tool-catalog.js");
63
+ const opl_js_1 = require("./opl.js");
57
64
  const fs = __importStar(require("node:fs"));
58
65
  // Additive dashboard section: core Ory service health (see status-system.ts).
59
66
  const status_system_js_1 = require("./status-system.js");
60
67
  function resolveNamespace() {
61
- return process.env.ORY_PERMISSION_NAMESPACE ?? "AgentTools";
68
+ return process.env.ORY_PERMISSION_NAMESPACE ?? "AgentTool";
62
69
  }
63
70
  /**
64
71
  * Probe the harness's built-in tool catalog against Ory, returning how many
65
- * tools the resolved user subject is allowed / denied / errored on. Returns
66
- * undefined when a probe can't run (no project URL, empty catalog, no user
67
- * identity). Never throws.
72
+ * tools the resolved user subject is allowed / denied / errored on, plus the
73
+ * live permission mode and the subject that was checked. Returns undefined when
74
+ * a probe can't run (no project URL, empty catalog, no user identity). Never
75
+ * throws.
76
+ *
77
+ * Checks route through {@link resolveCheckRelation}, i.e. the `access` permit —
78
+ * the same relation the runtime gate evaluates — so a tool carrying an explicit
79
+ * block is reported as denied here too, exactly as a tool call would be.
68
80
  */
69
81
  async function probePermissionsCoverage(harness) {
70
82
  const resolved = (0, config_js_1.resolveConfig)();
@@ -85,16 +97,30 @@ async function probePermissionsCoverage(harness) {
85
97
  await (0, agent_auth_js_1.ensureAgentIdentity)(client, { projectUrl: resolved.projectUrl }).catch(() => {
86
98
  /* best-effort; the probe still runs with whatever credentials we have */
87
99
  });
88
- const subject = (0, subject_js_1.resolveUserSubject)(client);
89
- if ((0, subject_js_1.subjectLabel)(subject) === "agent:unknown")
100
+ const subject = (0, subject_js_2.resolveUserSubject)(client);
101
+ const subjectId = (0, subject_js_2.subjectLabel)(subject);
102
+ if (subjectId === "agent:unknown")
90
103
  return undefined;
104
+ const agentSubject = (0, subject_js_2.resolveAgentSubject)(client);
105
+ const live = await (0, permission_mode_js_1.resolvePermissionMode)(client, subject, {
106
+ additionalSubjects: agentSubject ? [{ scope: "agent", subject: agentSubject }] : [],
107
+ });
91
108
  const namespace = resolveNamespace();
92
- const result = { total: catalog.length, allowed: 0, denied: 0, errored: 0 };
109
+ const relation = (0, permissions_js_1.resolveCheckRelation)("users");
110
+ const result = {
111
+ total: catalog.length,
112
+ allowed: 0,
113
+ denied: 0,
114
+ errored: 0,
115
+ mode: live.mode,
116
+ modeSource: live.source,
117
+ subject: subjectId,
118
+ };
93
119
  for (const tool of catalog) {
94
- const check = { namespace, object: tool, relation: "use", ...subject };
120
+ const check = { namespace, object: tool, relation, ...subject };
95
121
  try {
96
122
  const probe = await client.checkPermission(check, {
97
- spanAttributes: { toolName: tool, source: "status_coverage" },
123
+ activityAttributes: { toolName: tool, source: "status_coverage" },
98
124
  });
99
125
  if (probe.allowed)
100
126
  result.allowed++;
@@ -121,17 +147,18 @@ function collectConfig(harness) {
121
147
  projectIdSource: r.projectIdSource,
122
148
  projectName: r.projectName,
123
149
  workspaceName: r.workspaceName,
124
- apiKeyConfigured: !!r.apiKey,
125
- apiKeySource: r.apiKeySource,
126
150
  oauth2ClientId: r.oauth2ClientId,
127
151
  oauth2ClientIdSource: r.oauth2ClientIdSource,
128
- oauth2ClientName: r.oauth2ClientId ? branding_js_1.USER_LOGIN_CLIENT_NAME : undefined,
129
- auditOnly: r.auditOnly,
152
+ oauth2ClientName: (0, branding_js_1.isUserLoginClient)({ clientId: r.oauth2ClientId })
153
+ ? branding_js_1.USER_LOGIN_CLIENT_NAME
154
+ : undefined,
155
+ security: r.security,
130
156
  permissionMode: r.permissionMode,
131
157
  permissionModeSource: r.permissionModeSource,
132
158
  userSubjectNamespace: r.userSubjectNamespace,
133
159
  userSubjectNamespaceSource: r.userSubjectNamespaceSource,
134
160
  namespace: resolveNamespace(),
161
+ principalNamespaces: [...opl_js_1.PRINCIPAL_NAMESPACES],
135
162
  debugEnabled: process.env.ORY_AGENT_DEBUG === "true",
136
163
  };
137
164
  }
@@ -144,45 +171,98 @@ function collectUser() {
144
171
  return {
145
172
  tokenCache: (0, auth_store_js_1.isExpired)(tokens) ? "stale" : "present",
146
173
  subject: tokens.subject,
174
+ displayName: tokens.displayName ?? (0, auth_js_1.displayNameFromIdToken)(tokens.idToken),
147
175
  expiresInSeconds: tokens.expiresAt - nowSec,
148
176
  clientId: tokens.clientId,
149
177
  };
150
178
  }
151
- function collectAgent() {
152
- if (process.env.ORY_AGENT_API_KEY) {
153
- return { source: "static_api_key", subject: process.env.ORY_AGENT_SUBJECT_ID };
154
- }
179
+ /**
180
+ * Human-readable name for a DCR agent / sub-agent credential: the persisted
181
+ * `client_name` when present, else a best-effort reconstruction from the
182
+ * harness and (for sub-agents) type. Reconstruction uses the current hostname,
183
+ * which may differ from the registration host for credentials saved before the
184
+ * name was persisted — acceptable for a display-only label.
185
+ */
186
+ function resolveDynamicName(creds, subAgentType, harness) {
187
+ return (creds.clientName ??
188
+ (0, branding_js_1.agentClientName)({
189
+ harness: creds.harness ?? harness,
190
+ host: (0, node_os_1.hostname)() || "unknown-host",
191
+ subAgentType,
192
+ }));
193
+ }
194
+ function collectAgent(harness, sessionKey) {
155
195
  if (process.env.ORY_AGENT_CLIENT_ID && process.env.ORY_AGENT_CLIENT_SECRET) {
156
196
  return {
157
197
  source: "static_client_credentials",
158
198
  clientId: process.env.ORY_AGENT_CLIENT_ID,
159
- subject: process.env.ORY_AGENT_SUBJECT_ID ?? process.env.ORY_AGENT_CLIENT_ID,
199
+ subject: (0, subject_js_2.readAgentSubjectOverride)() ?? process.env.ORY_AGENT_CLIENT_ID,
160
200
  };
161
201
  }
162
- const persisted = (0, agent_auth_js_1.loadAgentDynamicCredentials)();
163
- if (!persisted)
202
+ const selected = sessionKey
203
+ ? (() => {
204
+ const credentials = (0, agent_auth_js_1.loadAgentDynamicCredentials)(harness, sessionKey);
205
+ return credentials ? { sessionKey, credentials } : undefined;
206
+ })()
207
+ : (0, agent_auth_js_1.latestAgentRegistration)(harness);
208
+ if (!selected)
164
209
  return { source: "unregistered" };
210
+ const { sessionKey: credentialSession, credentials: persisted } = selected;
165
211
  const resolved = (0, config_js_1.resolveConfig)();
166
212
  const mismatch = resolved.projectUrl && persisted.projectUrl !== resolved.projectUrl
167
213
  ? { registeredAgainst: persisted.projectUrl, current: resolved.projectUrl }
168
214
  : undefined;
215
+ const subject = (0, subject_js_2.readAgentSubjectOverride)() ?? persisted.clientId;
169
216
  return {
170
217
  source: "dcr",
218
+ name: resolveDynamicName(persisted, undefined, harness),
219
+ sessionKey: credentialSession,
220
+ ...(credentialSession === config_js_1.SESSIONLESS_KEY
221
+ ? {}
222
+ : { sessionSubject: `${subject_js_1.AGENT_NAMESPACE}:${subject}|${credentialSession}` }),
171
223
  clientId: persisted.clientId,
172
- subject: process.env.ORY_AGENT_SUBJECT_ID ?? persisted.clientId,
224
+ subject,
173
225
  registeredAt: new Date(persisted.registeredAt * 1000).toISOString(),
174
226
  projectUrlMismatch: mismatch,
175
227
  };
176
228
  }
229
+ function collectSubAgents(harness, requestedSession) {
230
+ const sessionKey = requestedSession
231
+ ?? (0, agent_auth_js_1.latestAgentRegistration)(harness)?.sessionKey
232
+ ?? (0, agent_auth_js_1.latestSubAgentSession)(harness);
233
+ const persisted = sessionKey ? (0, agent_auth_js_1.loadAllSubAgentDynamicCredentials)(harness, sessionKey) : {};
234
+ const resolved = (0, config_js_1.resolveConfig)();
235
+ return Object.entries(persisted)
236
+ .map(([subAgentType, creds]) => {
237
+ const mismatch = resolved.projectUrl && creds.projectUrl !== resolved.projectUrl
238
+ ? { registeredAgainst: creds.projectUrl, current: resolved.projectUrl }
239
+ : undefined;
240
+ return {
241
+ subAgentType,
242
+ name: resolveDynamicName(creds, subAgentType, harness),
243
+ clientId: creds.clientId,
244
+ subject: creds.clientId,
245
+ registeredAt: new Date(creds.registeredAt * 1000).toISOString(),
246
+ delegatedBy: creds.delegation?.delegatedBy,
247
+ projectUrlMismatch: mismatch,
248
+ };
249
+ })
250
+ .sort((a, b) => a.subAgentType.localeCompare(b.subAgentType));
251
+ }
177
252
  async function collectPermissions(harness) {
178
253
  const resolved = (0, config_js_1.resolveConfig)();
179
254
  const namespace = resolveNamespace();
180
255
  const catalog = (0, tool_catalog_js_1.getToolCatalog)(harness);
181
- const base = { mode: resolved.permissionMode, namespace };
182
- if (resolved.auditOnly)
183
- return { ...base, coverageStatus: "audit_only" };
184
- if (!resolved.projectUrl)
185
- return { ...base, coverageStatus: "no_project" };
256
+ // Falls back to the last-known cached mode on every path that can't reach the
257
+ // project to read it live.
258
+ const base = {
259
+ mode: resolved.permissionMode,
260
+ modeSource: resolved.permissionModeSource,
261
+ namespace,
262
+ };
263
+ // One reason covers both missing values: with either absent no check can run.
264
+ if (!resolved.security.connected)
265
+ return { ...base, coverageStatus: "not_connected" };
186
266
  if (catalog.length === 0) {
187
267
  return { ...base, coverageStatus: "no_catalog", knownHarnesses: [...tool_catalog_js_1.KNOWN_HARNESSES] };
188
268
  }
@@ -192,15 +272,17 @@ async function collectPermissions(harness) {
192
272
  const coverage = await probePermissionsCoverage(harness);
193
273
  if (!coverage)
194
274
  return { ...base, coverageStatus: "no_user" };
275
+ // The probe read the mode live; prefer it over the cached config value.
276
+ const withMode = { ...base, mode: coverage.mode, modeSource: coverage.modeSource };
195
277
  if (coverage.errored > 0 && coverage.allowed === 0 && coverage.denied === 0) {
196
- return { ...base, coverageStatus: "probe_failed", coverage };
278
+ return { ...withMode, coverageStatus: "probe_failed", coverage };
197
279
  }
198
280
  const status = coverage.allowed === coverage.total
199
- ? "bootstrapped"
281
+ ? "fully_granted"
200
282
  : coverage.allowed === 0
201
- ? "not_bootstrapped"
283
+ ? "no_grants"
202
284
  : "partial";
203
- return { ...base, coverageStatus: status, coverage };
285
+ return { ...withMode, coverageStatus: status, coverage };
204
286
  }
205
287
  function isUserTokenUsable() {
206
288
  const tokens = (0, auth_store_js_1.loadTokens)();
@@ -221,28 +303,29 @@ function readTail(file, limit) {
221
303
  return { file, count: lines.length, recent };
222
304
  }
223
305
  function collectActivity(harness) {
224
- const trace = readTail((0, client_js_1.resolveTraceFilePath)(harness), 10);
225
306
  const log = readTail((0, client_js_1.resolveDebugLogPath)(harness), 10);
226
307
  return {
227
- traceFile: trace.file,
228
- traceCount: trace.count,
229
- recentSpans: trace.recent,
230
308
  logFile: log.file,
231
309
  logCount: log.count,
232
- recentLogs: log.recent,
310
+ recentEvents: log.recent,
233
311
  };
234
312
  }
235
313
  /**
236
314
  * Gather the full structured status report for a harness. The permissions
237
315
  * section runs a live network probe when a project URL and user identity are
238
316
  * available (same conditions as the text `status` command).
317
+ *
318
+ * `sessionKey` is the run being reported on, when the caller is inside one. The
319
+ * standalone CLI passes none, so the report selects the harness's newest stored
320
+ * session rather than inventing an ambient session.
239
321
  */
240
- async function collectStatusReport(harness) {
322
+ async function collectStatusReport(harness, sessionKey) {
241
323
  return {
242
324
  harness,
243
325
  config: collectConfig(harness),
244
326
  user: collectUser(),
245
- agent: collectAgent(),
327
+ agent: collectAgent(harness, sessionKey),
328
+ subAgents: collectSubAgents(harness, sessionKey),
246
329
  permissions: await collectPermissions(harness),
247
330
  activity: collectActivity(harness),
248
331
  system: await (0, status_system_js_1.collectSystemServices)(),
package/dist/subject.d.ts CHANGED
@@ -1,24 +1,34 @@
1
1
  /**
2
2
  * User-subject resolution for permission checks.
3
3
  *
4
- * Permission checks need a subject reference that matches the form
5
- * stored in Keto. Two shapes are supported:
4
+ * Every principal user, agent, sub-agent is addressed as a **SubjectSet**
5
+ * `<namespace>:<object>` (e.g. `User:<UUID>`). This is the form the Ory Console's
6
+ * *Add relationship* dialog writes, the form the local-stack seed writes, and the
7
+ * Zanzibar-idiomatic composition point (grant `Role:developer` `use` on a tool and
8
+ * add principals to the role, rather than fanning out a tuple per subject). It is
9
+ * the only first-class subject form: Keto does no cross-form expansion, so a check
10
+ * must match the write shape exactly, and standardizing on one shape removes that
11
+ * silent-misfire footgun.
6
12
  *
7
- * - **SubjectSet** — `<namespace>:<object>` (e.g. `User:<UUID>`). This
8
- * is the form the Console's *Add relationship* dialog writes, so
9
- * dev/demo flows that grant tuples through the UI use it.
10
- *
11
- * - **Direct SubjectID** opaque string (e.g. `user:<UUID>` or
12
- * `session:<id>`). Backward-compatible default when no subject
13
- * namespace is configured.
14
- *
15
- * The selection is driven by the user-subject namespace: `ORY_USER_SUBJECT_NAMESPACE`
16
- * takes precedence, then the client's configured `userSubjectNamespace` (persisted
17
- * in the shared config, e.g. by the local-stack install path). When a namespace is
18
- * present the resolver builds a SubjectSet using it; otherwise it falls back to the
19
- * direct SubjectID chain.
13
+ * The namespace is resolved by precedence: `ORY_USER_SUBJECT_NAMESPACE` (env)
14
+ * the client's configured `userSubjectNamespace` (persisted in the shared config)
15
+ * the {@link DEFAULT_USER_SUBJECT_NAMESPACE} (`User`). The env/config values now
16
+ * *override* the default namespace; they are no longer an opt-in switch that
17
+ * toggles between SubjectSet and a legacy direct SubjectID.
20
18
  */
21
19
  import type { OryAgentClient } from "./client.js";
20
+ /**
21
+ * Default namespace a resolved user principal is addressed under. The
22
+ * local-stack seed, the Console *Add relationship* dialog, and the Network OPL
23
+ * provisioning all use `User`, so it is the out-of-the-box default.
24
+ */
25
+ export declare const DEFAULT_USER_SUBJECT_NAMESPACE = "User";
26
+ /** Namespace the agent principal is addressed under (delegation subjects). */
27
+ export declare const AGENT_NAMESPACE = "Agent";
28
+ /** Namespace a sub-agent principal is addressed under. */
29
+ export declare const SUBAGENT_NAMESPACE = "SubAgent";
30
+ /** Namespace the no-user-identity `session:<id>` fallback is addressed under. */
31
+ export declare const SESSION_NAMESPACE = "Session";
22
32
  export type UserSubjectRef = {
23
33
  subjectId: string;
24
34
  } | {
@@ -33,7 +43,7 @@ export type UserSubjectRef = {
33
43
  * client's user principal and the env overrides in
34
44
  * {@link resolveUserSubject}. The override only applies within `fn`'s
35
45
  * async context, so concurrent calls cannot observe each other's subject.
36
- * `ORY_USER_SUBJECT_NAMESPACE` SubjectSet shaping still applies.
46
+ * SubjectSet shaping still applies.
37
47
  *
38
48
  * A missing/empty `subject` is a no-op: `fn` runs with the normal
39
49
  * resolution chain.
@@ -46,13 +56,109 @@ export declare function runWithUserSubject<T>(subject: string | undefined, fn: (
46
56
  * legacy `ORY_AGENT_SUBJECT_ID`, then the caller-supplied `fallback`
47
57
  * (typically `session:<id>`).
48
58
  *
49
- * Returns a SubjectSet when a user-subject namespace is configured (env or
50
- * client config) and a concrete subject is available; otherwise a direct
51
- * SubjectID.
59
+ * Always returns a SubjectSet. A resolved principal is addressed under the
60
+ * configured user namespace (`ORY_USER_SUBJECT_NAMESPACE` client config
61
+ * {@link DEFAULT_USER_SUBJECT_NAMESPACE}). A `session:`-prefixed fallback is
62
+ * addressed under {@link SESSION_NAMESPACE} (`Session:<id>`). Only when nothing
63
+ * resolves at all do we emit the direct `agent:unknown` sentinel — an error
64
+ * marker that must never match a stored tuple.
52
65
  */
53
66
  export declare function resolveUserSubject(client: OryAgentClient, fallback?: string): UserSubjectRef;
54
67
  /**
55
68
  * Printable label for a `UserSubjectRef`. Used in denial messages and
56
- * span attributes. SubjectSets render as `<namespace>:<object>`.
69
+ * activity attributes. SubjectSets render as `<namespace>:<object>`.
57
70
  */
58
71
  export declare function subjectLabel(ref: UserSubjectRef): string;
72
+ /** Separator between the credential client id and narrower scoping axes. */
73
+ export declare const SUBJECT_AXIS_SEPARATOR = "|";
74
+ /** The env var that overrides the agent's subject. */
75
+ export declare const AGENT_SUBJECT_ID_ENV_VAR = "ORY_AGENT_SUBJECT_ID";
76
+ /**
77
+ * Validate a subject override (issue #241).
78
+ *
79
+ * `deriveSubject` above already refuses to produce a trailing separator,
80
+ * because a subject ending in `|` silently never matches a stored relation.
81
+ * The same care is owed to the *base* it joins onto, which arrives from an
82
+ * operator-settable env var and was previously used verbatim.
83
+ *
84
+ * A value containing the axis separator is rejected for two independent
85
+ * reasons:
86
+ *
87
+ * 1. **It collapses the identity grains.** The credential, session, and spawn
88
+ * subjects are told apart only by separator position, so an override
89
+ * containing one makes them ambiguous: `override="a|b"` in session `c` and
90
+ * `override="a"` in session `b|c` produce the same string. A session-scoped
91
+ * block written to stop one runaway run could then match the base credential, or a
92
+ * different run — and all three grains are read together in one batched
93
+ * check, so this is a live semantics break rather than a theoretical one.
94
+ * 2. **It forges a delegation-node reference** (#225). A join key is exactly
95
+ * `<identity>|<harness>|<host>|<session>`, and the two string spaces stay
96
+ * disjoint today only because a Hydra client id is a UUID and contains no
97
+ * separator. This env var is the one supported knob that breaks that.
98
+ *
99
+ * Rejected rather than sanitized: silently stripping the separator would
100
+ * produce a *different* subject than the operator asked for, which fails just
101
+ * as confusingly and is harder to notice. Ignoring the value falls back to the
102
+ * client id — a real, working identity — and says so.
103
+ */
104
+ export declare function validateSubjectOverride(raw: string | undefined, envVar?: string): {
105
+ value?: string;
106
+ warning?: string;
107
+ };
108
+ /**
109
+ * Read and validate `ORY_AGENT_SUBJECT_ID`. Returns `undefined` when unset or
110
+ * rejected, pushing an explanation onto `warnings` in the latter case.
111
+ *
112
+ * One reader for every site that consumes the override, so the rule cannot hold
113
+ * in the resolver and not in what `status` reports.
114
+ */
115
+ export declare function readAgentSubjectOverride(env?: NodeJS.ProcessEnv, warnings?: string[]): string | undefined;
116
+ /**
117
+ * The registered agent credential subject: `Agent:<clientId>`. For DCR this
118
+ * client belongs to one session; static credentials may be shared explicitly.
119
+ *
120
+ * `undefined` when no agent principal is populated — the agent gate never
121
+ * blocks, so a session with no resolved machine identity is normal and callers
122
+ * must treat a missing agent subject as "nothing to check", not as a deny.
123
+ */
124
+ export declare function resolveAgentSubject(client: OryAgentClient): UserSubjectRef | undefined;
125
+ /**
126
+ * The **session-level** agent subject: `Agent:<clientId>|<session>`, unique to
127
+ * one run. Used for a block that stops the session's narrower subject without
128
+ * blocking its base credential subject.
129
+ *
130
+ * `undefined` when there is no agent principal, and — deliberately — also when
131
+ * there is no session. A caller with no session concept (an SDK integration in
132
+ * a long-running service) has exactly one sessionless credential subject;
133
+ * inventing a placeholder session would either collapse every run onto
134
+ * one string or produce a subject no admin could have written.
135
+ */
136
+ export declare function resolveAgentSessionSubject(client: OryAgentClient, sessionId?: string): UserSubjectRef | undefined;
137
+ /**
138
+ * The registered sub-agent credential subject: `SubAgent:<clientId>` for one
139
+ * typed sub-agent in one session.
140
+ *
141
+ * `subAgentClientId` is the client id from {@link ensureSubAgentIdentity};
142
+ * `undefined` in, and `undefined` out, since a sub-agent whose identity did not
143
+ * resolve has nothing to check.
144
+ */
145
+ export declare function resolveSubAgentSubject(subAgentClientId: string | undefined): UserSubjectRef | undefined;
146
+ /**
147
+ * The **spawn-level** sub-agent subject:
148
+ * `SubAgent:<clientId>|<session>|<type>[|<spawn>]`, the finest grain available.
149
+ *
150
+ * `perSpawnId` distinguishes two *concurrent* same-type sub-agents and is only
151
+ * present on harnesses that expose one (Cursor's `subagent_id`, Claude Code's
152
+ * `agent_id`, OpenClaw's `childRunId`, …). Where the harness exposes none the
153
+ * subject stops at the type and concurrent spawns share it — a limit of the
154
+ * harness, not of this model.
155
+ *
156
+ * As with the agent session subject, a missing session yields `undefined`
157
+ * rather than a placeholder.
158
+ */
159
+ export declare function resolveSubAgentSpawnSubject(client: OryAgentClient, args: {
160
+ subAgentClientId?: string;
161
+ subAgentType: string;
162
+ perSpawnId?: string;
163
+ sessionId?: string;
164
+ }): UserSubjectRef | undefined;