@apifuse/provider-sdk 2.2.0-beta.58 → 2.2.0-beta.60

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/AUTHORING.md +25 -0
  2. package/CHANGELOG.md +8 -0
  3. package/README.md +18 -2
  4. package/bin/apifuse-dev.ts +2 -1
  5. package/bin/apifuse-pack-types.ts +49 -0
  6. package/bin/apifuse-perf.ts +41 -4
  7. package/bin/apifuse-record.ts +9 -2
  8. package/dist/cli/templates/provider/README.md.tpl +15 -3
  9. package/dist/config/loader.d.ts +7 -0
  10. package/dist/config/loader.js +43 -18
  11. package/dist/declaration-validation.d.ts +2 -1
  12. package/dist/declaration-validation.js +39 -0
  13. package/dist/define.d.ts +4 -4
  14. package/dist/define.js +25 -1
  15. package/dist/engine.d.ts +16 -0
  16. package/dist/engine.js +59 -3
  17. package/dist/error-resolution.js +13 -0
  18. package/dist/index.d.ts +7 -4
  19. package/dist/index.js +4 -2
  20. package/dist/lint.js +38 -17
  21. package/dist/provider.d.ts +3 -3
  22. package/dist/provider.js +2 -2
  23. package/dist/runtime/bounded-expiring-map.d.ts +21 -0
  24. package/dist/runtime/bounded-expiring-map.js +64 -0
  25. package/dist/runtime/browser.js +2 -1
  26. package/dist/runtime/cache.d.ts +2 -0
  27. package/dist/runtime/cache.js +11 -3
  28. package/dist/runtime/diagnostic-env-values.d.ts +5 -0
  29. package/dist/runtime/diagnostic-env-values.js +57 -0
  30. package/dist/runtime/diagnostic-env.d.ts +11 -0
  31. package/dist/runtime/diagnostic-env.js +35 -0
  32. package/dist/runtime/diagnostic-redactor.d.ts +49 -0
  33. package/dist/runtime/diagnostic-redactor.js +569 -0
  34. package/dist/runtime/egress-lease.d.ts +65 -0
  35. package/dist/runtime/egress-lease.js +236 -0
  36. package/dist/runtime/env.js +2 -1
  37. package/dist/runtime/http.js +1 -0
  38. package/dist/runtime/instrumentation.js +3 -0
  39. package/dist/runtime/ocr.js +2 -1
  40. package/dist/runtime/otlp.js +5 -4
  41. package/dist/runtime/proxy-nodemaven.js +3 -2
  42. package/dist/runtime/proxy-retry-policy.d.ts +4 -1
  43. package/dist/runtime/proxy-retry-policy.js +11 -2
  44. package/dist/runtime/request-options.d.ts +7 -0
  45. package/dist/runtime/request-options.js +62 -22
  46. package/dist/runtime/request-telemetry.d.ts +7 -9
  47. package/dist/runtime/resolver-config.d.ts +7 -0
  48. package/dist/runtime/resolver-config.js +8 -0
  49. package/dist/runtime/resolver-public.d.ts +3 -1
  50. package/dist/runtime/resolver-public.js +1 -1
  51. package/dist/runtime/resolver-shared.d.ts +9 -0
  52. package/dist/runtime/resolver-shared.js +57 -0
  53. package/dist/runtime/resolver-telemetry.d.ts +134 -0
  54. package/dist/runtime/resolver-telemetry.js +204 -0
  55. package/dist/runtime/resolver-usage.d.ts +24 -0
  56. package/dist/runtime/resolver-usage.js +60 -0
  57. package/dist/runtime/resolver-vendors/bindings.d.ts +5 -0
  58. package/dist/runtime/resolver-vendors/bindings.js +5 -0
  59. package/dist/runtime/resolver-vendors/browser.js +0 -1
  60. package/dist/runtime/resolver-vendors/capsolver.d.ts +2 -2
  61. package/dist/runtime/resolver-vendors/capsolver.js +21 -9
  62. package/dist/runtime/resolver-vendors/hypersolutions.d.ts +27 -0
  63. package/dist/runtime/resolver-vendors/hypersolutions.js +369 -0
  64. package/dist/runtime/resolver-vendors/twocaptcha.d.ts +2 -2
  65. package/dist/runtime/resolver-vendors/twocaptcha.js +26 -10
  66. package/dist/runtime/resolver-vendors/types.d.ts +35 -5
  67. package/dist/runtime/resolver-vendors/types.js +20 -10
  68. package/dist/runtime/resolver.d.ts +5 -1
  69. package/dist/runtime/resolver.js +400 -111
  70. package/dist/runtime/stealth-akamai-sbsd.d.ts +54 -0
  71. package/dist/runtime/stealth-akamai-sbsd.js +142 -0
  72. package/dist/runtime/stealth.js +547 -53
  73. package/dist/runtime/stt.js +3 -2
  74. package/dist/runtime/trace.d.ts +5 -0
  75. package/dist/runtime/trace.js +115 -20
  76. package/dist/schema.d.ts +20 -0
  77. package/dist/schema.js +72 -2
  78. package/dist/server/index.d.ts +2 -0
  79. package/dist/server/index.js +1 -0
  80. package/dist/server/self-test-redaction.js +8 -33
  81. package/dist/server/self-test.d.ts +4 -0
  82. package/dist/server/self-test.js +10 -1
  83. package/dist/server/sensitive-values.d.ts +26 -0
  84. package/dist/server/sensitive-values.js +131 -0
  85. package/dist/server/serve-implementation.d.ts +7 -1
  86. package/dist/server/serve-implementation.js +394 -125
  87. package/dist/server/trace-output.d.ts +2 -1
  88. package/dist/server/trace-output.js +46 -14
  89. package/dist/server/types.d.ts +6 -0
  90. package/dist/server/types.js +14 -0
  91. package/dist/stealth/profiles.d.ts +7 -1
  92. package/dist/stealth/profiles.js +9 -0
  93. package/dist/testing/run.js +6 -0
  94. package/dist/trace-sanitization.d.ts +4 -3
  95. package/dist/trace-sanitization.js +45 -22
  96. package/dist/types.d.ts +106 -10
  97. package/package.json +2 -1
  98. package/src/cli/templates/provider/README.md.tpl +15 -3
  99. package/src/config/loader.ts +47 -18
  100. package/src/declaration-validation.ts +47 -1
  101. package/src/define.ts +45 -3
  102. package/src/engine.ts +78 -3
  103. package/src/error-resolution.ts +13 -0
  104. package/src/index.ts +34 -1
  105. package/src/lint.ts +44 -18
  106. package/src/provider.ts +10 -0
  107. package/src/runtime/bounded-expiring-map.ts +67 -0
  108. package/src/runtime/browser.ts +2 -1
  109. package/src/runtime/cache.ts +13 -3
  110. package/src/runtime/diagnostic-env-values.ts +56 -0
  111. package/src/runtime/diagnostic-env.ts +55 -0
  112. package/src/runtime/diagnostic-redactor.ts +662 -0
  113. package/src/runtime/egress-lease.ts +341 -0
  114. package/src/runtime/env.ts +2 -1
  115. package/src/runtime/http.ts +1 -0
  116. package/src/runtime/instrumentation.ts +3 -0
  117. package/src/runtime/ocr.ts +2 -1
  118. package/src/runtime/otlp.ts +5 -4
  119. package/src/runtime/proxy-nodemaven.ts +3 -2
  120. package/src/runtime/proxy-retry-policy.ts +12 -1
  121. package/src/runtime/request-options.ts +76 -28
  122. package/src/runtime/request-telemetry.ts +5 -7
  123. package/src/runtime/resolver-config.ts +12 -0
  124. package/src/runtime/resolver-public.ts +21 -0
  125. package/src/runtime/resolver-shared.ts +75 -0
  126. package/src/runtime/resolver-telemetry.ts +362 -0
  127. package/src/runtime/resolver-usage.ts +96 -0
  128. package/src/runtime/resolver-vendors/bindings.ts +5 -0
  129. package/src/runtime/resolver-vendors/browser.ts +0 -1
  130. package/src/runtime/resolver-vendors/capsolver.ts +22 -3
  131. package/src/runtime/resolver-vendors/hypersolutions.ts +480 -0
  132. package/src/runtime/resolver-vendors/twocaptcha.ts +26 -12
  133. package/src/runtime/resolver-vendors/types.ts +60 -12
  134. package/src/runtime/resolver.ts +468 -126
  135. package/src/runtime/stealth-akamai-sbsd.ts +207 -0
  136. package/src/runtime/stealth.ts +727 -95
  137. package/src/runtime/stt.ts +3 -2
  138. package/src/runtime/trace.ts +168 -26
  139. package/src/schema.ts +80 -2
  140. package/src/server/index.ts +21 -0
  141. package/src/server/self-test-redaction.ts +10 -40
  142. package/src/server/self-test.ts +13 -0
  143. package/src/server/sensitive-values.ts +200 -0
  144. package/src/server/serve-implementation.ts +561 -136
  145. package/src/server/trace-output.ts +77 -17
  146. package/src/server/types.ts +14 -0
  147. package/src/stealth/profiles.ts +10 -0
  148. package/src/testing/run.ts +6 -0
  149. package/src/trace-sanitization.ts +70 -23
  150. package/src/types.ts +124 -19
