@executor-js/sdk 1.5.17 → 1.5.19

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.
@@ -181,10 +181,13 @@ var asFromTokenUrlAndIssuer = (tokenUrl, issuerUrl, options = {}) => {
181
181
  id_token_signing_alg_values_supported: [...options.idTokenSigningAlgValuesSupported]
182
182
  } : withIssuer;
183
183
  };
184
- var oauth4webapiRequestOptions = (targetUrl, timeoutMs, endpointUrlPolicy = {}) => {
184
+ var oauth4webapiRequestOptions = (targetUrl, timeoutMs, endpointUrlPolicy = {}, customFetch2) => {
185
185
  const options = {
186
186
  signal: AbortSignal.timeout(timeoutMs ?? OAUTH2_DEFAULT_TIMEOUT_MS)
187
187
  };
188
+ if (customFetch2) {
189
+ options[oauth.customFetch] = customFetch2;
190
+ }
188
191
  if (isLoopbackHttpUrl(targetUrl) || URL.canParse(targetUrl) && new URL(targetUrl).protocol === "http:" && endpointUrlPolicy.allowHttp === true) {
189
192
  options[oauth.allowInsecureRequests] = true;
190
193
  }
@@ -244,7 +247,12 @@ var exchangeAuthorizationCode = (input) => Effect.tryPromise({
244
247
  clientAuth,
245
248
  "authorization_code",
246
249
  params,
247
- oauth4webapiRequestOptions(input.tokenUrl, input.timeoutMs, input.endpointUrlPolicy)
250
+ oauth4webapiRequestOptions(
251
+ input.tokenUrl,
252
+ input.timeoutMs,
253
+ input.endpointUrlPolicy,
254
+ input.fetch
255
+ )
248
256
  );
249
257
  return await processTokenEndpointResponse(as, client, response);
250
258
  },
@@ -270,7 +278,12 @@ var exchangeClientCredentials = (input) => Effect.tryPromise({
270
278
  client,
271
279
  clientAuth,
272
280
  params,
273
- oauth4webapiRequestOptions(input.tokenUrl, input.timeoutMs, input.endpointUrlPolicy)
281
+ oauth4webapiRequestOptions(
282
+ input.tokenUrl,
283
+ input.timeoutMs,
284
+ input.endpointUrlPolicy,
285
+ input.fetch
286
+ )
274
287
  );
275
288
  const result = await oauth.processClientCredentialsResponse(as, client, response);
276
289
  return tokenResponseFrom(result);
@@ -302,7 +315,12 @@ var refreshAccessToken = (input) => Effect.tryPromise({
302
315
  clientAuth,
303
316
  input.refreshToken,
304
317
  {
305
- ...oauth4webapiRequestOptions(input.tokenUrl, input.timeoutMs, input.endpointUrlPolicy),
318
+ ...oauth4webapiRequestOptions(
319
+ input.tokenUrl,
320
+ input.timeoutMs,
321
+ input.endpointUrlPolicy,
322
+ input.fetch
323
+ ),
306
324
  additionalParameters
307
325
  }
308
326
  );
@@ -336,4 +354,4 @@ export {
336
354
  refreshAccessToken,
337
355
  shouldRefreshToken
338
356
  };
339
- //# sourceMappingURL=chunk-4XPVLX62.js.map
357
+ //# sourceMappingURL=chunk-BBLMWFXN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/oauth-helpers.ts"],"sourcesContent":["// ---------------------------------------------------------------------------\n// OAuth 2.0 helpers — generic, isomorphic building blocks.\n//\n// Thin wrappers around `oauth4webapi` (stateless; pure Web Crypto +\n// `fetch`, no deps; runs unchanged in Node, CF Workers, and browsers).\n// Each public helper is a single `Effect.tryPromise` call that delegates\n// the RFC work to the library and normalises the failure surface into\n// `OAuth2Error`.\n//\n// What stays hand-rolled:\n// - `OAuth2Error` — our tagged error; we want a stable shape across\n// every token-endpoint call\n// - `shouldRefreshToken` — skew check, trivial\n// - `buildAuthorizationUrl` — the library doesn't expose a raw\n// authorization-URL builder (it prefers PAR); a 30-line manual\n// construction keeps the call sync and lets callers opt out of PAR\n// ---------------------------------------------------------------------------\n\nimport { Data, Effect, Predicate } from \"effect\";\nimport * as oauth from \"oauth4webapi\";\n\n// ---------------------------------------------------------------------------\n// Errors\n// ---------------------------------------------------------------------------\n\nexport class OAuth2Error extends Data.TaggedError(\"OAuth2Error\")<{\n readonly message: string;\n /**\n * RFC 6749 §5.2 error code, when the token endpoint returned one\n * (`invalid_grant`, `invalid_client`, `unauthorized_client`, ...).\n * Callers use this to distinguish terminal failures (a refresh token\n * the AS no longer honours → re-auth required) from transient ones.\n */\n readonly error?: string;\n readonly cause?: unknown;\n}> {}\n\n// ---------------------------------------------------------------------------\n// Token response shape (RFC 6749 §5.1)\n// ---------------------------------------------------------------------------\n\nexport type OAuth2TokenResponse = {\n readonly access_token: string;\n readonly token_type?: string;\n readonly refresh_token?: string;\n readonly expires_in?: number;\n readonly scope?: string;\n};\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Refresh tokens this many ms before expiry to avoid mid-request expiration. */\nexport const OAUTH2_REFRESH_SKEW_MS = 60_000;\n\n/** Default token-endpoint timeout. */\nexport const OAUTH2_DEFAULT_TIMEOUT_MS = 20_000;\n\nexport interface OAuthEndpointUrlPolicy {\n readonly allowHttp?: boolean;\n}\n\nconst isLoopbackHttpUrl = (value: string): boolean => {\n if (!URL.canParse(value)) return false;\n const url = new URL(value);\n if (url.protocol !== \"http:\") return false;\n const hostname = url.hostname.toLowerCase();\n return (\n hostname === \"localhost\" ||\n hostname === \"0.0.0.0\" ||\n hostname === \"::1\" ||\n hostname === \"[::1]\" ||\n hostname.startsWith(\"127.\")\n );\n};\n\nexport const isSupportedOAuthEndpointUrl = (\n value: string,\n policy: OAuthEndpointUrlPolicy = {},\n): boolean => {\n if (!URL.canParse(value)) return false;\n const url = new URL(value);\n return (\n url.protocol === \"https:\" ||\n isLoopbackHttpUrl(value) ||\n (url.protocol === \"http:\" && policy.allowHttp === true)\n );\n};\n\nexport const assertSupportedOAuthEndpointUrl = (\n value: string,\n label = \"OAuth endpoint URL\",\n policy: OAuthEndpointUrlPolicy = {},\n): string => {\n if (isSupportedOAuthEndpointUrl(value, policy)) return value;\n // oxlint-disable-next-line executor/no-try-catch-or-throw, executor/no-error-constructor -- boundary: synchronous assertion helper used by URL constructors and Effect.try wrappers\n throw new TypeError(`${label} must use https: or loopback http:`);\n};\n\n// ---------------------------------------------------------------------------\n// PKCE (RFC 7636) — straight delegation to `oauth4webapi`\n// ---------------------------------------------------------------------------\n\nexport const createPkceCodeVerifier = (): string => oauth.generateRandomCodeVerifier();\n\nexport const createPkceCodeChallenge = (verifier: string): Promise<string> =>\n oauth.calculatePKCECodeChallenge(verifier);\n\n/** RFC 6749 `state` — an unguessable correlation token minted by `oauth.start`\n * and redeemed by `oauth.complete`. */\nexport const createOAuthState = (): string => oauth.generateRandomState();\n\n// ---------------------------------------------------------------------------\n// Authorization URL builder\n// ---------------------------------------------------------------------------\n\nexport type BuildAuthorizationUrlInput = {\n readonly authorizationUrl: string;\n readonly clientId: string;\n readonly redirectUrl: string;\n readonly scopes: readonly string[];\n readonly state: string;\n /** Pre-computed base64url S256 challenge (from `createPkceCodeChallenge`). */\n readonly codeChallenge: string;\n /** Separator between scopes. RFC 6749 says space; some providers use comma. */\n readonly scopeSeparator?: string;\n /** RFC 8707 Resource Indicator. MCP Authorization 2025-06-18 §\"Resource\n * Parameter Implementation\" requires clients to send this on every\n * authorization request, regardless of AS support. */\n readonly resource?: string;\n /** Provider-specific extras (e.g. Google's `access_type=offline`). */\n readonly extraParams?: Readonly<Record<string, string>>;\n readonly endpointUrlPolicy?: OAuthEndpointUrlPolicy;\n};\n\n/** Build an RFC 6749 §4.1.1 authorization URL. Sync; pre-computed\n * challenge lets this stay out of the Promise world. */\nexport const buildAuthorizationUrl = (input: BuildAuthorizationUrlInput): string => {\n const url = new URL(\n assertSupportedOAuthEndpointUrl(\n input.authorizationUrl,\n \"Authorization URL\",\n input.endpointUrlPolicy,\n ),\n );\n // Benign default kept by design: a single space is the RFC 6749 scope\n // separator. Callers targeting a legacy comma-separated provider pass\n // `scopeSeparator` explicitly (see the field's JSDoc).\n const separator = input.scopeSeparator ?? \" \";\n url.searchParams.set(\"client_id\", input.clientId);\n url.searchParams.set(\"redirect_uri\", input.redirectUrl);\n url.searchParams.set(\"response_type\", \"code\");\n if (input.scopes.length > 0) {\n url.searchParams.set(\"scope\", input.scopes.join(separator));\n }\n url.searchParams.set(\"state\", input.state);\n url.searchParams.set(\"code_challenge_method\", \"S256\");\n url.searchParams.set(\"code_challenge\", input.codeChallenge);\n if (input.resource) {\n url.searchParams.set(\"resource\", input.resource);\n }\n if (input.extraParams) {\n for (const [k, v] of Object.entries(input.extraParams)) {\n url.searchParams.set(k, v);\n }\n }\n return url.toString();\n};\n\n/** Provider-specific authorize-URL extras that are NOT RFC 6749 params, so the\n * generic flow must add them per-provider (keyed off the authorization host).\n *\n * Google: `access_type=offline` + `prompt=consent` are required to receive (and\n * keep receiving, across reconnects / scope changes) a REFRESH TOKEN — without\n * them Google issues an access-token-only grant that dies in ~1h and a\n * re-consent can silently keep the old scope set. Do not add\n * `include_granted_scopes=true` here: with historical grants on the same Google\n * consent app, Google folds those unrelated scopes into the new consent flow and\n * can fail inside accounts.google.com before returning to our callback. */\nexport const providerAuthorizeExtras = (\n authorizationUrl: string,\n): Readonly<Record<string, string>> => {\n // oxlint-disable-next-line executor/no-try-catch-or-throw -- boundary: URL() throws on invalid input → no provider extras\n try {\n const host = new URL(authorizationUrl).host.toLowerCase();\n if (host === \"accounts.google.com\") {\n return { access_type: \"offline\", prompt: \"consent\" };\n }\n } catch {\n // Unparseable authorization URL — let buildAuthorizationUrl surface the error.\n }\n return {};\n};\n\n// ---------------------------------------------------------------------------\n// Regional token-endpoint rebind\n//\n// Some authorization servers publish a single static metadata document that\n// advertises one region's token endpoint, but issue authorization codes that\n// are only redeemable at the *regional* host the user's org actually lives on.\n// The region comes back on the callback as a non-standard `domain` (or `site`)\n// query param: Datadog returns `domain=us5.datadoghq.com` while its metadata\n// statically advertises `app.datadoghq.com`. Redeeming the code at the\n// advertised host then fails with `invalid_grant`.\n//\n// `rebindTokenEndpointHostToCallbackDomain` swaps ONLY the hostname of the\n// configured token URL to the callback-supplied host, and ONLY when that host\n// is a sibling subdomain of the configured one (same parent after stripping the\n// leftmost DNS label, e.g. `app.datadoghq.com` and `us5.datadoghq.com` both\n// reduce to `datadoghq.com`). The token request carries the client secret, the\n// code, and the PKCE verifier, so an attacker-influenced `domain` must never be\n// able to point it at an arbitrary origin. Anything that fails the sibling\n// check, fails to parse, or isn't https falls back to the configured URL\n// unchanged.\n// ---------------------------------------------------------------------------\n\nconst hostnameFromCallbackDomain = (callbackDomain: string): string | undefined => {\n const trimmed = callbackDomain.trim();\n if (trimmed.length === 0) return undefined;\n // Datadog sends `domain` as a bare host and `site` as a full origin; accept\n // either by tolerating an optional scheme, then taking only the hostname.\n const candidate = trimmed.includes(\"://\") ? trimmed : `https://${trimmed}`;\n if (!URL.canParse(candidate)) return undefined;\n const url = new URL(candidate);\n // A legitimate regional host carries no port, credentials, or path.\n if (url.port !== \"\" || url.username !== \"\" || url.password !== \"\") return undefined;\n if (url.pathname !== \"/\" && url.pathname !== \"\") return undefined;\n return url.hostname.toLowerCase();\n};\n\n/** Parent domain after stripping the leftmost DNS label, or `undefined` when\n * the host has no sibling space (a single label, or a parent that is a bare\n * TLD). `app.datadoghq.com` -> `datadoghq.com`; `foo.com` -> undefined. */\nconst siblingParentDomainOf = (hostname: string): string | undefined => {\n const labels = hostname.split(\".\");\n if (labels.length < 3) return undefined;\n const parent = labels.slice(1).join(\".\");\n // Require the parent to itself be multi-label so a 2-label configured host\n // can never rebind across an entire TLD (e.g. foo.com -> bar.com).\n return parent.includes(\".\") ? parent : undefined;\n};\n\nexport const rebindTokenEndpointHostToCallbackDomain = (\n configuredTokenUrl: string,\n callbackDomain: string | null | undefined,\n): string => {\n if (!callbackDomain) return configuredTokenUrl;\n if (!URL.canParse(configuredTokenUrl)) return configuredTokenUrl;\n const configured = new URL(configuredTokenUrl);\n if (configured.protocol !== \"https:\") return configuredTokenUrl;\n const targetHost = hostnameFromCallbackDomain(callbackDomain);\n if (!targetHost) return configuredTokenUrl;\n const configuredHost = configured.hostname.toLowerCase();\n if (targetHost === configuredHost) return configuredTokenUrl;\n const configuredParent = siblingParentDomainOf(configuredHost);\n const targetParent = siblingParentDomainOf(targetHost);\n if (!configuredParent || !targetParent || configuredParent !== targetParent) {\n return configuredTokenUrl;\n }\n const rebound = new URL(configuredTokenUrl);\n rebound.hostname = targetHost;\n return rebound.toString();\n};\n\n// ---------------------------------------------------------------------------\n// Error mapping — `oauth4webapi`'s `process*Response` failure shapes are\n// either a WWW-Authenticate challenge or an RFC 6749 §5.2 error body,\n// both exposed via `.error` / `.error_description`. Probing the envelope\n// preserves RFC 6749 error-code semantics (e.g., mapping `invalid_grant`\n// to reauth-required) across wrappers.\n// ---------------------------------------------------------------------------\n\nconst isOAuth2Error = Predicate.isTagged(\"OAuth2Error\") as (cause: unknown) => cause is OAuth2Error;\n\nconst responseFromOAuthErrorCause = (cause: unknown): Response | undefined => {\n if (cause instanceof Response) return cause;\n if (typeof cause !== \"object\" || cause === null) return undefined;\n const envelope = cause as {\n readonly cause?: unknown;\n readonly response?: unknown;\n };\n if (envelope.response instanceof Response) return envelope.response;\n if (envelope.cause instanceof Response) return envelope.cause;\n return undefined;\n};\n\nconst redactTokenEndpointBody = (body: string): string =>\n body\n .replaceAll(\n /(\"(?:access_token|refresh_token|id_token|client_secret)\"\\s*:\\s*\")[^\"]*(\")/gi,\n \"$1[redacted]$2\",\n )\n .replaceAll(\n /((?:access_token|refresh_token|id_token|client_secret|code)=)[^&\\s]*/gi,\n \"$1[redacted]\",\n );\n\nconst tokenEndpointHttpSummary = async (response: Response): Promise<string> => {\n const status = `HTTP ${response.status}${response.statusText ? ` ${response.statusText}` : \"\"}`;\n const contentType = response.headers.get(\"content-type\");\n const url = response.url ? ` from ${response.url}` : \"\";\n const parts = [`${status}${url}`];\n if (contentType) parts.push(`content-type ${contentType}`);\n const preview = await bodyPreviewFromResponse(response);\n if (preview) parts.push(`body: ${preview}`);\n return parts.join(\"; \");\n};\n\nconst bodyPreviewFromResponse = async (response: Response): Promise<string | undefined> => {\n const text = await Promise.resolve()\n .then(() => response.clone().text())\n .then(\n (value) => value.trim(),\n () => \"\",\n );\n if (!text) return undefined;\n const redacted = redactTokenEndpointBody(text.replaceAll(/\\s+/g, \" \"));\n return redacted.length > 500 ? `${redacted.slice(0, 500)}...` : redacted;\n};\n\nconst toOAuth2Error = (cause: unknown): OAuth2Error => {\n if (isOAuth2Error(cause)) return cause;\n if (typeof cause === \"object\" && cause !== null) {\n const c = cause as {\n error?: unknown;\n error_description?: unknown;\n message?: unknown;\n };\n const code = typeof c.error === \"string\" ? c.error : undefined;\n const description =\n typeof c.error_description === \"string\"\n ? c.error_description\n : typeof c.message === \"string\"\n ? c.message\n : undefined;\n return new OAuth2Error({\n message: `OAuth token exchange failed: ${description ?? code ?? \"unknown error\"}`,\n error: code,\n cause,\n });\n }\n return new OAuth2Error({\n message: \"OAuth token exchange failed\",\n cause,\n });\n};\n\nconst toOAuth2ErrorWithHttpSummary = (cause: unknown): Effect.Effect<OAuth2Error> => {\n if (isOAuth2Error(cause)) return Effect.succeed(cause);\n const base = toOAuth2Error(cause);\n const response = responseFromOAuthErrorCause(cause);\n if (!response) return Effect.succeed(base);\n return Effect.promise(() => tokenEndpointHttpSummary(response)).pipe(\n Effect.map(\n (summary) =>\n new OAuth2Error({\n message: `${base.message} (${summary})`,\n error: base.error,\n cause,\n }),\n ),\n );\n};\n\nconst failOAuth2WithHttpSummary = (cause: unknown): Effect.Effect<never, OAuth2Error> =>\n toOAuth2ErrorWithHttpSummary(cause).pipe(Effect.flatMap((error) => Effect.fail(error)));\n\n// ---------------------------------------------------------------------------\n// oauth4webapi adapter helpers\n// ---------------------------------------------------------------------------\n\nexport type ClientAuthMethod = \"body\" | \"basic\";\n\n/**\n * The token-endpoint client-auth transport used when a caller doesn't specify\n * one. `\"body\"` is `client_secret_post` (the secret in the form body) — the\n * method our DCR registers (`token_endpoint_auth_method: client_secret_post`)\n * and the one every confidential client in the v2 model uses. EXPLICIT and\n * documented rather than a hidden inline `?? \"body\"`: callers that need\n * `client_secret_basic` pass `clientAuth: \"basic\"`. For PUBLIC clients (no\n * secret) the method is irrelevant — `pickClientAuth` returns `None()`.\n */\nexport const DEFAULT_CLIENT_AUTH_METHOD: ClientAuthMethod = \"body\";\n\nconst asFromTokenUrl = (\n tokenUrl: string,\n endpointUrlPolicy: OAuthEndpointUrlPolicy = {},\n): oauth.AuthorizationServer => {\n assertSupportedOAuthEndpointUrl(tokenUrl, \"Token URL\", endpointUrlPolicy);\n const url = new URL(tokenUrl);\n return {\n issuer: `${url.protocol}//${url.host}`,\n token_endpoint: tokenUrl,\n };\n};\n\nconst asFromTokenUrlAndIssuer = (\n tokenUrl: string,\n issuerUrl: string | null | undefined,\n options: {\n readonly idTokenSigningAlgValuesSupported?: readonly string[];\n readonly endpointUrlPolicy?: OAuthEndpointUrlPolicy;\n } = {},\n): oauth.AuthorizationServer => {\n const as = asFromTokenUrl(tokenUrl, options.endpointUrlPolicy);\n const withIssuer = issuerUrl ? { ...as, issuer: issuerUrl } : as;\n return options.idTokenSigningAlgValuesSupported\n ? {\n ...withIssuer,\n id_token_signing_alg_values_supported: [...options.idTokenSigningAlgValuesSupported],\n }\n : withIssuer;\n};\n\nconst oauth4webapiRequestOptions = (\n targetUrl: string,\n timeoutMs: number | undefined,\n endpointUrlPolicy: OAuthEndpointUrlPolicy = {},\n customFetch?: typeof globalThis.fetch,\n): Record<string, unknown> => {\n const options: Record<string, unknown> = {\n signal: AbortSignal.timeout(timeoutMs ?? OAUTH2_DEFAULT_TIMEOUT_MS),\n };\n if (customFetch) {\n (options as { [oauth.customFetch]?: typeof globalThis.fetch })[oauth.customFetch] = customFetch;\n }\n if (\n isLoopbackHttpUrl(targetUrl) ||\n (URL.canParse(targetUrl) &&\n new URL(targetUrl).protocol === \"http:\" &&\n endpointUrlPolicy.allowHttp === true)\n ) {\n (options as { [oauth.allowInsecureRequests]?: boolean })[oauth.allowInsecureRequests] = true;\n }\n return options;\n};\n\n// Select the token-endpoint client authentication. The secret's presence is the\n// EXPLICIT public-vs-confidential discriminator in the v2 model: a registered\n// client either has a secret (confidential — authenticate it) or has none\n// (public PKCE — `None()`, RFC 7636). This is not a silent guess: `loadClient`\n// persists a non-empty secret for confidential clients and null/\"\" for public\n// ones, so an absent secret here unambiguously means \"public client\". The\n// `method` only chooses HOW a present secret is sent (post vs basic).\nconst pickClientAuth = (\n clientSecret: string | null | undefined,\n method: ClientAuthMethod,\n): oauth.ClientAuth => {\n if (!clientSecret) return oauth.None();\n return method === \"basic\"\n ? oauth.ClientSecretBasic(clientSecret)\n : oauth.ClientSecretPost(clientSecret);\n};\n\nconst tokenResponseFrom = (r: oauth.TokenEndpointResponse): OAuth2TokenResponse => ({\n access_token: r.access_token,\n token_type: r.token_type,\n refresh_token: r.refresh_token,\n expires_in: typeof r.expires_in === \"number\" ? r.expires_in : undefined,\n scope: r.scope,\n});\n\n// MCP source connections are pure OAuth 2.0 — we never request `openid` and\n// never consume `id_token`. Some providers (PostHog, etc.) front an OIDC\n// backend and emit an `id_token` anyway; oauth4webapi then strict-validates\n// its claims against the AS metadata and rejects mismatches we don't care\n// about. Strip the field before delegation.\nconst stripIdToken = async (response: Response): Promise<Response> => {\n const body = await response\n .clone()\n .json()\n .then(\n (value: unknown) => value,\n () => null,\n );\n if (!body || typeof body !== \"object\" || !(\"id_token\" in (body as Record<string, unknown>))) {\n return response;\n }\n const { id_token: _ignored, ...rest } = body as Record<string, unknown>;\n return new Response(JSON.stringify(rest), {\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n });\n};\n\nconst processTokenEndpointResponse = async (\n as: oauth.AuthorizationServer,\n client: oauth.Client,\n response: Response,\n): Promise<OAuth2TokenResponse> =>\n tokenResponseFrom(\n await oauth.processGenericTokenEndpointResponse(as, client, await stripIdToken(response)),\n );\n\n// ---------------------------------------------------------------------------\n// Exchange authorization code → tokens\n// ---------------------------------------------------------------------------\n\nexport type ExchangeAuthorizationCodeInput = {\n readonly tokenUrl: string;\n readonly issuerUrl?: string | null;\n readonly clientId: string;\n readonly clientSecret?: string | null;\n readonly redirectUrl: string;\n readonly codeVerifier: string;\n readonly code: string;\n readonly clientAuth?: ClientAuthMethod;\n readonly idTokenSigningAlgValuesSupported?: readonly string[];\n /** RFC 8707 Resource Indicator. MCP Auth spec MUST-requires this on\n * the token request when the client knows the resource it intends\n * to call. */\n readonly resource?: string;\n readonly timeoutMs?: number;\n readonly endpointUrlPolicy?: OAuthEndpointUrlPolicy;\n readonly fetch?: typeof globalThis.fetch;\n};\n\nexport const exchangeAuthorizationCode = (\n input: ExchangeAuthorizationCodeInput,\n): Effect.Effect<OAuth2TokenResponse, OAuth2Error> =>\n Effect.tryPromise({\n try: async () => {\n const as = asFromTokenUrlAndIssuer(input.tokenUrl, input.issuerUrl, {\n idTokenSigningAlgValuesSupported: input.idTokenSigningAlgValuesSupported,\n endpointUrlPolicy: input.endpointUrlPolicy,\n });\n const client: oauth.Client = { client_id: input.clientId };\n const clientAuth = pickClientAuth(\n input.clientSecret,\n input.clientAuth ?? DEFAULT_CLIENT_AUTH_METHOD,\n );\n // `authorizationCodeGrantRequest` requires its `callbackParameters`\n // to have been returned from `validateAuthResponse`. Our public API\n // takes the `code` directly (the UI already validated `state` by\n // looking up the session), so skip the library's state-validation\n // rail and go through the generic grant request instead.\n const params = new URLSearchParams({\n code: input.code,\n redirect_uri: input.redirectUrl,\n code_verifier: input.codeVerifier,\n });\n if (input.resource) {\n params.set(\"resource\", input.resource);\n }\n const response = await oauth.genericTokenEndpointRequest(\n as,\n client,\n clientAuth,\n \"authorization_code\",\n params,\n oauth4webapiRequestOptions(\n input.tokenUrl,\n input.timeoutMs,\n input.endpointUrlPolicy,\n input.fetch,\n ),\n );\n return await processTokenEndpointResponse(as, client, response);\n },\n catch: (cause) => cause,\n }).pipe(Effect.catch(failOAuth2WithHttpSummary));\n\n// ---------------------------------------------------------------------------\n// Exchange client credentials → tokens (RFC 6749 §4.4)\n// ---------------------------------------------------------------------------\n\nexport type ExchangeClientCredentialsInput = {\n readonly tokenUrl: string;\n readonly clientId: string;\n readonly clientSecret: string;\n readonly scopes?: readonly string[];\n readonly scopeSeparator?: string;\n readonly clientAuth?: ClientAuthMethod;\n /** RFC 8707 Resource Indicator. MCP Authorization 2025-06-18 requires this\n * on token requests when the client knows the protected resource. */\n readonly resource?: string;\n readonly timeoutMs?: number;\n readonly endpointUrlPolicy?: OAuthEndpointUrlPolicy;\n readonly fetch?: typeof globalThis.fetch;\n};\n\nexport const exchangeClientCredentials = (\n input: ExchangeClientCredentialsInput,\n): Effect.Effect<OAuth2TokenResponse, OAuth2Error> =>\n Effect.tryPromise({\n try: async () => {\n const as = asFromTokenUrl(input.tokenUrl, input.endpointUrlPolicy);\n const client: oauth.Client = { client_id: input.clientId };\n const clientAuth = pickClientAuth(\n input.clientSecret,\n input.clientAuth ?? DEFAULT_CLIENT_AUTH_METHOD,\n );\n const params = new URLSearchParams();\n if (input.scopes && input.scopes.length > 0) {\n params.set(\"scope\", input.scopes.join(input.scopeSeparator ?? \" \"));\n }\n if (input.resource) {\n params.set(\"resource\", input.resource);\n }\n const response = await oauth.clientCredentialsGrantRequest(\n as,\n client,\n clientAuth,\n params,\n oauth4webapiRequestOptions(\n input.tokenUrl,\n input.timeoutMs,\n input.endpointUrlPolicy,\n input.fetch,\n ),\n );\n const result = await oauth.processClientCredentialsResponse(as, client, response);\n return tokenResponseFrom(result);\n },\n catch: (cause) => cause,\n }).pipe(Effect.catch(failOAuth2WithHttpSummary));\n\n// ---------------------------------------------------------------------------\n// Refresh access token\n// ---------------------------------------------------------------------------\n\nexport type RefreshAccessTokenInput = {\n readonly tokenUrl: string;\n readonly issuerUrl?: string | null;\n readonly clientId: string;\n readonly clientSecret?: string | null;\n readonly refreshToken: string;\n readonly scopes?: readonly string[];\n readonly scopeSeparator?: string;\n readonly clientAuth?: ClientAuthMethod;\n readonly idTokenSigningAlgValuesSupported?: readonly string[];\n /** RFC 8707 Resource Indicator — MCP spec MUST-requires this on\n * refresh requests so the new access token's audience is bound to\n * the same resource. */\n readonly resource?: string;\n readonly timeoutMs?: number;\n readonly endpointUrlPolicy?: OAuthEndpointUrlPolicy;\n readonly fetch?: typeof globalThis.fetch;\n};\n\nexport const refreshAccessToken = (\n input: RefreshAccessTokenInput,\n): Effect.Effect<OAuth2TokenResponse, OAuth2Error> =>\n Effect.tryPromise({\n try: async () => {\n const as = asFromTokenUrlAndIssuer(input.tokenUrl, input.issuerUrl, {\n idTokenSigningAlgValuesSupported: input.idTokenSigningAlgValuesSupported,\n endpointUrlPolicy: input.endpointUrlPolicy,\n });\n const client: oauth.Client = { client_id: input.clientId };\n const clientAuth = pickClientAuth(\n input.clientSecret,\n input.clientAuth ?? DEFAULT_CLIENT_AUTH_METHOD,\n );\n const extraParams = new URLSearchParams();\n if (input.scopes && input.scopes.length > 0) {\n extraParams.set(\"scope\", input.scopes.join(input.scopeSeparator ?? \" \"));\n }\n if (input.resource) {\n extraParams.set(\"resource\", input.resource);\n }\n const additionalParameters =\n Array.from(extraParams.keys()).length > 0 ? extraParams : undefined;\n const response = await oauth.refreshTokenGrantRequest(\n as,\n client,\n clientAuth,\n input.refreshToken,\n {\n ...oauth4webapiRequestOptions(\n input.tokenUrl,\n input.timeoutMs,\n input.endpointUrlPolicy,\n input.fetch,\n ),\n additionalParameters,\n },\n );\n const result = await oauth.processRefreshTokenResponse(\n as,\n client,\n await stripIdToken(response),\n );\n return tokenResponseFrom(result);\n },\n catch: (cause) => cause,\n }).pipe(Effect.catch(failOAuth2WithHttpSummary));\n\n// ---------------------------------------------------------------------------\n// Refresh-needed predicate\n// ---------------------------------------------------------------------------\n\nexport const shouldRefreshToken = (input: {\n readonly expiresAt: number | null;\n readonly now?: number;\n readonly skewMs?: number;\n}): boolean => {\n if (input.expiresAt === null) return false;\n const now = input.now ?? Date.now();\n const skew = input.skewMs ?? OAUTH2_REFRESH_SKEW_MS;\n return input.expiresAt <= now + skew;\n};\n"],"mappings":"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","names":["customFetch"]}
@@ -38,7 +38,7 @@ import {
38
38
  rebindTokenEndpointHostToCallbackDomain,
39
39
  refreshAccessToken,
40
40
  shouldRefreshToken
41
- } from "./chunk-4XPVLX62.js";
41
+ } from "./chunk-BBLMWFXN.js";
42
42
  import {
43
43
  AuthTemplateSlug,
44
44
  ConnectionAddress,
@@ -3167,7 +3167,14 @@ var ConnectionCreateInput = Schema2.Struct({
3167
3167
  }).check(
3168
3168
  Schema2.makeFilter((payload) => {
3169
3169
  const originCount = (payload.from === void 0 ? 0 : 1) + (payload.inputs === void 0 ? 0 : 1);
3170
- if (originCount !== 1) return "Expected exactly one provider credential origin";
3170
+ const isNoAuth = String(payload.template) === String(NO_AUTH_TEMPLATE);
3171
+ if (isNoAuth) {
3172
+ if (originCount > 0) {
3173
+ return 'A no-auth connection (template "none") takes no provider credential origin';
3174
+ }
3175
+ } else if (originCount !== 1) {
3176
+ return "Expected exactly one provider credential origin";
3177
+ }
3171
3178
  if (payload.inputs !== void 0 && Object.keys(payload.inputs).length === 0) {
3172
3179
  return "Expected at least one provider credential input";
3173
3180
  }
@@ -3251,9 +3258,23 @@ var OAuthCreateClientInput = Schema2.Struct({
3251
3258
  tokenUrl: Schema2.String,
3252
3259
  grant: OAuthGrantSchema,
3253
3260
  clientId: Schema2.String,
3254
- clientSecret: Schema2.String,
3255
3261
  resource: Schema2.optional(Schema2.NullOr(Schema2.String))
3256
3262
  });
3263
+ var OAuthCreateClientHandoffInput = Schema2.Struct({
3264
+ integration: Schema2.String,
3265
+ owner: Schema2.optional(OwnerSchema),
3266
+ slug: Schema2.optional(Schema2.String),
3267
+ grant: Schema2.optional(OAuthGrantSchema),
3268
+ clientId: Schema2.optional(Schema2.String),
3269
+ authorizationUrl: Schema2.optional(Schema2.String),
3270
+ tokenUrl: Schema2.optional(Schema2.String),
3271
+ resource: Schema2.optional(Schema2.NullOr(Schema2.String)),
3272
+ label: Schema2.optional(Schema2.String)
3273
+ });
3274
+ var OAuthCreateClientHandoffOutput = Schema2.Struct({
3275
+ url: Schema2.String,
3276
+ instructions: Schema2.String
3277
+ });
3257
3278
  var OAuthClientOutputRef = Schema2.Struct({
3258
3279
  client: Schema2.String
3259
3280
  });
@@ -3332,6 +3353,8 @@ var PolicyUpdateInputStd = schemaToStandard(PolicyUpdateInput);
3332
3353
  var PolicyRemoveInputStd = schemaToStandard(PolicyRemoveInput);
3333
3354
  var OAuthClientsListOutputStd = schemaToStandard(OAuthClientsListOutput);
3334
3355
  var OAuthCreateClientInputStd = schemaToStandard(OAuthCreateClientInput);
3356
+ var OAuthCreateClientHandoffInputStd = schemaToStandard(OAuthCreateClientHandoffInput);
3357
+ var OAuthCreateClientHandoffOutputStd = schemaToStandard(OAuthCreateClientHandoffOutput);
3335
3358
  var OAuthClientOutputRefStd = schemaToStandard(OAuthClientOutputRef);
3336
3359
  var OAuthRegisterDynamicInputStd = schemaToStandard(OAuthRegisterDynamicInput);
3337
3360
  var OAuthRemoveClientInputStd = schemaToStandard(OAuthRemoveClientInput);
@@ -3417,12 +3440,28 @@ var createConnectionInputFromTool = (input) => {
3417
3440
  )
3418
3441
  };
3419
3442
  };
3420
- var connectionCreateHandoffUrl = (webBaseUrl, input) => {
3443
+ var connectionCreateHandoffUrl = (webBaseUrl, orgSlug, input) => {
3421
3444
  const search = new URLSearchParams({ addAccount: "1" });
3422
3445
  if (input.owner !== void 0) search.set("owner", input.owner);
3423
3446
  if (input.template !== void 0) search.set("template", input.template);
3424
3447
  if (input.label !== void 0) search.set("label", input.label);
3425
- const path = `/integrations/${encodeURIComponent(input.integration)}?${search.toString()}`;
3448
+ const orgPrefix = orgSlug !== void 0 && orgSlug.length > 0 ? `/${orgSlug}` : "";
3449
+ const path = `${orgPrefix}/integrations/${encodeURIComponent(input.integration)}?${search.toString()}`;
3450
+ if (webBaseUrl === void 0 || webBaseUrl.length === 0) return path;
3451
+ return new URL(path, webBaseUrl.endsWith("/") ? webBaseUrl : `${webBaseUrl}/`).toString();
3452
+ };
3453
+ var oauthClientCreateHandoffUrl = (webBaseUrl, orgSlug, input) => {
3454
+ const search = new URLSearchParams({ addAccount: "1", oauthClient: "1" });
3455
+ if (input.owner !== void 0) search.set("owner", input.owner);
3456
+ if (input.slug !== void 0) search.set("clientSlug", input.slug);
3457
+ if (input.grant !== void 0) search.set("grant", input.grant);
3458
+ if (input.clientId !== void 0) search.set("clientId", input.clientId);
3459
+ if (input.authorizationUrl !== void 0) search.set("authorizationUrl", input.authorizationUrl);
3460
+ if (input.tokenUrl !== void 0) search.set("tokenUrl", input.tokenUrl);
3461
+ if (input.resource != null && input.resource.length > 0) search.set("resource", input.resource);
3462
+ if (input.label !== void 0) search.set("label", input.label);
3463
+ const orgPrefix = orgSlug !== void 0 && orgSlug.length > 0 ? `/${orgSlug}` : "";
3464
+ const path = `${orgPrefix}/integrations/${encodeURIComponent(input.integration)}?${search.toString()}`;
3426
3465
  if (webBaseUrl === void 0 || webBaseUrl.length === 0) return path;
3427
3466
  return new URL(path, webBaseUrl.endsWith("/") ? webBaseUrl : `${webBaseUrl}/`).toString();
3428
3467
  };
@@ -3485,9 +3524,16 @@ var coreToolsPlugin = definePlugin((options = {}) => ({
3485
3524
  }),
3486
3525
  tool({
3487
3526
  name: "connections.create",
3488
- description: "Low-level create or replace for a saved connection from provider item references. For normal API keys/tokens, use `connections.createHandoff` so the user enters the credential in the web UI. OAuth credentials should use `oauth.start`.",
3527
+ description: 'Low-level create or replace for a saved connection from provider item references. For a no-auth integration (public MCP server, public REST API), pass `template: "none"` with no `from`/`inputs` to wire it up directly. For normal API keys/tokens, use `connections.createHandoff` so the user enters the credential in the web UI. OAuth credentials should use `oauth.start`.',
3489
3528
  inputSchema: ConnectionCreateInputStd,
3490
3529
  outputSchema: ConnectionOutputStd,
3530
+ // Creating a connection binds a credential reference and roots a new
3531
+ // tool catalog: every tool that connection produces then becomes
3532
+ // callable. Even the no-auth (`template: "none"`) path pulls tools
3533
+ // from an arbitrary endpoint. Prompt-injected code could silently
3534
+ // wire an attacker-chosen integration or credential, so this is
3535
+ // approval-gated (the v1 `sources.configure` carried the same guard).
3536
+ annotations: { requiresApproval: true },
3491
3537
  execute: (input, { ctx }) => Effect5.map(
3492
3538
  ctx.connections.create(createConnectionInputFromTool(input)),
3493
3539
  connectionToOutput
@@ -3499,7 +3545,7 @@ var coreToolsPlugin = definePlugin((options = {}) => ({
3499
3545
  inputSchema: ConnectionCreateHandoffInputStd,
3500
3546
  outputSchema: ConnectionCreateHandoffOutputStd,
3501
3547
  execute: (input) => {
3502
- const url = connectionCreateHandoffUrl(options.webBaseUrl, input);
3548
+ const url = connectionCreateHandoffUrl(options.webBaseUrl, options.orgSlug, input);
3503
3549
  return Effect5.succeed({
3504
3550
  url,
3505
3551
  instructions: "Ask the user to open this URL and add the account in the Executor web UI. Do not ask them to paste the credential value into chat. After they finish, call connections.list for the integration to discover the created connection."
@@ -3511,6 +3557,10 @@ var coreToolsPlugin = definePlugin((options = {}) => ({
3511
3557
  description: "Remove a saved connection and its produced tools by owner, integration, and connection name.",
3512
3558
  inputSchema: ConnectionRefInputStd,
3513
3559
  outputSchema: RemovedOutputStd,
3560
+ // Deleting a connection drops it and every tool it produced, which
3561
+ // prompt-injected code could use to disrupt an integration or force a
3562
+ // re-add flow. Approval-gated, matching v1 `sources.remove`.
3563
+ annotations: { requiresApproval: true },
3514
3564
  execute: (input, { ctx }) => Effect5.map(ctx.connections.remove(connectionRefFromInput(input)), () => ({
3515
3565
  removed: true
3516
3566
  }))
@@ -3520,6 +3570,11 @@ var coreToolsPlugin = definePlugin((options = {}) => ({
3520
3570
  description: "Re-run an integration's tool production for a saved connection, replacing that connection's persisted tools.",
3521
3571
  inputSchema: ConnectionRefInputStd,
3522
3572
  outputSchema: ConnectionsRefreshOutputStd,
3573
+ // Refresh replaces a connection's persisted tool set; for a mutable
3574
+ // upstream (an MCP server whose catalog can change) this can swap in
3575
+ // different tools without confirmation. Approval-gated, matching v1
3576
+ // `sources.refresh`.
3577
+ annotations: { requiresApproval: true },
3523
3578
  execute: (input, { ctx }) => Effect5.map(ctx.connections.refresh(connectionRefFromInput(input)), (tools) => ({
3524
3579
  tools: tools.map(toolToOutput)
3525
3580
  }))
@@ -3563,9 +3618,20 @@ var coreToolsPlugin = definePlugin((options = {}) => ({
3563
3618
  }),
3564
3619
  tool({
3565
3620
  name: "oauth.clients.create",
3566
- description: "Register or replace an owner-scoped OAuth client from explicit client credentials. Use grant `client_credentials` for machine OAuth or `authorization_code` for browser consent flows.",
3621
+ description: "Register or replace an owner-scoped OAuth client WITHOUT a client secret: a PUBLIC client (PKCE / authorization_code) or a discovery-prefill placeholder. To register a CONFIDENTIAL client that has a secret, call `oauth.clients.createHandoff` instead so the human enters the secret in the web UI; never pass a client secret through this tool.",
3567
3622
  inputSchema: OAuthCreateClientInputStd,
3568
3623
  outputSchema: OAuthClientOutputRefStd,
3624
+ // This persists an OAuth client and REPLACES on slug collision. It
3625
+ // takes NO client secret: a secret would have to travel through the
3626
+ // agent's context window, so a confidential app is registered by the
3627
+ // human via `oauth.clients.createHandoff`. An empty secret registers a
3628
+ // PUBLIC client. The remaining risk is the write itself: prompt-injected
3629
+ // code could register a client with an attacker-controlled
3630
+ // authorizationUrl/tokenUrl, then drive `oauth.start` to mint a
3631
+ // connection and route the user's tokens to the attacker. The
3632
+ // highest-value gate here; matches v1 `sources.bindings.set`, which
3633
+ // guarded credential writes.
3634
+ annotations: { requiresApproval: true },
3569
3635
  execute: (input, { ctx }) => Effect5.map(
3570
3636
  ctx.oauth.createClient({
3571
3637
  owner: input.owner,
@@ -3574,17 +3640,40 @@ var coreToolsPlugin = definePlugin((options = {}) => ({
3574
3640
  tokenUrl: input.tokenUrl,
3575
3641
  grant: input.grant,
3576
3642
  clientId: input.clientId,
3577
- clientSecret: input.clientSecret,
3643
+ // No secret crosses the agent boundary; an empty secret registers
3644
+ // a public client. Confidential clients go through
3645
+ // `oauth.clients.createHandoff`.
3646
+ clientSecret: "",
3578
3647
  resource: input.resource ?? null
3579
3648
  }),
3580
3649
  (client) => ({ client: String(client) })
3581
3650
  )
3582
3651
  }),
3652
+ tool({
3653
+ name: "oauth.clients.createHandoff",
3654
+ description: "Return a browser URL that opens the Register-OAuth-app form for one integration, pre-filled with the non-secret fields (client id, endpoints, grant). Use this for any CONFIDENTIAL OAuth app: the user types the client secret directly in the web UI instead of sending it through the agent. After they register the app, call `oauth.clients.list` to discover its owner and slug, then `oauth.start`.",
3655
+ inputSchema: OAuthCreateClientHandoffInputStd,
3656
+ outputSchema: OAuthCreateClientHandoffOutputStd,
3657
+ // Pure URL builder: no DB write, no token, no secret. This is the SAFE
3658
+ // path (it routes the secret to the human in the browser), so it is
3659
+ // deliberately NOT approval-gated, mirroring `connections.createHandoff`.
3660
+ execute: (input) => {
3661
+ const url = oauthClientCreateHandoffUrl(options.webBaseUrl, options.orgSlug, input);
3662
+ return Effect5.succeed({
3663
+ url,
3664
+ instructions: "Ask the user to open this URL and register the OAuth app in the Executor web UI, entering the client secret there. Do not ask them to paste the client secret into chat. After they finish, call oauth.clients.list to find the registered client (owner + slug), then oauth.start."
3665
+ });
3666
+ }
3667
+ }),
3583
3668
  tool({
3584
3669
  name: "oauth.clients.registerDynamic",
3585
3670
  description: "Register an OAuth client through RFC 7591 Dynamic Client Registration and save the minted client for later `oauth.start` calls.",
3586
3671
  inputSchema: OAuthRegisterDynamicInputStd,
3587
3672
  outputSchema: OAuthClientOutputRefStd,
3673
+ // Same risk class as `oauth.clients.create`: registers a client at a
3674
+ // caller-supplied endpoint and persists the minted credentials for
3675
+ // later `oauth.start` abuse. Approval-gated. See `oauth.clients.create`.
3676
+ annotations: { requiresApproval: true },
3588
3677
  execute: (input, { ctx }) => Effect5.map(
3589
3678
  ctx.oauth.registerDynamicClient({
3590
3679
  owner: input.owner,
@@ -3607,6 +3696,11 @@ var coreToolsPlugin = definePlugin((options = {}) => ({
3607
3696
  description: "Remove an owner-scoped OAuth client by owner and slug. Existing connections are not cascaded.",
3608
3697
  inputSchema: OAuthRemoveClientInputStd,
3609
3698
  outputSchema: RemovedOutputStd,
3699
+ // Removing a client breaks token refresh for every connection that
3700
+ // depends on it (a silent DoS) and can force re-auth through an
3701
+ // attacker-supplied replacement. Approval-gated, matching v1
3702
+ // `sources.bindings.remove`.
3703
+ annotations: { requiresApproval: true },
3610
3704
  execute: (input, { ctx }) => Effect5.map(
3611
3705
  ctx.oauth.removeClient(input.owner, OAuthClientSlug.make(input.slug)),
3612
3706
  () => ({ removed: true })
@@ -3631,6 +3725,14 @@ var coreToolsPlugin = definePlugin((options = {}) => ({
3631
3725
  description: "Start OAuth through a registered client to mint a connection for an integration. `client_credentials` clients return `connected`; authorization-code clients return an authorization URL and state.",
3632
3726
  inputSchema: OAuthStartInputStd,
3633
3727
  outputSchema: OAuthStartOutputStd,
3728
+ // This is the materialization step that turns a registered client
3729
+ // into a live connection. For `client_credentials` it completes
3730
+ // synchronously (status `connected`) with no browser step, so a
3731
+ // prompt-injected call against an attacker-registered client mints a
3732
+ // credentialed connection with no human in the loop. The
3733
+ // authorization-code path already returns a URL the user must visit,
3734
+ // but one gate on the whole tool covers the silent path cleanly.
3735
+ annotations: { requiresApproval: true },
3634
3736
  execute: (input, { ctx }) => Effect5.map(
3635
3737
  ctx.oauth.start({
3636
3738
  client: OAuthClientSlug.make(input.client),
@@ -3678,6 +3780,11 @@ var coreToolsPlugin = definePlugin((options = {}) => ({
3678
3780
  description: "Create a tool policy. `pattern` matches a tool address tail (`integration.connection.tool`, `integration.*`, `*`); `action` is approve/require_approval/block. `owner` is org (workspace guardrail) or user (personal).",
3679
3781
  inputSchema: PolicyCreateInputStd,
3680
3782
  outputSchema: PolicyOutputStd,
3783
+ // A policy decides which tools run without confirmation, so creating
3784
+ // one can silence every other approval gate (e.g. `approve *`). It
3785
+ // must itself require approval, otherwise prompt-injected code could
3786
+ // disable approvals by writing its own bypass policy.
3787
+ annotations: { requiresApproval: true },
3681
3788
  execute: (input, { ctx }) => Effect5.map(
3682
3789
  ctx.core.policies.create({
3683
3790
  owner: input.owner,
@@ -3698,6 +3805,10 @@ var coreToolsPlugin = definePlugin((options = {}) => ({
3698
3805
  description: "Update a tool policy's pattern and/or action by id + owner.",
3699
3806
  inputSchema: PolicyUpdateInputStd,
3700
3807
  outputSchema: PolicyOutputStd,
3808
+ // Editing a policy can broaden a pattern or flip an action to
3809
+ // `approve`, weakening an approval gate just as creation can, so it
3810
+ // requires approval too. See `policies.create`.
3811
+ annotations: { requiresApproval: true },
3701
3812
  execute: (input, { ctx }) => Effect5.map(
3702
3813
  ctx.core.policies.update({
3703
3814
  id: input.id,
@@ -3719,6 +3830,10 @@ var coreToolsPlugin = definePlugin((options = {}) => ({
3719
3830
  description: "Remove a tool policy by id + owner.",
3720
3831
  inputSchema: PolicyRemoveInputStd,
3721
3832
  outputSchema: RemovedOutputStd,
3833
+ // Removing a policy can drop a `block` or `require_approval`
3834
+ // guardrail, so deletion is also approval-gated. See
3835
+ // `policies.create`.
3836
+ annotations: { requiresApproval: true },
3722
3837
  execute: (input, { ctx }) => Effect5.map(
3723
3838
  ctx.core.policies.remove({
3724
3839
  id: input.id,
@@ -4161,6 +4276,7 @@ var validateClientEndpoints = (input, endpointUrlPolicy) => Effect7.gen(function
4161
4276
  });
4162
4277
  var makeOAuthService = (deps) => {
4163
4278
  const httpClientLayer = deps.httpClientLayer ?? FetchHttpClient2.layer;
4279
+ const fetch = deps.fetch;
4164
4280
  const redirectUri = deps.redirectUri;
4165
4281
  const createClient = (input) => Effect7.gen(function* () {
4166
4282
  yield* validateClientEndpoints(input, deps.endpointUrlPolicy);
@@ -4365,7 +4481,8 @@ var makeOAuthService = (deps) => {
4365
4481
  clientSecret: client.clientSecret,
4366
4482
  scopes: requestedScopes,
4367
4483
  resource: client.resource ?? void 0,
4368
- endpointUrlPolicy: deps.endpointUrlPolicy
4484
+ endpointUrlPolicy: deps.endpointUrlPolicy,
4485
+ fetch
4369
4486
  }).pipe(
4370
4487
  Effect7.mapError(
4371
4488
  (cause) => new OAuthStartError({
@@ -4506,7 +4623,8 @@ var makeOAuthService = (deps) => {
4506
4623
  codeVerifier: session.pkceVerifier,
4507
4624
  code: input.code,
4508
4625
  resource: client.resource ?? void 0,
4509
- endpointUrlPolicy: deps.endpointUrlPolicy
4626
+ endpointUrlPolicy: deps.endpointUrlPolicy,
4627
+ fetch
4510
4628
  }).pipe(
4511
4629
  Effect7.mapError(
4512
4630
  (cause) => new OAuthCompleteError({
@@ -5178,6 +5296,7 @@ var createExecutor = (config) => Effect8.gen(function* () {
5178
5296
  const plugins = config.coreTools ? [
5179
5297
  coreToolsPlugin({
5180
5298
  webBaseUrl: config.coreTools.webBaseUrl,
5299
+ orgSlug: config.coreTools.orgSlug,
5181
5300
  includeProviders: config.coreTools.includeProviders
5182
5301
  }),
5183
5302
  ...userPlugins
@@ -5306,7 +5425,8 @@ var createExecutor = (config) => Effect8.gen(function* () {
5306
5425
  clientSecret,
5307
5426
  scopes: grantedScopes,
5308
5427
  resource: clientRow.resource ? String(clientRow.resource) : void 0,
5309
- endpointUrlPolicy: config.oauthEndpointUrlPolicy
5428
+ endpointUrlPolicy: config.oauthEndpointUrlPolicy,
5429
+ fetch: config.fetch
5310
5430
  }).pipe(
5311
5431
  // A client_credentials failure is never a rotated-refresh-token
5312
5432
  // problem, so do NOT map invalid_grant → reauth. Surface as a
@@ -5336,7 +5456,8 @@ var createExecutor = (config) => Effect8.gen(function* () {
5336
5456
  // RFC 8707: keep the re-minted token bound to the same resource
5337
5457
  // (MCP servers require this on refresh).
5338
5458
  resource: clientRow.resource ? String(clientRow.resource) : void 0,
5339
- endpointUrlPolicy: config.oauthEndpointUrlPolicy
5459
+ endpointUrlPolicy: config.oauthEndpointUrlPolicy,
5460
+ fetch: config.fetch
5340
5461
  }).pipe(
5341
5462
  Effect8.mapError(
5342
5463
  (cause) => cause.error === "invalid_grant" ? reauth(
@@ -5620,6 +5741,7 @@ var createExecutor = (config) => Effect8.gen(function* () {
5620
5741
  const result = yield* runtime.plugin.resolveTools({
5621
5742
  integration: rowToIntegration(integrationRow),
5622
5743
  config: decodeJsonColumn(integrationRow.config),
5744
+ httpClientLayer: runtime.ctx.httpClientLayer,
5623
5745
  connection: ref,
5624
5746
  template: existingRow ? AuthTemplateSlug.make(existingRow.template) : null,
5625
5747
  storage: runtime.storage,
@@ -6516,6 +6638,7 @@ ${approvalArgumentPreview(args)}`,
6516
6638
  })
6517
6639
  ),
6518
6640
  httpClientLayer: config.httpClientLayer,
6641
+ fetch: config.fetch,
6519
6642
  endpointUrlPolicy: config.oauthEndpointUrlPolicy,
6520
6643
  // EXPLICIT — no localhost default. When a caller omits `redirectUri` the
6521
6644
  // OAuth service receives `null` and redirect-requiring flows fail loudly
@@ -6739,4 +6862,4 @@ export {
6739
6862
  collectTables,
6740
6863
  createExecutor
6741
6864
  };
6742
- //# sourceMappingURL=chunk-5Q35SELN.js.map
6865
+ //# sourceMappingURL=chunk-MYRPYTSV.js.map