@apifuse/provider-sdk 2.2.0-beta.59 → 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 (72) hide show
  1. package/AUTHORING.md +16 -0
  2. package/CHANGELOG.md +4 -0
  3. package/bin/apifuse-dev.ts +2 -1
  4. package/bin/apifuse-perf.ts +41 -4
  5. package/bin/apifuse-record.ts +3 -2
  6. package/dist/config/loader.js +13 -12
  7. package/dist/engine.js +3 -2
  8. package/dist/index.d.ts +2 -1
  9. package/dist/index.js +1 -1
  10. package/dist/lint.js +36 -15
  11. package/dist/provider.d.ts +1 -1
  12. package/dist/provider.js +1 -1
  13. package/dist/runtime/browser.js +2 -1
  14. package/dist/runtime/cache.js +3 -2
  15. package/dist/runtime/diagnostic-env-values.d.ts +5 -0
  16. package/dist/runtime/diagnostic-env-values.js +57 -0
  17. package/dist/runtime/diagnostic-env.d.ts +11 -0
  18. package/dist/runtime/diagnostic-env.js +35 -0
  19. package/dist/runtime/diagnostic-redactor.d.ts +49 -0
  20. package/dist/runtime/diagnostic-redactor.js +569 -0
  21. package/dist/runtime/egress-lease.d.ts +2 -0
  22. package/dist/runtime/egress-lease.js +4 -1
  23. package/dist/runtime/env.js +2 -1
  24. package/dist/runtime/instrumentation.js +3 -0
  25. package/dist/runtime/ocr.js +2 -1
  26. package/dist/runtime/otlp.js +5 -4
  27. package/dist/runtime/proxy-nodemaven.js +3 -2
  28. package/dist/runtime/request-options.d.ts +7 -0
  29. package/dist/runtime/request-options.js +62 -22
  30. package/dist/runtime/resolver-vendors/browser.js +0 -1
  31. package/dist/runtime/resolver.js +3 -2
  32. package/dist/runtime/stt.js +3 -2
  33. package/dist/runtime/trace.d.ts +5 -0
  34. package/dist/runtime/trace.js +115 -20
  35. package/dist/schema.d.ts +11 -0
  36. package/dist/schema.js +56 -2
  37. package/dist/server/self-test-redaction.js +8 -33
  38. package/dist/server/sensitive-values.d.ts +26 -0
  39. package/dist/server/sensitive-values.js +131 -0
  40. package/dist/server/serve-implementation.js +219 -78
  41. package/dist/server/trace-output.d.ts +2 -1
  42. package/dist/server/trace-output.js +46 -14
  43. package/dist/trace-sanitization.d.ts +4 -3
  44. package/dist/trace-sanitization.js +45 -22
  45. package/package.json +2 -1
  46. package/src/config/loader.ts +13 -12
  47. package/src/engine.ts +3 -2
  48. package/src/index.ts +2 -0
  49. package/src/lint.ts +42 -16
  50. package/src/provider.ts +1 -0
  51. package/src/runtime/browser.ts +2 -1
  52. package/src/runtime/cache.ts +3 -2
  53. package/src/runtime/diagnostic-env-values.ts +56 -0
  54. package/src/runtime/diagnostic-env.ts +55 -0
  55. package/src/runtime/diagnostic-redactor.ts +662 -0
  56. package/src/runtime/egress-lease.ts +6 -1
  57. package/src/runtime/env.ts +2 -1
  58. package/src/runtime/instrumentation.ts +3 -0
  59. package/src/runtime/ocr.ts +2 -1
  60. package/src/runtime/otlp.ts +5 -4
  61. package/src/runtime/proxy-nodemaven.ts +3 -2
  62. package/src/runtime/request-options.ts +76 -28
  63. package/src/runtime/resolver-vendors/browser.ts +0 -1
  64. package/src/runtime/resolver.ts +3 -2
  65. package/src/runtime/stt.ts +3 -2
  66. package/src/runtime/trace.ts +168 -26
  67. package/src/schema.ts +63 -2
  68. package/src/server/self-test-redaction.ts +10 -40
  69. package/src/server/sensitive-values.ts +200 -0
  70. package/src/server/serve-implementation.ts +301 -96
  71. package/src/server/trace-output.ts +77 -17
  72. package/src/trace-sanitization.ts +70 -23
