@sentry/junior 0.81.0 → 0.82.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 (32) hide show
  1. package/dist/{agent-hooks-ZGTDOXQY.js → agent-hooks-5ZRILRC3.js} +1 -1
  2. package/dist/app.js +7 -905
  3. package/dist/chat/agent-dispatch/runner.d.ts +1 -1
  4. package/dist/chat/app/services.d.ts +1 -1
  5. package/dist/chat/egress/credentialed.d.ts +57 -0
  6. package/dist/chat/egress/plugin.d.ts +24 -0
  7. package/dist/chat/plugins/credential-hooks.d.ts +2 -0
  8. package/dist/chat/respond.d.ts +1 -1
  9. package/dist/chat/sandbox/{egress-credentials.d.ts → egress/credentials.d.ts} +28 -5
  10. package/dist/chat/sandbox/egress/policy.d.ts +32 -0
  11. package/dist/chat/sandbox/egress/proxy.d.ts +35 -0
  12. package/dist/chat/sandbox/{egress-session.d.ts → egress/session.d.ts} +41 -11
  13. package/dist/chat/sandbox/sandbox.d.ts +2 -2
  14. package/dist/chat/tools/types.d.ts +2 -1
  15. package/dist/{chunk-BOMLWLTF.js → chunk-NC6LR6U4.js} +1048 -13
  16. package/dist/{chunk-Y3YUOEAZ.js → chunk-TR2G37II.js} +14 -5
  17. package/dist/{chunk-RV5RYIJW.js → chunk-UXPG6ZIN.js} +28 -0
  18. package/dist/cli/chat.js +3 -3
  19. package/dist/cli/env.js +7 -1
  20. package/dist/cli/init.js +1 -0
  21. package/dist/cli/main.js +1 -1
  22. package/dist/cli/plugins.js +1 -1
  23. package/dist/handlers/agent-dispatch.d.ts +1 -1
  24. package/dist/handlers/sandbox-egress-proxy.d.ts +2 -2
  25. package/dist/reporting.js +1 -1
  26. package/dist/{runner-LNBN7BIN.js → runner-VRFJLG2M.js} +2 -2
  27. package/package.json +6 -5
  28. package/dist/chat/sandbox/egress-policy.d.ts +0 -15
  29. package/dist/chat/sandbox/egress-proxy.d.ts +0 -19
  30. /package/dist/chat/sandbox/{egress-oidc.d.ts → egress/oidc.d.ts} +0 -0
  31. /package/dist/chat/sandbox/{egress-schemas.d.ts → egress/schemas.d.ts} +0 -0
  32. /package/dist/chat/sandbox/{egress-tracing.d.ts → egress/tracing.d.ts} +0 -0