@@ -1,5 +1,7 @@
1
- import { createHash } from "node:crypto";
1
+ import { STATUS_CODES } from "node:http";
2
+ import { readDiagnosticEnv, withDiagnosticEnv } from "../runtime/diagnostic-env.js";
2
3
  import { AsyncLocalStorage } from "node:async_hooks";
4
+ import { createHash } from "node:crypto";
3
5
  import { existsSync } from "node:fs";
4
6
  import { createRequire } from "node:module";
5
7
  import { join } from "node:path";
@@ -7,41 +9,45 @@ import { Hono } from "hono";
7
9
  import { z } from "zod";
8
10
  import { AuthAbortError, createAuthFlowHelpers } from "../auth.js";
9
11
  import { validateFailClosedDeclaration } from "../declaration-validation.js";
10
- import { safeProviderErrorObservability } from "../error-observability.js";
11
12
  import { createInProcessProviderEngine, ENGINE_OWNED_PROXY_CREDENTIAL_ENV_NAMES, isEngineOwnedEnvName, readEngineProxyCredentials, } from "../engine.js";
13
+ import { safeProviderErrorObservability } from "../error-observability.js";
12
14
  import { SDK_OWNED_PROVIDER_ERROR_CODES, SDK_RUNTIME_OWNED_ERROR_CODES, SDK_STATUS_MAPPED_PROVIDER_ERROR_CODES, } from "../error-resolution.js";
13
- import { AuthError, isProviderError, isSessionExpiredError, isTransportError, isValidationError, ProviderError, } from "../errors.js";
15
+ import { AuthError, isProviderError, isSessionExpiredError, isTransportError, isValidationError, ProviderError, SDKError, } from "../errors.js";
14
16
  import { sanitizeDiagnosticText } from "../fixture-sanitization.js";
15
17
  import { loadProviderLocaleCatalogs, localizeAuthTurn, } from "../i18n/catalog.js";
16
- import { categoryForStatus, sourceForCategory, isRetryableCategory, PROVIDER_OBSERVABILITY_TAXONOMY_VERSION, } from "../observability.js";
18
+ import { categoryForStatus, isRetryableCategory, PROVIDER_OBSERVABILITY_TAXONOMY_VERSION, sourceForCategory, } from "../observability.js";
17
19
  import { createScratchpad } from "../runtime/auth-flow.js";
18
20
  import { createProviderCache } from "../runtime/cache.js";
19
21
  import { createProviderChoiceContext, PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV, } from "../runtime/choice.js";
20
22
  import { createCredentialContext } from "../runtime/credential.js";
23
+ import { bindDiagnosticSensitiveRegistry, compileProcessDiagnosticSensitiveValues, createDiagnosticRedactor, REDACTION_FAILED, isDiagnosticRedactionSuppressed, bindCriticalDiagnosticRedactor, redactCriticalDiagnosticText, redactDiagnosticText, registerDiagnosticSensitiveValues, } from "../runtime/diagnostic-redactor.js";
24
+ import { createCeremonyEgressLeaseRuntime, ENGINE_CEREMONY_EGRESS_LEASE, } from "../runtime/egress-lease.js";
21
25
  import { createEnvContext } from "../runtime/env.js";
22
26
  import { executeOperation } from "../runtime/executor.js";
23
27
  import { createHttpClient } from "../runtime/http.js";
24
28
  import { wrapWithInstrumentation } from "../runtime/instrumentation.js";
25
- import { getProviderBaseUrl } from "../runtime/provider.js";
26
29
  import { createOcrClientFromEnv } from "../runtime/ocr.js";
30
+ import { getProviderBaseUrl } from "../runtime/provider.js";
27
31
  import { PROXY_AUTH_IP_DENIED_CODE, PROXY_EDGE_AUTH_REJECTED_CODE, PROXY_POOL_EXHAUSTED_CODE, } from "../runtime/proxy-errors.js";
28
32
  import { PROVIDER_TELEMETRY_HEADER, ProxyTelemetryCollector, } from "../runtime/proxy-telemetry.js";
29
33
  import { closedEnum, RequestTelemetry, tenantOpaqueKeys, } from "../runtime/request-telemetry.js";
30
- import { createUnsupportedResolverClient, } from "../runtime/resolver-shared.js";
34
+ import { bindResolverTelemetry, createUnsupportedResolverClient, inheritResolverTelemetryBinding, } from "../runtime/resolver-shared.js";
35
+ import { ResolverTelemetryCollector, } from "../runtime/resolver-telemetry.js";
31
36
  import { assertRequiredSecretsPresent, listMissingRequiredSecrets, MISSING_SECRET_CODE, } from "../runtime/secrets.js";
32
37
  import { createProviderRuntimeStateFromEnv, createUnsupportedProviderRuntimeState, } from "../runtime/state.js";
33
38
  import { StealthCookieJar } from "../runtime/stealth-cookies.js";
34
39
  import { createSttClientFromEnv } from "../runtime/stt.js";
35
- import { createTraceContext, updateTraceContextExportMetadata, } from "../runtime/trace.js";
40
+ import { createTraceContext, getTraceRecorder, updateTraceContextExportMetadata, } from "../runtime/trace.js";
36
41
  import { resolveTraceConfigFromEnv } from "../runtime/trace-config.js";
37
42
  import { parseSchema } from "../schema.js";
38
- import { STATEFUL_NONCE_HEADER as STATEFUL_FORWARDING_NONCE_HEADER, STATEFUL_SIGNATURE_HEADER as STATEFUL_FORWARDING_SIGNATURE_HEADER, STATEFUL_TIMESTAMP_HEADER as STATEFUL_FORWARDING_TIMESTAMP_HEADER, verifyStatefulRequestSignature, } from "../stateful-signing.js";
39
43
  import { StatefulRoutingDeadlineError } from "../stateful/errors.js";
44
+ import { STATEFUL_NONCE_HEADER as STATEFUL_FORWARDING_NONCE_HEADER, STATEFUL_SIGNATURE_HEADER as STATEFUL_FORWARDING_SIGNATURE_HEADER, STATEFUL_TIMESTAMP_HEADER as STATEFUL_FORWARDING_TIMESTAMP_HEADER, verifyStatefulRequestSignature, } from "../stateful-signing.js";
40
45
  import { getStealthProfile } from "../stealth/profiles.js";
41
- import { sanitizeTraceAttributes } from "../trace-sanitization.js";
42
46
  import { APIFUSE_STREAM_DONE_EVENT, APIFUSE_STREAM_ERROR_EVENT, encodeSseEvent, error as streamError, } from "../stream.js";
47
+ import { sanitizeSpanNameForOutput, sanitizeTraceAttributes } from "../trace-sanitization.js";
43
48
  import { VALID_OPERATION_ERROR_STATUSES } from "../types.js";
44
49
  import { resolveSelfTestMasterSecrets } from "./self-test-token.js";
50
+ import { collectStaticDiagnosticSensitiveValues, createDiagnosticEnvObserver, } from "./sensitive-values.js";
45
51
  import { resolveServerTraceContextOptions } from "./trace-output.js";
46
52
  import { AuthFlowRequestSchema, OperationConnectionSchema, OperationRequestSchema, } from "./types.js";
47
53
  const DEFAULT_HOST = "0.0.0.0";
