@ory/argus 0.13.9 → 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 (133) hide show
  1. package/README.md +31 -45
  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 +43 -97
  6. package/assets/skills/ory-e2b-sandbox/SKILL.md +18 -18
  7. package/assets/skills/ory-temporal-worker/SKILL.md +38 -42
  8. package/assets/skills/permissions-onboarding/SKILL.md +131 -104
  9. package/dist/adapters.d.ts +93 -30
  10. package/dist/adapters.js +464 -136
  11. package/dist/agent-auth.d.ts +258 -68
  12. package/dist/agent-auth.js +998 -202
  13. package/dist/auth-store.d.ts +37 -2
  14. package/dist/auth-store.js +37 -3
  15. package/dist/auth.d.ts +40 -4
  16. package/dist/auth.js +247 -19
  17. package/dist/bash-parser.d.ts +98 -0
  18. package/dist/bash-parser.js +396 -0
  19. package/dist/branding.d.ts +128 -0
  20. package/dist/branding.js +151 -0
  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 -278
  26. package/dist/client.d.ts +175 -138
  27. package/dist/client.js +672 -391
  28. package/dist/config.d.ts +249 -57
  29. package/dist/config.js +486 -62
  30. package/dist/context.d.ts +10 -0
  31. package/dist/context.js +21 -0
  32. package/dist/contract-suite.d.ts +8 -8
  33. package/dist/contract-suite.js +88 -69
  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 +31 -19
  47. package/dist/index.js +182 -31
  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.d.ts +14 -0
  54. package/dist/local/health.js +50 -4
  55. package/dist/local/index.d.ts +2 -2
  56. package/dist/local/index.js +24 -10
  57. package/dist/local/manager.d.ts +20 -1
  58. package/dist/local/manager.js +160 -39
  59. package/dist/local/ports.d.ts +158 -0
  60. package/dist/local/ports.js +443 -0
  61. package/dist/local/seed.d.ts +22 -25
  62. package/dist/local/seed.js +88 -56
  63. package/dist/logger.d.ts +54 -25
  64. package/dist/logger.js +329 -63
  65. package/dist/mcp.d.ts +2 -2
  66. package/dist/mcp.js +10 -5
  67. package/dist/mirror-bootstrap.d.ts +48 -0
  68. package/dist/mirror-bootstrap.js +254 -0
  69. package/dist/opl.d.ts +289 -0
  70. package/dist/opl.js +446 -0
  71. package/dist/permission-mode.d.ts +87 -0
  72. package/dist/permission-mode.js +307 -0
  73. package/dist/permissions-cli.d.ts +13 -49
  74. package/dist/permissions-cli.js +154 -348
  75. package/dist/permissions.d.ts +148 -38
  76. package/dist/permissions.js +591 -45
  77. package/dist/post-install.d.ts +33 -0
  78. package/dist/post-install.js +127 -0
  79. package/dist/read-credential.d.ts +65 -0
  80. package/dist/read-credential.js +86 -0
  81. package/dist/registry/cli.js +5 -2
  82. package/dist/registry/config.d.ts +0 -17
  83. package/dist/registry/config.js +0 -23
  84. package/dist/registry/index.d.ts +1 -1
  85. package/dist/registry/index.js +2 -2
  86. package/dist/registry/manager.d.ts +4 -21
  87. package/dist/registry/manager.js +83 -55
  88. package/dist/runtime-credential.d.ts +140 -0
  89. package/dist/runtime-credential.js +572 -0
  90. package/dist/runtime.d.ts +408 -0
  91. package/dist/runtime.js +748 -0
  92. package/dist/setup.d.ts +23 -28
  93. package/dist/setup.js +57 -84
  94. package/dist/status-cli.d.ts +29 -13
  95. package/dist/status-cli.js +124 -144
  96. package/dist/status-data.d.ts +195 -0
  97. package/dist/status-data.js +333 -0
  98. package/dist/status-system.d.ts +24 -0
  99. package/dist/status-system.js +56 -0
  100. package/dist/subject.d.ts +126 -20
  101. package/dist/subject.js +215 -30
  102. package/dist/testing.d.ts +74 -38
  103. package/dist/testing.js +185 -68
  104. package/dist/tool-catalog.d.ts +53 -11
  105. package/dist/tool-catalog.js +164 -13
  106. package/dist/tool-metadata.d.ts +7 -6
  107. package/dist/tool-metadata.js +6 -5
  108. package/dist/types.d.ts +11 -1
  109. package/dist/uninstall.d.ts +74 -19
  110. package/dist/uninstall.js +224 -49
  111. package/dist/user-login.d.ts +22 -16
  112. package/dist/user-login.js +67 -96
  113. package/dist/watch-cli.d.ts +6 -0
  114. package/dist/watch-cli.js +217 -0
  115. package/package.json +3 -11
  116. package/dist/dev.d.ts +0 -103
  117. package/dist/dev.js +0 -584
  118. package/dist/interactive-setup.d.ts +0 -165
  119. package/dist/interactive-setup.js +0 -1546
  120. package/dist/local/jaeger-main.d.ts +0 -13
  121. package/dist/local/jaeger-main.js +0 -85
  122. package/dist/local/jaeger.d.ts +0 -50
  123. package/dist/local/jaeger.js +0 -162
  124. package/dist/otel/exporter.d.ts +0 -17
  125. package/dist/otel/exporter.js +0 -12
  126. package/dist/otel/index.d.ts +0 -2
  127. package/dist/otel/index.js +0 -8
  128. package/dist/otel/otlp.d.ts +0 -103
  129. package/dist/otel/otlp.js +0 -385
  130. package/dist/tracer.d.ts +0 -190
  131. package/dist/tracer.js +0 -481
  132. package/dist/watch-sandbox.d.ts +0 -9
  133. package/dist/watch-sandbox.js +0 -81