@@ -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,29 +9,30 @@ 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
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";
21
24
  import { createCeremonyEgressLeaseRuntime, ENGINE_CEREMONY_EGRESS_LEASE, } from "../runtime/egress-lease.js";
22
25
  import { createEnvContext } from "../runtime/env.js";
23
26
  import { executeOperation } from "../runtime/executor.js";
24
27
  import { createHttpClient } from "../runtime/http.js";
25
28
  import { wrapWithInstrumentation } from "../runtime/instrumentation.js";
26
- import { getProviderBaseUrl } from "../runtime/provider.js";
27
29
  import { createOcrClientFromEnv } from "../runtime/ocr.js";
30
+ import { getProviderBaseUrl } from "../runtime/provider.js";
28
31
  import { PROXY_AUTH_IP_DENIED_CODE, PROXY_EDGE_AUTH_REJECTED_CODE, PROXY_POOL_EXHAUSTED_CODE, } from "../runtime/proxy-errors.js";
29
32
  import { PROVIDER_TELEMETRY_HEADER, ProxyTelemetryCollector, } from "../runtime/proxy-telemetry.js";
30
33
  import { closedEnum, RequestTelemetry, tenantOpaqueKeys, } from "../runtime/request-telemetry.js";
34
+ import { bindResolverTelemetry, createUnsupportedResolverClient, inheritResolverTelemetryBinding, } from "../runtime/resolver-shared.js";
31
35
  import { ResolverTelemetryCollector, } from "../runtime/resolver-telemetry.js";
32
- import { createUnsupportedResolverClient, bindResolverTelemetry, inheritResolverTelemetryBinding, } from "../runtime/resolver-shared.js";
33
36
  import { assertRequiredSecretsPresent, listMissingRequiredSecrets, MISSING_SECRET_CODE, } from "../runtime/secrets.js";
34
37
  import { createProviderRuntimeStateFromEnv, createUnsupportedProviderRuntimeState, } from "../runtime/state.js";
35
38
  import { StealthCookieJar } from "../runtime/stealth-cookies.js";
@@ -37,13 +40,14 @@ import { createSttClientFromEnv } from "../runtime/stt.js";
37
40
  import { createTraceContext, getTraceRecorder, updateTraceContextExportMetadata, } from "../runtime/trace.js";
38
41
  import { resolveTraceConfigFromEnv } from "../runtime/trace-config.js";
39
42
  import { parseSchema } from "../schema.js";
40
- 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";
41
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";
42
45
  import { getStealthProfile } from "../stealth/profiles.js";
43
- import { sanitizeTraceAttributes } from "../trace-sanitization.js";
44
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";
45
48
  import { VALID_OPERATION_ERROR_STATUSES } from "../types.js";
46
49
  import { resolveSelfTestMasterSecrets } from "./self-test-token.js";
50
+ import { collectStaticDiagnosticSensitiveValues, createDiagnosticEnvObserver, } from "./sensitive-values.js";
47
51
  import { resolveServerTraceContextOptions } from "./trace-output.js";
48
52
  import { AuthFlowRequestSchema, OperationConnectionSchema, OperationRequestSchema, } from "./types.js";
49
53
  const DEFAULT_HOST = "0.0.0.0";
@@ -520,7 +524,7 @@ function createProviderContext(provider, request, operationId, options, state =
520
524
  }
521
525
  : {}),
522
526
  };