@@ -268,6 +274,9 @@ function createLazyStealthClient(onCleanupError, ...createArgs) {
268
274
  async fetch(...args) {
269
275
  return (await getSession()).fetch(...args);
270
276
  },
277
+ async replayChallenged(...args) {
278
+ return (await getSession()).replayChallenged(...args);
279
+ },
271
280
  redirects: {
272
281
  async run(...args) {
273
282
  return (await getSession()).redirects.run(...args);
@@ -304,13 +313,15 @@ function createLazyStealthClient(onCleanupError, ...createArgs) {
304
313
  function bindResolverSignalWithoutRuntime(resolver, defaultSignal) {
305
314
  if (!defaultSignal)
306
315
  return resolver;
307
- return {
316
+ const boundResolver = {
308
317
  solve(challenge, signal = defaultSignal, traceRecorder) {
309
318
  // Forward the instrumentation trace recorder so resolver.vendor.* spans
310
319
  // survive this wrapper. See ResolverSolveWithRecorder in resolver-shared.ts.
311
320
  return resolver.solve(challenge, signal, traceRecorder);
312
321
  },
313
322
  };
323
+ inheritResolverTelemetryBinding(resolver, boundResolver);
324
+ return boundResolver;
314
325
  }
315
326
  function getProviderStealthBaseUrl(provider) {
316
327
  const baseUrl = getProviderBaseUrl(provider);
@@ -323,6 +334,73 @@ function getProviderStealthBaseUrl(provider) {
323
334
  function getProviderStealthProfile(provider) {
324
335
  return provider.stealth ? getStealthProfile(provider.stealth) : undefined;
325
336
  }
337
+ /** One option set for ctx.resolver and the automatic SBSD solve, so both share identity scope, cache, proxy intent, and telemetry. */
338
+ function createResolverRuntimeOptions(provider, cache, identityScope, proxyPolicy, proxyClientOptions, stealthProfile, telemetry) {
339
+ return {
340
+ allowedHosts: provider.allowedHosts,
341
+ cache,
342
+ telemetry,
343
+ identityScope,
344
+ ...(proxyPolicy
345
+ ? {
346
+ proxyIntent: {
347
+ mode: proxyPolicy.mode,
348
+ ...proxyClientOptions,
349
+ ...(stealthProfile ? { userAgent: stealthProfile.userAgent } : {}),
350
+ },
351
+ }
352
+ : {}),
353
+ };
354
+ }
355
+ /** Shared predicate for the SBSD challenge runtime and the ceremony egress lease. */
356
+ function declaresAkamaiSbsdResolver(provider) {
357
+ return provider.resolver?.kinds.some((kind) => kind === "akamai_sbsd") === true;
358
+ }
359
+ function createStealthChallengeDetection(provider, resolverRuntime, resolverOverride, resolverOptions, trace, signal) {
360
+ const resolverDeclared = declaresAkamaiSbsdResolver(provider);
361
+ const detectOnly = provider.stealth?.challengeDetection?.akamaiSbsd === true;
362
+ if (!resolverDeclared && !detectOnly) {
363
+ return undefined;
364
+ }
365
+ return {
366
+ akamaiSbsd: {
367
+ allowedHosts: provider.allowedHosts ?? [],
368
+ // The profile belongs to the SBSD resolver; a resolver declared for other kinds
369
+ // may name a different browser without affecting detect-only classification.
370
+ ...(resolverDeclared && provider.resolver?.clientProfile
371
+ ? { clientProfile: provider.resolver.clientProfile }
372
+ : {}),
373
+ ...(resolverDeclared && resolverRuntime
374
+ ? {
375
+ async solve(challenge, transport, solveSignal) {
376
+ const resolver = resolverRuntime.bindResolverSignal((resolverOverride
377
+ ? bindResolverTelemetry(resolverOverride, resolverOptions.telemetry)
378
+ : undefined) ??
379
+ resolverRuntime.createResolverClientFromEnv(provider.resolver, undefined, {
380
+ allowedHosts: resolverOptions.allowedHosts,
381
+ cache: resolverOptions.cache,
382
+ telemetry: resolverOptions.telemetry,
383
+ identityScope: resolverOptions.identityScope,
384
+ // No proxyIntent: the transport is already bound to the initiating
385
+ // request's lease, and a second identity resolution would allocate
386
+ // again (and fail closed without allocator credentials) for nothing
387
+ // the SBSD adapters consume.
388
+ createTransport: () => transport,
389
+ }), signal);
390
+ // Same span and recorder threading as the instrumented ctx.resolver.solve.
391
+ const recorder = getTraceRecorder(trace);
392
+ const solve = () => resolver.solve(challenge, solveSignal, recorder ?? undefined);
393
+ return recorder
394
+ ? recorder.runSpan("resolver.solve", solve, {
395
+ attributes: { challenge_kind: challenge.kind },
396
+ })
397
+ : solve();
398
+ },
399
+ }
400
+ : {}),
401
+ },
402
+ };
403
+ }
326
404
  function isProductionProviderBrowserMode(provider, env = process.env) {
327
405
  if (provider.runtime !== "browser") {
328
406
  return false;
@@ -424,17 +502,29 @@ function createProviderContext(provider, request, operationId, options, state =
424
502
  engineCredentials: engineProxyCredentials,
425
503
  };
426
504
  const resolverIdentityScope = resolveProviderResolverIdentityScope(provider, proxyClientOptions.affinityKey, request.requestId);
505
+ const cache = createProviderCache({ providerId: provider.id });
427
506
  let wrappedContext;
507
+ const { capabilityModules } = options;
508
+ const resolverOptions = createResolverRuntimeOptions(provider, cache, resolverIdentityScope, proxyPolicy, proxyClientOptions, stealthProfile, scope.resolverTelemetry);
509
+ const challengeRuntime = createStealthChallengeDetection(provider, capabilityModules.resolver, options.resolver, resolverOptions, scope.trace, signal);
428
510
  const stealthClientOptions = {
429
511
  upstream: proxyClientOptions.upstream,
430
512
  affinityKey: proxyClientOptions.affinityKey,
431
513
  telemetry: scope.telemetry.proxy,
432
514
  engineCredentials: engineProxyCredentials,
433
515
  ...(signal ? { signal } : {}),
434
- ...(provider.stealth ? { stealth: provider.stealth } : {}),
516
+ // A declared resolver without a `stealth` block still gets the default Chrome client
517
+ // (auth FlowContext is not capability-gated), so the runtime must ride along.
518
+ ...(provider.stealth || challengeRuntime
519
+ ? {
520
+ stealth: {
521
+ ...(provider.stealth ?? {}),
522
+ ...(challengeRuntime ? { challengeRuntime } : {}),
523
+ },
524
+ }
525
+ : {}),
435
526
  };
436
- const { capabilityModules } = options;
437
- const logStealthCleanupError = (error) => logProviderCleanupError(options.logger, provider, "operation", operationId, request.requestId, "stealth", error);
527
+ const logStealthCleanupError = (error) => logProviderCleanupError(options.logger, provider, "operation", operationId, request.requestId, "stealth", error, scope.redact);
438
528
  const env = createEnvContext([
439
529
  ...providerSecretNames(provider),
440
530
  PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV,
@@ -450,7 +540,6 @@ function createProviderContext(provider, request, operationId, options, state =
450
540
  headers: request.headers ?? {},
451
541
  };
452
542
  const requestState = state.forConnection(requestContext.connectionId);
453
- const cache = createProviderCache({ providerId: provider.id });
454
543
  const bindings = {
455
544
  env,
456
545
  credential,
@@ -475,7 +564,7 @@ function createProviderContext(provider, request, operationId, options, state =
475
564
  browser: provider.runtime === "browser"
476
565
  ? capabilityModules.browser.createBrowserClient({
477
566
  allowedHosts: provider.allowedHosts,
478
- cdpUrl: process.env.APIFUSE__CDP_POOL__URL,
567
+ cdpUrl: readDiagnosticEnv("APIFUSE__CDP_POOL__URL"),
479
568
  headless: true,
480
569
  requireCdpPool: isProductionProviderBrowserMode(provider),
481
570
  stealth: true,
@@ -499,22 +588,13 @@ function createProviderContext(provider, request, operationId, options, state =
499
588
  ocr: options.ocr ?? createOcrClientFromEnv(provider.ocr),
500
589
  stt: options.stt ?? createSttClientFromEnv(provider.stt),
501
590
  resolver: capabilityModules.resolver
502
- ? capabilityModules.resolver.bindResolverSignal(options.resolver ??
503
- capabilityModules.resolver.createResolverClientFromEnv(provider.resolver, undefined, {
504
- allowedHosts: provider.allowedHosts,
505
- cache,
506
- identityScope: resolverIdentityScope,
507
- ...(proxyPolicy
508
- ? {
509
- proxyIntent: {
510
- mode: proxyPolicy.mode,
511
- ...proxyClientOptions,
512
- ...(stealthProfile ? { userAgent: stealthProfile.userAgent } : {}),
513
- },
514
- }
515
- : {}),
516
- }), signal)
517
- : bindResolverSignalWithoutRuntime(options.resolver ??
591
+ ? capabilityModules.resolver.bindResolverSignal((options.resolver
592
+ ? bindResolverTelemetry(options.resolver, resolverOptions.telemetry)
593
+ : undefined) ??
594
+ capabilityModules.resolver.createResolverClientFromEnv(provider.resolver, undefined, resolverOptions), signal)
595
+ : bindResolverSignalWithoutRuntime((options.resolver
596
+ ? bindResolverTelemetry(options.resolver, resolverOptions.telemetry)
597
+ : undefined) ??
518
598
  createUnsupportedResolverClient("Provider does not declare resolver capability"), signal),
519
599
  choice: createProviderChoiceContext({
520
600
  providerId: provider.id,
@@ -564,7 +644,7 @@ export function resolveAuthFlowProxyAffinityKey(provider, request) {
564
644
  request.providerId ??
565
645
  provider.id);
566
646
  }
567
- function createAuthFlowContext(provider, request, options, state, scope, signal) {
647
+ function createAuthFlowContext(provider, request, route, options, state, scope, signal) {
568
648
  const baseUrl = getProviderBaseUrl(provider);
569
649
  const stealthBaseUrl = getProviderStealthBaseUrl(provider);
570
650
  const stealthProfile = getProviderStealthProfile(provider);
@@ -578,17 +658,51 @@ function createAuthFlowContext(provider, request, options, state, scope, signal)
578
658
  telemetry: scope.telemetry.proxy,
579
659
  engineCredentials: engineProxyCredentials,
580
660
  };
661
+ // The lease pins the proxy egress a challenged ceremony solved on, so only providers
662
+ // that declare the akamai_sbsd resolver kind carry one (same predicate as the challenge
663
+ // runtime). `abort` never evaluates the key or tenant: a user must be able to cancel a
664
+ // stranded flow even when engine provisioning is broken, and an abort is not a
665
+ // continuation of the ceremony the lease pins.
666
+ const ceremonyEgressLease = route !== "abort" &&
667
+ declaresAkamaiSbsdResolver(provider) &&
668
+ proxyPolicy &&
669
+ proxyPolicy.mode !== "disabled"
670
+ ? createCeremonyEgressLeaseRuntime({
671
+ tenantId: request.tenantId,
672
+ providerId: provider.id,
673
+ flowId: request.flowId,
674
+ affinityKey: proxyClientOptions.affinityKey,
675
+ onHandle: (handle) => registerDiagnosticSensitiveValues(scope.trace, [handle]),
676
+ ...(request.engine?.egressLease ? { handle: request.engine.egressLease } : {}),
677
+ })
678
+ : undefined;
679
+ if (route !== "abort" && request.engine?.egressLease && !ceremonyEgressLease) {
680
+ throw new SDKError("An egress lease was supplied for an auth flow that does not use a ceremony lease", { code: "EGRESS_LEASE_INVALID" });
681
+ }
581
682
  const resolverIdentityScope = resolveProviderResolverIdentityScope(provider, proxyClientOptions.affinityKey, request.requestId);
683
+ const cache = createProviderCache({ providerId: provider.id });
684
+ const { capabilityModules } = options;
685
+ const resolverOptions = createResolverRuntimeOptions(provider, cache, resolverIdentityScope, proxyPolicy, proxyClientOptions, stealthProfile, scope.resolverTelemetry);
686
+ const challengeRuntime = createStealthChallengeDetection(provider, capabilityModules.resolver, options.resolver, resolverOptions, scope.trace, signal);
582
687
  const stealthClientOptions = {
583
688
  upstream: proxyClientOptions.upstream,
584
689
  affinityKey: proxyClientOptions.affinityKey,
585
690
  telemetry: scope.telemetry.proxy,
586
691
  engineCredentials: engineProxyCredentials,
692
+ ...(ceremonyEgressLease ? { [ENGINE_CEREMONY_EGRESS_LEASE]: ceremonyEgressLease } : {}),
587
693
  ...(signal ? { signal } : {}),
588
- ...(provider.stealth ? { stealth: provider.stealth } : {}),
694
+ // A declared resolver without a `stealth` block still gets the default Chrome client
695
+ // (auth FlowContext is not capability-gated), so the runtime must ride along.
696
+ ...(provider.stealth || challengeRuntime
697
+ ? {
698
+ stealth: {
699
+ ...(provider.stealth ?? {}),
700
+ ...(challengeRuntime ? { challengeRuntime } : {}),
701
+ },
702
+ }
703
+ : {}),
589
704
  };
590
- const { capabilityModules } = options;
591
- const logStealthCleanupError = (error) => logProviderCleanupError(options.logger, provider, "auth", "flow", request.requestId, "stealth", error);
705
+ const logStealthCleanupError = (error) => logProviderCleanupError(options.logger, provider, "auth", "flow", request.requestId, "stealth", error, scope.redact);
592
706
  const credential = request.connection
593
707
  ? createCredentialContext({
594
708
  allowedKeys: provider.credential?.keys,
@@ -597,7 +711,6 @@ function createAuthFlowContext(provider, request, options, state, scope, signal)
597
711
  values: request.connection.secrets,
598
712
  })
599
713
  : undefined;
600
- const cache = createProviderCache({ providerId: provider.id });
601
714
  const context = wrapWithInstrumentation({
602
715
  flowId: request.flowId,
603
716
  connectionId: resolveOperationConnectionId(request),
@@ -636,28 +749,23 @@ function createAuthFlowContext(provider, request, options, state, scope, signal)
636
749
  ocr: options.ocr ?? createOcrClientFromEnv(provider.ocr),
637
750
  stt: options.stt ?? createSttClientFromEnv(provider.stt),
638
751
  resolver: capabilityModules.resolver
639
- ? capabilityModules.resolver.bindResolverSignal(options.resolver ??
640
- capabilityModules.resolver.createResolverClientFromEnv(provider.resolver, undefined, {
641
- allowedHosts: provider.allowedHosts,
642
- cache,
643
- identityScope: resolverIdentityScope,
644
- ...(proxyPolicy
645
- ? {
646
- proxyIntent: {
647
- mode: proxyPolicy.mode,
648
- ...proxyClientOptions,
649
- ...(stealthProfile ? { userAgent: stealthProfile.userAgent } : {}),
650
- },
651
- }
652
- : {}),
653
- }), signal)
654
- : bindResolverSignalWithoutRuntime(options.resolver ??
752
+ ? capabilityModules.resolver.bindResolverSignal((options.resolver
753
+ ? bindResolverTelemetry(options.resolver, resolverOptions.telemetry)
754
+ : undefined) ??
755
+ capabilityModules.resolver.createResolverClientFromEnv(provider.resolver, undefined, resolverOptions), signal)
756
+ : bindResolverSignalWithoutRuntime((options.resolver
757
+ ? bindResolverTelemetry(options.resolver, resolverOptions.telemetry)
758
+ : undefined) ??
655
759
  createUnsupportedResolverClient("Provider does not declare resolver capability"), signal),
656
760
  auth: createAuthFlowHelpers({ signal }),
657
761
  });
658
762
  return {
659
763
  context,
660
764
  getPatch: flowContextStore.getPatch,
765
+ getEngineState() {
766
+ const egressLease = ceremonyEgressLease?.handle();
767
+ return egressLease ? { egressLease } : undefined;
768
+ },
661
769
  };
662
770
  }
663
771
  const defaultProviderServerLogger = (event) => {
@@ -683,11 +791,11 @@ function finishRequestCost(input) {
683
791
  cpuTotalMicros: Math.max(0, cpuDelta.user + cpuDelta.system),
684
792
  };
685
793
  }
686
- function zodDetails(error) {
794
+ function zodDetails(error, redact) {
687
795
  return error.issues.map((issue) => ({
688
- path: issue.path.join("."),
689
- code: issue.code,
690
- message: issue.message,
796
+ path: sanitizeSpanNameForOutput(issue.path.join("."), redact),
797
+ code: sanitizeSpanNameForOutput(issue.code, redact),
798
+ message: sanitizeSpanNameForOutput(issue.message, redact),
691
799
  }));
692
800
  }
693
801
  // Category-level projection with code-aware honesty overrides: a missing
@@ -710,7 +818,7 @@ function publicErrorSource(error, category) {
710
818
  }
711
819
  return sourceForCategory(category);
712
820
  }
713
- function toErrorResponse(error, requestId, observabilityDetails) {
821
+ function toErrorResponse(error, requestId, observabilityDetails, redact) {
714
822
  const observability = observabilityDetails;
715
823
  const source = publicErrorSource(error, observability.category);
716
824
  if (error instanceof StatefulRoutingDeadlineError) {
@@ -746,7 +854,7 @@ function toErrorResponse(error, requestId, observabilityDetails) {
746
854
  ...(requestId ? { requestId } : {}),
747
855
  retryable: observability.retryable,
748
856
  source,
749
- details: zodDetails(error),
857
+ details: zodDetails(error, redact),
750
858
  },
751
859
  };
752
860
  }
@@ -994,13 +1102,13 @@ function extractRequestId(raw) {
994
1102
  }
995
1103
  const MAX_PROVIDER_ERROR_CAUSE_FRAMES = 5;
996
1104
  const MAX_PROVIDER_ERROR_CAUSE_MESSAGE_LENGTH = 300;
997
- function providerErrorCauseMessage(message) {
998
- const sanitized = sanitizeDiagnosticText(message);
1105
+ function providerErrorCauseMessage(message, redact) {
1106
+ const sanitized = sanitizeDiagnosticText(redactDiagnosticText(message, redact));
999
1107
  return sanitized.length > MAX_PROVIDER_ERROR_CAUSE_MESSAGE_LENGTH
1000
1108
  ? `${sanitized.slice(0, MAX_PROVIDER_ERROR_CAUSE_MESSAGE_LENGTH)}… [truncated]`
1001
1109
  : sanitized;
1002
1110
  }
1003
- function providerErrorCauseChain(error) {
1111
+ function providerErrorCauseChain(error, redact) {
1004
1112
  if (!(error instanceof Error) && !isProviderError(error))
1005
1113
  return undefined;
1006
1114
  const seen = new Set([error]);
@@ -1014,9 +1122,9 @@ function providerErrorCauseChain(error) {
1014
1122
  const providerObservability = safeProviderErrorObservability(cause);
1015
1123
  const causeCode = providerErrorCode(cause);
1016
1124
  frames.push({
1017
- errorClass: cause.name,
1018
- ...(causeCode !== undefined ? { code: causeCode } : {}),
1019
- message: providerErrorCauseMessage(message),
1125
+ errorClass: redactDiagnosticText(cause.name, redact),
1126
+ ...(causeCode !== undefined ? { code: redactDiagnosticText(causeCode, redact) } : {}),
1127
+ message: providerErrorCauseMessage(message, redact),
1020
1128
  messageLength: message.length,
1021
1129
  messageFingerprint: createHash("sha256").update(message).digest("hex").slice(0, 12),
1022
1130
  ...(providerObservability ? { providerObservability } : {}),
@@ -1025,7 +1133,7 @@ function providerErrorCauseChain(error) {
1025
1133
  }
1026
1134
  return frames.length > 0 ? frames : undefined;
1027
1135
  }
1028
- function logProviderError(logger, provider, kind, route, requestId, error, status, cost, declaredErrorCode, telemetry, observabilityDetails, correlation = {}) {
1136
+ function logProviderError(logger, provider, kind, route, requestId, error, status, cost, declaredErrorCode, telemetry, observabilityDetails, correlation = {}, redact) {
1029
1137
  const providerCode = isProviderError(error) ? providerErrorCode(error) : undefined;
1030
1138
  const code = isProviderError(error)
1031
1139
  ? (providerCode ?? "provider_error")
@@ -1036,7 +1144,7 @@ function logProviderError(logger, provider, kind, route, requestId, error, statu
1036
1144
  : "internal_error";
1037
1145
  const errorClass = error instanceof Error ? error.name : typeof error;
1038
1146
  const message = error instanceof Error ? error.message : String(error);
1039
- const causeChain = providerErrorCauseChain(error);
1147
+ const causeChain = providerErrorCauseChain(error, redact);
1040
1148
  const details = observabilityDetails;
1041
1149
  const isUnregisteredProviderErrorCode = status === 500 &&
1042
1150
  isProviderError(error) &&
@@ -1055,24 +1163,28 @@ function logProviderError(logger, provider, kind, route, requestId, error, statu
1055
1163
  emit({
1056
1164
  level: status >= 500 ? "error" : "warn",
1057
1165
  event: "provider_request_failed",
1058
- providerId: provider.id,
1166
+ providerId: redactDiagnosticText(provider.id, redact),
1059
1167
  kind,
1060
- route,
1061
- ...(requestId ? { requestId } : {}),
1168
+ route: redactCriticalDiagnosticText(route, redact),
1169
+ ...(requestId ? { requestId: redactCriticalDiagnosticText(requestId, redact) } : {}),
1062
1170
  ...(correlation.connectionId !== undefined
1063
- ? { connectionId: correlation.connectionId }
1171
+ ? { connectionId: redactDiagnosticText(correlation.connectionId, redact) }
1172
+ : {}),
1173
+ ...(correlation.flowId !== undefined
1174
+ ? { flowId: redactDiagnosticText(correlation.flowId, redact) }
1175
+ : {}),
1176
+ ...(correlation.tenantId !== undefined
1177
+ ? { tenantId: redactDiagnosticText(correlation.tenantId, redact) }
1064
1178
  : {}),
1065
- ...(correlation.flowId !== undefined ? { flowId: correlation.flowId } : {}),
1066
- ...(correlation.tenantId !== undefined ? { tenantId: correlation.tenantId } : {}),
1067
1179
  ...(correlation.requestedProviderId !== undefined
1068
- ? { requestedProviderId: correlation.requestedProviderId }
1180
+ ? { requestedProviderId: redactDiagnosticText(correlation.requestedProviderId, redact) }
1069
1181
  : {}),
1070
1182
  status,
1071
1183
  ...cost,
1072
1184
  ...(telemetryPayload ?? {}),
1073
- code,
1074
- errorClass,
1075
- message,
1185
+ code: sanitizeSpanNameForOutput(redactCriticalDiagnosticText(code, redact)),
1186
+ errorClass: redactDiagnosticText(errorClass, redact),
1187
+ message: sanitizeDiagnosticText(redactDiagnosticText(message, redact)),
1076
1188
  ...(causeChain ? { causeChain } : {}),
1077
1189
  ...(providerObservability ? { providerObservability } : {}),
1078
1190
  ...(details.upstreamStatus ? { upstreamStatus: details.upstreamStatus } : {}),
@@ -1085,42 +1197,46 @@ function logProviderError(logger, provider, kind, route, requestId, error, statu
1085
1197
  signalFix: "Declare this code (with status and retryable) in the operation's errorCodes so it serves its intended status instead of 500.",
1086
1198
  }
1087
1199
  : {}),
1088
- ...(error instanceof z.ZodError ? { issues: zodDetails(error) } : {}),
1200
+ ...(error instanceof z.ZodError ? { issues: zodDetails(error, redact) } : {}),
1089
1201
  });
1090
1202
  }
1091
- function logProviderCleanupError(logger, provider, kind, operationId, requestId, resource, error) {
1203
+ function logProviderCleanupError(logger, provider, kind, operationId, requestId, resource, error, redact) {
1092
1204
  const emit = typeof logger === "function" ? logger : defaultProviderServerLogger;
1093
1205
  const errorClass = error instanceof Error ? error.name : typeof error;
1094
1206
  const message = error instanceof Error ? error.message : String(error);
1095
1207
  emit({
1096
1208
  level: "warn",
1097
1209
  event: "provider_cleanup_failed",
1098
- providerId: provider.id,
1210
+ providerId: redactDiagnosticText(provider.id, redact),
1099
1211
  kind,
1100
- route: operationId,
1101
- ...(requestId ? { requestId } : {}),
1212
+ route: redactCriticalDiagnosticText(operationId, redact),
1213
+ ...(requestId ? { requestId: redactCriticalDiagnosticText(requestId, redact) } : {}),
1102
1214
  resource,
1103
- errorClass,
1104
- message,
1215
+ errorClass: redactDiagnosticText(errorClass, redact),
1216
+ message: sanitizeDiagnosticText(redactDiagnosticText(message, redact)),
1105
1217
  });
1106
1218
  }
1107
- function logProviderSuccess(logger, provider, kind, route, requestId, status, cost, telemetry, correlation = {}) {
1219
+ function logProviderSuccess(logger, provider, kind, route, requestId, status, cost, telemetry, correlation = {}, redact) {
1108
1220
  const telemetryPayload = telemetry?.toLogPayload();
1109
1221
  const emit = typeof logger === "function" ? logger : defaultProviderServerLogger;
1110
1222
  emit({
1111
1223
  level: "info",
1112
1224
  event: "provider_request_completed",
1113
- providerId: provider.id,
1225
+ providerId: redactDiagnosticText(provider.id, redact),
1114
1226
  kind,
1115
- route,
1116
- ...(requestId ? { requestId } : {}),
1227
+ route: redactCriticalDiagnosticText(route, redact),
1228
+ ...(requestId ? { requestId: redactCriticalDiagnosticText(requestId, redact) } : {}),
1117
1229
  ...(correlation.connectionId !== undefined
1118
- ? { connectionId: correlation.connectionId }
1230
+ ? { connectionId: redactDiagnosticText(correlation.connectionId, redact) }
1231
+ : {}),
1232
+ ...(correlation.flowId !== undefined
1233
+ ? { flowId: redactDiagnosticText(correlation.flowId, redact) }
1234
+ : {}),
1235
+ ...(correlation.tenantId !== undefined
1236
+ ? { tenantId: redactDiagnosticText(correlation.tenantId, redact) }
1119
1237
  : {}),
1120
- ...(correlation.flowId !== undefined ? { flowId: correlation.flowId } : {}),
1121
- ...(correlation.tenantId !== undefined ? { tenantId: correlation.tenantId } : {}),
1122
1238
  ...(correlation.requestedProviderId !== undefined
1123
- ? { requestedProviderId: correlation.requestedProviderId }
1239
+ ? { requestedProviderId: redactDiagnosticText(correlation.requestedProviderId, redact) }
1124
1240
  : {}),
1125
1241
  status,
1126
1242
  ...cost,
@@ -1173,9 +1289,106 @@ function requestTraceId(headers, requestId) {
1173
1289
  ? createHash("sha256").update(requestId, "utf8").digest("hex").slice(0, 32)
1174
1290
  : undefined));
1175
1291
  }
1292
+ /** Harvest only credential subtrees, iteratively and before schema diagnostics. */
1293
+ function rawRequestCredentials(rawBody, kind) {
1294
+ const record = (value) => value !== null && typeof value === "object" ? value : undefined;
1295
+ const own = (value, key) => {
1296
+ const object = record(value);
1297
+ return object && Object.hasOwn(object, key) ? object[key] : undefined;
1298
+ };
1299
+ const body = kind === "stateful" ? own(rawBody, "operationRequest") : rawBody;
1300
+ const values = [];
1301
+ let remainingBytes = 64 * 1024;
1302
+ let exhausted = false;
1303
+ const add = (value) => {
1304
+ if (typeof value !== "string")
1305
+ return;
1306
+ const bytes = Buffer.byteLength(value, "utf8");
1307
+ if (bytes > remainingBytes) {
1308
+ exhausted = true;
1309
+ return;
1310
+ }
1311
+ remainingBytes -= bytes;
1312
+ if (value)
1313
+ values.push(value);
1314
+ const trimmed = value.trim();
1315
+ if (trimmed && trimmed !== value)
1316
+ values.push(trimmed);
1317
+ };
1318
+ function* entries(object) {
1319
+ for (const key in object) {
1320
+ if (Object.hasOwn(object, key))
1321
+ yield [key, object[key]];
1322
+ }
1323
+ }
1324
+ const visit = (root) => {
1325
+ const object = record(root);
1326
+ if (!object) {
1327
+ add(root);
1328
+ return;
1329
+ }
1330
+ // Connection inventories are usually flat. Walk the root directly and only
1331
+ // allocate iterator frames when a nested credential container is encountered.
1332
+ for (const key in object) {
1333
+ if (!Object.hasOwn(object, key))
1334
+ continue;
1335
+ const value = object[key];
1336
+ add(key);
1337
+ if (exhausted)
1338
+ break;
1339
+ const child = record(value);
1340
+ if (!child) {
1341
+ add(value);
1342
+ if (exhausted)
1343
+ break;
1344
+ continue;
1345
+ }
1346
+ const stack = [{ iterator: entries(child), depth: 1 }];
1347
+ while (stack.length && !exhausted) {
1348
+ const frame = stack[stack.length - 1];
1349
+ const entry = frame.iterator.next();
1350
+ if (entry.done) {
1351
+ stack.pop();
1352
+ continue;
1353
+ }
1354
+ const [key, value] = entry.value;
1355
+ add(key);
1356
+ if (exhausted)
1357
+ break;
1358
+ const child = record(value);
1359
+ if (child) {
1360
+ if (frame.depth >= 8) {
1361
+ exhausted = true;
1362
+ break;
1363
+ }
1364
+ stack.push({ iterator: entries(child), depth: frame.depth + 1 });
1365
+ }
1366
+ else
1367
+ add(value);
1368
+ }
1369
+ if (exhausted)
1370
+ break;
1371
+ }
1372
+ };
1373
+ visit(own(own(body, "connection"), "secrets"));
1374
+ if (kind === "auth" && !exhausted) {
1375
+ add(own(own(body, "engine"), "egressLease"));
1376
+ if (!exhausted)
1377
+ visit(own(body, "input"));
1378
+ }
1379
+ return { values, exhausted };
1380
+ }
1176
1381
  function createRequestScope(input) {
1382
+ const sensitiveRegistry = createDiagnosticRedactor([], input.staticSensitiveValues);
1383
+ const redact = (text) => redactDiagnosticText(text, sensitiveRegistry.redact);
1384
+ bindCriticalDiagnosticRedactor(redact, sensitiveRegistry.redactCritical, sensitiveRegistry.redactStructured, () => sensitiveRegistry.suppressed);
1177
1385
  const requestCost = startRequestCost();
1178
- const traceConfig = resolveTraceConfigFromEnv();
1386
+ const observeEnv = {
1387
+ observe: createDiagnosticEnvObserver(input.provider, sensitiveRegistry.add, sensitiveRegistry.has),
1388
+ finished: false,
1389
+ staticValues: input.staticSensitiveValues,
1390
+ };
1391
+ const traceConfig = withDiagnosticEnv(observeEnv, () => resolveTraceConfigFromEnv());
1179
1392
  const details = {
1180
1393
  route: input.route,
1181
1394
  requestId: input.requestId,
@@ -1194,13 +1407,17 @@ function createRequestScope(input) {
1194
1407
  const initialTraceId = requestTraceId(input.headers, input.requestId);
1195
1408
  const trace = traceConfig
1196
1409
  ? createTraceContext({
1197
- ...resolveServerTraceContextOptions(traceConfig, traceAttributes),
1410
+ ...withDiagnosticEnv(observeEnv, () => resolveServerTraceContextOptions(traceConfig, traceAttributes, undefined, redact)),
1411
+ redact,
1198
1412
  ...(initialTraceId ? { traceId: initialTraceId } : {}),
1199
1413
  })
1200
- : createTraceContext();
1414
+ : createTraceContext({ redact });
1415
+ bindDiagnosticSensitiveRegistry(trace, sensitiveRegistry);
1201
1416
  const proxyCollector = new ProxyTelemetryCollector();
1417
+ const resolverCollector = new ResolverTelemetryCollector({ redact });
1202
1418
  const telemetry = new RequestTelemetry(trace);
1203
1419
  telemetry.register(proxyCollector);
1420
+ telemetry.register(resolverCollector);
1204
1421
  let rootRunner = (fn) => fn();
1205
1422
  let resolveRoot;
1206
1423
  const rootTerminal = new Promise((resolve) => {
@@ -1221,13 +1438,13 @@ function createRequestScope(input) {
1221
1438
  return clientCancelledError();
1222
1439
  return undefined;
1223
1440
  };
1224
- const root = trace.span(`request:${input.kind}:${input.route}`, async () => {
1441
+ const root = withDiagnosticEnv(observeEnv, () => trace.span(`request:${input.kind}:${input.route}`, async () => {
1225
1442
  rootRunner = AsyncLocalStorage.snapshot();
1226
1443
  const outcome = await rootTerminal;
1227
1444
  const error = terminalError(outcome);
1228
1445
  if (error !== undefined)
1229
1446
  throw error;
1230
- });
1447
+ }));
1231
1448
  void root.catch(() => undefined);
1232
1449
  const settleRoot = () => {
1233
1450
  if (!terminalOutcome || streamingSetupPending || rootSettled)
@@ -1263,7 +1480,7 @@ function createRequestScope(input) {
1263
1480
  traceAttributes.flow_id = details.flowId;
1264
1481
  traceAttributes.route = details.route;
1265
1482
  const traceId = requestTraceId(details.headers, details.requestId);
1266
- const sanitizedAttributes = Object.fromEntries(Object.entries(sanitizeTraceAttributes(traceAttributes)).map(([key, value]) => [
1483
+ const sanitizedAttributes = Object.fromEntries(Object.entries(sanitizeTraceAttributes(traceAttributes, redact)).map(([key, value]) => [
1267
1484
  key,
1268
1485
  String(value),
1269
1486
  ]));
@@ -1273,8 +1490,17 @@ function createRequestScope(input) {
1273
1490
  });
1274
1491
  };
1275
1492
  const scope = {
1493
+ redact,
1276
1494
  trace,
1277
1495
  telemetry,
1496
+ resolverTelemetry: resolverCollector,
1497
+ seedCredentials(rawBody, kind) {
1498
+ const harvested = rawRequestCredentials(rawBody, kind);
1499
+ sensitiveRegistry.add(harvested.values);
1500
+ // An incomplete inventory cannot certify free text: emit less.
1501
+ if (harvested.exhausted)
1502
+ sensitiveRegistry.suppress();
1503
+ },
1278
1504
  enrich(enrichment) {
1279
1505
  if (terminalOutcome)
1280
1506
  return;
@@ -1336,21 +1562,21 @@ function createRequestScope(input) {
1336
1562
  terminalize(outcome) {
1337
1563
  if (finishedResult)
1338
1564
  return finishedResult;
1565
+ observeEnv.finished = true;
1339
1566
  terminalOutcome = outcome;
1340
1567
  const error = terminalError(outcome);
1341
1568
  finishedResult = {};
1342
1569
  try {
1343
1570
  const declaredErrorCode = error === undefined ? undefined : input.declaredErrorCode?.(error);
1344
- const status = outcome.status ??
1345
- (error === undefined ? 200 : toStatusCode(error, declaredErrorCode));
1571
+ const status = outcome.status ?? (error === undefined ? 200 : toStatusCode(error, declaredErrorCode));
1346
1572
  finishedResult = headerSnapshot(error);
1347
1573
  const cost = finishRequestCost(requestCost);
1348
1574
  try {
1349
1575
  if (error === undefined) {
1350
- logProviderSuccess(input.logger, input.provider, input.kind, details.route, details.requestId, status, cost, telemetry, details.correlation);
1576
+ logProviderSuccess(input.logger, input.provider, input.kind, details.route, details.requestId, status, cost, telemetry, details.correlation, redact);
1351
1577
  }
1352
1578
  else {
1353
- logProviderError(input.logger, input.provider, input.kind, details.route, details.requestId, error, status, cost, declaredErrorCode, telemetry, finishedResult.errorObservability, details.correlation);
1579
+ logProviderError(input.logger, input.provider, input.kind, details.route, details.requestId, error, status, cost, declaredErrorCode, telemetry, finishedResult.errorObservability, details.correlation, redact);
1354
1580
  }
1355
1581
  }
1356
1582
  catch {
@@ -1384,25 +1610,55 @@ function createRequestScope(input) {
1384
1610
  };
1385
1611
  return scope;
1386
1612
  }
1387
- function responseWithRequestScopeHeaders(response, finished) {
1613
+ // A raw `Response` result skips `toErrorResponse`. Its payload shape stays
1614
+ // provider-owned; known credentials in error payloads still need redaction.
1615
+ // The observability header is SDK-owned: derive it
1616
+ // from the status. Only timeouts, rate limits, and 5xx are retryable;
1617
+ // `isRetryableCategory` would otherwise mark every other 4xx (`upstream_http`)
1618
+ // as retryable.
1619
+ function rawResponseErrorObservability(status) {
1620
+ return {
1621
+ category: categoryForStatus(status),
1622
+ taxonomyVersion: PROVIDER_OBSERVABILITY_TAXONOMY_VERSION,
1623
+ retryable: status === 408 || status === 429 || status >= 500,
1624
+ };
1625
+ }
1626
+ // `rawStatusFallback` is decided by the caller from the outcome, not from a
1627
+ // missing snapshot: a failed outcome whose thrown value is `undefined` also has
1628
+ // no `errorObservability`, and its 500 carries the SDK envelope, not a raw body.
1629
+ async function responseWithRequestScopeHeaders(response, finished, rawStatusFallback, redact) {
1388
1630
  const headers = new Headers(response.headers);
1389
1631
  headers.delete(PROVIDER_TELEMETRY_HEADER);
1390
1632
  if (finished.providerTelemetryHeader) {
1391
1633
  headers.set(PROVIDER_TELEMETRY_HEADER, finished.providerTelemetryHeader);
1392
1634
  }
1393
- if (finished.errorObservability) {
1394
- headers.set(ERROR_OBSERVABILITY_HEADER, JSON.stringify(finished.errorObservability));
1395
- }
1396
- return new Response(response.body, {
1635
+ headers.delete(ERROR_OBSERVABILITY_HEADER);
1636
+ const errorObservability = finished.errorObservability ??
1637
+ (rawStatusFallback && response.status >= 400
1638
+ ? rawResponseErrorObservability(response.status)
1639
+ : undefined);
1640
+ if (errorObservability) {
1641
+ headers.set(ERROR_OBSERVABILITY_HEADER, JSON.stringify(errorObservability));
1642
+ }
1643
+ // Raw Response bodies are provider-owned. Preserve their stream and representation.
1644
+ const body = response.body;
1645
+ const statusText = rawStatusFallback && response.status >= 400
1646
+ ? redactDiagnosticText(response.statusText, redact)
1647
+ : response.statusText;
1648
+ return new Response(body, {
1397
1649
  headers,
1398
1650
  status: response.status,
1399
- statusText: response.statusText,
1651
+ // When free text cannot be certified, use the SDK's standard HTTP reason
1652
+ // phrase. It preserves the status contract without trusting provider text.
1653
+ statusText: statusText === REDACTION_FAILED && isDiagnosticRedactionSuppressed(redact)
1654
+ ? (STATUS_CODES[response.status] ?? "")
1655
+ : statusText,
1400
1656
  });
1401
1657
  }
1402
- function finalizeRequestResponse(scope, response, outcome) {
1658
+ async function finalizeRequestResponse(scope, response, outcome) {
1403
1659
  try {
1404
1660
  const error = outcome.kind === "failed" ? outcome.error : undefined;
1405
- const finalResponse = responseWithRequestScopeHeaders(response, scope.snapshotHeaders(error));
1661
+ const finalResponse = await responseWithRequestScopeHeaders(response, scope.snapshotHeaders(error), outcome.kind === "completed", scope.redact);
1406
1662
  scope.terminalize(outcome);
1407
1663
  return finalResponse;
1408
1664
  }
@@ -1580,7 +1836,7 @@ function toSseResponse(operation, result, lifecycle, requestId) {
1580
1836
  }
1581
1837
  catch (error) {
1582
1838
  const message = error instanceof Error ? error.message : "Stream failed";
1583
- controller.enqueue(encoder.encode(encodeSseEvent(streamError("stream_error", message, {
1839
+ controller.enqueue(encoder.encode(encodeSseEvent(streamError("stream_error", sanitizeDiagnosticText(redactDiagnosticText(message, lifecycle.redact)), {
1584
1840
  ...(requestId ? { requestId } : {}),
1585
1841
  }))));
1586
1842
  controller.close();
@@ -1679,7 +1935,7 @@ function getSseTransport(operation) {
1679
1935
  function getHttpStreamTransport(operation) {
1680
1936
  return operation.transport?.kind === "http-stream" ? operation.transport : undefined;
1681
1937
  }
1682
- function toAuthFlowResponse(result, contextPatch) {
1938
+ function toAuthFlowResponse(result, contextPatch, engine) {
1683
1939
  if (result instanceof Response) {
1684
1940
  return result;
1685
1941
  }
@@ -1689,6 +1945,7 @@ function toAuthFlowResponse(result, contextPatch) {
1689
1945
  return {
1690
1946
  data: result,
1691
1947
  ...(contextPatch ? { contextPatch } : {}),
1948
+ ...(engine ? { engine } : {}),
1692
1949
  };
1693
1950
  }
1694
1951
  function authFlowLocaleFromHeaders(headers) {
@@ -1759,7 +2016,7 @@ async function handleOperation(provider, request, operationId, options, state =
1759
2016
  ctx.stealth.close?.();
1760
2017
  }
1761
2018
  catch (error) {
1762
- logProviderCleanupError(options.logger, provider, "operation", operationId, request.requestId, "stealth", error);
2019
+ logProviderCleanupError(options.logger, provider, "operation", operationId, request.requestId, "stealth", error, scope.redact);
1763
2020
  }
1764
2021
  }
1765
2022
  if ("browser" in ctx) {
@@ -1767,11 +2024,12 @@ async function handleOperation(provider, request, operationId, options, state =
1767
2024
  await ctx.browser.close?.();
1768
2025
  }
1769
2026
  catch (error) {
1770
- logProviderCleanupError(options.logger, provider, "operation", operationId, request.requestId, "browser", error);
2027
+ logProviderCleanupError(options.logger, provider, "operation", operationId, request.requestId, "browser", error, scope.redact);
1771
2028
  }
1772
2029
  }
1773
2030
  };
1774
2031
  const streamLifecycle = {
2032
+ redact: scope.redact,
1775
2033
  runStep(fn) {
1776
2034
  return scope.runStreamStep(fn);
1777
2035
  },
@@ -1823,8 +2081,9 @@ async function handleAuthFlow(provider, request, route, options, state, scope, s
1823
2081
  // depend on declared secrets (client ids/secrets), so fail structured before
1824
2082
  // any flow code runs instead of at whatever point the ceremony first reads
1825
2083
  // the env. `abort` stays exempt: a user must always be able to cancel a
1826
- // stranded flow even when provisioning is broken.
1827
- const { context, getPatch } = createAuthFlowContext(provider, request, options, state, scope, signal);
2084
+ // stranded flow even when provisioning is broken (createAuthFlowContext
2085
+ // applies the same exemption to the engine ceremony lease).
2086
+ const { context, getPatch, getEngineState } = createAuthFlowContext(provider, request, route, options, state, scope, signal);
1828
2087
  try {
1829
2088
  if (route !== "abort") {
1830
2089
  assertRequiredSecretsPresent(provider, context.env);
@@ -1855,11 +2114,11 @@ async function handleAuthFlow(provider, request, route, options, state, scope, s
1855
2114
  isAuthTurn(result)
1856
2115
  ? materializeAuthFlowTurn(provider, request, result)
1857
2116
  : result;
1858
- return toAuthFlowResponse(materializedResult, getPatch());
2117
+ return toAuthFlowResponse(materializedResult, getPatch(), getEngineState());
1859
2118
  }
1860
2119
  catch (error) {
1861
2120
  if (error instanceof AuthAbortError) {
1862
- return toAuthFlowResponse(error.turn, getPatch());
2121
+ return toAuthFlowResponse(error.turn, getPatch(), getEngineState());
1863
2122
  }
1864
2123
  throw error;
1865
2124
  }
@@ -1868,7 +2127,7 @@ async function handleAuthFlow(provider, request, route, options, state, scope, s
1868
2127
  context.stealth.close?.();
1869
2128
  }
1870
2129
  catch (error) {
1871
- logProviderCleanupError(options.logger, provider, "auth", route, request.requestId, "stealth", error);
2130
+ logProviderCleanupError(options.logger, provider, "auth", route, request.requestId, "stealth", error, scope.redact);
1872
2131
  }
1873
2132
  }
1874
2133
  }
@@ -1974,13 +2233,13 @@ function operationRequestFromForwardingEnvelope(envelope) {
1974
2233
  ...(envelope.deadlineAt !== undefined ? { deadlineAt: envelope.deadlineAt } : {}),
1975
2234
  };
1976
2235
  }
1977
- function parseStatefulForwardingEnvelope(rawBody) {
2236
+ function parseStatefulForwardingEnvelope(rawBody, redact) {
1978
2237
  const parsed = ProviderServerStatefulForwardEnvelopeSchema.safeParse(rawBody);
1979
2238
  if (parsed.success)
1980
2239
  return parsed.data;
1981
2240
  throw new ProviderError("Stateful forwarding envelope is invalid.", {
1982
2241
  code: "STATEFUL_FORWARDING_ENVELOPE_INVALID",
1983
- details: zodDetails(parsed.error),
2242
+ details: zodDetails(parsed.error, redact),
1984
2243
  });
1985
2244
  }
1986
2245
  /**
@@ -2014,6 +2273,10 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
2014
2273
  engine: serverOptions.engine ?? createInProcessProviderEngine(),
2015
2274
  };
2016
2275
  const app = new Hono();
2276
+ // Compile the startup inventory once, composed with the shared outside-context fallback.
2277
+ const staticSensitiveValues = compileProcessDiagnosticSensitiveValues(collectStaticDiagnosticSensitiveValues(provider, {
2278
+ statefulForwardingSecret: options.statefulForwarding?.secret,
2279
+ }));
2017
2280
  const logger = options.logger ?? defaultProviderServerLogger;
2018
2281
  const operationErrorCodes = buildOperationErrorCodeLookup(provider);
2019
2282
  const statefulForwardingReplayCache = new StatefulForwardingReplayCache(options.statefulForwarding?.replayCacheMaxEntries ??
@@ -2054,6 +2317,7 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
2054
2317
  const operation = "stateful-internal";
2055
2318
  const requestScope = createRequestScope({
2056
2319
  provider,
2320
+ staticSensitiveValues,
2057
2321
  kind: "operation",
2058
2322
  route: operation,
2059
2323
  headers: Object.fromEntries(c.req.raw.headers.entries()),
@@ -2084,7 +2348,8 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
2084
2348
  code: "STATEFUL_FORWARDING_ENVELOPE_INVALID",
2085
2349
  });
2086
2350
  }
2087
- const envelope = parseStatefulForwardingEnvelope(rawBody);
2351
+ requestScope.seedCredentials(rawBody, "stateful");
2352
+ const envelope = parseStatefulForwardingEnvelope(rawBody, requestScope.redact);
2088
2353
  if (envelope.providerId !== provider.id) {
2089
2354
  throw new ProviderError("Stateful forwarding envelope providerId does not match the served provider.", { code: "STATEFUL_FORWARDING_PROVIDER_MISMATCH" });
2090
2355
  }
@@ -2185,7 +2450,7 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
2185
2450
  ...(requestId ? { requestId } : {}),
2186
2451
  });
2187
2452
  const observabilityDetails = errorObservabilityDetails(error, declaredErrorCode);
2188
- const response = c.json(toErrorResponse(error, requestId, observabilityDetails), status);
2453
+ const response = c.json(toErrorResponse(error, requestId, observabilityDetails, requestScope.redact), status);
2189
2454
  return finalizeRequestResponse(requestScope, response, {
2190
2455
  kind: "failed",
2191
2456
  status,
@@ -2198,6 +2463,7 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
2198
2463
  const operation = c.req.param("operation");
2199
2464
  const requestScope = createRequestScope({
2200
2465
  provider,
2466
+ staticSensitiveValues,
2201
2467
  kind: "operation",
2202
2468
  route: operation,
2203
2469
  operationId: operation,
@@ -2211,6 +2477,7 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
2211
2477
  .clone()
2212
2478
  .json()
2213
2479
  .catch(() => undefined);
2480
+ requestScope.seedCredentials(rawBody, "operation");
2214
2481
  const body = OperationRequestSchema.parse(rawBody);
2215
2482
  const requestHeaders = Object.fromEntries(c.req.raw.headers.entries());
2216
2483
  body.headers = { ...requestHeaders, ...body.headers };
@@ -2228,7 +2495,7 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
2228
2495
  const handled = await handleOperation(provider, body, operation, options, state, requestScope, c.req.raw.signal);
2229
2496
  const response = handled instanceof Response ? handled : c.json(handled);
2230
2497
  return streaming
2231
- ? responseWithRequestScopeHeaders(response, requestScope.snapshotHeaders())
2498
+ ? responseWithRequestScopeHeaders(response, requestScope.snapshotHeaders(), true, requestScope.redact)
2232
2499
  : response;
2233
2500
  };
2234
2501
  const response = streaming
@@ -2247,7 +2514,7 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
2247
2514
  const requestId = extractRequestId(rawBody);
2248
2515
  requestScope.enrich({ ...(requestId ? { requestId } : {}) });
2249
2516
  const observabilityDetails = errorObservabilityDetails(error, declaredErrorCode);
2250
- const response = c.json(toErrorResponse(error, requestId, observabilityDetails), status);
2517
+ const response = c.json(toErrorResponse(error, requestId, observabilityDetails, requestScope.redact), status);
2251
2518
  return finalizeRequestResponse(requestScope, response, {
2252
2519
  kind: "failed",
2253
2520
  status,
@@ -2267,6 +2534,7 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
2267
2534
  let rawBody;
2268
2535
  const requestScope = createRequestScope({
2269
2536
  provider,
2537
+ staticSensitiveValues,
2270
2538
  kind: "auth",
2271
2539
  route: logRoute,
2272
2540
  headers: Object.fromEntries(c.req.raw.headers.entries()),
@@ -2278,6 +2546,7 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
2278
2546
  .clone()
2279
2547
  .json()
2280
2548
  .catch(() => undefined);
2549
+ requestScope.seedCredentials(rawBody, "auth");
2281
2550
  const body = withAuthRequestHeaders(AuthFlowRequestSchema.parse(rawBody), c.req.raw.headers);
2282
2551
  requestScope.enrich({
2283
2552
  requestId: body.requestId,
@@ -2306,7 +2575,7 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
2306
2575
  const requestId = extractRequestId(rawBody);
2307
2576
  requestScope.enrich({ ...(requestId ? { requestId } : {}) });
2308
2577
  const observabilityDetails = errorObservabilityDetails(error);
2309
- const response = c.json(toErrorResponse(error, requestId, observabilityDetails), status);
2578
+ const response = c.json(toErrorResponse(error, requestId, observabilityDetails, requestScope.redact), status);
2310
2579
  return finalizeRequestResponse(requestScope, response, {
2311
2580
  kind: "failed",
2312
2581
  status,