@@ -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,30 +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";
28
- }
29
- /**
30
- * Human-readable annotation for where the resolved user-login flag came
31
- * from, so `status` readers know whether to look at the env var or the
32
- * config file to change it.
33
- */
34
- function formatUserLoginSource(source) {
35
- switch (source) {
36
- case "env":
37
- return " (ORY_USER_LOGIN env var)";
38
- case "config":
39
- return " (saved in config; change with: configure --user-login|--no-user-login)";
40
- case "default":
41
- return "";
42
- }
29
+ return process.env.ORY_PERMISSION_NAMESPACE ?? "AgentTool";
43
30
  }
44
31
  /**
45
32
  * Format a relative duration like "4h 32m" or "12s" for a future deadline.
@@ -61,27 +48,16 @@ function formatExpiresIn(expiresAtSec, nowMs = Date.now()) {
61
48
  return `${remaining}s`;
62
49
  }
63
50
  /**
64
- * Render the "User identity" block. Shows the `ORY_USER_LOGIN` state and,
65
- * when a PKCE token is cached, the resolved subject and expiry.
51
+ * Render the "User identity" block. The interactive PKCE login runs every
52
+ * session; this shows the cached token's subject and expiry, if any.
66
53
  */
67
54
  function printUserIdentitySection() {
68
- const resolved = (0, config_js_1.resolveConfig)();
69
- const loginOn = resolved.userLogin;
70
55
  const tokens = (0, auth_store_js_1.loadTokens)();
71
56
  console.log("");
72
57
  console.log("User identity (interactive PKCE login):");
73
- const loginState = loginOn
74
- ? `on${formatUserLoginSource(resolved.userLoginSource)}`
75
- : "off (enable with: configure --user-login, or ORY_USER_LOGIN=true)";
76
- console.log(` Login: ${loginState}`);
77
58
  if (!tokens) {
78
59
  console.log(" Token cache: empty");
79
- if (loginOn) {
80
- console.log(" Subject: (will resolve at next session start)");
81
- }
82
- else {
83
- console.log(" Subject: (login disabled — no PKCE flow will run)");
84
- }
60
+ console.log(" Subject: (will resolve at next session start)");
85
61
  return;
86
62
  }
87
63
  const expired = (0, auth_store_js_1.isExpired)(tokens);
@@ -91,6 +67,10 @@ function printUserIdentitySection() {
91
67
  else {
92
68
  console.log(` Token cache: present (expires in ${formatExpiresIn(tokens.expiresAt)})`);
93
69
  }
70
+ const displayName = tokens.displayName ?? (0, auth_js_1.displayNameFromIdToken)(tokens.idToken);
71
+ if (displayName) {
72
+ console.log(` Name: ${displayName}`);
73
+ }
94
74
  const subject = tokens.subject ? `user:${tokens.subject}` : "(no subject claim on cached token)";
95
75
  console.log(` Subject: ${subject}`);
96
76
  if (tokens.clientId) {
@@ -99,183 +79,182 @@ function printUserIdentitySection() {
99
79
  }
100
80
  /**
101
81
  * Render the "Agent identity" block. Summarizes the resolved machine
102
- * 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.
103
85
  */
104
- function printAgentIdentitySection() {
86
+ function printAgentIdentitySection(harness) {
105
87
  console.log("");
106
88
  console.log("Agent identity (non-interactive):");
107
- // Static overrides take precedence — surface them so the reader knows
89
+ // The one static override takes precedence — surface it so the reader knows
108
90
  // why DCR isn't running.
109
- if (process.env.ORY_AGENT_API_KEY) {
110
- console.log(" Source: static API key (ORY_AGENT_API_KEY)");
111
- if (process.env.ORY_AGENT_SUBJECT_ID) {
112
- console.log(` Subject: ${process.env.ORY_AGENT_SUBJECT_ID}`);
113
- }
114
- return;
115
- }
116
91
  if (process.env.ORY_AGENT_CLIENT_ID && process.env.ORY_AGENT_CLIENT_SECRET) {
117
92
  console.log(" Source: static client_credentials (ORY_AGENT_CLIENT_ID/SECRET)");
118
93
  console.log(` Client ID: ${process.env.ORY_AGENT_CLIENT_ID}`);
119
- 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;
120
95
  console.log(` Subject: ${subject}`);
121
96
  return;
122
97
  }
123
- const persisted = (0, agent_auth_js_1.loadAgentDynamicCredentials)();
124
- if (!persisted) {
98
+ const latest = (0, agent_auth_js_1.latestAgentRegistration)(harness);
99
+ if (!latest) {
125
100
  console.log(" Source: not registered yet");
126
101
  console.log(" Status: first session start will register an OAuth2 client (RFC 7591)");
127
102
  return;
128
103
  }
104
+ const { sessionKey, credentials: persisted } = latest;
129
105
  const resolved = (0, config_js_1.resolveConfig)();
130
106
  const mismatched = resolved.projectUrl && persisted.projectUrl !== resolved.projectUrl;
131
107
  console.log(" Source: OAuth2 Dynamic Client Registration (RFC 7591)");
132
108
  console.log(` Status: registered`);
109
+ console.log(` Name: ${(0, status_data_js_1.resolveDynamicName)(persisted)}`);
133
110
  console.log(` Client ID: ${persisted.clientId}`);
134
- const subject = process.env.ORY_AGENT_SUBJECT_ID ?? persisted.clientId;
135
- console.log(` Subject: ${subject}`);
111
+ const subject = (0, subject_js_1.readAgentSubjectOverride)() ?? persisted.clientId;
112
+ console.log(` Subject: ${subject_js_1.AGENT_NAMESPACE}:${subject}`);
136
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
+ }
137
119
  if (mismatched) {
138
120
  console.log(` Warning: registered against ${persisted.projectUrl}, but current project URL is ${resolved.projectUrl}`);
139
121
  console.log(" run `agent unregister` to clear stale credentials");
140
122
  }
141
123
  }
142
- async function probePermissionsCoverage(harness) {
143
- const resolved = (0, config_js_1.resolveConfig)();
144
- if (!resolved.projectUrl)
145
- return undefined;
146
- const catalog = (0, tool_catalog_js_1.getToolCatalog)(harness);
147
- if (catalog.length === 0)
148
- return undefined;
149
- const tokens = (0, auth_store_js_1.loadTokens)();
150
- const subjectOverride = process.env.ORY_USER_SUBJECT_ID;
151
- if (!subjectOverride && (!tokens || (0, auth_store_js_1.isExpired)(tokens))) {
152
- return undefined;
153
- }
154
- const client = client_js_1.OryAgentClient.fromEnv(harness);
155
- if (tokens && !(0, auth_store_js_1.isExpired)(tokens)) {
156
- client.setUserPrincipal({
157
- subject: tokens.subject,
158
- token: tokens.accessToken,
159
- });
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;
160
143
  }
161
- await (0, agent_auth_js_2.ensureAgentIdentity)(client, { projectUrl: resolved.projectUrl }).catch(() => {
162
- /* best-effort; the probe still runs with whatever credentials we have */
163
- });
164
- const subject = (0, subject_js_1.resolveUserSubject)(client);
165
- const subjectId = (0, subject_js_1.subjectLabel)(subject);
166
- if (subjectId === "agent:unknown")
167
- return undefined;
168
- const namespace = resolveNamespace();
169
- const result = {
170
- total: catalog.length,
171
- allowed: 0,
172
- denied: 0,
173
- errored: 0,
174
- };
175
- for (const tool of catalog) {
176
- const check = {
177
- namespace,
178
- object: tool,
179
- relation: "use",
180
- ...subject,
181
- };
182
- try {
183
- const probe = await client.checkPermission(check, {
184
- spanAttributes: { toolName: tool, source: "status_coverage" },
185
- });
186
- if (probe.allowed)
187
- result.allowed++;
188
- else
189
- result.denied++;
190
- }
191
- catch (err) {
192
- const ory = err;
193
- result.errored++;
194
- 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}`);
195
159
  }
196
- }
197
- return result;
160
+ });
198
161
  }
199
162
  /**
200
- * Render the "Permissions" block. Always prints the configured mode and
201
- * namespace; runs a network probe of the harness's built-in tool catalog
202
- * only when a project URL is configured *and* a user identity is
203
- * 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.
204
172
  */
205
173
  async function printPermissionsSection(binName, harness) {
206
174
  const resolved = (0, config_js_1.resolveConfig)();
207
175
  const namespace = resolveNamespace();
208
176
  const catalog = (0, tool_catalog_js_1.getToolCatalog)(harness);
177
+ const cachedMode = `${resolved.permissionMode}${formatModeSuffix(resolved.permissionModeSource)}`;
209
178
  console.log("");
210
179
  console.log("Permissions:");
211
- console.log(` Mode: ${resolved.permissionMode}${formatModeSuffix(resolved.permissionModeSource)}`);
212
- console.log(` Namespace: ${namespace}`);
213
- if (resolved.auditOnly) {
214
- console.log(" Coverage: n/a (audit-only modeOry checks disabled)");
215
- return;
216
- }
217
- if (!resolved.projectUrl) {
218
- console.log(" Coverage: n/a (no project URL configured)");
219
- 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)})`);
220
184
  }
221
185
  if (catalog.length === 0) {
222
- 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}")`);
223
187
  console.log(` known harnesses: ${tool_catalog_js_1.KNOWN_HARNESSES.join(", ")}`);
224
188
  return;
225
189
  }
226
- const hasUser = !!process.env.ORY_USER_SUBJECT_ID || isUserTokenUsable();
227
- if (!hasUser) {
228
- console.log(` Coverage: n/a (no cached user identity run with ORY_USER_LOGIN=true once,`);
229
- 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)");
230
193
  return;
231
194
  }
232
- process.stdout.write(" Coverage: checking…\r");
233
- const probe = await probePermissionsCoverage(harness);
234
- // Clear the "checking…" line by overwriting it.
235
- 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");
236
207
  if (!probe) {
237
- console.log(" Coverage: unavailable (no user identity could be resolved)");
238
- return;
208
+ return printNoCoverage(cachedMode, namespace, "unavailable (no user identity could be resolved)");
239
209
  }
240
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}`);
241
214
  if (errored > 0 && allowed === 0 && denied === 0) {
242
215
  console.log(` Coverage: probe failed (${probe.errorCode ?? "unknown"})`);
243
216
  return;
244
217
  }
245
218
  const status = allowed === total
246
- ? `bootstrapped (${allowed}/${total} built-in tools)`
219
+ ? `fully granted (${allowed}/${total} built-in tools)`
247
220
  : allowed === 0
248
- ? `not bootstrapped (0/${total} built-in tools)`
221
+ ? `no grants (0/${total} built-in tools)`
249
222
  : `partial (${allowed}/${total} built-in tools allowed)`;
250
223
  console.log(` Coverage: ${status}`);
251
224
  if (denied > 0) {
252
- 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)");
253
226
  }
254
227
  else if (errored > 0) {
255
- 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`);
256
229
  }
