@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
@@ -2,13 +2,14 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.printUserIdentitySection = printUserIdentitySection;
4
4
  exports.printAgentIdentitySection = printAgentIdentitySection;
5
+ exports.printSubAgentIdentitiesSection = printSubAgentIdentitiesSection;
5
6
  exports.printPermissionsSection = printPermissionsSection;
6
7
  exports.runStatusCommand = runStatusCommand;
7
8
  /**
8
9
  * Unified `status` CLI implementation. Each harness's `cli/main.ts` `status`
9
10
  * subcommand delegates here so onboarding readers see configuration, user
10
- * identity, agent identity, permissions, and the harness-specific plugin
11
- * registration block in a single pass.
11
+ * identity, agent identity, sub-agent identities, permissions, and the
12
+ * harness-specific plugin registration block in a single pass.
12
13
  *
13
14
  * The drill-down commands (`agent status`, `permissions status`) remain
14
15
  * unchanged — this composes their inputs without altering their output.
@@ -16,15 +17,16 @@ exports.runStatusCommand = runStatusCommand;
16
17
  const config_js_1 = require("./config.js");
17
18
  const auth_store_js_1 = require("./auth-store.js");
18
19
  const agent_auth_js_1 = require("./agent-auth.js");
19
- const client_js_1 = require("./client.js");
20
- const logger_js_1 = require("./logger.js");
21
- const agent_auth_js_2 = require("./agent-auth.js");
20
+ const branding_js_1 = require("./branding.js");
22
21
  const subject_js_1 = require("./subject.js");
22
+ const logger_js_1 = require("./logger.js");
23
+ const auth_js_1 = require("./auth.js");
24
+ const status_data_js_1 = require("./status-data.js");
23
25
  const tool_catalog_js_1 = require("./tool-catalog.js");
24
26
  const cli_js_1 = require("./cli.js");
25
27
  const cli_invocation_js_1 = require("./cli-invocation.js");
26
28
  function resolveNamespace() {
27
- return process.env.ORY_PERMISSION_NAMESPACE ?? "AgentTools";
29
+ return process.env.ORY_PERMISSION_NAMESPACE ?? "AgentTool";
28
30
  }
29
31
  /**
30
32
  * Format a relative duration like "4h 32m" or "12s" for a future deadline.
@@ -65,6 +67,10 @@ function printUserIdentitySection() {
65
67
  else {
66
68
  console.log(` Token cache: present (expires in ${formatExpiresIn(tokens.expiresAt)})`);
67
69
  }
70
+ const displayName = tokens.displayName ?? (0, auth_js_1.displayNameFromIdToken)(tokens.idToken);
71
+ if (displayName) {
72
+ console.log(` Name: ${displayName}`);
73
+ }
68
74
  const subject = tokens.subject ? `user:${tokens.subject}` : "(no subject claim on cached token)";
69
75
  console.log(` Subject: ${subject}`);
70
76
  if (tokens.clientId) {
@@ -73,183 +79,182 @@ function printUserIdentitySection() {
73
79
  }
74
80
  /**
75
81
  * Render the "Agent identity" block. Summarizes the resolved machine
76
- * credential — static env override, persisted DCR, or unconfigured.
82
+ * credential — static env override, persisted DCR, or unconfigured. Each
83
+ * installed harness has its own agent registration, so the report is for
84
+ * `harness`'s identity only.
77
85
  */
