@agent-native/core 0.205.0 → 0.206.0-nightly-20261009011246
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/a2a/client.d.ts +2 -2
- package/dist/agent/engine/anthropic-engine.d.ts +1 -1
- package/dist/agent/engine/builder-engine.d.ts +1 -1
- package/dist/agent/engine/builder-engine.js +23 -8
- package/dist/agent/engine/error-telemetry.d.ts +5 -0
- package/dist/agent/engine/error-telemetry.js +41 -0
- package/dist/agent/engine/registry.d.ts +1 -6
- package/dist/agent/engine/registry.js +2 -31
- package/dist/agent/model-config.d.ts +26 -14
- package/dist/agent/model-config.js +131 -8
- package/dist/agent/model-version.d.ts +19 -0
- package/dist/agent/model-version.js +153 -0
- package/dist/agent/production-agent.d.ts +1 -0
- package/dist/agent/production-agent.js +24 -6
- package/dist/agent/provider-model-selection.js +6 -4
- package/dist/agent/run-manager.js +5 -11
- package/dist/agent/thread-data-builder.js +14 -6
- package/dist/authorization/check-action.js +8 -4
- package/dist/chat-threads/store.js +22 -9
- package/dist/client/analytics.d.ts +1 -0
- package/dist/client/analytics.js +10 -2
- package/dist/client/chat/runtime.js +18 -0
- package/dist/client/chat-model-groups.d.ts +3 -0
- package/dist/client/error-format.js +10 -7
- package/dist/client/onboarding/use-onboarding.d.ts +12 -0
- package/dist/client/onboarding/use-onboarding.js +491 -4
- package/dist/client/session-replay-privacy.d.ts +8 -0
- package/dist/client/session-replay-privacy.js +8 -0
- package/dist/client/session-replay.d.ts +1 -0
- package/dist/client/session-replay.js +18 -9
- package/dist/client/sse-event-processor.js +10 -6
- package/dist/client/use-chat-models.d.ts +5 -0
- package/dist/client/use-chat-models.js +51 -5
- package/dist/client/use-session.d.ts +8 -1
- package/dist/client/use-session.js +20 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/deploy/build.js +353 -3
- package/dist/file-upload/actions/upload-image.d.ts +2 -2
- package/dist/jobs/backfill-run-history-ownership.d.ts +12 -0
- package/dist/jobs/backfill-run-history-ownership.js +87 -0
- package/dist/jobs/background-automation-runner.js +3 -9
- package/dist/jobs/remote-execution.js +3 -9
- package/dist/jobs/run-history-ownership.d.ts +5 -0
- package/dist/jobs/run-history-ownership.js +16 -0
- package/dist/jobs/run-now.js +2 -3
- package/dist/localization/agent-resources-messages.d.ts +3 -0
- package/dist/localization/agent-resources-messages.js +33 -0
- package/dist/mcp/actions/create-org-service-token.js +5 -7
- package/dist/mcp/build-server.d.ts +23 -18
- package/dist/mcp/build-server.js +220 -212
- package/dist/mcp/connect-route.d.ts +11 -2
- package/dist/mcp/connect-route.js +34 -24
- package/dist/mcp/connect-store.d.ts +9 -13
- package/dist/mcp/connect-store.js +11 -24
- package/dist/mcp/credential-membership.js +17 -6
- package/dist/mcp/oauth-route.d.ts +11 -1
- package/dist/mcp/oauth-route.js +28 -6
- package/dist/mcp/oauth-token.d.ts +12 -2
- package/dist/mcp/oauth-token.js +15 -4
- package/dist/mcp/server.js +9 -5
- package/dist/notifications/routes.d.ts +2 -2
- package/dist/observability/posthog-ai.d.ts +1 -0
- package/dist/observability/posthog-ai.js +20 -8
- package/dist/observability/routes.d.ts +6 -6
- package/dist/observability/traces.js +61 -39
- package/dist/org/app-roles.d.ts +8 -1
- package/dist/org/app-roles.js +29 -9
- package/dist/org/context.js +24 -8
- package/dist/org/index.d.ts +2 -2
- package/dist/org/workspace-app-identity.d.ts +1 -0
- package/dist/org/workspace-app-identity.js +25 -0
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +11 -11
- package/dist/provider-api/actions/provider-api.d.ts +3 -3
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/scripts/agent-engines/list-agent-engines.js +1 -0
- package/dist/server/action-routes.js +8 -1
- package/dist/server/agent-chat-plugin.js +1 -0
- package/dist/server/agent-engine-default-model-route.d.ts +1 -1
- package/dist/server/auth.js +33 -17
- package/dist/server/bearer-credential-refusal.d.ts +23 -0
- package/dist/server/bearer-credential-refusal.js +67 -0
- package/dist/server/better-auth-instance.js +13 -6
- package/dist/server/builder-browser.d.ts +2 -0
- package/dist/server/builder-browser.js +16 -3
- package/dist/server/capture-error.d.ts +1 -0
- package/dist/server/capture-error.js +28 -4
- package/dist/server/complete-text.js +16 -3
- package/dist/server/core-routes-plugin.js +2 -0
- package/dist/server/dev-action-bridge.js +7 -1
- package/dist/server/google-oauth.js +6 -3
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/recap-image-route.js +4 -0
- package/dist/server/request-context.d.ts +16 -0
- package/dist/server/request-context.js +17 -0
- package/dist/shared/agent-chat-context.d.ts +1 -0
- package/dist/shared/agent-chat-context.js +26 -0
- package/dist/shared/index.d.ts +1 -1
- package/dist/shared/index.js +1 -1
- package/dist/shared/reasoning-effort.js +10 -1
- package/dist/shared/ssr-session-bootstrap.d.ts +7 -0
- package/dist/shared/ssr-session-bootstrap.js +21 -0
- package/dist/sharing/access.js +86 -5
- package/dist/templates/builder-code-starter/server/routes/index.get.ts +2 -0
- package/dist/triggers/routes.js +2 -1
- package/docs/content/automation-connectors.mdx +5 -3
- package/docs/content/automations.mdx +78 -49
- package/docs/content/observability.mdx +16 -5
- package/docs/content/organizations-teams-permissions.mdx +44 -2
- package/docs/content/recurring-jobs.mdx +4 -8
- package/docs/content/template-forms.mdx +6 -0
- package/package.json +5 -4
- package/src/templates/builder-code-starter/server/routes/index.get.ts +2 -0
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { getMethod, getHeader } from "h3";
|
|
3
|
-
import { signA2AToken } from "../a2a/client.js";
|
|
4
3
|
import { getAppConfig } from "../app-config/index.js";
|
|
5
4
|
import { mcpSettingsMessagesForLocale } from "../localization/mcp-settings-messages.js";
|
|
6
5
|
import { resolveLocaleFromRequest } from "../localization/server.js";
|
|
@@ -13,8 +12,9 @@ import { publicFrameworkPath } from "../server/framework-route-prefix.js";
|
|
|
13
12
|
import { readBody } from "../server/h3-helpers.js";
|
|
14
13
|
import { FRAMEWORK_INTERNAL_ROUTE_PREFIX } from "../shared/framework-route-prefix.js";
|
|
15
14
|
import { MCP_CONNECT_MCP_URL_TEMPLATE, getMcpConnectGuides, getMcpStaticTokenFallback, interpolateMcpConnectTemplate, resolveMcpConnectGuideId, } from "../shared/mcp-connect-content.js";
|
|
16
|
-
import { ensureConnectTables, recordMintedToken, listTokens, revokeToken, normalizeServiceName, serviceIdentityEmail, createDeviceCode, getDeviceCode, getDeviceCodeByUserCode, approveDeviceCode, consumeDeviceCode, claimDeviceCodeForMint, finishDeviceCodeMint, expireDeviceCode, MCP_CONNECT_OAUTH_CLIENT_ID,
|
|
15
|
+
import { ensureConnectTables, recordMintedToken, listTokens, revokeToken, normalizeServiceName, serviceIdentityEmail, createDeviceCode, getDeviceCode, getDeviceCodeByUserCode, approveDeviceCode, consumeDeviceCode, claimDeviceCodeForMint, finishDeviceCodeMint, expireDeviceCode, MCP_CONNECT_OAUTH_CLIENT_ID, DEFAULT_TOKEN_TTL_DAYS, MIN_TOKEN_TTL_DAYS, MAX_TOKEN_TTL_DAYS, MAX_SERVICE_TOKEN_TTL_DAYS, DEVICE_CODE_TTL_MS, } from "./connect-store.js";
|
|
17
16
|
import { McpCredentialIssuanceError, withMcpCredentialIssuance, } from "./credential-issuance.js";
|
|
17
|
+
import { getMcpOAuthIssuer, resolveMcpOAuthIssuer } from "./oauth-route.js";
|
|
18
18
|
import { MCP_OAUTH_DEFAULT_SCOPE, signMcpOAuthAccessToken, } from "./oauth-token.js";
|
|
19
19
|
import { resolveMcpOrgChoices } from "./org-choice.js";
|
|
20
20
|
import { MCP_PUBLIC_ROUTE_PREFIX } from "./route-paths.js";
|
|
@@ -157,7 +157,7 @@ async function mintConnectToken(params) {
|
|
|
157
157
|
ownerEmail: params.email,
|
|
158
158
|
orgId: params.orgId,
|
|
159
159
|
orgDomain,
|
|
160
|
-
|
|
160
|
+
issuer: params.issuer,
|
|
161
161
|
expiresIn: `${params.ttlDays}d`,
|
|
162
162
|
jti,
|
|
163
163
|
...(params.catalogScope === "full" ? { catalogScope: "full" } : {}),
|
|
@@ -171,34 +171,38 @@ async function mintConnectToken(params) {
|
|
|
171
171
|
return { token, jti };
|
|
172
172
|
});
|
|
173
173
|
}
|
|
174
|
+
/**
|
|
175
|
+
* Connect tokens are MCP OAuth access tokens bound to this app's MCP URL, so
|
|
176
|
+
* they share OAuth's verification path. Do not sign them as A2A JWTs again:
|
|
177
|
+
* that format let cross-app trust rules reinterpret a credential this app
|
|
178
|
+
* issued.
|
|
179
|
+
*/
|
|
174
180
|
async function signConnectToken(params) {
|
|
175
|
-
if (readDeployCredentialEnv("A2A_SECRET")?.trim()) {
|
|
176
|
-
return signA2AToken(params.ownerEmail, params.orgDomain, undefined, {
|
|
177
|
-
preferGlobalSecret: true,
|
|
178
|
-
expiresIn: params.expiresIn,
|
|
179
|
-
extraClaims: {
|
|
180
|
-
jti: params.jti,
|
|
181
|
-
scope: MCP_CONNECT_SCOPE,
|
|
182
|
-
...(params.includeOrgIdClaim && params.orgId
|
|
183
|
-
? { org_id: params.orgId }
|
|
184
|
-
: {}),
|
|
185
|
-
...(params.catalogScope === "full" ? { catalog_scope: "full" } : {}),
|
|
186
|
-
},
|
|
187
|
-
});
|
|
188
|
-
}
|
|
189
181
|
return signMcpOAuthAccessToken({
|
|
190
182
|
ownerEmail: params.ownerEmail,
|
|
191
183
|
orgId: params.orgId ?? null,
|
|
192
184
|
orgDomain: params.orgDomain ?? null,
|
|
193
185
|
clientId: MCP_CONNECT_OAUTH_CLIENT_ID,
|
|
194
186
|
scope: MCP_OAUTH_DEFAULT_SCOPE,
|
|
195
|
-
resource: mcpResourceUrl(params.
|
|
196
|
-
issuer: params.
|
|
187
|
+
resource: mcpResourceUrl(params.issuer),
|
|
188
|
+
issuer: params.issuer,
|
|
197
189
|
jti: params.jti,
|
|
198
190
|
expiresIn: params.expiresIn,
|
|
199
191
|
...(params.catalogScope === "full" ? { catalogScope: "full" } : {}),
|
|
192
|
+
...(params.service ? { service: true } : {}),
|
|
200
193
|
});
|
|
201
194
|
}
|
|
195
|
+
/**
|
|
196
|
+
* Neither the request nor configuration names this app's public URL, so no
|
|
197
|
+
* audience `verifyAuth` would accept can be resolved. Guessing one would mint a
|
|
198
|
+
* token the app then refuses.
|
|
199
|
+
*/
|
|
200
|
+
export class OrgServiceTokenAppUrlError extends Error {
|
|
201
|
+
constructor() {
|
|
202
|
+
super("Could not determine the app URL needed to mint a token. Set APP_URL on the deployment.");
|
|
203
|
+
this.name = "OrgServiceTokenAppUrlError";
|
|
204
|
+
}
|
|
205
|
+
}
|
|
202
206
|
/**
|
|
203
207
|
* Mint an ORG SERVICE token: a connect-scoped, revocable bearer whose subject
|
|
204
208
|
* is the synthetic service identity `svc-<name>@service.<orgId>` instead of a
|
|
@@ -209,7 +213,7 @@ async function signConnectToken(params) {
|
|
|
209
213
|
*
|
|
210
214
|
* The token value is returned exactly once and never persisted — only the
|
|
211
215
|
* random `jti` is stored, so the standard revocation path
|
|
212
|
-
* (`
|
|
216
|
+
* (`lookupConnectTokenOrg` in `verifyAuth`) applies to service tokens identically.
|
|
213
217
|
*
|
|
214
218
|
* The `create-org-service-token` action gates on org owner/admin before
|
|
215
219
|
* calling this. Offboarding can remove that admin before the mint, so the
|
|
@@ -221,6 +225,9 @@ export async function mintOrgServiceToken(params) {
|
|
|
221
225
|
const serviceEmail = serviceIdentityEmail(serviceName, params.orgId);
|
|
222
226
|
const orgDomain = await resolveOrgDomain(params.orgId);
|
|
223
227
|
const ttlDays = clampTtlDays(params.ttlDays ?? DEFAULT_TOKEN_TTL_DAYS, MAX_SERVICE_TOKEN_TTL_DAYS);
|
|
228
|
+
const issuer = resolveMcpOAuthIssuer(params.appUrl || undefined);
|
|
229
|
+
if (!issuer)
|
|
230
|
+
throw new OrgServiceTokenAppUrlError();
|
|
224
231
|
await prepareConnectIssuance();
|
|
225
232
|
return withMcpCredentialIssuance({
|
|
226
233
|
email: params.createdBy,
|
|
@@ -233,10 +240,10 @@ export async function mintOrgServiceToken(params) {
|
|
|
233
240
|
ownerEmail: serviceEmail,
|
|
234
241
|
orgId: params.orgId,
|
|
235
242
|
orgDomain,
|
|
236
|
-
|
|
243
|
+
issuer,
|
|
237
244
|
expiresIn: `${ttlDays}d`,
|
|
238
245
|
jti,
|
|
239
|
-
|
|
246
|
+
service: true,
|
|
240
247
|
});
|
|
241
248
|
const id = await recordMintedToken({
|
|
242
249
|
jti,
|
|
@@ -1205,6 +1212,9 @@ export async function handleMcpConnect(event, subpath, options = {}) {
|
|
|
1205
1212
|
const origin = deriveOrigin(event);
|
|
1206
1213
|
const basePath = configuredBasePath();
|
|
1207
1214
|
const appUrl = `${origin}${basePath}`;
|
|
1215
|
+
// Tokens bind to an audience verifyAuth accepts. Reached through an alias of
|
|
1216
|
+
// a configured public URL, that differs from the appUrl this page displays.
|
|
1217
|
+
const tokenIssuer = getMcpOAuthIssuer(event) ?? appUrl;
|
|
1208
1218
|
let requestUrl = null;
|
|
1209
1219
|
try {
|
|
1210
1220
|
requestUrl = new URL(event.node?.req?.url ?? event.path ?? "/", "http://an.invalid");
|
|
@@ -1295,7 +1305,7 @@ export async function handleMcpConnect(event, subpath, options = {}) {
|
|
|
1295
1305
|
orgId: defaultOrganizationId,
|
|
1296
1306
|
label,
|
|
1297
1307
|
ttlDays,
|
|
1298
|
-
|
|
1308
|
+
issuer: tokenIssuer,
|
|
1299
1309
|
requestOrigin: origin,
|
|
1300
1310
|
...(catalogScope ? { catalogScope } : {}),
|
|
1301
1311
|
});
|
|
@@ -1451,7 +1461,7 @@ export async function handleMcpConnect(event, subpath, options = {}) {
|
|
|
1451
1461
|
ownerEmail: claimed.ownerEmail,
|
|
1452
1462
|
orgId: claimed.orgId,
|
|
1453
1463
|
orgDomain,
|
|
1454
|
-
|
|
1464
|
+
issuer: tokenIssuer,
|
|
1455
1465
|
expiresIn: `${DEFAULT_TOKEN_TTL_DAYS}d`,
|
|
1456
1466
|
jti,
|
|
1457
1467
|
...(claimed.catalogScope
|
|
@@ -57,33 +57,29 @@ export declare function recordMintedToken(params: {
|
|
|
57
57
|
serviceName?: string | null;
|
|
58
58
|
createdBy?: string | null;
|
|
59
59
|
}, exec?: DbExec): Promise<string>;
|
|
60
|
-
/**
|
|
61
|
-
* Returns true when the given `jti` corresponds to a token that has been
|
|
62
|
-
* revoked. Fails OPEN on a store/DB error: a transient Neon WS drop must not
|
|
63
|
-
* lock every connected agent out. Signature verification is unaffected — this
|
|
64
|
-
* is only the post-verify revoke check (see `verifyAuth` in build-server.ts).
|
|
65
|
-
*/
|
|
66
|
-
/**
|
|
67
|
-
* Throws when the revocation state can't be read. Answering "not revoked"
|
|
68
|
-
* instead would let a transient read failure admit a revoked token.
|
|
69
|
-
*/
|
|
70
|
-
export declare function isJtiRevoked(jti: string): Promise<boolean>;
|
|
71
60
|
export type StoredConnectTokenIdentity = Pick<MintedTokenRow, "kind" | "ownerEmail" | "orgId">;
|
|
72
61
|
export type ConnectTokenOrgLookup = ({
|
|
73
62
|
status: "found";
|
|
74
63
|
} & StoredConnectTokenIdentity) | {
|
|
64
|
+
status: "revoked";
|
|
65
|
+
} | {
|
|
75
66
|
status: "missing";
|
|
76
67
|
} | {
|
|
77
68
|
status: "unavailable";
|
|
78
69
|
};
|
|
70
|
+
/**
|
|
71
|
+
* A connect token's standing, read in one query. `unavailable` covers a read
|
|
72
|
+
* failure and a malformed row: answering `missing` or `found` instead would
|
|
73
|
+
* admit a revoked token, or refuse a live one, on a transient error.
|
|
74
|
+
*/
|
|
79
75
|
export declare function lookupConnectTokenOrg(jti: string): Promise<ConnectTokenOrgLookup>;
|
|
80
76
|
export declare function listTokens(ownerEmail: string): Promise<MintedTokenRow[]>;
|
|
81
77
|
export declare function listOrgServiceTokens(orgId: string): Promise<MintedTokenRow[]>;
|
|
82
78
|
/**
|
|
83
79
|
* Revoke an org service token by id, scoped to `orgId` AND `kind = 'service'`
|
|
84
80
|
* so a caller can never revoke another org's token (or someone's personal
|
|
85
|
-
* token) through this path. Uses the same `revoked_at` gate
|
|
86
|
-
* checks, so revocation takes effect on the next request like personal
|
|
81
|
+
* token) through this path. Uses the same `revoked_at` gate
|
|
82
|
+
* `lookupConnectTokenOrg` checks, so revocation takes effect on the next request like personal
|
|
87
83
|
* tokens. Idempotent; returns true when a row actually transitioned.
|
|
88
84
|
*/
|
|
89
85
|
export declare function revokeOrgServiceToken(orgId: string, id: string): Promise<boolean>;
|
|
@@ -127,37 +127,23 @@ export async function recordMintedToken(params, exec) {
|
|
|
127
127
|
return id;
|
|
128
128
|
}
|
|
129
129
|
/**
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
* is only the post-verify revoke check (see `verifyAuth` in build-server.ts).
|
|
130
|
+
* A connect token's standing, read in one query. `unavailable` covers a read
|
|
131
|
+
* failure and a malformed row: answering `missing` or `found` instead would
|
|
132
|
+
* admit a revoked token, or refuse a live one, on a transient error.
|
|
134
133
|
*/
|
|
135
|
-
/**
|
|
136
|
-
* Throws when the revocation state can't be read. Answering "not revoked"
|
|
137
|
-
* instead would let a transient read failure admit a revoked token.
|
|
138
|
-
*/
|
|
139
|
-
export async function isJtiRevoked(jti) {
|
|
140
|
-
await ensureTable();
|
|
141
|
-
const client = getDbExec();
|
|
142
|
-
const { rows } = await client.execute({
|
|
143
|
-
sql: `SELECT revoked_at FROM mcp_connect_tokens WHERE jti = ?`,
|
|
144
|
-
args: [jti],
|
|
145
|
-
});
|
|
146
|
-
if (rows.length === 0)
|
|
147
|
-
return false;
|
|
148
|
-
const revokedAt = rows[0].revoked_at ?? rows[0].revokedAt;
|
|
149
|
-
return revokedAt != null;
|
|
150
|
-
}
|
|
151
134
|
export async function lookupConnectTokenOrg(jti) {
|
|
152
135
|
try {
|
|
153
136
|
await ensureTable();
|
|
154
137
|
const client = getDbExec();
|
|
155
138
|
const { rows } = await client.execute({
|
|
156
|
-
sql: `SELECT org_id, owner_email, kind FROM mcp_connect_tokens WHERE jti = ?`,
|
|
139
|
+
sql: `SELECT org_id, owner_email, kind, revoked_at FROM mcp_connect_tokens WHERE jti = ?`,
|
|
157
140
|
args: [jti],
|
|
158
141
|
});
|
|
159
142
|
if (rows.length === 0)
|
|
160
143
|
return { status: "missing" };
|
|
144
|
+
if ((rows[0].revoked_at ?? rows[0].revokedAt) != null) {
|
|
145
|
+
return { status: "revoked" };
|
|
146
|
+
}
|
|
161
147
|
const rawOrgId = rows[0].org_id ?? rows[0].orgId;
|
|
162
148
|
const ownerEmail = rows[0].owner_email ?? rows[0].ownerEmail;
|
|
163
149
|
const kind = rows[0].kind;
|
|
@@ -176,7 +162,8 @@ export async function lookupConnectTokenOrg(jti) {
|
|
|
176
162
|
: null,
|
|
177
163
|
};
|
|
178
164
|
}
|
|
179
|
-
catch {
|
|
165
|
+
catch (error) {
|
|
166
|
+
console.error("[mcp] Connect-token lookup failed:", error);
|
|
180
167
|
return { status: "unavailable" };
|
|
181
168
|
}
|
|
182
169
|
}
|
|
@@ -223,8 +210,8 @@ export async function listOrgServiceTokens(orgId) {
|
|
|
223
210
|
/**
|
|
224
211
|
* Revoke an org service token by id, scoped to `orgId` AND `kind = 'service'`
|
|
225
212
|
* so a caller can never revoke another org's token (or someone's personal
|
|
226
|
-
* token) through this path. Uses the same `revoked_at` gate
|
|
227
|
-
* checks, so revocation takes effect on the next request like personal
|
|
213
|
+
* token) through this path. Uses the same `revoked_at` gate
|
|
214
|
+
* `lookupConnectTokenOrg` checks, so revocation takes effect on the next request like personal
|
|
228
215
|
* tokens. Idempotent; returns true when a row actually transitioned.
|
|
229
216
|
*/
|
|
230
217
|
export async function revokeOrgServiceToken(orgId, id) {
|
|
@@ -23,15 +23,26 @@ export async function checkCredentialOrgMembership(input) {
|
|
|
23
23
|
// A human OAuth subject can use a service-shaped address. Only the local
|
|
24
24
|
// record of an authenticated connect token proves it is a service identity.
|
|
25
25
|
const stored = input.storedConnectToken;
|
|
26
|
-
if (stored?.kind === "service" &&
|
|
27
|
-
stored.ownerEmail === email &&
|
|
28
|
-
stored.orgId === orgId &&
|
|
29
|
-
implicitServiceOrgRole({ email, orgId, requestOrgId: stored.orgId })) {
|
|
30
|
-
return "member";
|
|
31
|
-
}
|
|
32
26
|
const lookup = () => isOrgMember(orgId, email, { requireOrganizationMetadata: true });
|
|
33
27
|
const context = getRequestContext();
|
|
34
28
|
try {
|
|
29
|
+
if (stored?.kind === "service" &&
|
|
30
|
+
stored.ownerEmail === email &&
|
|
31
|
+
stored.orgId === orgId &&
|
|
32
|
+
implicitServiceOrgRole({ email, orgId, requestOrgId: stored.orgId })) {
|
|
33
|
+
const organization = await getDbExec().execute({
|
|
34
|
+
sql: `SELECT identity_authority, identity_id
|
|
35
|
+
FROM organizations WHERE id = ? LIMIT 1`,
|
|
36
|
+
args: [orgId],
|
|
37
|
+
});
|
|
38
|
+
const metadata = organization.rows[0];
|
|
39
|
+
if (!metadata)
|
|
40
|
+
return "not-member";
|
|
41
|
+
return String(metadata.identity_authority ?? "").trim() ||
|
|
42
|
+
String(metadata.identity_id ?? "").trim()
|
|
43
|
+
? "not-member"
|
|
44
|
+
: "member";
|
|
45
|
+
}
|
|
35
46
|
const member = !input.requestOrigin || context?.requestOrigin
|
|
36
47
|
? await lookup()
|
|
37
48
|
: await runWithRequestContext({ ...context, requestOrigin: input.requestOrigin }, lookup);
|
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
import type { H3Event } from "h3";
|
|
2
|
+
import { type BearerCredentialRefusal } from "../server/bearer-credential-refusal.js";
|
|
2
3
|
export interface McpOAuthRouteOptions {
|
|
3
4
|
appId?: string;
|
|
4
5
|
appName?: string;
|
|
5
6
|
}
|
|
6
7
|
export declare function getMcpOAuthIssuer(event: H3Event): string | undefined;
|
|
8
|
+
/**
|
|
9
|
+
* The issuer MCP credentials are bound to, given the caller's request origin.
|
|
10
|
+
* Verification accepts only this issuer's resources, so anything that mints a
|
|
11
|
+
* bearer for this app must resolve its audience here, not from request headers:
|
|
12
|
+
* a configured public URL wins over the host a page was reached through.
|
|
13
|
+
*/
|
|
14
|
+
export declare function resolveMcpOAuthIssuer(requestOrigin: string | undefined): string | undefined;
|
|
15
|
+
export declare function getMcpConnectUrl(event: H3Event): string | undefined;
|
|
7
16
|
export declare function getMcpOAuthResource(event: H3Event, routePath?: string): string | undefined;
|
|
8
17
|
/**
|
|
9
18
|
* All plausible MCP OAuth resource audiences for the given request, deduped.
|
|
@@ -16,7 +25,8 @@ export declare function getMcpOAuthResource(event: H3Event, routePath?: string):
|
|
|
16
25
|
*/
|
|
17
26
|
export declare function getMcpOAuthAudiences(event: H3Event, routePath?: string): string[];
|
|
18
27
|
export declare function getMcpOAuthProtectedResourceMetadataUrl(event: H3Event, routePath?: string): string | undefined;
|
|
19
|
-
|
|
28
|
+
/** `refusal` is set only when the request presented a bearer token. */
|
|
29
|
+
export declare function buildMcpOAuthChallenge(event: H3Event, routePath?: string, refusal?: BearerCredentialRefusal): string;
|
|
20
30
|
export declare function handleMcpOAuthProtectedResourceMetadata(event: H3Event): Response;
|
|
21
31
|
export declare function handleMcpOAuthAuthorizationServerMetadata(event: H3Event): Response;
|
|
22
32
|
export declare function handleMcpOAuth(event: H3Event, subpath: string, options?: McpOAuthRouteOptions): Promise<Response>;
|
package/dist/mcp/oauth-route.js
CHANGED
|
@@ -2,6 +2,7 @@ import { createHash, createHmac, timingSafeEqual } from "node:crypto";
|
|
|
2
2
|
import { getHeader, getMethod, getQuery, setResponseStatus } from "h3";
|
|
3
3
|
import { getOrgDomain } from "../org/context.js";
|
|
4
4
|
import { getConfiguredLoginHtml, getSession } from "../server/auth.js";
|
|
5
|
+
import { describeBearerCredentialRefusalWithRecovery, } from "../server/bearer-credential-refusal.js";
|
|
5
6
|
import { getAuthSecret } from "../server/better-auth-instance.js";
|
|
6
7
|
import { CREDENTIAL_MEMBERSHIP_UNAVAILABLE_MESSAGE } from "../server/credential-membership-unavailable.js";
|
|
7
8
|
import { getOrigin } from "../server/google-oauth.js";
|
|
@@ -126,11 +127,24 @@ function deriveOrigin(event) {
|
|
|
126
127
|
return host ? `${proto}://${host}` : "";
|
|
127
128
|
}
|
|
128
129
|
export function getMcpOAuthIssuer(event) {
|
|
129
|
-
|
|
130
|
+
return resolveMcpOAuthIssuer(deriveOrigin(event));
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* The issuer MCP credentials are bound to, given the caller's request origin.
|
|
134
|
+
* Verification accepts only this issuer's resources, so anything that mints a
|
|
135
|
+
* bearer for this app must resolve its audience here, not from request headers:
|
|
136
|
+
* a configured public URL wins over the host a page was reached through.
|
|
137
|
+
*/
|
|
138
|
+
export function resolveMcpOAuthIssuer(requestOrigin) {
|
|
139
|
+
const baseUrl = configuredPublicBaseUrl() || requestOrigin;
|
|
130
140
|
if (!baseUrl)
|
|
131
141
|
return undefined;
|
|
132
142
|
return appendConfiguredBasePath(baseUrl);
|
|
133
143
|
}
|
|
144
|
+
export function getMcpConnectUrl(event) {
|
|
145
|
+
const issuer = getMcpOAuthIssuer(event);
|
|
146
|
+
return issuer ? `${issuer}${MCP_PUBLIC_ROUTE_PREFIX}/connect` : undefined;
|
|
147
|
+
}
|
|
134
148
|
function normalizeMcpResourcePath(routePath) {
|
|
135
149
|
if (routePath === MCP_LEGACY_ROUTE_PREFIX ||
|
|
136
150
|
routePath === MCP_DIRECTORY_ROUTE_PREFIX) {
|
|
@@ -198,12 +212,20 @@ export function getMcpOAuthProtectedResourceMetadataUrl(event, routePath = MCP_P
|
|
|
198
212
|
}
|
|
199
213
|
return metadataUrl.toString();
|
|
200
214
|
}
|
|
201
|
-
|
|
215
|
+
/** `refusal` is set only when the request presented a bearer token. */
|
|
216
|
+
export function buildMcpOAuthChallenge(event, routePath = MCP_PUBLIC_ROUTE_PREFIX, refusal) {
|
|
202
217
|
const metadata = getMcpOAuthProtectedResourceMetadataUrl(event, routePath);
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
218
|
+
const params = [
|
|
219
|
+
...(metadata ? [`resource_metadata="${metadata}"`] : []),
|
|
220
|
+
`scope="${MCP_OAUTH_RESOURCE_SCOPE}"`,
|
|
221
|
+
...(refusal
|
|
222
|
+
? [
|
|
223
|
+
`error="invalid_token"`,
|
|
224
|
+
`error_description="${describeBearerCredentialRefusalWithRecovery(refusal, getMcpConnectUrl(event))}"`,
|
|
225
|
+
]
|
|
226
|
+
: []),
|
|
227
|
+
];
|
|
228
|
+
return `Bearer ${params.join(", ")}`;
|
|
207
229
|
}
|
|
208
230
|
function protectedResourcePathFromRequest(event) {
|
|
209
231
|
let pathname = event.url?.pathname ?? "";
|
|
@@ -2,7 +2,15 @@ import { MCP_OAUTH_ACCESS_TOKEN_TTL, MCP_OAUTH_ACCESS_TOKEN_TTL_SECONDS } from "
|
|
|
2
2
|
export { MCP_OAUTH_ACCESS_TOKEN_TTL, MCP_OAUTH_ACCESS_TOKEN_TTL_SECONDS };
|
|
3
3
|
export declare const MCP_OAUTH_SCOPES: readonly ["mcp:read", "mcp:write", "mcp:apps", "offline_access"];
|
|
4
4
|
export declare const MCP_OAUTH_DEFAULT_SCOPE: string;
|
|
5
|
+
export declare const MCP_OAUTH_TOKEN_TYPE = "agent-native-mcp-oauth";
|
|
5
6
|
declare const MCP_OAUTH_CREDENTIAL_VERSION = 2;
|
|
7
|
+
/**
|
|
8
|
+
* Service credentials carry a version that verifiers predating service
|
|
9
|
+
* identity assurance reject. Those verifiers admit any MCP OAuth token as a
|
|
10
|
+
* verified user, so a service token they accepted could approve gated actions.
|
|
11
|
+
* Never sign a service credential with `MCP_OAUTH_CREDENTIAL_VERSION`.
|
|
12
|
+
*/
|
|
13
|
+
declare const MCP_OAUTH_SERVICE_CREDENTIAL_VERSION = 3;
|
|
6
14
|
export interface McpOAuthAccessTokenClaims {
|
|
7
15
|
sub: string;
|
|
8
16
|
org_id?: string | null;
|
|
@@ -11,8 +19,8 @@ export interface McpOAuthAccessTokenClaims {
|
|
|
11
19
|
client_id: string;
|
|
12
20
|
resource: string;
|
|
13
21
|
jti?: string;
|
|
14
|
-
typ:
|
|
15
|
-
credential_version: typeof MCP_OAUTH_CREDENTIAL_VERSION;
|
|
22
|
+
typ: typeof MCP_OAUTH_TOKEN_TYPE;
|
|
23
|
+
credential_version: typeof MCP_OAUTH_CREDENTIAL_VERSION | typeof MCP_OAUTH_SERVICE_CREDENTIAL_VERSION;
|
|
16
24
|
}
|
|
17
25
|
export declare function normalizeOAuthScope(input: unknown): string | null;
|
|
18
26
|
export declare function scopeList(scope: string | undefined): string[];
|
|
@@ -31,6 +39,8 @@ export declare function signMcpOAuthAccessToken(params: {
|
|
|
31
39
|
jti?: string;
|
|
32
40
|
expiresIn?: string | number;
|
|
33
41
|
catalogScope?: "full";
|
|
42
|
+
/** An org service identity, not a person. */
|
|
43
|
+
service?: true;
|
|
34
44
|
}): Promise<string>;
|
|
35
45
|
export declare function verifyMcpOAuthAccessToken(token: string, resource: string | string[] | undefined): Promise<{
|
|
36
46
|
userEmail: string;
|
package/dist/mcp/oauth-token.js
CHANGED
|
@@ -11,7 +11,15 @@ export const MCP_OAUTH_SCOPES = [
|
|
|
11
11
|
"offline_access",
|
|
12
12
|
];
|
|
13
13
|
export const MCP_OAUTH_DEFAULT_SCOPE = MCP_OAUTH_SCOPES.join(" ");
|
|
14
|
+
export const MCP_OAUTH_TOKEN_TYPE = "agent-native-mcp-oauth";
|
|
14
15
|
const MCP_OAUTH_CREDENTIAL_VERSION = 2;
|
|
16
|
+
/**
|
|
17
|
+
* Service credentials carry a version that verifiers predating service
|
|
18
|
+
* identity assurance reject. Those verifiers admit any MCP OAuth token as a
|
|
19
|
+
* verified user, so a service token they accepted could approve gated actions.
|
|
20
|
+
* Never sign a service credential with `MCP_OAUTH_CREDENTIAL_VERSION`.
|
|
21
|
+
*/
|
|
22
|
+
const MCP_OAUTH_SERVICE_CREDENTIAL_VERSION = 3;
|
|
15
23
|
function signingSecret() {
|
|
16
24
|
return new TextEncoder().encode(process.env.A2A_SECRET?.trim() || getAuthSecret());
|
|
17
25
|
}
|
|
@@ -60,8 +68,10 @@ export function parseMcpOAuthOrgIdClaim(payload) {
|
|
|
60
68
|
}
|
|
61
69
|
export async function signMcpOAuthAccessToken(params) {
|
|
62
70
|
return new jose.SignJWT({
|
|
63
|
-
typ:
|
|
64
|
-
credential_version:
|
|
71
|
+
typ: MCP_OAUTH_TOKEN_TYPE,
|
|
72
|
+
credential_version: params.service
|
|
73
|
+
? MCP_OAUTH_SERVICE_CREDENTIAL_VERSION
|
|
74
|
+
: MCP_OAUTH_CREDENTIAL_VERSION,
|
|
65
75
|
sub: params.ownerEmail,
|
|
66
76
|
...(params.orgId !== undefined ? { org_id: params.orgId } : {}),
|
|
67
77
|
...(params.orgDomain ? { org_domain: params.orgDomain } : {}),
|
|
@@ -122,9 +132,10 @@ export async function verifyMcpOAuthAccessToken(token, resource) {
|
|
|
122
132
|
if (!payload)
|
|
123
133
|
return null;
|
|
124
134
|
try {
|
|
125
|
-
if (payload.typ !==
|
|
135
|
+
if (payload.typ !== MCP_OAUTH_TOKEN_TYPE)
|
|
126
136
|
return null;
|
|
127
|
-
if (payload.credential_version !== MCP_OAUTH_CREDENTIAL_VERSION
|
|
137
|
+
if (payload.credential_version !== MCP_OAUTH_CREDENTIAL_VERSION &&
|
|
138
|
+
payload.credential_version !== MCP_OAUTH_SERVICE_CREDENTIAL_VERSION)
|
|
128
139
|
return null;
|
|
129
140
|
if (typeof payload.resource !== "string")
|
|
130
141
|
return null;
|
package/dist/mcp/server.js
CHANGED
|
@@ -2,6 +2,7 @@ import { defineEventHandler, setResponseStatus, setResponseHeader, getMethod, ge
|
|
|
2
2
|
import { getAppConfig } from "../app-config/store.js";
|
|
3
3
|
import { getConfiguredAppBasePath } from "../server/app-base-path.js";
|
|
4
4
|
import { isLoopbackRequest } from "../server/auth.js";
|
|
5
|
+
import { describeBearerCredentialRefusal, } from "../server/bearer-credential-refusal.js";
|
|
5
6
|
import { CREDENTIAL_MEMBERSHIP_UNAVAILABLE_MESSAGE } from "../server/credential-membership-unavailable.js";
|
|
6
7
|
import { getH3App } from "../server/framework-request-handler.js";
|
|
7
8
|
import { getOrigin } from "../server/google-oauth.js";
|
|
@@ -88,7 +89,7 @@ function buildWebRequest(event, method, routePath = MCP_PUBLIC_ROUTE_PREFIX) {
|
|
|
88
89
|
const url = `${proto}://${host}${basePath}${routePath}`;
|
|
89
90
|
return new Request(url, { method, headers });
|
|
90
91
|
}
|
|
91
|
-
function buildUnauthorizedBody(event, routePath = MCP_PUBLIC_ROUTE_PREFIX) {
|
|
92
|
+
function buildUnauthorizedBody(event, routePath = MCP_PUBLIC_ROUTE_PREFIX, refusal) {
|
|
92
93
|
const issuer = getMcpOAuthIssuer(event);
|
|
93
94
|
const mcpUrl = getMcpOAuthResource(event, routePath);
|
|
94
95
|
const resourceMetadataUrl = getMcpOAuthProtectedResourceMetadataUrl(event, routePath);
|
|
@@ -101,7 +102,7 @@ function buildUnauthorizedBody(event, routePath = MCP_PUBLIC_ROUTE_PREFIX) {
|
|
|
101
102
|
const authorizeUrl = issuer
|
|
102
103
|
? `${issuer}${MCP_PUBLIC_ROUTE_PREFIX}/oauth/authorize`
|
|
103
104
|
: undefined;
|
|
104
|
-
const
|
|
105
|
+
const instructions = command
|
|
105
106
|
? `Authentication required. Run \`${command}\` to re-authenticate this ` +
|
|
106
107
|
`MCP connector without reinstalling it (or, in a Claude Code host, ` +
|
|
107
108
|
`run /mcp and choose Authenticate), then retry. For first-time ` +
|
|
@@ -110,7 +111,10 @@ function buildUnauthorizedBody(event, routePath = MCP_PUBLIC_ROUTE_PREFIX) {
|
|
|
110
111
|
"then retry.";
|
|
111
112
|
return {
|
|
112
113
|
error: "Unauthorized",
|
|
113
|
-
|
|
114
|
+
...(refusal ? { reason: refusal } : {}),
|
|
115
|
+
message: refusal
|
|
116
|
+
? `${describeBearerCredentialRefusal(refusal)} ${instructions}`
|
|
117
|
+
: instructions,
|
|
114
118
|
authenticate: {
|
|
115
119
|
...(command ? { command } : {}),
|
|
116
120
|
...(firstTimeCommand ? { firstTimeCommand } : {}),
|
|
@@ -263,8 +267,8 @@ async function handleMcpRequestInternal(event, config, routePath = MCP_PUBLIC_RO
|
|
|
263
267
|
}
|
|
264
268
|
if (!authResult.authed) {
|
|
265
269
|
setResponseStatus(event, 401);
|
|
266
|
-
setResponseHeader(event, "WWW-Authenticate", buildMcpOAuthChallenge(event, routePath));
|
|
267
|
-
return buildUnauthorizedBody(event, routePath);
|
|
270
|
+
setResponseHeader(event, "WWW-Authenticate", buildMcpOAuthChallenge(event, routePath, authResult.refusal));
|
|
271
|
+
return buildUnauthorizedBody(event, routePath, authResult.refusal);
|
|
268
272
|
}
|
|
269
273
|
const body = method === "POST" ? await readBody(event) : undefined;
|
|
270
274
|
onMcpMethod?.(directoryLogMethod(body) ?? method);
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
export declare function createNotificationsHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<"" | import("./types.js").Notification[] | {
|
|
2
2
|
count: number;
|
|
3
3
|
updated?: undefined;
|
|
4
|
-
ok?: undefined;
|
|
5
4
|
error?: undefined;
|
|
5
|
+
ok?: undefined;
|
|
6
6
|
} | {
|
|
7
7
|
count?: undefined;
|
|
8
8
|
updated: number;
|
|
9
|
-
ok?: undefined;
|
|
10
9
|
error?: undefined;
|
|
10
|
+
ok?: undefined;
|
|
11
11
|
} | {
|
|
12
12
|
count?: undefined;
|
|
13
13
|
updated?: undefined;
|
|
@@ -5,6 +5,7 @@ export interface AiErrorDetail {
|
|
|
5
5
|
terminal_code?: string;
|
|
6
6
|
terminal_state?: string;
|
|
7
7
|
retryable?: boolean;
|
|
8
|
+
cause?: string;
|
|
8
9
|
}
|
|
9
10
|
export declare function resolveAiError(isError: boolean, error: AiErrorDetail | undefined): AiErrorDetail | undefined;
|
|
10
11
|
export declare function boundAiContent(value: unknown): {
|
|
@@ -1,14 +1,16 @@
|
|
|
1
|
+
import { runErrorTelemetryProperties } from "../agent/engine/error-telemetry.js";
|
|
2
|
+
import { isToolDoneFailure } from "../agent/tool-done-error.js";
|
|
1
3
|
import { sendPostHogEvent } from "../tracking/providers.js";
|
|
2
|
-
import { boundedText } from "../tracking/redaction.js";
|
|
3
4
|
export const MAX_AI_CONTENT_BYTES = 128 * 1024;
|
|
4
5
|
export const MAX_AI_SPANS_PER_RUN = 100;
|
|
5
|
-
const UNREPORTED_ERROR = {
|
|
6
|
-
message: "failed without a reported error message",
|
|
7
|
-
};
|
|
8
6
|
export function resolveAiError(isError, error) {
|
|
9
7
|
if (!isError)
|
|
10
8
|
return undefined;
|
|
11
|
-
return error
|
|
9
|
+
return failedRunErrorDetail(error?.message, {
|
|
10
|
+
state: error?.terminal_state,
|
|
11
|
+
code: error?.terminal_code,
|
|
12
|
+
retryable: error?.retryable,
|
|
13
|
+
});
|
|
12
14
|
}
|
|
13
15
|
function trackAiEvent(name, properties, userId, occurredAt) {
|
|
14
16
|
for (const key of Object.keys(properties)) {
|
|
@@ -122,7 +124,12 @@ export function toPostHogMessages(value) {
|
|
|
122
124
|
...(typeof part.toolName === "string"
|
|
123
125
|
? { name: part.toolName }
|
|
124
126
|
: {}),
|
|
125
|
-
content:
|
|
127
|
+
content: isToolDoneFailure({
|
|
128
|
+
isError: part.isError,
|
|
129
|
+
result: part.content,
|
|
130
|
+
})
|
|
131
|
+
? "[tool error message omitted from telemetry]"
|
|
132
|
+
: part.content,
|
|
126
133
|
});
|
|
127
134
|
break;
|
|
128
135
|
case "tool-call":
|
|
@@ -245,12 +252,17 @@ export function emitAiFeedbackSurveyEvent(input) {
|
|
|
245
252
|
export function toAiErrorDetail(errorMessage, terminalOutcome) {
|
|
246
253
|
if (!errorMessage && !terminalOutcome?.code)
|
|
247
254
|
return undefined;
|
|
255
|
+
return failedRunErrorDetail(errorMessage, terminalOutcome);
|
|
256
|
+
}
|
|
257
|
+
function failedRunErrorDetail(errorMessage, terminalOutcome) {
|
|
258
|
+
const { error_code, error_cause } = runErrorTelemetryProperties(terminalOutcome?.code, errorMessage);
|
|
248
259
|
return {
|
|
249
|
-
message:
|
|
260
|
+
message: `Agent run failed (${error_code})`,
|
|
261
|
+
cause: error_cause,
|
|
250
262
|
...(terminalOutcome?.state
|
|
251
263
|
? { terminal_state: terminalOutcome.state }
|
|
252
264
|
: {}),
|
|
253
|
-
|
|
265
|
+
terminal_code: error_code,
|
|
254
266
|
...(terminalOutcome?.retryable !== undefined
|
|
255
267
|
? { retryable: terminalOutcome.retryable }
|
|
256
268
|
: {}),
|
|
@@ -27,43 +27,43 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
|
|
|
27
27
|
thumbsUpRate: number;
|
|
28
28
|
avgEvalScore: number;
|
|
29
29
|
} | {
|
|
30
|
-
ok?: undefined;
|
|
31
30
|
error: string;
|
|
32
31
|
message: string;
|
|
33
32
|
summary?: undefined;
|
|
34
33
|
spans?: undefined;
|
|
35
|
-
} | {
|
|
36
34
|
ok?: undefined;
|
|
35
|
+
} | {
|
|
37
36
|
message?: undefined;
|
|
38
37
|
summary: import("./types.js").TraceSummary;
|
|
39
38
|
spans: import("./types.js").TraceSpan[];
|
|
40
39
|
error?: undefined;
|
|
41
|
-
} | {
|
|
42
40
|
ok?: undefined;
|
|
41
|
+
} | {
|
|
43
42
|
message?: undefined;
|
|
44
43
|
summary?: undefined;
|
|
45
44
|
spans?: undefined;
|
|
46
45
|
traceMissing: true;
|
|
47
46
|
id: string;
|
|
48
47
|
error?: undefined;
|
|
49
|
-
} | {
|
|
50
48
|
ok?: undefined;
|
|
49
|
+
} | {
|
|
51
50
|
message?: undefined;
|
|
52
51
|
summary?: undefined;
|
|
53
52
|
spans?: undefined;
|
|
54
53
|
traceMissing?: undefined;
|
|
55
54
|
id: string;
|
|
56
55
|
error?: undefined;
|
|
57
|
-
} | {
|
|
58
56
|
ok?: undefined;
|
|
57
|
+
} | {
|
|
59
58
|
message?: undefined;
|
|
60
59
|
summary?: undefined;
|
|
61
60
|
spans?: undefined;
|
|
62
61
|
error: any;
|
|
62
|
+
ok?: undefined;
|
|
63
63
|
} | {
|
|
64
64
|
message?: undefined;
|
|
65
65
|
summary?: undefined;
|
|
66
66
|
spans?: undefined;
|
|
67
|
-
ok: boolean;
|
|
68
67
|
error?: undefined;
|
|
68
|
+
ok: boolean;
|
|
69
69
|
}>>;
|