257
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
+ }
258
237
  function isUserTokenUsable() {
259
238
  const tokens = (0, auth_store_js_1.loadTokens)();
260
239
  return !!tokens && !(0, auth_store_js_1.isExpired)(tokens);
261
240
  }
262
241
  function formatModeSuffix(source) {
263
242
  switch (source) {
264
- case "env":
265
- return " (from ORY_PERMISSION_MODE env)";
266
- case "config":
267
- 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)";
268
247
  case "default":
269
- return " (default)";
248
+ return " (default — not yet read from the project)";
270
249
  }
271
250
  }
272
251
  /**
273
252
  * Compose the unified `status` output for a plugin CLI. Sections render
274
253
  * in this order: title → configuration → user identity → agent identity
275
- * → permissions → hooks & plugin → environment → recent activity
276
- * (traces + debug log).
254
+ * → sub-agent identities → permissions → hooks & plugin → environment →
255
+ * recent activity from the unified activity/debug log.
277
256
  *
278
- * Each section degrades gracefully — missing project URL, audit-only
257
+ * Each section degrades gracefully — Agent Security not connected
279
258
  * mode, and empty token caches all surface as inline "n/a" lines rather
280
259
  * than aborting the command.
281
260
  */
@@ -292,19 +271,20 @@ async function runStatusCommand(binName, harness, options) {
292
271
  console.log("");
293
272
  (0, cli_js_1.printOryConfig)();
294
273
  printUserIdentitySection();
295
- printAgentIdentitySection();
274
+ printAgentIdentitySection(harness);
275
+ printSubAgentIdentitiesSection(harness);
296
276
  await printPermissionsSection(binName, harness);
297
277
  if (options.printPluginSection) {
298
278
  console.log("");
299
- options.printPluginSection();
279
+ await options.printPluginSection();
300
280
  }
301
281
  (0, cli_js_1.printEnvironment)();
302
282
  console.log("");
303
283
  console.log("Recent activity:");
304
- (0, cli_js_1.printTraceTail)(harness);
305
284
  (0, cli_js_1.printLogTail)(harness);
306
285
  console.log("");
307
- 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");
308
288
  console.log("");
309
289
  console.log(`Drill into any section with:`);
310
290
  console.log(` ${(0, cli_invocation_js_1.oryNpx)(binName)} agent status`);
@@ -0,0 +1,195 @@
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";
15
+ import { type StatusSystemSection } from "./status-system.js";
16
+ export interface PermissionsCoverage {
17
+ total: number;
18
+ allowed: number;
19
+ denied: number;
20
+ errored: number;
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;
32
+ }
33
+ /**
34
+ * Probe the harness's built-in tool catalog against Ory, returning how many
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.
43
+ */
44
+ export declare function probePermissionsCoverage(harness: string): Promise<PermissionsCoverage | undefined>;
45
+ export interface StatusConfigSection {
46
+ configPath: string;
47
+ /** Whether a config file actually exists at `configPath` (vs. where one would be written). */
48
+ configExists: boolean;
49
+ projectUrl?: string;
50
+ projectUrlSource: "env" | "config" | "none";
51
+ projectId?: string;
52
+ projectIdSource: "env" | "config" | "none";
53
+ /** Human-readable project / workspace names captured at install (display only). */
54
+ projectName?: string;
55
+ workspaceName?: string;
56
+ oauth2ClientId?: string;
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. */
63
+ oauth2ClientName?: string;
64
+ /** Whether Agent Security runs, derived from the project and broker URLs. */
65
+ security: SecurityConnection;
66
+ permissionMode: "observe" | "enforce";
67
+ permissionModeSource: "server" | "cache" | "default";
68
+ userSubjectNamespace?: string;
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. */
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[];
78
+ /** Whether debug logging is on (`ORY_AGENT_DEBUG=true`). Env-only, read at
79
+ * process start by the logger — not persisted config, so it's shown read-only. */
80
+ debugEnabled: boolean;
81
+ }
82
+ export interface StatusUserSection {
83
+ tokenCache: "empty" | "present" | "stale";
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;
93
+ expiresInSeconds?: number;
94
+ clientId?: string;
95
+ }
96
+ export type AgentIdentitySource = "static_client_credentials" | "dcr" | "unregistered";
97
+ export interface StatusAgentSection {
98
+ source: AgentIdentitySource;
99
+ /** Human-readable OAuth2 client_name for a DCR agent, when known. */
100
+ name?: string;
101
+ clientId?: string;
102
+ subject?: string;
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;
137
+ projectUrlMismatch?: {
138
+ registeredAgainst: string;
139
+ current: string;
140
+ };
141
+ }
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";
145
+ export interface StatusPermissionsSection {
146
+ mode: "observe" | "enforce";
147
+ /** Where `mode` came from: a live project read, the local cache, or the default. */
148
+ modeSource: PermissionModeSource;
149
+ namespace: string;
150
+ coverageStatus: PermissionsCoverageStatus;
151
+ coverage?: PermissionsCoverage;
152
+ knownHarnesses?: string[];
153
+ }
154
+ export interface StatusActivityEntry {
155
+ raw: string;
156
+ parsed?: Record<string, unknown>;
157
+ }
158
+ export interface StatusActivitySection {
159
+ logFile?: string;
160
+ logCount: number;
161
+ recentEvents: StatusActivityEntry[];
162
+ }
163
+ export interface StatusReport {
164
+ harness: string;
165
+ config: StatusConfigSection;
166
+ user: StatusUserSection;
167
+ agent: StatusAgentSection;
168
+ /** Persisted per-type sub-agent DCR identities (may be empty). */
169
+ subAgents: StatusSubAgentSection[];
170
+ permissions: StatusPermissionsSection;
171
+ activity: StatusActivitySection;
172
+ /** Core Ory service health (Kratos, Hydra, Keto) — the dashboard's "System information". */
173
+ system: StatusSystemSection;
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;
186
+ /**
187
+ * Gather the full structured status report for a harness. The permissions
188
+ * section runs a live network probe when a project URL and user identity are
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.
194
+ */
195
+ export declare function collectStatusReport(harness: string, sessionKey?: string): Promise<StatusReport>;