78
- function printAgentIdentitySection() {
86
+ function printAgentIdentitySection(harness) {
79
87
  console.log("");
80
88
  console.log("Agent identity (non-interactive):");
81
- // Static overrides take precedence — surface them so the reader knows
89
+ // The one static override takes precedence — surface it so the reader knows
82
90
  // why DCR isn't running.
83
- if (process.env.ORY_AGENT_API_KEY) {
84
- console.log(" Source: static API key (ORY_AGENT_API_KEY)");
85
- if (process.env.ORY_AGENT_SUBJECT_ID) {
86
- console.log(` Subject: ${process.env.ORY_AGENT_SUBJECT_ID}`);
87
- }
88
- return;
89
- }
90
91
  if (process.env.ORY_AGENT_CLIENT_ID && process.env.ORY_AGENT_CLIENT_SECRET) {
91
92
  console.log(" Source: static client_credentials (ORY_AGENT_CLIENT_ID/SECRET)");
92
93
  console.log(` Client ID: ${process.env.ORY_AGENT_CLIENT_ID}`);
93
- const subject = process.env.ORY_AGENT_SUBJECT_ID ?? process.env.ORY_AGENT_CLIENT_ID;
94
+ const subject = (0, subject_js_1.readAgentSubjectOverride)() ?? process.env.ORY_AGENT_CLIENT_ID;
94
95
  console.log(` Subject: ${subject}`);
95
96
  return;
96
97
  }
97
- const persisted = (0, agent_auth_js_1.loadAgentDynamicCredentials)();
98
- if (!persisted) {
98
+ const latest = (0, agent_auth_js_1.latestAgentRegistration)(harness);
99
+ if (!latest) {
99
100
  console.log(" Source: not registered yet");
100
101
  console.log(" Status: first session start will register an OAuth2 client (RFC 7591)");
101
102
  return;
102
103
  }
104
+ const { sessionKey, credentials: persisted } = latest;
103
105
  const resolved = (0, config_js_1.resolveConfig)();
104
106
  const mismatched = resolved.projectUrl && persisted.projectUrl !== resolved.projectUrl;
105
107
  console.log(" Source: OAuth2 Dynamic Client Registration (RFC 7591)");
106
108
  console.log(` Status: registered`);
109
+ console.log(` Name: ${(0, status_data_js_1.resolveDynamicName)(persisted)}`);
107
110
  console.log(` Client ID: ${persisted.clientId}`);
108
- const subject = process.env.ORY_AGENT_SUBJECT_ID ?? persisted.clientId;
109
- console.log(` Subject: ${subject}`);
111
+ const subject = (0, subject_js_1.readAgentSubjectOverride)() ?? persisted.clientId;
112
+ console.log(` Subject: ${subject_js_1.AGENT_NAMESPACE}:${subject}`);
110
113
  console.log(` Registered: ${new Date(persisted.registeredAt * 1000).toISOString()}`);
114
+ console.log(` Session: ${sessionKey}`);
115
+ console.log(` Retained: ${(0, agent_auth_js_1.countAgentSessions)(harness)} session(s)`);
116
+ if (sessionKey !== config_js_1.SESSIONLESS_KEY) {
117
+ console.log(` Session ID: ${subject_js_1.AGENT_NAMESPACE}:${subject}|${sessionKey}`);
118
+ }
111
119
  if (mismatched) {
112
120
  console.log(` Warning: registered against ${persisted.projectUrl}, but current project URL is ${resolved.projectUrl}`);
113
121
  console.log(" run `agent unregister` to clear stale credentials");
114
122
  }
115
123
  }
116
- async function probePermissionsCoverage(harness) {
117
- const resolved = (0, config_js_1.resolveConfig)();
118
- if (!resolved.projectUrl)
119
- return undefined;
120
- const catalog = (0, tool_catalog_js_1.getToolCatalog)(harness);
121
- if (catalog.length === 0)
122
- return undefined;
123
- const tokens = (0, auth_store_js_1.loadTokens)();
124
- const subjectOverride = process.env.ORY_USER_SUBJECT_ID;
125
- if (!subjectOverride && (!tokens || (0, auth_store_js_1.isExpired)(tokens))) {
126
- return undefined;
127
- }
128
- const client = client_js_1.OryAgentClient.fromEnv(harness);
129
- if (tokens && !(0, auth_store_js_1.isExpired)(tokens)) {
130
- client.setUserPrincipal({
131
- subject: tokens.subject,
132
- token: tokens.accessToken,
133
- });
124
+ /**
125
+ * Render the "Sub-agent identities" block — one entry per typed sub-agent
126
+ * (e.g. `Explore`, `general-purpose`) the agent has registered a distinct
127
+ * OAuth2 client for via DCR. All sub-agent identities are dynamic
128
+ * registrations. Scoped to `harness` — sub-agents belong to that harness's
129
+ * agent. A no-op when none exist yet.
130
+ */
131
+ function printSubAgentIdentitiesSection(harness) {
132
+ const sessionKey = (0, agent_auth_js_1.latestAgentRegistration)(harness)?.sessionKey ?? (0, agent_auth_js_1.latestSubAgentSession)(harness);
133
+ const subAgents = sessionKey
134
+ ? (0, agent_auth_js_1.loadAllSubAgentDynamicCredentials)(harness, sessionKey)
135
+ : {};
136
+ const types = Object.keys(subAgents).sort((a, b) => a.localeCompare(b));
137
+ console.log("");
138
+ console.log(`Sub-agent identities${sessionKey ? ` (session ${(0, branding_js_1.shortSessionLabel)(sessionKey)})` : ""}:`);
139
+ if (types.length === 0) {
140
+ console.log(" None registered yet — one is registered the first time the agent");
141
+ console.log(" spawns a typed sub-agent (e.g. a Task / Explore sub-agent).");
142
+ return;
134
143
  }
135
- await (0, agent_auth_js_2.ensureAgentIdentity)(client, { projectUrl: resolved.projectUrl }).catch(() => {
136
- /* best-effort; the probe still runs with whatever credentials we have */
137
- });
138
- const subject = (0, subject_js_1.resolveUserSubject)(client);
139
- const subjectId = (0, subject_js_1.subjectLabel)(subject);
140
- if (subjectId === "agent:unknown")
141
- return undefined;
142
- const namespace = resolveNamespace();
143
- const result = {
144
- total: catalog.length,
145
- allowed: 0,
146
- denied: 0,
147
- errored: 0,
148
- };
149
- for (const tool of catalog) {
150
- const check = {
151
- namespace,
152
- object: tool,
153
- relation: "use",
154
- ...subject,
155
- };
156
- try {
157
- const probe = await client.checkPermission(check, {
158
- spanAttributes: { toolName: tool, source: "status_coverage" },
159
- });
160
- if (probe.allowed)
161
- result.allowed++;
162
- else
163
- result.denied++;
164
- }
165
- catch (err) {
166
- const ory = err;
167
- result.errored++;
168
- result.errorCode = result.errorCode ?? ory.code ?? "unknown";
144
+ const resolved = (0, config_js_1.resolveConfig)();
145
+ types.forEach((type, i) => {
146
+ if (i > 0)
147
+ console.log("");
148
+ const creds = subAgents[type];
149
+ const subject = creds.delegation?.delegatedBy
150
+ ? `subagent:${creds.clientId} (delegated by ${creds.delegation.delegatedBy})`
151
+ : `subagent:${creds.clientId}`;
152
+ console.log(` ${type}:`);
153
+ console.log(` Name: ${(0, status_data_js_1.resolveDynamicName)(creds, type)}`);
154
+ console.log(` Client ID: ${creds.clientId}`);
155
+ console.log(` Subject: ${subject}`);
156
+ console.log(` Registered: ${new Date(creds.registeredAt * 1000).toISOString()}`);
157
+ if (resolved.projectUrl && creds.projectUrl !== resolved.projectUrl) {
158
+ console.log(` Warning: registered against ${creds.projectUrl}, but current project URL is ${resolved.projectUrl}`);
169
159
  }
170
- }
171
- return result;
160
+ });
172
161
  }
173
162
  /**
174
- * Render the "Permissions" block. Always prints the configured mode and
175
- * namespace; runs a network probe of the harness's built-in tool catalog
176
- * only when a project URL is configured *and* a user identity is
177
- * available (cached PKCE tokens or `ORY_USER_SUBJECT_ID`).
163
+ * Render the "Permissions" block: the deny posture, the namespace, and the
164
+ * coverage of the harness's built-in tool catalog.
165
+ *
166
+ * The network probe runs only when a project URL is configured *and* a user
167
+ * identity is available (cached PKCE tokens or `ORY_USER_SUBJECT_ID`). When it
168
+ * runs, the reported **Mode is read live from the project** in the same pass, so
169
+ * this line answers "what would a tool call do right now?" rather than echoing a
170
+ * cache that is only refreshed at session start. On every other path the last
171
+ * cached value is reported, annotated as such.
178
172
  */
179
173
  async function printPermissionsSection(binName, harness) {
180
174
  const resolved = (0, config_js_1.resolveConfig)();
181
175
  const namespace = resolveNamespace();
182
176
  const catalog = (0, tool_catalog_js_1.getToolCatalog)(harness);
177
+ const cachedMode = `${resolved.permissionMode}${formatModeSuffix(resolved.permissionModeSource)}`;
183
178
  console.log("");
184
179
  console.log("Permissions:");
185
- console.log(` Mode: ${resolved.permissionMode}${formatModeSuffix(resolved.permissionModeSource)}`);
186
- console.log(` Namespace: ${namespace}`);
187
- if (resolved.auditOnly) {
188
- console.log(" Coverage: n/a (audit-only modeOry checks disabled)");
189
- return;
190
- }
191
- if (!resolved.projectUrl) {
192
- console.log(" Coverage: n/a (no project URL configured)");
193
- return;
180
+ // Reasons the probe can't run. Each reports the cached mode, since there is no
181
+ // way to read the live one without reaching the project.
182
+ if (!resolved.security.connected) {
183
+ return printNoCoverage(cachedMode, namespace, `n/a (Agent Security not connected ${(0, config_js_1.describeSecurityGap)(resolved.security)})`);
194
184
  }
195
185
  if (catalog.length === 0) {
196
- console.log(` Coverage: n/a (no built-in catalog for harness "${harness}")`);
186
+ printNoCoverage(cachedMode, namespace, `n/a (no built-in catalog for harness "${harness}")`);
197
187
  console.log(` known harnesses: ${tool_catalog_js_1.KNOWN_HARNESSES.join(", ")}`);
198
188
  return;
199
189
  }
200
- const hasUser = !!process.env.ORY_USER_SUBJECT_ID || isUserTokenUsable();
201
- if (!hasUser) {
202
- console.log(` Coverage: n/a (no cached user identity — start a session once to sign in,`);
203
- console.log(` or set ORY_USER_SUBJECT_ID to probe a known subject)`);
190
+ if (!(process.env.ORY_USER_SUBJECT_ID || isUserTokenUsable())) {
191
+ printNoCoverage(cachedMode, namespace, "n/a (no cached user identity — start a session once to sign in,");
192
+ console.log(" or set ORY_USER_SUBJECT_ID to probe a known subject)");
204
193
  return;
205
194
  }
206
- process.stdout.write(" Coverage: checking…\r");
207
- const probe = await probePermissionsCoverage(harness);
208
- // Clear the "checking…" line by overwriting it.
209
- process.stdout.write(" ".repeat(40) + "\r");
195
+ // The probe reads the live mode in the same pass, so hold the Mode line until
196
+ // it returns rather than printing a cached value we are about to supersede.
197
+ //
198
+ // The progress line is carriage-return overwritten, which only renders as
199
+ // intended on a terminal — piped or redirected output would keep the literal
200
+ // \r and the spaces, so skip it entirely when stdout isn't a TTY.
201
+ const spinner = process.stdout.isTTY === true;
202
+ if (spinner)
203
+ process.stdout.write(" checking…\r");
204
+ const probe = await (0, status_data_js_1.probePermissionsCoverage)(harness);
205
+ if (spinner)
206
+ process.stdout.write(" ".repeat(40) + "\r");
210
207
  if (!probe) {
211
- console.log(" Coverage: unavailable (no user identity could be resolved)");
212
- return;
208
+ return printNoCoverage(cachedMode, namespace, "unavailable (no user identity could be resolved)");
213
209
  }
214
210
  const { total, allowed, denied, errored } = probe;
211
+ console.log(` Mode: ${probe.mode}${formatModeSuffix(probe.modeSource)}`);
212
+ console.log(` Namespace: ${namespace}`);
213
+ console.log(` Subject: ${probe.subject}`);
215
214
  if (errored > 0 && allowed === 0 && denied === 0) {
216
215
  console.log(` Coverage: probe failed (${probe.errorCode ?? "unknown"})`);
217
216
  return;
218
217
  }
219
218
  const status = allowed === total
220
- ? `bootstrapped (${allowed}/${total} built-in tools)`
219
+ ? `fully granted (${allowed}/${total} built-in tools)`
221
220
  : allowed === 0
222
- ? `not bootstrapped (0/${total} built-in tools)`
221
+ ? `no grants (0/${total} built-in tools)`
223
222
  : `partial (${allowed}/${total} built-in tools allowed)`;
224
223
  console.log(` Coverage: ${status}`);
225
224
  if (denied > 0) {
226
- console.log(` run "${(0, cli_invocation_js_1.oryNpx)(binName)} permissions bootstrap" to grant the missing tools`);
225
+ console.log(" grant the missing tools in the Ory Console (Agent Security)");
227
226
  }
228
227
  else if (errored > 0) {
229
- console.log(` ${errored} probe error(s) — run "${(0, cli_invocation_js_1.oryNpx)(binName)} permissions status" for details`);
228
+ console.log(` ${errored} probe error(s) — run "${(0, cli_invocation_js_1.oryNpx)(binName)} permissions" for details`);
230
229
  }
231
230
  }
231
+ /** The Mode / Namespace / Coverage lines for every path that can't probe. */
232
+ function printNoCoverage(mode, namespace, coverage) {
233
+ console.log(` Mode: ${mode}`);
234
+ console.log(` Namespace: ${namespace}`);
235
+ console.log(` Coverage: ${coverage}`);
236
+ }
232
237
  function isUserTokenUsable() {
233
238
  const tokens = (0, auth_store_js_1.loadTokens)();
234
239
  return !!tokens && !(0, auth_store_js_1.isExpired)(tokens);
235
240
  }
236
241
  function formatModeSuffix(source) {
237
242
  switch (source) {
238
- case "env":
239
- return " (from ORY_PERMISSION_MODE env)";
240
- case "config":
241
- return " (from config change with `permissions observe|enforce`)";
243
+ case "server":
244
+ return " (read from the Ory project)";
245
+ case "cache":
246
+ return " (cachedproject unreachable)";
242
247
  case "default":
243
- return " (default)";
248
+ return " (default — not yet read from the project)";
244
249
  }
245
250
  }
246
251
  /**
247
252
  * Compose the unified `status` output for a plugin CLI. Sections render
248
253
  * in this order: title → configuration → user identity → agent identity
249
- * → permissions → hooks & plugin → environment → recent activity
250
- * (traces + debug log).
254
+ * → sub-agent identities → permissions → hooks & plugin → environment →
255
+ * recent activity from the unified activity/debug log.
251
256
  *
252
- * Each section degrades gracefully — missing project URL, audit-only
257
+ * Each section degrades gracefully — Agent Security not connected
253
258
  * mode, and empty token caches all surface as inline "n/a" lines rather
254
259
  * than aborting the command.
255
260
  */
@@ -266,19 +271,20 @@ async function runStatusCommand(binName, harness, options) {
266
271
  console.log("");
267
272
  (0, cli_js_1.printOryConfig)();
268
273
  printUserIdentitySection();
269
- printAgentIdentitySection();
274
+ printAgentIdentitySection(harness);
275
+ printSubAgentIdentitiesSection(harness);
270
276
  await printPermissionsSection(binName, harness);
271
277
  if (options.printPluginSection) {
272
278
  console.log("");
273
- options.printPluginSection();
279
+ await options.printPluginSection();
274
280
  }
275
281
  (0, cli_js_1.printEnvironment)();
276
282
  console.log("");
277
283
  console.log("Recent activity:");
278
- (0, cli_js_1.printTraceTail)(harness);
279
284
  (0, cli_js_1.printLogTail)(harness);
280
285
  console.log("");
281
- console.log(` Watch traces live: ${(0, cli_invocation_js_1.oryNpx)(binName)} watch`);
286
+ console.log(` Live activity/debug: ${(0, cli_invocation_js_1.oryNpx)(binName)} watch`);
287
+ console.log(" Include verbose diagnostics: set ORY_AGENT_DEBUG=true before starting the harness");
282
288
  console.log("");
283
289
  console.log(`Drill into any section with:`);
284
290
  console.log(` ${(0, cli_invocation_js_1.oryNpx)(binName)} agent status`);
@@ -1,3 +1,17 @@
1
+ /**
2
+ * Structured status data — the data-gathering half of the `status` command,
3
+ * shared by the text renderer ({@link file://./status-cli.ts}) and the local
4
+ * web dashboard. `collectStatusReport(harness)` returns a plain, serializable
5
+ * object mirroring every section `status` prints (configuration, user identity,
6
+ * agent identity, permissions coverage, recent activity), so the two surfaces
7
+ * never drift.
8
+ *
9
+ * The live permission-coverage probe ({@link probePermissionsCoverage}) lives
10
+ * here too and is imported back by `status-cli.ts`, so there is a single probe
11
+ * implementation.
12
+ */
13
+ import { type PermissionMode, type SecurityConnection } from "./config.js";
14
+ import { type PermissionModeSource } from "./permission-mode.js";
1
15
  import { type StatusSystemSection } from "./status-system.js";
2
16
  export interface PermissionsCoverage {
3
17
  total: number;
@@ -5,12 +19,27 @@ export interface PermissionsCoverage {
5
19
  denied: number;
6
20
  errored: number;
7
21
  errorCode?: string;
22
+ /**
23
+ * The **live** deny posture, read from the project during the same probe.
24
+ * `status` reports this rather than the cached `config.permissionMode`: the
25
+ * two can disagree (the cache is only refreshed at session start), and the
26
+ * cached one is the misleading answer to "what would a tool call do now?".
27
+ */
28
+ mode: PermissionMode;
29
+ modeSource: PermissionModeSource;
30
+ /** The subject the probe actually checked as, e.g. `User:<id>`. */
31
+ subject: string;
8
32
  }
9
33
  /**
10
34
  * Probe the harness's built-in tool catalog against Ory, returning how many
11
- * tools the resolved user subject is allowed / denied / errored on. Returns
12
- * undefined when a probe can't run (no project URL, empty catalog, no user
13
- * identity). Never throws.
35
+ * tools the resolved user subject is allowed / denied / errored on, plus the
36
+ * live permission mode and the subject that was checked. Returns undefined when
37
+ * a probe can't run (no project URL, empty catalog, no user identity). Never
38
+ * throws.
39
+ *
40
+ * Checks route through {@link resolveCheckRelation}, i.e. the `access` permit —
41
+ * the same relation the runtime gate evaluates — so a tool carrying an explicit
42
+ * block is reported as denied here too, exactly as a tool call would be.
14
43
  */
15
44
  export declare function probePermissionsCoverage(harness: string): Promise<PermissionsCoverage | undefined>;
16
45
  export interface StatusConfigSection {
@@ -24,19 +53,28 @@ export interface StatusConfigSection {
24
53
  /** Human-readable project / workspace names captured at install (display only). */
25
54
  projectName?: string;
26
55
  workspaceName?: string;
27
- apiKeyConfigured: boolean;
28
- apiKeySource: "env" | "config" | "none";
29
56
  oauth2ClientId?: string;
30
- oauth2ClientIdSource: "env" | "config" | "none";
31
- /** Display name of the shared PKCE user-login client. Deterministic from
32
- * branding; present only when a client id is configured. */
57
+ oauth2ClientIdSource: "env" | "config" | "default";
58
+ /** Display name of the shared PKCE user-login client. Present only when the
59
+ * configured client id *is* the reserved shared-login id a project may
60
+ * legitimately be pointed at some other public client (or at one provisioned
61
+ * before the id was reserved), and labelling that one with the shared
62
+ * client's name would claim an identity it does not have. */
33
63
  oauth2ClientName?: string;
34
- auditOnly: boolean;
64
+ /** Whether Agent Security runs, derived from the project and broker URLs. */
65
+ security: SecurityConnection;
35
66
  permissionMode: "observe" | "enforce";
36
- permissionModeSource: "env" | "config" | "default";
67
+ permissionModeSource: "server" | "cache" | "default";
37
68
  userSubjectNamespace?: string;
38
69
  userSubjectNamespaceSource: "env" | "config" | "none";
70
+ /** The tool namespace tool-use checks are addressed under (`ORY_PERMISSION_NAMESPACE`
71
+ * → default `AgentTool`). This is the namespace the `access` permit is checked in. */
39
72
  namespace: string;
73
+ /** The principal namespaces the model addresses subjects under — `User`, `Agent`,
74
+ * `SubAgent`, and the `Session` fallback. These are the subject namespaces the
75
+ * checked `namespace` references; sourced from the canonical OPL constants so the
76
+ * dashboard lists the whole model, not just the tool namespace. */
77
+ principalNamespaces: string[];
40
78
  /** Whether debug logging is on (`ORY_AGENT_DEBUG=true`). Env-only, read at
41
79
  * process start by the logger — not persisted config, so it's shown read-only. */
42
80
  debugEnabled: boolean;
@@ -44,23 +82,70 @@ export interface StatusConfigSection {
44
82
  export interface StatusUserSection {
45
83
  tokenCache: "empty" | "present" | "stale";
46
84
  subject?: string;
85
+ /**
86
+ * Human-readable name for the signed-in user (email / name /
87
+ * preferred_username), decoded from the persisted id_token. Present only
88
+ * when the id_token carries a profile claim — i.e. the login requested the
89
+ * `profile` / `email` scopes. Absent otherwise; callers fall back to the
90
+ * subject id.
91
+ */
92
+ displayName?: string;
47
93
  expiresInSeconds?: number;
48
94
  clientId?: string;
49
95
  }
50
- export type AgentIdentitySource = "static_api_key" | "static_client_credentials" | "dcr" | "unregistered";
96
+ export type AgentIdentitySource = "static_client_credentials" | "dcr" | "unregistered";
51
97
  export interface StatusAgentSection {
52
98
  source: AgentIdentitySource;
99
+ /** Human-readable OAuth2 client_name for a DCR agent, when known. */
100
+ name?: string;
53
101
  clientId?: string;
54
102
  subject?: string;
55
103
  registeredAt?: string;
104
+ /**
105
+ * The session whose credential this report describes, if any. Sessionless SDK
106
+ * callers report the reserved `sessionless` key.
107
+ */
108
+ sessionKey?: string;
109
+ /**
110
+ * The narrower per-session subject for this run. Absent when there is no
111
+ * ambient session (an SDK integration with no session concept).
112
+ */
113
+ sessionSubject?: string;
114
+ projectUrlMismatch?: {
115
+ registeredAgainst: string;
116
+ current: string;
117
+ };
118
+ }
119
+ /**
120
+ * A typed sub-agent's persisted DCR identity — one row per sub-agent type
121
+ * (e.g. `Explore`, `general-purpose`) the agent has spawned and registered.
122
+ * All sub-agent identities are dynamic registrations, so this always mirrors
123
+ * an `agent.subAgents[<harness>][<session>][<type>]` entry in the shared config
124
+ * — the report covers the newest session of the harness it is run for, since a
125
+ * sub-agent belongs to one specific run of that harness's agent.
126
+ */
127
+ export interface StatusSubAgentSection {
128
+ /** Sub-agent type key (the config map key), e.g. `"Explore"`. */
129
+ subAgentType: string;
130
+ /** Human-readable OAuth2 client_name for this sub-agent, when known. */
131
+ name?: string;
132
+ clientId: string;
133
+ subject: string;
134
+ registeredAt?: string;
135
+ /** The delegator recorded at registration (`agent:<sub>`), when known. */
136
+ delegatedBy?: string;
56
137
  projectUrlMismatch?: {
57
138
  registeredAgainst: string;
58
139
  current: string;
59
140
  };
60
141
  }
61
- export type PermissionsCoverageStatus = "audit_only" | "no_project" | "no_catalog" | "no_user" | "bootstrapped" | "partial" | "not_bootstrapped" | "probe_failed";
142
+ export type PermissionsCoverageStatus =
143
+ /** Agent Security isn't connected — no project URL and/or OAuth2 client id. */
144
+ "not_connected" | "no_catalog" | "no_user" | "fully_granted" | "partial" | "no_grants" | "probe_failed";
62
145
  export interface StatusPermissionsSection {
63
146
  mode: "observe" | "enforce";
147
+ /** Where `mode` came from: a live project read, the local cache, or the default. */
148
+ modeSource: PermissionModeSource;
64
149
  namespace: string;
65
150
  coverageStatus: PermissionsCoverageStatus;
66
151
  coverage?: PermissionsCoverage;
@@ -71,26 +156,40 @@ export interface StatusActivityEntry {
71
156
  parsed?: Record<string, unknown>;
72
157
  }
73
158
  export interface StatusActivitySection {
74
- traceFile?: string;
75
- traceCount: number;
76
- recentSpans: StatusActivityEntry[];
77
159
  logFile?: string;
78
160
  logCount: number;
79
- recentLogs: StatusActivityEntry[];
161
+ recentEvents: StatusActivityEntry[];
80
162
  }
81
163
  export interface StatusReport {
82
164
  harness: string;
83
165
  config: StatusConfigSection;
84
166
  user: StatusUserSection;
85
167
  agent: StatusAgentSection;
168
+ /** Persisted per-type sub-agent DCR identities (may be empty). */
169
+ subAgents: StatusSubAgentSection[];
86
170
  permissions: StatusPermissionsSection;
87
171
  activity: StatusActivitySection;
88
172
  /** Core Ory service health (Kratos, Hydra, Keto) — the dashboard's "System information". */
89
173
  system: StatusSystemSection;
90
174
  }
175
+ /**
176
+ * Human-readable name for a DCR agent / sub-agent credential: the persisted
177
+ * `client_name` when present, else a best-effort reconstruction from the
178
+ * harness and (for sub-agents) type. Reconstruction uses the current hostname,
179
+ * which may differ from the registration host for credentials saved before the
180
+ * name was persisted — acceptable for a display-only label.
181
+ */
182
+ export declare function resolveDynamicName(creds: {
183
+ clientName?: string;
184
+ harness?: string;
185
+ }, subAgentType?: string, harness?: string): string;
91
186
  /**
92
187
  * Gather the full structured status report for a harness. The permissions
93
188
  * section runs a live network probe when a project URL and user identity are
94
189
  * available (same conditions as the text `status` command).
190
+ *
191
+ * `sessionKey` is the run being reported on, when the caller is inside one. The
192
+ * standalone CLI passes none, so the report selects the harness's newest stored
193
+ * session rather than inventing an ambient session.
95
194
  */
96
- export declare function collectStatusReport(harness: string): Promise<StatusReport>;
195
+ export declare function collectStatusReport(harness: string, sessionKey?: string): Promise<StatusReport>;