523
- 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);
524
528
  const env = createEnvContext([
525
529
  ...providerSecretNames(provider),
526
530
  PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV,
@@ -560,7 +564,7 @@ function createProviderContext(provider, request, operationId, options, state =
560
564
  browser: provider.runtime === "browser"
561
565
  ? capabilityModules.browser.createBrowserClient({
562
566
  allowedHosts: provider.allowedHosts,
563
- cdpUrl: process.env.APIFUSE__CDP_POOL__URL,
567
+ cdpUrl: readDiagnosticEnv("APIFUSE__CDP_POOL__URL"),
564
568
  headless: true,
565
569
  requireCdpPool: isProductionProviderBrowserMode(provider),
566
570
  stealth: true,
@@ -668,6 +672,7 @@ function createAuthFlowContext(provider, request, route, options, state, scope,
668
672
  providerId: provider.id,
669
673
  flowId: request.flowId,
670
674
  affinityKey: proxyClientOptions.affinityKey,
675
+ onHandle: (handle) => registerDiagnosticSensitiveValues(scope.trace, [handle]),
671
676
  ...(request.engine?.egressLease ? { handle: request.engine.egressLease } : {}),
672
677
  })
673
678
  : undefined;
@@ -697,7 +702,7 @@ function createAuthFlowContext(provider, request, route, options, state, scope,
697
702
  }
698
703
  : {}),
699
704
  };
700
- 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);
701
706
  const credential = request.connection
702
707
  ? createCredentialContext({
703
708
  allowedKeys: provider.credential?.keys,
@@ -786,11 +791,11 @@ function finishRequestCost(input) {
786
791
  cpuTotalMicros: Math.max(0, cpuDelta.user + cpuDelta.system),
787
792
  };
788
793
  }
789
- function zodDetails(error) {
794
+ function zodDetails(error, redact) {
790
795
  return error.issues.map((issue) => ({
791
- path: issue.path.join("."),
792
- code: issue.code,
793
- message: issue.message,
796
+ path: sanitizeSpanNameForOutput(issue.path.join("."), redact),
797
+ code: sanitizeSpanNameForOutput(issue.code, redact),
798
+ message: sanitizeSpanNameForOutput(issue.message, redact),
794
799
  }));
795
800
  }
796
801
  // Category-level projection with code-aware honesty overrides: a missing
@@ -813,7 +818,7 @@ function publicErrorSource(error, category) {
813
818
  }
814
819
  return sourceForCategory(category);
815
820
  }
816
- function toErrorResponse(error, requestId, observabilityDetails) {
821
+ function toErrorResponse(error, requestId, observabilityDetails, redact) {
817
822
  const observability = observabilityDetails;
818
823
  const source = publicErrorSource(error, observability.category);
819
824
  if (error instanceof StatefulRoutingDeadlineError) {
@@ -849,7 +854,7 @@ function toErrorResponse(error, requestId, observabilityDetails) {
849
854
  ...(requestId ? { requestId } : {}),
850
855
  retryable: observability.retryable,
851
856
  source,
852
- details: zodDetails(error),
857
+ details: zodDetails(error, redact),
853
858
  },
854
859
  };
855
860
  }
@@ -1097,13 +1102,13 @@ function extractRequestId(raw) {
1097
1102
  }
1098
1103
  const MAX_PROVIDER_ERROR_CAUSE_FRAMES = 5;
1099
1104
  const MAX_PROVIDER_ERROR_CAUSE_MESSAGE_LENGTH = 300;
1100
- function providerErrorCauseMessage(message) {
1101
- const sanitized = sanitizeDiagnosticText(message);
1105
+ function providerErrorCauseMessage(message, redact) {
1106
+ const sanitized = sanitizeDiagnosticText(redactDiagnosticText(message, redact));
1102
1107
  return sanitized.length > MAX_PROVIDER_ERROR_CAUSE_MESSAGE_LENGTH
1103
1108
  ? `${sanitized.slice(0, MAX_PROVIDER_ERROR_CAUSE_MESSAGE_LENGTH)}… [truncated]`
1104
1109
  : sanitized;
1105
1110
  }
1106
- function providerErrorCauseChain(error) {
1111
+ function providerErrorCauseChain(error, redact) {
1107
1112
  if (!(error instanceof Error) && !isProviderError(error))
1108
1113
  return undefined;
1109
1114
  const seen = new Set([error]);
@@ -1117,9 +1122,9 @@ function providerErrorCauseChain(error) {
1117
1122
  const providerObservability = safeProviderErrorObservability(cause);
1118
1123
  const causeCode = providerErrorCode(cause);
1119
1124
  frames.push({
1120
- errorClass: cause.name,
1121
- ...(causeCode !== undefined ? { code: causeCode } : {}),
1122
- message: providerErrorCauseMessage(message),
1125
+ errorClass: redactDiagnosticText(cause.name, redact),
1126
+ ...(causeCode !== undefined ? { code: redactDiagnosticText(causeCode, redact) } : {}),
1127
+ message: providerErrorCauseMessage(message, redact),
1123
1128
  messageLength: message.length,
1124
1129
  messageFingerprint: createHash("sha256").update(message).digest("hex").slice(0, 12),
1125
1130
  ...(providerObservability ? { providerObservability } : {}),
@@ -1128,7 +1133,7 @@ function providerErrorCauseChain(error) {
1128
1133
  }
1129
1134
  return frames.length > 0 ? frames : undefined;
1130
1135
  }
1131
- 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) {
1132
1137
  const providerCode = isProviderError(error) ? providerErrorCode(error) : undefined;
1133
1138
  const code = isProviderError(error)
1134
1139
  ? (providerCode ?? "provider_error")
@@ -1139,7 +1144,7 @@ function logProviderError(logger, provider, kind, route, requestId, error, statu
1139
1144
  : "internal_error";
1140
1145
  const errorClass = error instanceof Error ? error.name : typeof error;
1141
1146
  const message = error instanceof Error ? error.message : String(error);
1142
- const causeChain = providerErrorCauseChain(error);
1147
+ const causeChain = providerErrorCauseChain(error, redact);
1143
1148
  const details = observabilityDetails;
1144
1149
  const isUnregisteredProviderErrorCode = status === 500 &&
1145
1150
  isProviderError(error) &&
@@ -1158,24 +1163,28 @@ function logProviderError(logger, provider, kind, route, requestId, error, statu
1158
1163
  emit({
1159
1164
  level: status >= 500 ? "error" : "warn",
1160
1165
  event: "provider_request_failed",
1161
- providerId: provider.id,
1166
+ providerId: redactDiagnosticText(provider.id, redact),
1162
1167
  kind,
1163
- route,
1164
- ...(requestId ? { requestId } : {}),
1168
+ route: redactCriticalDiagnosticText(route, redact),
1169
+ ...(requestId ? { requestId: redactCriticalDiagnosticText(requestId, redact) } : {}),
1165
1170
  ...(correlation.connectionId !== undefined
1166
- ? { 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) }
1167
1178
  : {}),
1168
- ...(correlation.flowId !== undefined ? { flowId: correlation.flowId } : {}),
1169
- ...(correlation.tenantId !== undefined ? { tenantId: correlation.tenantId } : {}),
1170
1179
  ...(correlation.requestedProviderId !== undefined
1171
- ? { requestedProviderId: correlation.requestedProviderId }
1180
+ ? { requestedProviderId: redactDiagnosticText(correlation.requestedProviderId, redact) }
1172
1181
  : {}),
1173
1182
  status,
1174
1183
  ...cost,
1175
1184
  ...(telemetryPayload ?? {}),
1176
- code,
1177
- errorClass,
1178
- message,
1185
+ code: sanitizeSpanNameForOutput(redactCriticalDiagnosticText(code, redact)),
1186
+ errorClass: redactDiagnosticText(errorClass, redact),
1187
+ message: sanitizeDiagnosticText(redactDiagnosticText(message, redact)),
1179
1188
  ...(causeChain ? { causeChain } : {}),
1180
1189
  ...(providerObservability ? { providerObservability } : {}),
1181
1190
  ...(details.upstreamStatus ? { upstreamStatus: details.upstreamStatus } : {}),
@@ -1188,42 +1197,46 @@ function logProviderError(logger, provider, kind, route, requestId, error, statu
1188
1197
  signalFix: "Declare this code (with status and retryable) in the operation's errorCodes so it serves its intended status instead of 500.",
1189
1198
  }
1190
1199
  : {}),
1191
- ...(error instanceof z.ZodError ? { issues: zodDetails(error) } : {}),
1200
+ ...(error instanceof z.ZodError ? { issues: zodDetails(error, redact) } : {}),
1192
1201
  });
1193
1202
  }
1194
- function logProviderCleanupError(logger, provider, kind, operationId, requestId, resource, error) {
1203
+ function logProviderCleanupError(logger, provider, kind, operationId, requestId, resource, error, redact) {
1195
1204
  const emit = typeof logger === "function" ? logger : defaultProviderServerLogger;
1196
1205
  const errorClass = error instanceof Error ? error.name : typeof error;
1197
1206
  const message = error instanceof Error ? error.message : String(error);
1198
1207
  emit({
1199
1208
  level: "warn",
1200
1209
  event: "provider_cleanup_failed",
1201
- providerId: provider.id,
1210
+ providerId: redactDiagnosticText(provider.id, redact),
1202
1211
  kind,
1203
- route: operationId,
1204
- ...(requestId ? { requestId } : {}),
1212
+ route: redactCriticalDiagnosticText(operationId, redact),
1213
+ ...(requestId ? { requestId: redactCriticalDiagnosticText(requestId, redact) } : {}),
1205
1214
  resource,
1206
- errorClass,
1207
- message,
1215
+ errorClass: redactDiagnosticText(errorClass, redact),
1216
+ message: sanitizeDiagnosticText(redactDiagnosticText(message, redact)),
1208
1217
  });
1209
1218
  }
1210
- function logProviderSuccess(logger, provider, kind, route, requestId, status, cost, telemetry, correlation = {}) {
1219
+ function logProviderSuccess(logger, provider, kind, route, requestId, status, cost, telemetry, correlation = {}, redact) {
1211
1220
  const telemetryPayload = telemetry?.toLogPayload();
1212
1221
  const emit = typeof logger === "function" ? logger : defaultProviderServerLogger;
1213
1222
  emit({
1214
1223
  level: "info",
1215
1224
  event: "provider_request_completed",
1216
- providerId: provider.id,
1225
+ providerId: redactDiagnosticText(provider.id, redact),
1217
1226
  kind,
1218
- route,
1219
- ...(requestId ? { requestId } : {}),
1227
+ route: redactCriticalDiagnosticText(route, redact),
1228
+ ...(requestId ? { requestId: redactCriticalDiagnosticText(requestId, redact) } : {}),
1220
1229
  ...(correlation.connectionId !== undefined
1221
- ? { 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) }
1222
1237
  : {}),
1223
- ...(correlation.flowId !== undefined ? { flowId: correlation.flowId } : {}),
1224
- ...(correlation.tenantId !== undefined ? { tenantId: correlation.tenantId } : {}),
1225
1238
  ...(correlation.requestedProviderId !== undefined
1226
- ? { requestedProviderId: correlation.requestedProviderId }
1239
+ ? { requestedProviderId: redactDiagnosticText(correlation.requestedProviderId, redact) }
1227
1240
  : {}),
1228
1241
  status,
1229
1242
  ...cost,
@@ -1276,9 +1289,106 @@ function requestTraceId(headers, requestId) {
1276
1289
  ? createHash("sha256").update(requestId, "utf8").digest("hex").slice(0, 32)
1277
1290
  : undefined));
1278
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
+ }
1279
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);
1280
1385
  const requestCost = startRequestCost();
1281
- 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());
1282
1392
  const details = {
1283
1393
  route: input.route,
1284
1394
  requestId: input.requestId,
@@ -1297,12 +1407,14 @@ function createRequestScope(input) {
1297
1407
  const initialTraceId = requestTraceId(input.headers, input.requestId);
1298
1408
  const trace = traceConfig
1299
1409
  ? createTraceContext({
1300
- ...resolveServerTraceContextOptions(traceConfig, traceAttributes),
1410
+ ...withDiagnosticEnv(observeEnv, () => resolveServerTraceContextOptions(traceConfig, traceAttributes, undefined, redact)),
1411
+ redact,
1301
1412
  ...(initialTraceId ? { traceId: initialTraceId } : {}),
1302
1413
  })
1303
- : createTraceContext();
1414
+ : createTraceContext({ redact });
1415
+ bindDiagnosticSensitiveRegistry(trace, sensitiveRegistry);
1304
1416
  const proxyCollector = new ProxyTelemetryCollector();
1305
- const resolverCollector = new ResolverTelemetryCollector();
1417
+ const resolverCollector = new ResolverTelemetryCollector({ redact });
1306
1418
  const telemetry = new RequestTelemetry(trace);
1307
1419
  telemetry.register(proxyCollector);
1308
1420
  telemetry.register(resolverCollector);
@@ -1326,13 +1438,13 @@ function createRequestScope(input) {
1326
1438
  return clientCancelledError();
1327
1439
  return undefined;
1328
1440
  };
1329
- const root = trace.span(`request:${input.kind}:${input.route}`, async () => {
1441
+ const root = withDiagnosticEnv(observeEnv, () => trace.span(`request:${input.kind}:${input.route}`, async () => {
1330
1442
  rootRunner = AsyncLocalStorage.snapshot();
1331
1443
  const outcome = await rootTerminal;
1332
1444
  const error = terminalError(outcome);
1333
1445
  if (error !== undefined)
1334
1446
  throw error;
1335
- });
1447
+ }));
1336
1448
  void root.catch(() => undefined);
1337
1449
  const settleRoot = () => {
1338
1450
  if (!terminalOutcome || streamingSetupPending || rootSettled)
@@ -1368,7 +1480,7 @@ function createRequestScope(input) {
1368
1480
  traceAttributes.flow_id = details.flowId;
1369
1481
  traceAttributes.route = details.route;
1370
1482
  const traceId = requestTraceId(details.headers, details.requestId);
1371
- const sanitizedAttributes = Object.fromEntries(Object.entries(sanitizeTraceAttributes(traceAttributes)).map(([key, value]) => [
1483
+ const sanitizedAttributes = Object.fromEntries(Object.entries(sanitizeTraceAttributes(traceAttributes, redact)).map(([key, value]) => [
1372
1484
  key,
1373
1485
  String(value),
1374
1486
  ]));
@@ -1378,9 +1490,17 @@ function createRequestScope(input) {
1378
1490
  });
1379
1491
  };
1380
1492
  const scope = {
1493
+ redact,
1381
1494
  trace,
1382
1495
  telemetry,
1383
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
+ },
1384
1504
  enrich(enrichment) {
1385
1505
  if (terminalOutcome)
1386
1506
  return;
@@ -1442,21 +1562,21 @@ function createRequestScope(input) {
1442
1562
  terminalize(outcome) {
1443
1563
  if (finishedResult)
1444
1564
  return finishedResult;
1565
+ observeEnv.finished = true;
1445
1566
  terminalOutcome = outcome;
1446
1567
  const error = terminalError(outcome);
1447
1568
  finishedResult = {};
1448
1569
  try {
1449
1570
  const declaredErrorCode = error === undefined ? undefined : input.declaredErrorCode?.(error);
1450
- const status = outcome.status ??
1451
- (error === undefined ? 200 : toStatusCode(error, declaredErrorCode));
1571
+ const status = outcome.status ?? (error === undefined ? 200 : toStatusCode(error, declaredErrorCode));
1452
1572
  finishedResult = headerSnapshot(error);
1453
1573
  const cost = finishRequestCost(requestCost);
1454
1574
  try {
1455
1575
  if (error === undefined) {
1456
- 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);
1457
1577
  }
1458
1578
  else {
1459
- 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);
1460
1580
  }
1461
1581
  }
1462
1582
  catch {
@@ -1490,8 +1610,9 @@ function createRequestScope(input) {
1490
1610
  };
1491
1611
  return scope;
1492
1612
  }
1493
- // A raw `Response` result skips `toErrorResponse`, so its body stays
1494
- // provider-owned, but the observability header is still SDK-owned: derive it
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
1495
1616
  // from the status. Only timeouts, rate limits, and 5xx are retryable;
1496
1617
  // `isRetryableCategory` would otherwise mark every other 4xx (`upstream_http`)
1497
1618
  // as retryable.
@@ -1505,7 +1626,7 @@ function rawResponseErrorObservability(status) {
1505
1626
  // `rawStatusFallback` is decided by the caller from the outcome, not from a
1506
1627
  // missing snapshot: a failed outcome whose thrown value is `undefined` also has
1507
1628
  // no `errorObservability`, and its 500 carries the SDK envelope, not a raw body.
1508
- function responseWithRequestScopeHeaders(response, finished, rawStatusFallback) {
1629
+ async function responseWithRequestScopeHeaders(response, finished, rawStatusFallback, redact) {
1509
1630
  const headers = new Headers(response.headers);
1510
1631
  headers.delete(PROVIDER_TELEMETRY_HEADER);
1511
1632
  if (finished.providerTelemetryHeader) {
@@ -1519,16 +1640,25 @@ function responseWithRequestScopeHeaders(response, finished, rawStatusFallback)
1519
1640
  if (errorObservability) {
1520
1641
  headers.set(ERROR_OBSERVABILITY_HEADER, JSON.stringify(errorObservability));
1521
1642
  }
1522
- return new Response(response.body, {
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, {
1523
1649
  headers,
1524
1650
  status: response.status,
1525
- 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,
1526
1656
  });
1527
1657
  }
1528
- function finalizeRequestResponse(scope, response, outcome) {
1658
+ async function finalizeRequestResponse(scope, response, outcome) {
1529
1659
  try {
1530
1660
  const error = outcome.kind === "failed" ? outcome.error : undefined;
1531
- const finalResponse = responseWithRequestScopeHeaders(response, scope.snapshotHeaders(error), outcome.kind === "completed");
1661
+ const finalResponse = await responseWithRequestScopeHeaders(response, scope.snapshotHeaders(error), outcome.kind === "completed", scope.redact);
1532
1662
  scope.terminalize(outcome);
1533
1663
  return finalResponse;
1534
1664
  }
@@ -1706,7 +1836,7 @@ function toSseResponse(operation, result, lifecycle, requestId) {
1706
1836
  }
1707
1837
  catch (error) {
1708
1838
  const message = error instanceof Error ? error.message : "Stream failed";
1709
- controller.enqueue(encoder.encode(encodeSseEvent(streamError("stream_error", message, {
1839
+ controller.enqueue(encoder.encode(encodeSseEvent(streamError("stream_error", sanitizeDiagnosticText(redactDiagnosticText(message, lifecycle.redact)), {
1710
1840
  ...(requestId ? { requestId } : {}),
1711
1841
  }))));
1712
1842
  controller.close();
@@ -1886,7 +2016,7 @@ async function handleOperation(provider, request, operationId, options, state =
1886
2016
  ctx.stealth.close?.();
1887
2017
  }
1888
2018
  catch (error) {
1889
- logProviderCleanupError(options.logger, provider, "operation", operationId, request.requestId, "stealth", error);
2019
+ logProviderCleanupError(options.logger, provider, "operation", operationId, request.requestId, "stealth", error, scope.redact);
1890
2020
  }
1891
2021
  }
1892
2022
  if ("browser" in ctx) {
@@ -1894,11 +2024,12 @@ async function handleOperation(provider, request, operationId, options, state =
1894
2024
  await ctx.browser.close?.();
1895
2025
  }
1896
2026
  catch (error) {
1897
- logProviderCleanupError(options.logger, provider, "operation", operationId, request.requestId, "browser", error);
2027
+ logProviderCleanupError(options.logger, provider, "operation", operationId, request.requestId, "browser", error, scope.redact);
1898
2028
  }
1899
2029
  }
1900
2030
  };
1901
2031
  const streamLifecycle = {
2032
+ redact: scope.redact,
1902
2033
  runStep(fn) {
1903
2034
  return scope.runStreamStep(fn);
1904
2035
  },
@@ -1996,7 +2127,7 @@ async function handleAuthFlow(provider, request, route, options, state, scope, s
1996
2127
  context.stealth.close?.();
1997
2128
  }
1998
2129
  catch (error) {
1999
- logProviderCleanupError(options.logger, provider, "auth", route, request.requestId, "stealth", error);
2130
+ logProviderCleanupError(options.logger, provider, "auth", route, request.requestId, "stealth", error, scope.redact);
2000
2131
  }
2001
2132
  }
2002
2133
  }
@@ -2102,13 +2233,13 @@ function operationRequestFromForwardingEnvelope(envelope) {
2102
2233
  ...(envelope.deadlineAt !== undefined ? { deadlineAt: envelope.deadlineAt } : {}),
2103
2234
  };
2104
2235
  }
2105
- function parseStatefulForwardingEnvelope(rawBody) {
2236
+ function parseStatefulForwardingEnvelope(rawBody, redact) {
2106
2237
  const parsed = ProviderServerStatefulForwardEnvelopeSchema.safeParse(rawBody);
2107
2238
  if (parsed.success)
2108
2239
  return parsed.data;
2109
2240
  throw new ProviderError("Stateful forwarding envelope is invalid.", {
2110
2241
  code: "STATEFUL_FORWARDING_ENVELOPE_INVALID",
2111
- details: zodDetails(parsed.error),
2242
+ details: zodDetails(parsed.error, redact),
2112
2243
  });
2113
2244
  }
2114
2245
  /**
@@ -2142,6 +2273,10 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
2142
2273
  engine: serverOptions.engine ?? createInProcessProviderEngine(),
2143
2274
  };
2144
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
+ }));
2145
2280
  const logger = options.logger ?? defaultProviderServerLogger;
2146
2281
  const operationErrorCodes = buildOperationErrorCodeLookup(provider);
2147
2282
  const statefulForwardingReplayCache = new StatefulForwardingReplayCache(options.statefulForwarding?.replayCacheMaxEntries ??
@@ -2182,6 +2317,7 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
2182
2317
  const operation = "stateful-internal";
2183
2318
  const requestScope = createRequestScope({
2184
2319
  provider,
2320
+ staticSensitiveValues,
2185
2321
  kind: "operation",
2186
2322
  route: operation,
2187
2323
  headers: Object.fromEntries(c.req.raw.headers.entries()),
@@ -2212,7 +2348,8 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
2212
2348
  code: "STATEFUL_FORWARDING_ENVELOPE_INVALID",
2213
2349
  });
2214
2350
  }
2215
- const envelope = parseStatefulForwardingEnvelope(rawBody);
2351
+ requestScope.seedCredentials(rawBody, "stateful");
2352
+ const envelope = parseStatefulForwardingEnvelope(rawBody, requestScope.redact);
2216
2353
  if (envelope.providerId !== provider.id) {
2217
2354
  throw new ProviderError("Stateful forwarding envelope providerId does not match the served provider.", { code: "STATEFUL_FORWARDING_PROVIDER_MISMATCH" });
2218
2355
  }
@@ -2313,7 +2450,7 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
2313
2450
  ...(requestId ? { requestId } : {}),
2314
2451
  });
2315
2452
  const observabilityDetails = errorObservabilityDetails(error, declaredErrorCode);
2316
- const response = c.json(toErrorResponse(error, requestId, observabilityDetails), status);
2453
+ const response = c.json(toErrorResponse(error, requestId, observabilityDetails, requestScope.redact), status);
2317
2454
  return finalizeRequestResponse(requestScope, response, {
2318
2455
  kind: "failed",
2319
2456
  status,
@@ -2326,6 +2463,7 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
2326
2463
  const operation = c.req.param("operation");
2327
2464
  const requestScope = createRequestScope({
2328
2465
  provider,
2466
+ staticSensitiveValues,
2329
2467
  kind: "operation",
2330
2468
  route: operation,
2331
2469
  operationId: operation,
@@ -2339,6 +2477,7 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
2339
2477
  .clone()
2340
2478
  .json()
2341
2479
  .catch(() => undefined);
2480
+ requestScope.seedCredentials(rawBody, "operation");
2342
2481
  const body = OperationRequestSchema.parse(rawBody);
2343
2482
  const requestHeaders = Object.fromEntries(c.req.raw.headers.entries());
2344
2483
  body.headers = { ...requestHeaders, ...body.headers };
@@ -2356,7 +2495,7 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
2356
2495
  const handled = await handleOperation(provider, body, operation, options, state, requestScope, c.req.raw.signal);
2357
2496
  const response = handled instanceof Response ? handled : c.json(handled);
2358
2497
  return streaming
2359
- ? responseWithRequestScopeHeaders(response, requestScope.snapshotHeaders(), true)
2498
+ ? responseWithRequestScopeHeaders(response, requestScope.snapshotHeaders(), true, requestScope.redact)
2360
2499
  : response;
2361
2500
  };
2362
2501
  const response = streaming
@@ -2375,7 +2514,7 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
2375
2514
  const requestId = extractRequestId(rawBody);
2376
2515
  requestScope.enrich({ ...(requestId ? { requestId } : {}) });
2377
2516
  const observabilityDetails = errorObservabilityDetails(error, declaredErrorCode);
2378
- const response = c.json(toErrorResponse(error, requestId, observabilityDetails), status);
2517
+ const response = c.json(toErrorResponse(error, requestId, observabilityDetails, requestScope.redact), status);
2379
2518
  return finalizeRequestResponse(requestScope, response, {
2380
2519
  kind: "failed",
2381
2520
  status,
@@ -2395,6 +2534,7 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
2395
2534
  let rawBody;
2396
2535
  const requestScope = createRequestScope({
2397
2536
  provider,
2537
+ staticSensitiveValues,
2398
2538
  kind: "auth",
2399
2539
  route: logRoute,
2400
2540
  headers: Object.fromEntries(c.req.raw.headers.entries()),
@@ -2406,6 +2546,7 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
2406
2546
  .clone()
2407
2547
  .json()
2408
2548
  .catch(() => undefined);
2549
+ requestScope.seedCredentials(rawBody, "auth");
2409
2550
  const body = withAuthRequestHeaders(AuthFlowRequestSchema.parse(rawBody), c.req.raw.headers);
2410
2551
  requestScope.enrich({
2411
2552
  requestId: body.requestId,
@@ -2434,7 +2575,7 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
2434
2575
  const requestId = extractRequestId(rawBody);
2435
2576
  requestScope.enrich({ ...(requestId ? { requestId } : {}) });
2436
2577
  const observabilityDetails = errorObservabilityDetails(error);
2437
- const response = c.json(toErrorResponse(error, requestId, observabilityDetails), status);
2578
+ const response = c.json(toErrorResponse(error, requestId, observabilityDetails, requestScope.redact), status);
2438
2579
  return finalizeRequestResponse(requestScope, response, {
2439
2580
  kind: "failed",
2440
2581
  status,