@@ -42,7 +42,7 @@ import {
42
42
  getPlugins,
43
43
  getSlackToolContext,
44
44
  resolveChannelCapabilities
45
- } from "./chunk-Y3YUOEAZ.js";
45
+ } from "./chunk-TR2G37II.js";
46
46
  import {
47
47
  createPluginLogger,
48
48
  createPluginState
@@ -122,6 +122,7 @@ import {
122
122
  parseSkillInvocation
123
123
  } from "./chunk-WCXVEQPI.js";
124
124
  import {
125
+ CredentialUnavailableError,
125
126
  credentialContextSchema,
126
127
  pluginCatalogRuntime,
127
128
  resolveAuthTokenPlaceholder,
@@ -191,7 +192,7 @@ function getConfigDefaults() {
191
192
  return cloneDefaults(installDefaults);
192
193
  }
193
194
 
194
- // src/chat/sandbox/egress-tracing.ts
195
+ // src/chat/sandbox/egress/tracing.ts
195
196
  function isValidDomainPattern(domain) {
196
197
  if (domain.includes("*")) {
197
198
  return domain.startsWith("*.") && domain.indexOf("*", 1) === -1;
@@ -7162,10 +7163,10 @@ async function startOAuthFlow(provider, input) {
7162
7163
  };
7163
7164
  }
7164
7165
 
7165
- // src/chat/sandbox/egress-session.ts
7166
+ // src/chat/sandbox/egress/session.ts
7166
7167
  import { createHmac, randomUUID, timingSafeEqual } from "crypto";
7167
7168
 
7168
- // src/chat/sandbox/egress-schemas.ts
7169
+ // src/chat/sandbox/egress/schemas.ts
7169
7170
  import { z } from "zod";
7170
7171
  import {
7171
7172
  pluginAuthorizationSchema,
@@ -7230,7 +7231,7 @@ function parseSandboxEgressPermissionDeniedSignal(value) {
7230
7231
  return result.success ? result.data : void 0;
7231
7232
  }
7232
7233
 
7233
- // src/chat/sandbox/egress-session.ts
7234
+ // src/chat/sandbox/egress/session.ts
7234
7235
  var SANDBOX_EGRESS_PROXY_PATH = "/api/internal/sandbox-egress";
7235
7236
  var SANDBOX_EGRESS_TOKEN_VERSION = "v1";
7236
7237
  var SANDBOX_EGRESS_HMAC_CONTEXT = "junior.sandbox_egress.v1";
@@ -7432,7 +7433,7 @@ async function consumeSandboxEgressPermissionDeniedSignal(egressId) {
7432
7433
  return signal;
7433
7434
  }
7434
7435
 
7435
- // src/chat/sandbox/egress-policy.ts
7436
+ // src/chat/sandbox/egress/policy.ts
7436
7437
  function matchesSandboxEgressDomain(host, domain) {
7437
7438
  return host.toLowerCase() === domain.toLowerCase();
7438
7439
  }
@@ -11212,6 +11213,1028 @@ function createMcpAuthOrchestration(input) {
11212
11213
  };
11213
11214
  }
11214
11215
 
11216
+ // src/chat/egress/plugin.ts
11217
+ import { randomUUID as randomUUID4 } from "crypto";
11218
+
11219
+ // src/chat/plugins/credential-hooks.ts
11220
+ import {
11221
+ pluginAuthorizationSchema as pluginAuthorizationSchema2,
11222
+ pluginCredentialResultSchema,
11223
+ pluginGrantSchema as pluginGrantSchema2,
11224
+ pluginProviderAccountSchema as pluginProviderAccountSchema2
11225
+ } from "@sentry/junior-plugin-api";
11226
+ function parseSchema(schema, value, message) {
11227
+ const result = schema.safeParse(value);
11228
+ if (!result.success) {
11229
+ throw new Error(message);
11230
+ }
11231
+ return result.data;
11232
+ }
11233
+ function parseAuthorization(value, pluginName) {
11234
+ if (value === void 0) {
11235
+ return void 0;
11236
+ }
11237
+ const authorization = parseSchema(
11238
+ pluginAuthorizationSchema2,
11239
+ value,
11240
+ `Plugin "${pluginName}" grant authorization is invalid`
11241
+ );
11242
+ if (authorization.provider !== pluginName) {
11243
+ throw new Error(
11244
+ `Plugin "${pluginName}" grant authorization provider must match the issuing plugin`
11245
+ );
11246
+ }
11247
+ return authorization;
11248
+ }
11249
+ function parseGrant(value, pluginName) {
11250
+ return parseSchema(
11251
+ pluginGrantSchema2,
11252
+ value,
11253
+ `Plugin "${pluginName}" grantForEgress returned an invalid grant`
11254
+ );
11255
+ }
11256
+ function pluginFor(provider) {
11257
+ return getPlugins().find((candidate) => candidate.manifest.name === provider);
11258
+ }
11259
+ function basePluginContext(plugin) {
11260
+ const pluginName = plugin.manifest.name;
11261
+ return {
11262
+ plugin: { name: pluginName },
11263
+ log: createPluginLogger(pluginName),
11264
+ db: getDb()
11265
+ };
11266
+ }
11267
+ function parseCredentialResult(value, pluginName) {
11268
+ const result = parseSchema(
11269
+ pluginCredentialResultSchema,
11270
+ value,
11271
+ `Plugin "${pluginName}" issueCredential result is invalid`
11272
+ );
11273
+ if (result.type === "lease") {
11274
+ parseAuthorization(result.lease.authorization, pluginName);
11275
+ return result;
11276
+ }
11277
+ if (result.type === "unavailable") {
11278
+ return result;
11279
+ }
11280
+ parseAuthorization(result.authorization, pluginName);
11281
+ return result;
11282
+ }
11283
+ async function selectPluginGrant(input) {
11284
+ const plugin = pluginFor(input.provider);
11285
+ const hook = plugin?.hooks?.grantForEgress;
11286
+ if (!plugin || !hook) {
11287
+ return void 0;
11288
+ }
11289
+ const result = await hook({
11290
+ ...basePluginContext(plugin),
11291
+ request: {
11292
+ ...input.bodyText !== void 0 ? { bodyText: input.bodyText } : {},
11293
+ method: input.method,
11294
+ ...input.operation ? { operation: input.operation } : {},
11295
+ url: input.upstreamUrl.toString()
11296
+ }
11297
+ });
11298
+ return result === void 0 ? void 0 : parseGrant(result, plugin.manifest.name);
11299
+ }
11300
+ async function onPluginEgressResponse(input) {
11301
+ const plugin = pluginFor(input.provider);
11302
+ const hook = plugin?.hooks?.onEgressResponse;
11303
+ if (!plugin || !hook) {
11304
+ return {};
11305
+ }
11306
+ let permissionDenied;
11307
+ await hook({
11308
+ ...basePluginContext(plugin),
11309
+ grant: input.grant,
11310
+ permissionDenied(message) {
11311
+ const trimmed = message.trim();
11312
+ if (!trimmed) {
11313
+ throw new Error(
11314
+ `Plugin "${plugin.manifest.name}" onEgressResponse permissionDenied message is empty`
11315
+ );
11316
+ }
11317
+ permissionDenied = { message: trimmed };
11318
+ },
11319
+ request: {
11320
+ method: input.method,
11321
+ ...input.operation ? { operation: input.operation } : {},
11322
+ url: input.upstreamUrl.toString()
11323
+ },
11324
+ response: input.response
11325
+ });
11326
+ return permissionDenied ? { permissionDenied } : {};
11327
+ }
11328
+ function hasEgressCredentialHooks(provider) {
11329
+ const hooks = pluginFor(provider)?.hooks;
11330
+ return Boolean(hooks?.grantForEgress || hooks?.issueCredential);
11331
+ }
11332
+ async function resolvePluginOAuthAccount(input) {
11333
+ const plugin = pluginFor(input.provider);
11334
+ const hook = plugin?.hooks?.resolveOAuthAccount;
11335
+ if (!plugin || !hook) {
11336
+ return void 0;
11337
+ }
11338
+ const account = await hook({
11339
+ ...basePluginContext(plugin),
11340
+ tokens: input.tokens
11341
+ });
11342
+ return account === void 0 ? void 0 : parseSchema(
11343
+ pluginProviderAccountSchema2,
11344
+ account,
11345
+ `Plugin "${plugin.manifest.name}" resolveOAuthAccount returned an invalid account`
11346
+ );
11347
+ }
11348
+ async function issuePluginCredential(input) {
11349
+ const plugin = pluginFor(input.provider);
11350
+ const hook = plugin?.hooks?.issueCredential;
11351
+ if (!plugin || !hook) {
11352
+ throw new Error(`Plugin "${input.provider}" has no issueCredential hook`);
11353
+ }
11354
+ const currentUserId = input.actor.type === "user" ? input.actor.userId : void 0;
11355
+ const credentialSubjectUserId = input.credentialSubject?.userId;
11356
+ const result = await hook({
11357
+ ...basePluginContext(plugin),
11358
+ actor: input.actor,
11359
+ grant: input.grant,
11360
+ ...input.credentialSubject ? { credentialSubject: input.credentialSubject } : {},
11361
+ tokens: {
11362
+ ...currentUserId ? {
11363
+ currentUser: {
11364
+ userId: currentUserId,
11365
+ get: async () => await input.userTokenStore.get(
11366
+ currentUserId,
11367
+ plugin.manifest.name
11368
+ ),
11369
+ set: async (tokens) => {
11370
+ await input.userTokenStore.set(
11371
+ currentUserId,
11372
+ plugin.manifest.name,
11373
+ tokens
11374
+ );
11375
+ },
11376
+ withRefresh: async (callback) => await input.userTokenStore.withRefresh(
11377
+ currentUserId,
11378
+ plugin.manifest.name,
11379
+ callback
11380
+ )
11381
+ }
11382
+ } : {},
11383
+ ...credentialSubjectUserId ? {
11384
+ credentialSubject: {
11385
+ userId: credentialSubjectUserId,
11386
+ get: async () => await input.userTokenStore.get(
11387
+ credentialSubjectUserId,
11388
+ plugin.manifest.name
11389
+ ),
11390
+ set: async (tokens) => {
11391
+ await input.userTokenStore.set(
11392
+ credentialSubjectUserId,
11393
+ plugin.manifest.name,
11394
+ tokens
11395
+ );
11396
+ },
11397
+ withRefresh: async (callback) => await input.userTokenStore.withRefresh(
11398
+ credentialSubjectUserId,
11399
+ plugin.manifest.name,
11400
+ callback
11401
+ )
11402
+ }
11403
+ } : {}
11404
+ }
11405
+ });
11406
+ return parseCredentialResult(result, plugin.manifest.name);
11407
+ }
11408
+
11409
+ // src/chat/sandbox/egress/credentials.ts
11410
+ var HTTP_READ_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD", "OPTIONS"]);
11411
+ var SandboxEgressCredentialError = class extends Error {
11412
+ authorization;
11413
+ grant;
11414
+ kind;
11415
+ provider;
11416
+ constructor(input) {
11417
+ super(input.message);
11418
+ this.name = "SandboxEgressCredentialError";
11419
+ this.authorization = input.authorization;
11420
+ this.grant = input.grant;
11421
+ this.kind = input.kind;
11422
+ this.provider = input.provider;
11423
+ }
11424
+ };
11425
+ function defaultGrantForProvider(input) {
11426
+ const access = HTTP_READ_METHODS.has(
11427
+ input.method.toUpperCase()
11428
+ ) ? "read" : "write";
11429
+ return {
11430
+ source: "broker",
11431
+ grant: {
11432
+ name: "default",
11433
+ access,
11434
+ reason: `sandbox-egress:${input.provider}:${access}`
11435
+ }
11436
+ };
11437
+ }
11438
+ function oauthAuthorizationForProvider(provider) {
11439
+ const oauth = pluginCatalogRuntime.getOAuthConfig(provider);
11440
+ return oauth ? {
11441
+ type: "oauth",
11442
+ provider,
11443
+ ...oauth.scope ? { scope: oauth.scope } : {}
11444
+ } : void 0;
11445
+ }
11446
+ function credentialSubjectFromContext(context) {
11447
+ return "subject" in context.credentials && context.credentials.subject ? { type: "user", userId: context.credentials.subject.userId } : void 0;
11448
+ }
11449
+ function assertLeaseTransformsOwnedByProvider(provider, lease) {
11450
+ for (const transform of lease.headerTransforms) {
11451
+ if (resolveSandboxEgressProviderForHost(transform.domain) !== provider) {
11452
+ throw new Error(
11453
+ `Credential lease for ${provider} included header transform for unowned domain ${transform.domain}`
11454
+ );
11455
+ }
11456
+ }
11457
+ }
11458
+ async function selectSandboxEgressGrant(input) {
11459
+ if (!hasEgressCredentialHooks(input.provider)) {
11460
+ return defaultGrantForProvider(input);
11461
+ }
11462
+ const pluginGrant = await selectPluginGrant({
11463
+ ...input.bodyText !== void 0 ? { bodyText: input.bodyText } : {},
11464
+ ...input.operation ? { operation: input.operation } : {},
11465
+ provider: input.provider,
11466
+ method: input.method,
11467
+ upstreamUrl: input.upstreamUrl
11468
+ });
11469
+ if (!pluginGrant) {
11470
+ throw new Error(
11471
+ `Plugin "${input.provider}" grantForEgress must return a grant for sandbox egress`
11472
+ );
11473
+ }
11474
+ return { source: "plugin", grant: pluginGrant };
11475
+ }
11476
+ function authorizationForSandboxEgressGrant(provider, selection) {
11477
+ return selection.source === "broker" ? oauthAuthorizationForProvider(provider) : void 0;
11478
+ }
11479
+ async function sandboxEgressCredentialLease(provider, selection, context) {
11480
+ const { grant } = selection;
11481
+ const cached = await getSandboxEgressCredentialLease(
11482
+ provider,
11483
+ grant.name,
11484
+ context
11485
+ );
11486
+ if (cached) {
11487
+ if (selection.source === "plugin" && cached.grant.access !== grant.access) {
11488
+ throw new Error(
11489
+ `Cached credential lease for ${provider}/${grant.name} has ${cached.grant.access} access, but ${grant.access} was selected`
11490
+ );
11491
+ }
11492
+ return {
11493
+ ...cached,
11494
+ grant
11495
+ };
11496
+ }
11497
+ let lease;
11498
+ if (selection.source === "plugin") {
11499
+ const credentialSubject = credentialSubjectFromContext(context);
11500
+ const pluginResult = await issuePluginCredential({
11501
+ provider,
11502
+ grant,
11503
+ actor: context.credentials.actor,
11504
+ ...credentialSubject ? { credentialSubject } : {},
11505
+ userTokenStore: createUserTokenStore()
11506
+ });
11507
+ if (pluginResult.type === "needed") {
11508
+ throw new SandboxEgressCredentialError({
11509
+ provider,
11510
+ grant,
11511
+ kind: "auth_required",
11512
+ authorization: pluginResult.authorization,
11513
+ message: pluginResult.message
11514
+ });
11515
+ }
11516
+ if (pluginResult.type === "unavailable") {
11517
+ throw new SandboxEgressCredentialError({
11518
+ provider,
11519
+ grant,
11520
+ kind: "unavailable",
11521
+ message: pluginResult.message
11522
+ });
11523
+ }
11524
+ lease = pluginResult.lease;
11525
+ } else {
11526
+ try {
11527
+ lease = await issueProviderCredentialLease({
11528
+ context: context.credentials,
11529
+ provider,
11530
+ reason: grant.reason ?? `sandbox-egress:${provider}:default`
11531
+ });
11532
+ } catch (error) {
11533
+ if (error instanceof CredentialUnavailableError) {
11534
+ throw new SandboxEgressCredentialError({
11535
+ provider,
11536
+ grant,
11537
+ kind: "auth_required",
11538
+ authorization: authorizationForSandboxEgressGrant(
11539
+ provider,
11540
+ selection
11541
+ ),
11542
+ message: error.message
11543
+ });
11544
+ }
11545
+ throw error;
11546
+ }
11547
+ }
11548
+ const headerTransforms = lease.headerTransforms ?? [];
11549
+ if (headerTransforms.length === 0) {
11550
+ throw new Error(
11551
+ `Credential lease for ${provider} did not include header transforms`
11552
+ );
11553
+ }
11554
+ const leaseExpiresAtMs = Date.parse(lease.expiresAt);
11555
+ if (!Number.isFinite(leaseExpiresAtMs) || leaseExpiresAtMs <= Date.now()) {
11556
+ throw new Error(`Credential lease for ${provider} is expired`);
11557
+ }
11558
+ const authorization = selection.source === "broker" ? oauthAuthorizationForProvider(provider) : lease.authorization;
11559
+ const cachedLease = {
11560
+ provider,
11561
+ grant,
11562
+ ...lease.account ? { account: lease.account } : {},
11563
+ ...authorization ? { authorization } : {},
11564
+ expiresAt: lease.expiresAt,
11565
+ headerTransforms
11566
+ };
11567
+ assertLeaseTransformsOwnedByProvider(provider, cachedLease);
11568
+ await setSandboxEgressCredentialLease(context, cachedLease);
11569
+ return cachedLease;
11570
+ }
11571
+ function hasSandboxEgressLeaseTransformForHost(lease, host) {
11572
+ return lease.headerTransforms.some(
11573
+ (transform) => matchesSandboxEgressDomain(host, transform.domain)
11574
+ );
11575
+ }
11576
+
11577
+ // src/chat/egress/credentialed.ts
11578
+ import {
11579
+ EgressAuthRequired,
11580
+ EgressPolicyDenied
11581
+ } from "@sentry/junior-plugin-api";
11582
+ var HOP_BY_HOP_HEADERS = /* @__PURE__ */ new Set([
11583
+ "connection",
11584
+ "host",
11585
+ "keep-alive",
11586
+ "proxy-authenticate",
11587
+ "proxy-authorization",
11588
+ "te",
11589
+ "trailer",
11590
+ "transfer-encoding",
11591
+ "upgrade"
11592
+ ]);
11593
+ var PROXY_ONLY_HEADERS = /* @__PURE__ */ new Set([
11594
+ "vercel-sandbox-oidc-token",
11595
+ "vercel-forwarded-host",
11596
+ "vercel-forwarded-scheme",
11597
+ "vercel-forwarded-port",
11598
+ "vercel-forwarded-path"
11599
+ ]);
11600
+ var TRACE_PROPAGATION_HEADERS = /* @__PURE__ */ new Set([
11601
+ "baggage",
11602
+ "sentry-trace",
11603
+ "traceparent"
11604
+ ]);
11605
+ var DECODED_RESPONSE_HEADERS = /* @__PURE__ */ new Set([
11606
+ "content-encoding",
11607
+ "content-length"
11608
+ ]);
11609
+ var UPSTREAM_TOKEN_REJECTION_STATUS = 401;
11610
+ var UPSTREAM_PERMISSION_REJECTION_STATUS = 403;
11611
+ var GRANT_SELECTION_BODY_TEXT_LIMIT_BYTES = 64 * 1024;
11612
+ var RESPONSE_BODY_TEXT_LIMIT_BYTES = 64 * 1024;
11613
+ function authRequiredResponse(input) {
11614
+ return new Response(
11615
+ `junior-auth-required provider=${input.provider} grant=${input.grant.name} access=${input.grant.access} 401 unauthorized
11616
+ ${input.message}`,
11617
+ {
11618
+ status: 401,
11619
+ headers: {
11620
+ "content-type": "text/plain; charset=utf-8",
11621
+ "cache-control": "no-store"
11622
+ }
11623
+ }
11624
+ );
11625
+ }
11626
+ function policyDeniedResponse(error) {
11627
+ return Response.json(
11628
+ { error: error.message },
11629
+ {
11630
+ status: 403,
11631
+ headers: {
11632
+ "cache-control": "no-store"
11633
+ }
11634
+ }
11635
+ );
11636
+ }
11637
+ function shouldLogSandboxEgressInfo() {
11638
+ const environment = (process.env.SENTRY_ENVIRONMENT ?? process.env.VERCEL_ENV ?? process.env.NODE_ENV ?? "").trim().toLowerCase();
11639
+ return environment !== "production";
11640
+ }
11641
+ function egressAttributes(input) {
11642
+ return {
11643
+ ...input.egressId ? { "app.sandbox.egress_id": input.egressId } : {},
11644
+ ...input.provider ? { "app.provider.name": input.provider } : {},
11645
+ ...input.grantName ? { "app.grant.name": input.grantName } : {},
11646
+ ...input.grantAccess ? { "app.grant.access": input.grantAccess } : {},
11647
+ ...input.grantReason ? { "app.grant.reason": input.grantReason } : {},
11648
+ ...input.host ? { "server.address": input.host } : {},
11649
+ ...input.method ? { "http.request.method": input.method } : {},
11650
+ ...input.path ? { "url.path": input.path } : {},
11651
+ ...input.status ? { "http.response.status_code": input.status } : {}
11652
+ };
11653
+ }
11654
+ function redactedProxyPath(pathname) {
11655
+ if (pathname.startsWith(`${SANDBOX_EGRESS_PROXY_PATH}/`)) {
11656
+ return `${SANDBOX_EGRESS_PROXY_PATH}/<token>`;
11657
+ }
11658
+ return pathname;
11659
+ }
11660
+ function routingAttributes(request, upstreamUrl) {
11661
+ const proxyUrl = new URL(request.url);
11662
+ const attributes = {
11663
+ "app.sandbox.egress.proxy_path": redactedProxyPath(proxyUrl.pathname)
11664
+ };
11665
+ if (upstreamUrl) {
11666
+ attributes["app.sandbox.egress.upstream_path"] = upstreamUrl.pathname;
11667
+ const gitService = upstreamUrl.searchParams.get("service");
11668
+ if (upstreamUrl.hostname.toLowerCase() === "github.com" && (gitService === "git-upload-pack" || gitService === "git-receive-pack")) {
11669
+ attributes["app.sandbox.egress.git_service"] = gitService;
11670
+ }
11671
+ }
11672
+ return attributes;
11673
+ }
11674
+ function displayedUpstreamPath(upstreamUrl) {
11675
+ const gitService = upstreamUrl.searchParams.get("service");
11676
+ if (upstreamUrl.hostname.toLowerCase() === "github.com" && (gitService === "git-upload-pack" || gitService === "git-receive-pack")) {
11677
+ return `${upstreamUrl.pathname}?service=${gitService}`;
11678
+ }
11679
+ return upstreamUrl.pathname;
11680
+ }
11681
+ function upstreamPermissionAttributes(provider, upstream) {
11682
+ if (provider !== "github") {
11683
+ return {};
11684
+ }
11685
+ return {
11686
+ "app.github.accepted_permissions": upstream.headers.get("x-accepted-github-permissions") ?? void 0,
11687
+ "app.github.sso": upstream.headers.get("x-github-sso") ?? void 0
11688
+ };
11689
+ }
11690
+ function githubPermissionHeaders(upstream) {
11691
+ const acceptedPermissions = upstream.headers.get(
11692
+ "x-accepted-github-permissions"
11693
+ );
11694
+ const sso = upstream.headers.get("x-github-sso");
11695
+ return {
11696
+ ...acceptedPermissions ? { acceptedPermissions } : {},
11697
+ ...sso ? { sso } : {}
11698
+ };
11699
+ }
11700
+ function permissionDeniedMessage(provider, grant) {
11701
+ return `${provider} returned HTTP 403 after Junior injected the ${grant.name} grant. Junior forwarded the request; this is not a local runtime block.`;
11702
+ }
11703
+ function isEgressAuthRequired(error) {
11704
+ return error instanceof EgressAuthRequired || error instanceof Error && error.name === "EgressAuthRequired";
11705
+ }
11706
+ function isEgressPolicyDenied(error) {
11707
+ return error instanceof EgressPolicyDenied || error instanceof Error && error.name === "EgressPolicyDenied";
11708
+ }
11709
+ function logSandboxEgressUpstreamRequest(input) {
11710
+ if (!shouldLogSandboxEgressInfo()) {
11711
+ return;
11712
+ }
11713
+ logInfo(
11714
+ "sandbox_egress_upstream_request",
11715
+ {},
11716
+ {
11717
+ ...egressAttributes({
11718
+ egressId: input.egressId,
11719
+ grantAccess: input.grantAccess,
11720
+ grantName: input.grantName,
11721
+ grantReason: input.grantReason,
11722
+ host: input.upstreamUrl.hostname,
11723
+ method: input.request.method,
11724
+ path: input.upstreamUrl.pathname,
11725
+ provider: input.provider,
11726
+ status: input.upstream.status
11727
+ }),
11728
+ ...routingAttributes(input.request, input.upstreamUrl),
11729
+ "app.sandbox.egress.upstream_ok": input.upstream.ok
11730
+ },
11731
+ `Sandbox egress ${input.request.method} ${input.upstreamUrl.hostname}${displayedUpstreamPath(input.upstreamUrl)} -> ${input.upstream.status}`
11732
+ );
11733
+ }
11734
+ async function requestBodyBytes(request) {
11735
+ if (request.method === "GET" || request.method === "HEAD" || request.body === null) {
11736
+ return void 0;
11737
+ }
11738
+ return await request.arrayBuffer();
11739
+ }
11740
+ function isGrantSelectionBodyVisible(input) {
11741
+ return input.provider === "github" && input.upstreamUrl.hostname.toLowerCase() === "api.github.com" && input.upstreamUrl.pathname.toLowerCase().endsWith("/graphql");
11742
+ }
11743
+ function grantSelectionBodyText(input) {
11744
+ if (input.body === void 0) {
11745
+ return void 0;
11746
+ }
11747
+ if (input.body.byteLength > GRANT_SELECTION_BODY_TEXT_LIMIT_BYTES) {
11748
+ if (!input.operation && input.provider === "github" && input.request.method.toUpperCase() === "POST" && input.upstreamUrl.hostname.toLowerCase() === "api.github.com" && input.upstreamUrl.pathname.toLowerCase().endsWith("/graphql")) {
11749
+ throw new EgressPolicyDenied(
11750
+ "GitHub GraphQL request body is too large for Junior to inspect before issuing credentials."
11751
+ );
11752
+ }
11753
+ return void 0;
11754
+ }
11755
+ return new TextDecoder().decode(input.body);
11756
+ }
11757
+ function responseContentLength(upstream) {
11758
+ const raw = upstream.headers.get("content-length");
11759
+ if (!raw) {
11760
+ return void 0;
11761
+ }
11762
+ const parsed = Number(raw);
11763
+ return Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : void 0;
11764
+ }
11765
+ async function responseTextWithinLimit(upstream, maxBytes) {
11766
+ const limit = Math.min(
11767
+ Math.max(0, Math.floor(maxBytes)),
11768
+ RESPONSE_BODY_TEXT_LIMIT_BYTES
11769
+ );
11770
+ if (limit <= 0) {
11771
+ return void 0;
11772
+ }
11773
+ const contentLength = responseContentLength(upstream);
11774
+ if (contentLength !== void 0 && contentLength > limit) {
11775
+ return void 0;
11776
+ }
11777
+ let clone;
11778
+ try {
11779
+ clone = upstream.clone();
11780
+ } catch {
11781
+ return void 0;
11782
+ }
11783
+ const body = clone.body;
11784
+ if (!body) {
11785
+ return "";
11786
+ }
11787
+ const reader = body.getReader();
11788
+ const chunks = [];
11789
+ let bytes = 0;
11790
+ try {
11791
+ while (true) {
11792
+ const { done, value } = await reader.read();
11793
+ if (done) {
11794
+ break;
11795
+ }
11796
+ if (!value) {
11797
+ continue;
11798
+ }
11799
+ bytes += value.byteLength;
11800
+ if (bytes > limit) {
11801
+ await reader.cancel().catch(() => void 0);
11802
+ return void 0;
11803
+ }
11804
+ chunks.push(value);
11805
+ }
11806
+ } catch {
11807
+ await reader.cancel().catch(() => void 0);
11808
+ return void 0;
11809
+ } finally {
11810
+ reader.releaseLock();
11811
+ }
11812
+ const combined = new Uint8Array(bytes);
11813
+ let offset = 0;
11814
+ for (const chunk of chunks) {
11815
+ combined.set(chunk, offset);
11816
+ offset += chunk.byteLength;
11817
+ }
11818
+ return new TextDecoder().decode(combined);
11819
+ }
11820
+ function requestHeaders(request, lease, upstreamHost, tracePropagation) {
11821
+ const headers = new Headers();
11822
+ const mayPropagateTrace = shouldPropagateSandboxEgressTrace(
11823
+ upstreamHost,
11824
+ tracePropagation
11825
+ );
11826
+ request.headers.forEach((value, key) => {
11827
+ const normalized = key.toLowerCase();
11828
+ if (HOP_BY_HOP_HEADERS.has(normalized) || PROXY_ONLY_HEADERS.has(normalized)) {
11829
+ return;
11830
+ }
11831
+ if (TRACE_PROPAGATION_HEADERS.has(normalized) && !mayPropagateTrace) {
11832
+ return;
11833
+ }
11834
+ headers.append(key, value);
11835
+ });
11836
+ for (const transform of lease.headerTransforms) {
11837
+ if (!matchesSandboxEgressDomain(upstreamHost, transform.domain)) {
11838
+ continue;
11839
+ }
11840
+ for (const [key, value] of Object.entries(transform.headers)) {
11841
+ if (TRACE_PROPAGATION_HEADERS.has(key.toLowerCase()) && !mayPropagateTrace) {
11842
+ continue;
11843
+ }
11844
+ headers.set(key, value);
11845
+ }
11846
+ }
11847
+ return headers;
11848
+ }
11849
+ function responseHeaders(upstream) {
11850
+ const headers = new Headers();
11851
+ upstream.headers.forEach((value, key) => {
11852
+ const normalized = key.toLowerCase();
11853
+ if (!HOP_BY_HOP_HEADERS.has(normalized) && !DECODED_RESPONSE_HEADERS.has(normalized)) {
11854
+ headers.append(key, value);
11855
+ }
11856
+ });
11857
+ return headers;
11858
+ }
11859
+ function leaseLogAttributes(input) {
11860
+ return {
11861
+ ...egressAttributes({
11862
+ egressId: input.egressId,
11863
+ grantAccess: input.lease.grant.access,
11864
+ grantName: input.lease.grant.name,
11865
+ grantReason: input.lease.grant.reason,
11866
+ host: input.upstreamUrl.hostname,
11867
+ method: input.request.method,
11868
+ path: input.upstreamUrl.pathname,
11869
+ provider: input.provider,
11870
+ status: input.status
11871
+ }),
11872
+ ...routingAttributes(input.request, input.upstreamUrl),
11873
+ ...input.upstream ? upstreamPermissionAttributes(input.provider, input.upstream) : {}
11874
+ };
11875
+ }
11876
+ async function recordSandboxAuthRequired(input) {
11877
+ await setSandboxEgressAuthRequiredSignal(input.credentialContext, {
11878
+ provider: input.provider,
11879
+ grant: input.grant,
11880
+ kind: input.kind ?? "auth_required",
11881
+ ...input.authorization ? { authorization: input.authorization } : {},
11882
+ message: input.message
11883
+ });
11884
+ }
11885
+ async function recordSandboxPermissionDenied(input) {
11886
+ await setSandboxEgressPermissionDeniedSignal(input.credentialContext, {
11887
+ provider: input.provider,
11888
+ grant: input.lease.grant,
11889
+ ...input.lease.account ? { account: input.lease.account } : {},
11890
+ message: input.message,
11891
+ source: "upstream",
11892
+ status: input.upstream.status,
11893
+ upstreamHost: input.upstreamUrl.hostname,
11894
+ upstreamPath: displayedUpstreamPath(input.upstreamUrl),
11895
+ ...input.provider === "github" ? githubPermissionHeaders(input.upstream) : {}
11896
+ });
11897
+ }
11898
+ async function executeCredentialedEgressRequest(input) {
11899
+ const {
11900
+ activeEgressId,
11901
+ credentialContext,
11902
+ deps,
11903
+ operation,
11904
+ provider,
11905
+ request,
11906
+ upstreamUrl
11907
+ } = input;
11908
+ const bodyForGrantSelection = isGrantSelectionBodyVisible({
11909
+ provider,
11910
+ upstreamUrl
11911
+ }) ? await requestBodyBytes(request) : void 0;
11912
+ let grantSelection;
11913
+ try {
11914
+ grantSelection = await selectSandboxEgressGrant({
11915
+ bodyText: grantSelectionBodyText({
11916
+ body: bodyForGrantSelection,
11917
+ ...operation ? { operation } : {},
11918
+ provider,
11919
+ request,
11920
+ upstreamUrl
11921
+ }),
11922
+ ...operation ? { operation } : {},
11923
+ provider,
11924
+ method: request.method,
11925
+ upstreamUrl
11926
+ });
11927
+ } catch (error) {
11928
+ if (isEgressPolicyDenied(error)) {
11929
+ logWarn(
11930
+ "sandbox_egress_policy_denied",
11931
+ {},
11932
+ {
11933
+ ...egressAttributes({
11934
+ egressId: activeEgressId,
11935
+ host: upstreamUrl.hostname,
11936
+ method: request.method,
11937
+ path: upstreamUrl.pathname,
11938
+ provider,
11939
+ status: 403
11940
+ }),
11941
+ ...routingAttributes(request, upstreamUrl)
11942
+ },
11943
+ error.message
11944
+ );
11945
+ return policyDeniedResponse(error);
11946
+ }
11947
+ throw error;
11948
+ }
11949
+ const issueCredentialLease = deps.issueCredentialLease ?? sandboxEgressCredentialLease;
11950
+ const clearCredentialLease = deps.clearCredentialLease ?? clearSandboxEgressCredentialLease;
11951
+ const recordAuthRequired = deps.recordAuthRequired ?? recordSandboxAuthRequired;
11952
+ const recordPermissionDenied = deps.recordPermissionDenied ?? recordSandboxPermissionDenied;
11953
+ let lease;
11954
+ try {
11955
+ lease = await issueCredentialLease(
11956
+ provider,
11957
+ grantSelection,
11958
+ credentialContext
11959
+ );
11960
+ } catch (error) {
11961
+ if (error instanceof SandboxEgressCredentialError) {
11962
+ await recordAuthRequired({
11963
+ credentialContext,
11964
+ provider: error.provider,
11965
+ grant: error.grant,
11966
+ kind: error.kind,
11967
+ authorization: error.authorization,
11968
+ message: error.message
11969
+ });
11970
+ const isAuthRequired = error.kind === "auth_required";
11971
+ logWarn(
11972
+ isAuthRequired ? "sandbox_egress_credential_needed" : "sandbox_egress_credential_unavailable",
11973
+ {},
11974
+ {
11975
+ ...egressAttributes({
11976
+ egressId: activeEgressId,
11977
+ grantAccess: error.grant.access,
11978
+ grantName: error.grant.name,
11979
+ grantReason: error.grant.reason,
11980
+ host: upstreamUrl.hostname,
11981
+ method: request.method,
11982
+ path: upstreamUrl.pathname,
11983
+ provider: error.provider,
11984
+ status: 401
11985
+ }),
11986
+ ...routingAttributes(request, upstreamUrl)
11987
+ },
11988
+ isAuthRequired ? "Sandbox egress grant needs user authorization before issuing a credential lease" : "Sandbox egress credential lease is unavailable for selected grant"
11989
+ );
11990
+ return authRequiredResponse({
11991
+ provider: error.provider,
11992
+ grant: error.grant,
11993
+ message: error.message
11994
+ });
11995
+ }
11996
+ throw error;
11997
+ }
11998
+ const attributes = (status, upstream2) => leaseLogAttributes({
11999
+ egressId: activeEgressId,
12000
+ lease,
12001
+ provider,
12002
+ request,
12003
+ status,
12004
+ ...upstream2 ? { upstream: upstream2 } : {},
12005
+ upstreamUrl
12006
+ });
12007
+ if (!hasSandboxEgressLeaseTransformForHost(lease, upstreamUrl.hostname)) {
12008
+ logWarn(
12009
+ "sandbox_egress_transform_missing",
12010
+ {},
12011
+ {
12012
+ ...attributes(403),
12013
+ "app.sandbox.egress.transform_domains": lease.headerTransforms.map(
12014
+ (transform) => transform.domain
12015
+ )
12016
+ },
12017
+ "Sandbox egress credential lease does not cover forwarded host"
12018
+ );
12019
+ return Response.json(
12020
+ { error: "Credential lease does not cover forwarded host" },
12021
+ { status: 403 }
12022
+ );
12023
+ }
12024
+ const fetchImpl = deps.fetch ?? fetch;
12025
+ const headers = requestHeaders(
12026
+ request,
12027
+ lease,
12028
+ upstreamUrl.hostname,
12029
+ deps.tracePropagation ?? {}
12030
+ );
12031
+ const body = bodyForGrantSelection ?? await requestBodyBytes(request);
12032
+ const intercepted = await deps.interceptHttp?.({
12033
+ provider,
12034
+ request: new Request(upstreamUrl, {
12035
+ method: request.method,
12036
+ headers,
12037
+ ...body !== void 0 ? { body } : {}
12038
+ }),
12039
+ upstreamUrl
12040
+ });
12041
+ if (intercepted) {
12042
+ return intercepted;
12043
+ }
12044
+ const upstream = await fetchImpl(upstreamUrl, {
12045
+ method: request.method,
12046
+ headers,
12047
+ ...body !== void 0 ? { body } : {},
12048
+ redirect: "manual"
12049
+ });
12050
+ try {
12051
+ const effects = await onPluginEgressResponse({
12052
+ provider,
12053
+ grant: lease.grant,
12054
+ method: request.method,
12055
+ ...operation ? { operation } : {},
12056
+ upstreamUrl,
12057
+ response: {
12058
+ headers: new Headers(upstream.headers),
12059
+ readText: async (maxBytes) => await responseTextWithinLimit(upstream, maxBytes),
12060
+ status: upstream.status
12061
+ }
12062
+ });
12063
+ if (effects.permissionDenied) {
12064
+ await recordPermissionDenied({
12065
+ credentialContext,
12066
+ provider,
12067
+ lease,
12068
+ message: effects.permissionDenied.message,
12069
+ upstream,
12070
+ upstreamUrl
12071
+ });
12072
+ logWarn(
12073
+ "sandbox_egress_upstream_permission_classified",
12074
+ {},
12075
+ {
12076
+ ...attributes(upstream.status, upstream)
12077
+ },
12078
+ "Sandbox egress plugin classified upstream response as permission denied"
12079
+ );
12080
+ }
12081
+ } catch (error) {
12082
+ if (!isEgressAuthRequired(error)) {
12083
+ throw error;
12084
+ }
12085
+ await clearCredentialLease(provider, lease.grant.name, credentialContext);
12086
+ await recordAuthRequired({
12087
+ credentialContext,
12088
+ provider,
12089
+ grant: lease.grant,
12090
+ authorization: error.authorization ?? lease.authorization,
12091
+ message: error.message
12092
+ });
12093
+ logWarn(
12094
+ "sandbox_egress_upstream_auth_required_classified",
12095
+ {},
12096
+ {
12097
+ ...attributes(upstream.status, upstream)
12098
+ },
12099
+ "Sandbox egress plugin classified upstream response as auth required"
12100
+ );
12101
+ await upstream.body?.cancel().catch(() => void 0);
12102
+ return authRequiredResponse({
12103
+ provider,
12104
+ grant: lease.grant,
12105
+ message: error.message
12106
+ });
12107
+ }
12108
+ logSandboxEgressUpstreamRequest({
12109
+ egressId: activeEgressId,
12110
+ grantAccess: lease.grant.access,
12111
+ grantName: lease.grant.name,
12112
+ grantReason: lease.grant.reason,
12113
+ provider,
12114
+ request,
12115
+ upstream,
12116
+ upstreamUrl
12117
+ });
12118
+ if (upstream.status >= 400) {
12119
+ logWarn(
12120
+ "sandbox_egress_upstream_error_response",
12121
+ {},
12122
+ {
12123
+ ...attributes(upstream.status, upstream),
12124
+ "error.type": `http_${upstream.status}`
12125
+ },
12126
+ `Sandbox egress upstream returned HTTP ${upstream.status}`
12127
+ );
12128
+ }
12129
+ if (upstream.status === UPSTREAM_TOKEN_REJECTION_STATUS || upstream.status === UPSTREAM_PERMISSION_REJECTION_STATUS) {
12130
+ logWarn(
12131
+ "sandbox_egress_upstream_auth_rejected",
12132
+ {},
12133
+ {
12134
+ ...attributes(upstream.status, upstream),
12135
+ ...upstream.status === UPSTREAM_TOKEN_REJECTION_STATUS ? {
12136
+ "app.sandbox.egress.www_authenticate": upstream.headers.get("www-authenticate") ?? void 0
12137
+ } : {}
12138
+ },
12139
+ upstream.status === UPSTREAM_TOKEN_REJECTION_STATUS ? "Sandbox egress upstream auth rejected injected credential" : "Sandbox egress upstream permission denied"
12140
+ );
12141
+ if (upstream.status === UPSTREAM_TOKEN_REJECTION_STATUS) {
12142
+ await clearCredentialLease(provider, lease.grant.name, credentialContext);
12143
+ await recordAuthRequired({
12144
+ credentialContext,
12145
+ provider,
12146
+ grant: lease.grant,
12147
+ authorization: lease.authorization,
12148
+ message: `Provider rejected the injected ${provider} credential.`
12149
+ });
12150
+ await upstream.body?.cancel().catch(() => void 0);
12151
+ return authRequiredResponse({
12152
+ provider,
12153
+ grant: lease.grant,
12154
+ message: `Provider rejected the injected ${provider} credential.
12155
+ `
12156
+ });
12157
+ } else {
12158
+ await clearCredentialLease(provider, lease.grant.name, credentialContext);
12159
+ await recordPermissionDenied({
12160
+ credentialContext,
12161
+ provider,
12162
+ lease,
12163
+ message: permissionDeniedMessage(provider, lease.grant),
12164
+ upstream,
12165
+ upstreamUrl
12166
+ });
12167
+ }
12168
+ }
12169
+ return new Response(upstream.body, {
12170
+ status: upstream.status,
12171
+ statusText: upstream.statusText,
12172
+ headers: responseHeaders(upstream)
12173
+ });
12174
+ }
12175
+
12176
+ // src/chat/egress/plugin.ts
12177
+ function credentialContextForPluginEgress(credentialContext) {
12178
+ return {
12179
+ credentials: credentialContext,
12180
+ contextId: randomUUID4(),
12181
+ egressId: `plugin-egress:${randomUUID4()}`,
12182
+ expiresAtMs: Date.now() + 30 * 60 * 1e3
12183
+ };
12184
+ }
12185
+ function createPluginEgress(deps) {
12186
+ const credentialContext = deps.credentialContext ? credentialContextForPluginEgress(deps.credentialContext) : void 0;
12187
+ return {
12188
+ async fetch(input) {
12189
+ const operation = input.operation.trim();
12190
+ if (!operation) {
12191
+ throw new Error("Plugin egress operation is required");
12192
+ }
12193
+ if (!credentialContext) {
12194
+ throw new PluginCredentialFailureError(
12195
+ input.provider,
12196
+ `Cannot issue ${input.provider} credentials without a credential context.`
12197
+ );
12198
+ }
12199
+ const upstreamUrl = new URL(input.request.url);
12200
+ if (upstreamUrl.protocol !== "https:") {
12201
+ throw new Error("Plugin egress requires HTTPS provider URLs");
12202
+ }
12203
+ const resolvedProvider = resolveSandboxEgressProviderForHost(
12204
+ upstreamUrl.hostname
12205
+ );
12206
+ if (resolvedProvider !== input.provider) {
12207
+ throw new Error(
12208
+ `Plugin egress provider "${input.provider}" does not own ${upstreamUrl.hostname}`
12209
+ );
12210
+ }
12211
+ return await executeCredentialedEgressRequest({
12212
+ activeEgressId: credentialContext.egressId,
12213
+ credentialContext,
12214
+ deps: {
12215
+ ...deps.fetch ? { fetch: deps.fetch } : {},
12216
+ recordAuthRequired: async (signal) => {
12217
+ await deps.pluginAuth.handleAuthRequired({
12218
+ ...signal.authorization ? { authorization: signal.authorization } : {},
12219
+ grant: signal.grant,
12220
+ kind: signal.kind ?? "auth_required",
12221
+ message: signal.message,
12222
+ provider: signal.provider
12223
+ });
12224
+ },
12225
+ recordPermissionDenied: async () => {
12226
+ },
12227
+ tracePropagation: {}
12228
+ },
12229
+ operation,
12230
+ provider: input.provider,
12231
+ request: input.request,
12232
+ upstreamUrl
12233
+ });
12234
+ }
12235
+ };
12236
+ }
12237
+
11215
12238
  // src/chat/respond.ts
11216
12239
  var AGENT_ABORT_SETTLE_GRACE_MS = 5e3;
11217
12240
  function sleep3(ms) {
@@ -11824,6 +12847,23 @@ async function generateAssistantReplyInPrivacyContext(messageText2, context, con
11824
12847
  userText: userInput,
11825
12848
  artifactState: context.artifactState,
11826
12849
  configuration: configurationValues,
12850
+ egress: createPluginEgress({
12851
+ credentialContext: context.credentialContext,
12852
+ pluginAuth: {
12853
+ async handleAuthRequired(signal) {
12854
+ await pluginAuth.maybeHandleAuthSignal({
12855
+ auth_required: {
12856
+ ...signal.authorization ? { authorization: signal.authorization } : {},
12857
+ createdAtMs: Date.now(),
12858
+ grant: signal.grant,
12859
+ kind: signal.kind,
12860
+ message: signal.message,
12861
+ provider: signal.provider
12862
+ }
12863
+ });
12864
+ }
12865
+ }
12866
+ }),
11827
12867
  mcpToolManager: turnMcpToolManager,
11828
12868
  sandbox,
11829
12869
  surface,
@@ -13175,7 +14215,6 @@ export {
13175
14215
  setConfigDefaults,
13176
14216
  getConfigDefaults,
13177
14217
  createUserTokenStore,
13178
- issueProviderCredentialLease,
13179
14218
  listThreadReplies,
13180
14219
  postSlackMessage,
13181
14220
  deleteSlackMessage,
@@ -13190,14 +14229,8 @@ export {
13190
14229
  startOAuthFlow,
13191
14230
  SANDBOX_EGRESS_PROXY_PATH,
13192
14231
  parseSandboxEgressCredentialToken,
13193
- setSandboxEgressCredentialLease,
13194
- getSandboxEgressCredentialLease,
13195
- clearSandboxEgressCredentialLease,
13196
- setSandboxEgressAuthRequiredSignal,
13197
- setSandboxEgressPermissionDeniedSignal,
13198
14232
  normalizeSandboxEgressTracePropagationDomains,
13199
14233
  shouldPropagateSandboxEgressTrace,
13200
- matchesSandboxEgressDomain,
13201
14234
  resolveSandboxEgressProviderForHost,
13202
14235
  shouldEmitDevAgentTrace,
13203
14236
  AuthorizationFlowDisabledError,
@@ -13228,6 +14261,8 @@ export {
13228
14261
  markTurnFailed,
13229
14262
  isRedundantReactionAckText,
13230
14263
  finalizeMcpAuthorization,
14264
+ resolvePluginOAuthAccount,
14265
+ executeCredentialedEgressRequest,
13231
14266
  generateAssistantReply,
13232
14267
  estimateTextTokens,
13233
14268
  getAgentContextCompactionTriggerTokens,