@apifuse/provider-sdk 2.2.0-beta.62 → 2.2.0-beta.64

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 (261) hide show
  1. package/AUTHORING.md +367 -9
  2. package/CHANGELOG.md +8 -0
  3. package/LICENSE +21 -0
  4. package/README.md +1 -0
  5. package/bin/apifuse-check.ts +84 -2
  6. package/bin/apifuse-dev.ts +11 -31
  7. package/bin/apifuse-migrate-deployment.ts +200 -0
  8. package/bin/apifuse-pack-check.ts +11 -4
  9. package/bin/apifuse-pack-types.ts +187 -4
  10. package/bin/apifuse-record.ts +44 -32
  11. package/bin/apifuse-submit-check.ts +9 -5
  12. package/bin/apifuse-test.ts +3 -1
  13. package/dist/cli/commands.d.ts +1 -1
  14. package/dist/cli/commands.js +11 -0
  15. package/dist/cli/deployment-intent.d.ts +178 -0
  16. package/dist/cli/deployment-intent.js +277 -0
  17. package/dist/cli/migrate-deployment-intent.d.ts +132 -0
  18. package/dist/cli/migrate-deployment-intent.js +1552 -0
  19. package/dist/cli/migrate-provider-shape.d.ts +5 -1
  20. package/dist/cli/migrate-provider-shape.js +4 -2
  21. package/dist/cli/resolver-runtime.d.ts +21 -0
  22. package/dist/cli/resolver-runtime.js +31 -0
  23. package/dist/define.d.ts +3 -0
  24. package/dist/define.js +5 -0
  25. package/dist/dev.d.ts +3 -1
  26. package/dist/dev.js +6 -1
  27. package/dist/engine.d.ts +35 -1
  28. package/dist/engine.js +21 -2
  29. package/dist/error-resolution.d.ts +1 -0
  30. package/dist/error-resolution.js +54 -0
  31. package/dist/errors.d.ts +39 -1
  32. package/dist/errors.js +50 -0
  33. package/dist/handle-meta.d.ts +36 -1
  34. package/dist/handle-meta.js +16 -0
  35. package/dist/handle.d.ts +23 -5
  36. package/dist/handle.js +54 -8
  37. package/dist/health-scenario.d.ts +38 -0
  38. package/dist/i18n/error-messages.d.ts +86 -0
  39. package/dist/i18n/error-messages.js +192 -0
  40. package/dist/i18n/index.d.ts +1 -0
  41. package/dist/i18n/index.js +1 -0
  42. package/dist/i18n/keys.d.ts +12 -0
  43. package/dist/i18n/keys.js +76 -5
  44. package/dist/index.d.ts +11 -3
  45. package/dist/index.js +9 -1
  46. package/dist/lint.d.ts +15 -0
  47. package/dist/lint.js +469 -82
  48. package/dist/provider.d.ts +6 -6
  49. package/dist/provider.js +3 -3
  50. package/dist/runtime/browser-telemetry-binding.d.ts +5 -0
  51. package/dist/runtime/browser-telemetry-binding.js +83 -0
  52. package/dist/runtime/browser-telemetry.d.ts +76 -0
  53. package/dist/runtime/browser-telemetry.js +165 -0
  54. package/dist/runtime/browser.d.ts +3 -0
  55. package/dist/runtime/browser.js +208 -47
  56. package/dist/runtime/cache-telemetry.d.ts +71 -0
  57. package/dist/runtime/cache-telemetry.js +177 -0
  58. package/dist/runtime/cache.js +67 -7
  59. package/dist/runtime/capability-response.d.ts +3 -0
  60. package/dist/runtime/capability-response.js +56 -0
  61. package/dist/runtime/capability-telemetry.d.ts +46 -0
  62. package/dist/runtime/capability-telemetry.js +68 -0
  63. package/dist/runtime/diagnostic-env.d.ts +3 -0
  64. package/dist/runtime/diagnostic-env.js +10 -1
  65. package/dist/runtime/engine-mode.d.ts +74 -0
  66. package/dist/runtime/engine-mode.js +152 -0
  67. package/dist/runtime/handle.js +9 -1
  68. package/dist/runtime/http-telemetry-guard.d.ts +10 -0
  69. package/dist/runtime/http-telemetry-guard.js +62 -10
  70. package/dist/runtime/http.js +141 -35
  71. package/dist/runtime/instrumentation.js +92 -13
  72. package/dist/runtime/ocr-telemetry.d.ts +82 -0
  73. package/dist/runtime/ocr-telemetry.js +192 -0
  74. package/dist/runtime/ocr.js +35 -8
  75. package/dist/runtime/request-telemetry.d.ts +2 -0
  76. package/dist/runtime/request-telemetry.js +2 -1
  77. package/dist/runtime/resolver-runtime-options.d.ts +14 -0
  78. package/dist/runtime/resolver-runtime-options.js +33 -0
  79. package/dist/runtime/resolver-shared.d.ts +3 -2
  80. package/dist/runtime/resolver-shared.js +4 -4
  81. package/dist/runtime/resolver-telemetry.d.ts +1 -0
  82. package/dist/runtime/resolver-telemetry.js +1 -1
  83. package/dist/runtime/resolver-vendors/browser.d.ts +2 -0
  84. package/dist/runtime/resolver-vendors/browser.js +46 -0
  85. package/dist/runtime/resolver-vendors/hosts.d.ts +21 -0
  86. package/dist/runtime/resolver-vendors/hosts.js +35 -0
  87. package/dist/runtime/resolver-vendors/hypersolutions.js +2 -2
  88. package/dist/runtime/resolver-vendors/types.d.ts +5 -4
  89. package/dist/runtime/resolver.d.ts +4 -1
  90. package/dist/runtime/resolver.js +46 -13
  91. package/dist/runtime/state-telemetry.d.ts +79 -0
  92. package/dist/runtime/state-telemetry.js +194 -0
  93. package/dist/runtime/state.d.ts +3 -0
  94. package/dist/runtime/state.js +26 -7
  95. package/dist/runtime/stealth-cookies.d.ts +5 -1
  96. package/dist/runtime/stealth-cookies.js +21 -1
  97. package/dist/runtime/stealth-owned-headers.d.ts +26 -0
  98. package/dist/runtime/stealth-owned-headers.js +37 -0
  99. package/dist/runtime/stealth-telemetry.d.ts +92 -0
  100. package/dist/runtime/stealth-telemetry.js +179 -0
  101. package/dist/runtime/stealth.d.ts +4 -1
  102. package/dist/runtime/stealth.js +228 -85
  103. package/dist/runtime/stt-telemetry.d.ts +77 -0
  104. package/dist/runtime/stt-telemetry.js +176 -0
  105. package/dist/runtime/stt.js +15 -6
  106. package/dist/runtime-boundary-lint.d.ts +132 -0
  107. package/dist/runtime-boundary-lint.js +586 -0
  108. package/dist/server/error-stack-frames.d.ts +35 -0
  109. package/dist/server/error-stack-frames.js +219 -0
  110. package/dist/server/index.d.ts +7 -0
  111. package/dist/server/index.js +4 -0
  112. package/dist/server/sdk-version.d.ts +3 -0
  113. package/dist/server/sdk-version.js +19 -0
  114. package/dist/server/self-test.js +1 -18
  115. package/dist/server/sensitive-values.d.ts +1 -1
  116. package/dist/server/sensitive-values.js +9 -0
  117. package/dist/server/serve-implementation.d.ts +51 -2
  118. package/dist/server/serve-implementation.js +337 -90
  119. package/dist/server/types.d.ts +1 -0
  120. package/dist/server/types.js +7 -0
  121. package/dist/stateful/stateful-provider-owner-forwarder.js +22 -0
  122. package/dist/stateful/stateful-provider-runtime-context.d.ts +3 -0
  123. package/dist/stateful/stateful-provider-runtime-context.js +1 -0
  124. package/dist/types.d.ts +123 -1
  125. package/dist/typescript-module.d.ts +39 -0
  126. package/dist/typescript-module.js +72 -0
  127. package/package.json +6 -1
  128. package/src/cli/commands.ts +14 -0
  129. package/src/cli/deployment-intent.ts +364 -0
  130. package/src/cli/migrate-deployment-intent.ts +1908 -0
  131. package/src/cli/migrate-provider-shape.ts +10 -4
  132. package/src/cli/resolver-runtime.ts +72 -0
  133. package/src/define.ts +13 -0
  134. package/src/dev.ts +9 -2
  135. package/src/engine.ts +38 -2
  136. package/src/error-resolution.ts +58 -0
  137. package/src/errors.ts +77 -1
  138. package/src/handle-meta.ts +51 -1
  139. package/src/handle.ts +92 -10
  140. package/src/health-scenario.ts +38 -0
  141. package/src/i18n/error-messages.ts +255 -0
  142. package/src/i18n/index.ts +1 -0
  143. package/src/i18n/keys.ts +80 -6
  144. package/src/index.ts +86 -0
  145. package/src/lint.ts +604 -91
  146. package/src/provider.ts +12 -0
  147. package/src/runtime/browser-telemetry-binding.ts +92 -0
  148. package/src/runtime/browser-telemetry.ts +289 -0
  149. package/src/runtime/browser.ts +249 -65
  150. package/src/runtime/cache-telemetry.ts +247 -0
  151. package/src/runtime/cache.ts +68 -17
  152. package/src/runtime/capability-response.ts +53 -0
  153. package/src/runtime/capability-telemetry.ts +101 -0
  154. package/src/runtime/diagnostic-env.ts +11 -1
  155. package/src/runtime/engine-mode.ts +233 -0
  156. package/src/runtime/handle.ts +9 -1
  157. package/src/runtime/http-telemetry-guard.ts +66 -13
  158. package/src/runtime/http.ts +168 -37
  159. package/src/runtime/instrumentation.ts +105 -16
  160. package/src/runtime/ocr-telemetry.ts +308 -0
  161. package/src/runtime/ocr.ts +114 -74
  162. package/src/runtime/request-telemetry.ts +2 -1
  163. package/src/runtime/resolver-runtime-options.ts +50 -0
  164. package/src/runtime/resolver-shared.ts +9 -4
  165. package/src/runtime/resolver-telemetry.ts +1 -1
  166. package/src/runtime/resolver-vendors/browser.ts +65 -7
  167. package/src/runtime/resolver-vendors/hosts.ts +43 -0
  168. package/src/runtime/resolver-vendors/hypersolutions.ts +2 -2
  169. package/src/runtime/resolver-vendors/types.ts +5 -4
  170. package/src/runtime/resolver.ts +64 -21
  171. package/src/runtime/state-telemetry.ts +298 -0
  172. package/src/runtime/state.ts +46 -45
  173. package/src/runtime/stealth-cookies.ts +24 -1
  174. package/src/runtime/stealth-owned-headers.ts +41 -0
  175. package/src/runtime/stealth-telemetry.ts +304 -0
  176. package/src/runtime/stealth.ts +355 -150
  177. package/src/runtime/stt-telemetry.ts +282 -0
  178. package/src/runtime/stt.ts +93 -71
  179. package/src/runtime-boundary-lint.ts +703 -0
  180. package/src/server/error-stack-frames.ts +214 -0
  181. package/src/server/index.ts +53 -0
  182. package/src/server/sdk-version.ts +22 -0
  183. package/src/server/self-test.ts +1 -21
  184. package/src/server/sensitive-values.ts +14 -0
  185. package/src/server/serve-implementation.ts +569 -108
  186. package/src/server/types.ts +7 -0
  187. package/src/stateful/stateful-provider-owner-forwarder.ts +23 -0
  188. package/src/stateful/stateful-provider-runtime-context.ts +4 -0
  189. package/src/types.ts +128 -1
  190. package/src/typescript-module.ts +100 -0
  191. package/src/cli/__tests__/fixtures/migrate-operation-declaration/approval-override.ts.txt +0 -6
  192. package/src/cli/__tests__/fixtures/migrate-operation-declaration/codemod-syntax.ts.txt +0 -3
  193. package/src/cli/__tests__/fixtures/migrate-operation-declaration/connection-precedence.ts.txt +0 -10
  194. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-a.ts.txt +0 -3
  195. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-b.ts.txt +0 -3
  196. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-barrel.ts.txt +0 -2
  197. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-index.ts.txt +0 -3
  198. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-computed.ts.txt +0 -6
  199. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-cycle-a.ts.txt +0 -1
  200. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-cycle-b.ts.txt +0 -1
  201. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-cycle-index.ts.txt +0 -3
  202. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-barrel.ts.txt +0 -7
  203. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-catalog.ts.txt +0 -19
  204. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-docs.ts.txt +0 -6
  205. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-index.ts.txt +0 -3
  206. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-stores.ts.txt +0 -8
  207. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-indirect-config.ts.txt +0 -5
  208. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-nonstatic.ts.txt +0 -4
  209. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-package-index.ts.txt +0 -3
  210. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-index.ts.txt +0 -3
  211. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-leaf.ts.txt +0 -10
  212. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-one.ts.txt +0 -1
  213. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-two.ts.txt +0 -1
  214. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-composed-index.ts.txt +0 -3
  215. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-composed-one.ts.txt +0 -6
  216. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-composed-two.ts.txt +0 -6
  217. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-mixed-index.ts.txt +0 -14
  218. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-registry.ts.txt +0 -3
  219. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-star-package-barrel.ts.txt +0 -2
  220. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-star-package-index.ts.txt +0 -3
  221. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-star-package-leaf.ts.txt +0 -3
  222. package/src/cli/__tests__/fixtures/migrate-operation-declaration/docs-conflict.ts.txt +0 -8
  223. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-hoisted-array-spread.ts.txt +0 -15
  224. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-imported-array-spread.ts.txt +0 -9
  225. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-kebab-operation-id.ts.txt +0 -17
  226. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-map.ts.txt +0 -5
  227. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-mixed-array-spread.ts.txt +0 -24
  228. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-operation.ts.txt +0 -16
  229. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-snake-operation-id.ts.txt +0 -21
  230. package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-duplicate-provider.ts.txt +0 -11
  231. package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-id-unresolved.ts.txt +0 -7
  232. package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-map.ts.txt +0 -3
  233. package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-one-to-many.ts.txt +0 -18
  234. package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-one-to-one.ts.txt +0 -13
  235. package/src/cli/__tests__/fixtures/migrate-operation-declaration/hoist-all.ts.txt +0 -31
  236. package/src/cli/__tests__/fixtures/migrate-operation-declaration/hoisted-const.ts.txt +0 -11
  237. package/src/cli/__tests__/fixtures/migrate-operation-declaration/imported-spread.ts.txt +0 -11
  238. package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-map.ts.txt +0 -11
  239. package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-cast-tail.ts.txt +0 -21
  240. package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-ekitan.ts.txt +0 -11
  241. package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-override.ts.txt +0 -14
  242. package/src/cli/__tests__/fixtures/migrate-operation-declaration/locale-canonical-en.json +0 -1
  243. package/src/cli/__tests__/fixtures/migrate-operation-declaration/locale-canonical-ja.json +0 -1
  244. package/src/cli/__tests__/fixtures/migrate-operation-declaration/locale-canonical-ko.json +0 -1
  245. package/src/cli/__tests__/fixtures/migrate-operation-declaration/locale-existing-operation-namespace-en.json +0 -8
  246. package/src/cli/__tests__/fixtures/migrate-operation-declaration/missing-english-locale.ts.txt +0 -7
  247. package/src/cli/__tests__/fixtures/migrate-operation-declaration/no-safety.ts.txt +0 -6
  248. package/src/cli/__tests__/fixtures/migrate-operation-declaration/non-literal.ts.txt +0 -7
  249. package/src/cli/__tests__/fixtures/migrate-operation-declaration/redundant-approval.ts.txt +0 -6
  250. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-binding-ambiguous.ts.txt +0 -12
  251. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-imported-other.ts.txt +0 -7
  252. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-imported.ts.txt +0 -5
  253. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-key-ambiguous.ts.txt +0 -19
  254. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-shorthand.ts.txt +0 -9
  255. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-typed.ts.txt +0 -11
  256. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-unrelated.ts.txt +0 -13
  257. package/src/cli/__tests__/fixtures/migrate-operation-declaration/safety-conflict.ts.txt +0 -7
  258. package/src/cli/__tests__/fixtures/migrate-operation-declaration/stream.ts.txt +0 -7
  259. package/src/cli/__tests__/fixtures/migrate-operation-declaration/tool-router-spread.ts.txt +0 -15
  260. package/src/cli/__tests__/fixtures/migrate-operation-declaration/unparseable.ts.txt +0 -4
  261. package/src/cli/__tests__/fixtures/migrate-operation-declaration/verbatim-template.ts.txt +0 -12
@@ -1,3 +1,4 @@
1
+ import { bindCapabilityRoot } from "../runtime/capability-telemetry.js";
1
2
  import { STATUS_CODES } from "node:http";
2
3
  import { readDiagnosticEnv, withDiagnosticEnv } from "../runtime/diagnostic-env.js";
3
4
  import { AsyncLocalStorage } from "node:async_hooks";
@@ -9,15 +10,20 @@ import { Hono } from "hono";
9
10
  import { z } from "zod";
10
11
  import { AuthAbortError, createAuthFlowHelpers } from "../auth.js";
11
12
  import { validateFailClosedDeclaration } from "../declaration-validation.js";
12
- import { createInProcessProviderEngine, ENGINE_OWNED_PROXY_CREDENTIAL_ENV_NAMES, isEngineOwnedEnvName, readEngineProxyCredentials, } from "../engine.js";
13
+ import { ENGINE_OWNED_PROXY_CREDENTIAL_ENV_NAMES, isEngineOwnedEnvName, readEngineProxyCredentials, } from "../engine.js";
14
+ import { createEngineForMode, isUnavailableProviderEngine, resolveProviderEngineMode, } from "../runtime/engine-mode.js";
15
+ import { SDK_VERSION } from "./sdk-version.js";
13
16
  import { safeProviderErrorObservability } from "../error-observability.js";
17
+ import { providerErrorStackFrames } from "./error-stack-frames.js";
14
18
  import { SDK_OWNED_PROVIDER_ERROR_CODES, SDK_RUNTIME_OWNED_ERROR_CODES, SDK_STATUS_MAPPED_PROVIDER_ERROR_CODES, } from "../error-resolution.js";
15
19
  import { AuthError, isProviderError, isSessionExpiredError, isTransportError, isValidationError, ProviderError, SDKError, } from "../errors.js";
16
20
  import { sanitizeDiagnosticText } from "../fixture-sanitization.js";
17
21
  import { loadProviderLocaleCatalogs, localizeAuthTurn, } from "../i18n/catalog.js";
22
+ import { acceptLanguageHeaderValue, DEFAULT_PROVIDER_ERROR_LOCALE, localizeProviderErrorText, PROVIDER_ERROR_LOCALES, resolveProviderErrorLocale, } from "../i18n/error-messages.js";
18
23
  import { categoryForStatus, isRetryableCategory, PROVIDER_OBSERVABILITY_TAXONOMY_VERSION, sourceForCategory, } from "../observability.js";
19
24
  import { createScratchpad } from "../runtime/auth-flow.js";
20
25
  import { createProviderCache } from "../runtime/cache.js";
26
+ import { bindCacheTelemetry, CacheTelemetryCollector } from "../runtime/cache-telemetry.js";
21
27
  import { createHandleContext } from "../runtime/handle.js";
22
28
  import { createCredentialContext } from "../runtime/credential.js";
23
29
  import { bindDiagnosticSensitiveRegistry, compileProcessDiagnosticSensitiveValues, createDiagnosticRedactor, REDACTION_FAILED, isDiagnosticRedactionSuppressed, bindCriticalDiagnosticRedactor, redactCriticalDiagnosticText, redactDiagnosticText, registerDiagnosticSensitiveValues, } from "../runtime/diagnostic-redactor.js";
@@ -30,16 +36,23 @@ import { bindHttpTelemetry, isSupportedHttpBinding, warnUnsupportedHttpBinding,
30
36
  import { HttpTelemetryCollector } from "../runtime/http-telemetry.js";
31
37
  import { wrapWithInstrumentation } from "../runtime/instrumentation.js";
32
38
  import { createOcrClientFromEnv } from "../runtime/ocr.js";
39
+ import { bindOcrTelemetry, OcrTelemetryCollector } from "../runtime/ocr-telemetry.js";
33
40
  import { getProviderBaseUrl } from "../runtime/provider.js";
34
41
  import { PROXY_AUTH_IP_DENIED_CODE, PROXY_EDGE_AUTH_REJECTED_CODE, PROXY_POOL_EXHAUSTED_CODE, } from "../runtime/proxy-errors.js";
35
42
  import { PROVIDER_TELEMETRY_HEADER, ProxyTelemetryCollector, } from "../runtime/proxy-telemetry.js";
36
43
  import { closedEnum, RequestTelemetry, tenantOpaqueKeys, } from "../runtime/request-telemetry.js";
37
44
  import { bindResolverTelemetry, createUnsupportedResolverClient, inheritResolverTelemetryBinding, } from "../runtime/resolver-shared.js";
38
45
  import { ResolverTelemetryCollector, } from "../runtime/resolver-telemetry.js";
46
+ import { createResolverRuntimeOptions, resolveNativeProxyPolicy, } from "../runtime/resolver-runtime-options.js";
39
47
  import { assertRequiredSecretsPresent, listMissingRequiredSecrets, MISSING_SECRET_CODE, } from "../runtime/secrets.js";
40
48
  import { createProviderRuntimeStateFromEnv, createUnsupportedProviderRuntimeState, } from "../runtime/state.js";
49
+ import { StateTelemetryCollector, instrumentProviderRuntimeState, } from "../runtime/state-telemetry.js";
41
50
  import { StealthCookieJar } from "../runtime/stealth-cookies.js";
51
+ import { StealthTelemetryCollector } from "../runtime/stealth-telemetry.js";
52
+ import { BrowserTelemetryCollector } from "../runtime/browser-telemetry.js";
53
+ import { bindBrowserTelemetry, isSupportedBrowserBinding, warnUnsupportedBrowserBinding, } from "../runtime/browser-telemetry-binding.js";
42
54
  import { createSttClientFromEnv } from "../runtime/stt.js";
55
+ import { bindSttTelemetry, SttTelemetryCollector } from "../runtime/stt-telemetry.js";
43
56
  import { createTraceContext, getTraceRecorder, updateTraceContextExportMetadata, } from "../runtime/trace.js";
44
57
  import { resolveTraceConfigFromEnv } from "../runtime/trace-config.js";
45
58
  import { parseSchema } from "../schema.js";
@@ -83,7 +96,6 @@ function providerErrorCode(error) {
83
96
  return undefined;
84
97
  }
85
98
  }
86
- const AUTH_FLOW_LOCALES = ["en", "ko", "ja"];
87
99
  const retryResponseMeta = new WeakMap();
88
100
  const STATEFUL_INTERNAL_OPERATIONS_ROUTE = "/__apifuse/stateful/operations";
89
101
  const STATEFUL_FORWARDING_SOURCE_POD_HEADER = "x-apifuse-stateful-source-pod";
@@ -113,6 +125,18 @@ export const ProviderServerStatefulForwardEnvelopeSchema = z
113
125
  }).strict(),
114
126
  })
115
127
  .strict();
128
+ // Inbound compatibility boundary for rolling deploys, mirroring the outbound
129
+ // one in stateful-provider-owner-forwarder.ts: emitted envelopes stay strict
130
+ // (above), while a received envelope's nested operation request keeps the
131
+ // gateway route's tolerance and strips unknown keys. A source pod on a newer
132
+ // SDK carrying an additive OperationRequestSchema field (tenantId, #164) must
133
+ // not be rejected with STATEFUL_FORWARDING_ENVELOPE_INVALID by an owner pod
134
+ // that predates the field: the rejection is terminal for the operation and the
135
+ // two pods coexist for the whole rolling update. The envelope's own keys stay
136
+ // strict on both sides, because they are protocol rather than payload.
137
+ const ReceivedStatefulForwardEnvelopeSchema = ProviderServerStatefulForwardEnvelopeSchema.extend({
138
+ operationRequest: z.object(ProviderServerStatefulForwardEnvelopeSchema.shape.operationRequest.shape),
139
+ });
116
140
  const require = createRequire(import.meta.url);
117
141
  function normalizedCapabilityCause(error) {
118
142
  if (error instanceof Error)
@@ -327,24 +351,6 @@ function getProviderStealthBaseUrl(provider) {
327
351
  function getProviderStealthProfile(provider) {
328
352
  return provider.stealth ? getStealthProfile(provider.stealth) : undefined;
329
353
  }
330
- /** One option set for ctx.resolver and the automatic SBSD solve, so both share identity scope, cache, proxy intent, and telemetry. */
331
- function createResolverRuntimeOptions(provider, cache, identityScope, proxyPolicy, proxyClientOptions, stealthProfile, telemetry) {
332
- return {
333
- allowedHosts: provider.allowedHosts,
334
- cache,
335
- telemetry,
336
- identityScope,
337
- ...(proxyPolicy
338
- ? {
339
- proxyIntent: {
340
- mode: proxyPolicy.mode,
341
- ...proxyClientOptions,
342
- ...(stealthProfile ? { userAgent: stealthProfile.userAgent } : {}),
343
- },
344
- }
345
- : {}),
346
- };
347
- }
348
354
  /** Shared predicate for the SBSD challenge runtime and the ceremony egress lease. */
349
355
  function declaresAkamaiSbsdResolver(provider) {
350
356
  return provider.resolver?.kinds.some((kind) => kind === "akamai_sbsd") === true;
@@ -367,12 +373,13 @@ function createStealthChallengeDetection(provider, resolverRuntime, resolverOver
367
373
  ? {
368
374
  async solve(challenge, transport, solveSignal) {
369
375
  const resolver = resolverRuntime.bindResolverSignal((resolverOverride
370
- ? bindResolverTelemetry(resolverOverride, resolverOptions.telemetry)
376
+ ? bindResolverTelemetry(resolverOverride, resolverOptions.telemetry, resolverOptions.browserTelemetry)
371
377
  : undefined) ??
372
378
  resolverRuntime.createResolverClientFromEnv(provider.resolver, undefined, {
373
379
  allowedHosts: resolverOptions.allowedHosts,
374
380
  cache: resolverOptions.cache,
375
381
  telemetry: resolverOptions.telemetry,
382
+ browserTelemetry: resolverOptions.browserTelemetry,
376
383
  identityScope: resolverOptions.identityScope,
377
384
  // No proxyIntent: the transport is already bound to the initiating
378
385
  // request's lease, and a second identity resolution would allocate
@@ -465,19 +472,16 @@ function resolveOperationConnectionId(request) {
465
472
  // absent so it can never override a valid id or key a real scope. Requests
466
473
  // without any usable id fall back to the documented missing-connection
467
474
  // sentinel scope instead of scoping context/affinity/state under "".
468
- return (normalizeConnectionId(request.connection?.id) ?? normalizeConnectionId(request.connectionId));
475
+ return normalizeIdentifier(request.connection?.id) ?? normalizeIdentifier(request.connectionId);
469
476
  }
470
- function normalizeConnectionId(id) {
471
- return id === "" ? undefined : id;
477
+ function resolveOperationTenantId(request) {
478
+ // Only the gateway-asserted envelope field is consulted; caller headers can
479
+ // never populate the principal scope. Same rule as connection ids: "" is a
480
+ // malformed identifier, not a principal.
481
+ return normalizeIdentifier(request.tenantId);
472
482
  }
473
- function resolveNativeProxyPolicy(provider) {
474
- if (typeof provider.proxy === "object")
475
- return provider.proxy;
476
- if (provider.proxy === true)
477
- return { mode: "optional" };
478
- if (provider.proxy === false)
479
- return { mode: "disabled" };
480
- return undefined;
483
+ function normalizeIdentifier(id) {
484
+ return id === "" ? undefined : id;
481
485
  }
482
486
  function providerSecretNames(provider) {
483
487
  return (provider.secrets?.map((secret) => secret.name) ?? []).filter((name) => !isEngineOwnedEnvName(name));
@@ -495,11 +499,12 @@ function createProviderContext(provider, request, operationId, options, state =
495
499
  engineCredentials: engineProxyCredentials,
496
500
  };
497
501
  const resolverIdentityScope = resolveProviderResolverIdentityScope(provider, proxyClientOptions.affinityKey, request.requestId);
498
- const cache = createProviderCache({ providerId: provider.id });
502
+ const cache = createProviderCache(bindCacheTelemetry({ providerId: provider.id }, scope.cacheTelemetry));
499
503
  const { capabilityModules } = options;
500
- const resolverOptions = createResolverRuntimeOptions(provider, cache, resolverIdentityScope, proxyPolicy, proxyClientOptions, stealthProfile, scope.resolverTelemetry);
504
+ const resolverOptions = createResolverRuntimeOptions(provider, cache, resolverIdentityScope, proxyPolicy, proxyClientOptions, stealthProfile, scope.resolverTelemetry, scope.browserTelemetry);
501
505
  const challengeRuntime = createStealthChallengeDetection(provider, capabilityModules.resolver, options.resolver, resolverOptions, scope.trace, signal);
502
506
  const stealthClientOptions = {
507
+ stealthTelemetry: scope.stealthTelemetry,
503
508
  upstream: proxyClientOptions.upstream,
504
509
  affinityKey: proxyClientOptions.affinityKey,
505
510
  telemetry: scope.telemetry.proxy,
@@ -524,11 +529,15 @@ function createProviderContext(provider, request, operationId, options, state =
524
529
  scopes: request.connection?.scopes,
525
530
  values: request.connection?.secrets,
526
531
  });
532
+ const operationTenantId = resolveOperationTenantId(request);
527
533
  const requestContext = {
528
534
  connectionId: resolveOperationConnectionId(request),
535
+ // Absent stays absent: an own property with an undefined value would
536
+ // change the shape of ctx.request for every existing consumer.
537
+ ...(operationTenantId !== undefined ? { tenantId: operationTenantId } : {}),
529
538
  headers: request.headers ?? {},
530
539
  };
531
- const requestState = state.forConnection(requestContext.connectionId);
540
+ const requestState = instrumentProviderRuntimeState(state, scope.stateTelemetry, scope.redact).forConnection(requestContext.connectionId);
532
541
  const bindings = {
533
542
  env,
534
543
  credential,
@@ -554,6 +563,7 @@ function createProviderContext(provider, request, operationId, options, state =
554
563
  requireCdpPool: isProductionProviderBrowserMode(provider),
555
564
  stealth: true,
556
565
  engine: provider.browser?.engine,
566
+ telemetry: scope.browserTelemetry,
557
567
  })
558
568
  : createBrowserStub(),
559
569
  ...(provider.native
@@ -572,15 +582,15 @@ function createProviderContext(provider, request, operationId, options, state =
572
582
  : {}),
573
583
  trace: scope.trace,
574
584
  auth: createAuthStub(),
575
- ocr: options.ocr ?? createOcrClientFromEnv(provider.ocr),
576
- stt: options.stt ?? createSttClientFromEnv(provider.stt),
585
+ ocr: bindCapabilityRoot(bindOcrTelemetry(options.ocr ?? createOcrClientFromEnv(provider.ocr), scope.ocrTelemetry), scope.run),
586
+ stt: bindCapabilityRoot(bindSttTelemetry(options.stt ?? createSttClientFromEnv(provider.stt), scope.sttTelemetry), scope.run),
577
587
  resolver: capabilityModules.resolver
578
588
  ? capabilityModules.resolver.bindResolverSignal((options.resolver
579
- ? bindResolverTelemetry(options.resolver, resolverOptions.telemetry)
589
+ ? bindResolverTelemetry(options.resolver, resolverOptions.telemetry, resolverOptions.browserTelemetry)
580
590
  : undefined) ??
581
591
  capabilityModules.resolver.createResolverClientFromEnv(provider.resolver, undefined, resolverOptions), signal)
582
592
  : bindResolverSignalWithoutRuntime((options.resolver
583
- ? bindResolverTelemetry(options.resolver, resolverOptions.telemetry)
593
+ ? bindResolverTelemetry(options.resolver, resolverOptions.telemetry, resolverOptions.browserTelemetry)
584
594
  : undefined) ??
585
595
  createUnsupportedResolverClient("Provider does not declare resolver capability"), signal),
586
596
  handle: createHandleContext({
@@ -595,6 +605,7 @@ function createProviderContext(provider, request, operationId, options, state =
595
605
  }),
596
606
  };
597
607
  const candidateHttp = bindings.http;
608
+ const candidateBrowser = bindings.browser;
598
609
  const attached = options.engine.attach({ provider, bindings });
599
610
  const hostHttp = provider.http ? attached.http : undefined;
600
611
  const supportedHostHttp = provider.http && isSupportedHttpBinding(hostHttp);
@@ -603,10 +614,23 @@ function createProviderContext(provider, request, operationId, options, state =
603
614
  const observedHttp = supportedHostHttp && hostHttp !== candidateHttp
604
615
  ? bindHttpTelemetry(hostHttp, scope.httpTelemetry)
605
616
  : undefined;
606
- const contextTarget = observedHttp
617
+ const hostBrowser = provider.runtime === "browser" ? attached.browser : undefined;
618
+ const supportedHostBrowser = provider.runtime === "browser" && isSupportedBrowserBinding(hostBrowser);
619
+ if (provider.runtime === "browser" && !supportedHostBrowser)
620
+ warnUnsupportedBrowserBinding();
621
+ const observedBrowser = supportedHostBrowser && hostBrowser !== candidateBrowser
622
+ ? bindBrowserTelemetry(hostBrowser, scope.browserTelemetry)
623
+ : undefined;
624
+ const contextTarget = observedBrowser || observedHttp
607
625
  ? new Proxy(attached, {
608
626
  get(target, property, receiver) {
609
- return property === "http" ? observedHttp : Reflect.get(target, property, receiver);
627
+ if (property === "http" && observedHttp)
628
+ return observedHttp;
629
+ if (property === "http" && provider.http && !supportedHostHttp)
630
+ return undefined;
631
+ if (property === "browser" && observedBrowser)
632
+ return observedBrowser;
633
+ return Reflect.get(target, property, receiver);
610
634
  },
611
635
  })
612
636
  : provider.http && !supportedHostHttp
@@ -687,11 +711,12 @@ function createAuthFlowContext(provider, request, route, options, state, scope,
687
711
  throw new SDKError("An egress lease was supplied for an auth flow that does not use a ceremony lease", { code: "EGRESS_LEASE_INVALID" });
688
712
  }
689
713
  const resolverIdentityScope = resolveProviderResolverIdentityScope(provider, proxyClientOptions.affinityKey, request.requestId);
690
- const cache = createProviderCache({ providerId: provider.id });
714
+ const cache = createProviderCache(bindCacheTelemetry({ providerId: provider.id }, scope.cacheTelemetry));
691
715
  const { capabilityModules } = options;
692
- const resolverOptions = createResolverRuntimeOptions(provider, cache, resolverIdentityScope, proxyPolicy, proxyClientOptions, stealthProfile, scope.resolverTelemetry);
716
+ const resolverOptions = createResolverRuntimeOptions(provider, cache, resolverIdentityScope, proxyPolicy, proxyClientOptions, stealthProfile, scope.resolverTelemetry, scope.browserTelemetry);
693
717
  const challengeRuntime = createStealthChallengeDetection(provider, capabilityModules.resolver, options.resolver, resolverOptions, scope.trace, signal);
694
718
  const stealthClientOptions = {
719
+ stealthTelemetry: scope.stealthTelemetry,
695
720
  upstream: proxyClientOptions.upstream,
696
721
  affinityKey: proxyClientOptions.affinityKey,
697
722
  telemetry: scope.telemetry.proxy,
@@ -730,12 +755,23 @@ function createAuthFlowContext(provider, request, route, options, state, scope,
730
755
  ...(signal ? { signal } : {}),
731
756
  httpTelemetry: scope.httpTelemetry,
732
757
  }),
733
- state: state.forConnection(resolveOperationConnectionId(request)),
758
+ state: instrumentProviderRuntimeState(state, scope.stateTelemetry, scope.redact).forConnection(resolveOperationConnectionId(request)),
734
759
  stealth: stealthBaseUrl
735
760
  ? capabilityModules.stealth
736
761
  ? capabilityModules.stealth.createStealthClient(stealthBaseUrl, stealthClientOptions)
737
762
  : createLazyStealthClient(logStealthCleanupError, stealthBaseUrl, stealthClientOptions)
738
763
  : createStealthStub(),
764
+ browser: provider.runtime === "browser"
765
+ ? capabilityModules.browser.createBrowserClient({
766
+ allowedHosts: provider.allowedHosts,
767
+ cdpUrl: readDiagnosticEnv("APIFUSE__CDP_POOL__URL"),
768
+ headless: true,
769
+ requireCdpPool: isProductionProviderBrowserMode(provider),
770
+ stealth: true,
771
+ engine: provider.browser?.engine,
772
+ telemetry: scope.browserTelemetry,
773
+ })
774
+ : createBrowserStub(),
739
775
  ...(provider.native
740
776
  ? {
741
777
  native: {
@@ -756,15 +792,15 @@ function createAuthFlowContext(provider, request, route, options, state, scope,
756
792
  ]),
757
793
  credential,
758
794
  context: flowContextStore.context,
759
- ocr: options.ocr ?? createOcrClientFromEnv(provider.ocr),
760
- stt: options.stt ?? createSttClientFromEnv(provider.stt),
795
+ ocr: bindCapabilityRoot(bindOcrTelemetry(options.ocr ?? createOcrClientFromEnv(provider.ocr), scope.ocrTelemetry), scope.run),
796
+ stt: bindCapabilityRoot(bindSttTelemetry(options.stt ?? createSttClientFromEnv(provider.stt), scope.sttTelemetry), scope.run),
761
797
  resolver: capabilityModules.resolver
762
798
  ? capabilityModules.resolver.bindResolverSignal((options.resolver
763
- ? bindResolverTelemetry(options.resolver, resolverOptions.telemetry)
799
+ ? bindResolverTelemetry(options.resolver, resolverOptions.telemetry, resolverOptions.browserTelemetry)
764
800
  : undefined) ??
765
801
  capabilityModules.resolver.createResolverClientFromEnv(provider.resolver, undefined, resolverOptions), signal)
766
802
  : bindResolverSignalWithoutRuntime((options.resolver
767
- ? bindResolverTelemetry(options.resolver, resolverOptions.telemetry)
803
+ ? bindResolverTelemetry(options.resolver, resolverOptions.telemetry, resolverOptions.browserTelemetry)
768
804
  : undefined) ??
769
805
  createUnsupportedResolverClient("Provider does not declare resolver capability"), signal),
770
806
  auth: createAuthFlowHelpers({ signal }),
@@ -828,7 +864,76 @@ function publicErrorSource(error, category) {
828
864
  }
829
865
  return sourceForCategory(category);
830
866
  }
831
- function toErrorResponse(error, requestId, observabilityDetails, redact) {
867
+ function localeKeyOption(error, key) {
868
+ const value = providerErrorOption(error, key);
869
+ return typeof value === "string" ? value : undefined;
870
+ }
871
+ function declaredLocaleKey(declaration, key) {
872
+ const value = declaration?.[key];
873
+ return typeof value === "string" ? value : undefined;
874
+ }
875
+ function providerErrorParams(error) {
876
+ const params = providerErrorOption(error, "params");
877
+ return params !== null && typeof params === "object" && !Array.isArray(params)
878
+ ? params
879
+ : undefined;
880
+ }
881
+ /**
882
+ * Resolution order for one localized envelope field:
883
+ *
884
+ * 1. the throw site's `messageKey`/`fixKey`;
885
+ * 2. the declared `errorCodes[]` entry's key for the thrown code;
886
+ * 3. the derived `errors.<code>.<field>` convention;
887
+ * 4. the English literal the provider threw.
888
+ *
889
+ * Steps 2 and 3 are skipped for SDK-owned failures (transport, Zod, stateful
890
+ * deadline, SDK runtime codes): their text is the SDK's contract, and a
891
+ * provider catalog entry must not be able to relabel one. An explicit key on
892
+ * the thrown error still wins, because that is the provider deliberately
893
+ * describing an error it constructed itself.
894
+ */
895
+ function localizedErrorField(error, field, literal, localization) {
896
+ if (!localization?.catalogs)
897
+ return literal;
898
+ const optionKey = field === "message" ? "messageKey" : "fixKey";
899
+ const candidates = [
900
+ { kind: "key", key: localeKeyOption(error, optionKey) },
901
+ ];
902
+ if (!sdkOwnsErrorResolution(error)) {
903
+ candidates.push({ kind: "key", key: declaredLocaleKey(localization.declaredErrorCode, optionKey) }, { kind: "derived", code: providerErrorCode(error), field });
904
+ }
905
+ return localizeProviderErrorText({
906
+ catalogs: localization.catalogs,
907
+ locale: localization.locale,
908
+ candidates,
909
+ params: providerErrorParams(error),
910
+ fallback: literal,
911
+ });
912
+ }
913
+ /**
914
+ * Reads `accept-language` out of the request envelope's `headers` map.
915
+ *
916
+ * The gateway stamps the negotiated locale there for auth flows (it persists
917
+ * the start locale on the flow state so continue/poll turns stay in one
918
+ * language), and the operation route merges HTTP headers into the same map, so
919
+ * the envelope is the higher-precedence source.
920
+ */
921
+ function requestBodyAcceptLanguage(rawBody) {
922
+ if (!rawBody || typeof rawBody !== "object")
923
+ return undefined;
924
+ const headers = Object.getOwnPropertyDescriptor(rawBody, "headers")?.value;
925
+ if (!headers || typeof headers !== "object" || Array.isArray(headers))
926
+ return undefined;
927
+ return acceptLanguageHeaderValue(headers);
928
+ }
929
+ function errorEnvelopeLocalization(options) {
930
+ return {
931
+ ...(options.catalogs ? { catalogs: options.catalogs } : {}),
932
+ locale: resolveProviderErrorLocale(requestBodyAcceptLanguage(options.rawBody), options.requestHeaders?.get("accept-language")),
933
+ ...(options.declaredErrorCode ? { declaredErrorCode: options.declaredErrorCode } : {}),
934
+ };
935
+ }
936
+ function toErrorResponse(error, requestId, observabilityDetails, redact, localization) {
832
937
  const observability = observabilityDetails;
833
938
  const source = publicErrorSource(error, observability.category);
834
939
  if (error instanceof StatefulRoutingDeadlineError) {
@@ -844,14 +949,17 @@ function toErrorResponse(error, requestId, observabilityDetails, redact) {
844
949
  }
845
950
  if (isProviderError(error)) {
846
951
  const details = providerErrorOption(error, "details");
952
+ const literalFix = providerErrorOption(error, "fix");
953
+ const fix = localizedErrorField(error, "fix", typeof literalFix === "string" && literalFix.length > 0 ? literalFix : undefined, localization);
954
+ const literalMessage = publicProviderErrorMessage(error);
847
955
  return {
848
956
  error: {
849
957
  code: providerErrorCode(error) ?? "provider_error",
850
- message: publicProviderErrorMessage(error),
958
+ message: localizedErrorField(error, "message", literalMessage, localization) ?? literalMessage,
851
959
  ...(requestId ? { requestId } : {}),
852
960
  retryable: observability.retryable,
853
961
  source,
854
- ...(providerErrorOption(error, "fix") ? { fix: providerErrorOption(error, "fix") } : {}),
962
+ ...(fix ? { fix } : {}),
855
963
  ...(details !== undefined ? { details } : {}),
856
964
  },
857
965
  };
@@ -1097,6 +1205,11 @@ function publicProviderErrorMessage(error) {
1097
1205
  return "Request timed out";
1098
1206
  if (providerErrorCode(error) === "transport_network_error")
1099
1207
  return "Network error";
1208
+ // Provider-local fault: no request was issued, so an upstream-flavored
1209
+ // message would misattribute it (category is provider_error).
1210
+ if (providerErrorCode(error) === "http_header_factory_failed") {
1211
+ return "Request header preparation failed";
1212
+ }
1100
1213
  if (providerErrorCode(error) === "upstream_http_error" && error.status) {
1101
1214
  return `Upstream request failed with status ${error.status}`;
1102
1215
  }
@@ -1220,6 +1333,11 @@ function logProviderError(logger, provider, kind, route, requestId, error, statu
1220
1333
  const errorClass = error instanceof Error ? error.name : typeof error;
1221
1334
  const message = error instanceof Error ? error.message : String(error);
1222
1335
  const causeChain = providerErrorCauseChain(error, redact);
1336
+ // Masked 500 bodies carry only errorClass, so the log is the sole channel
1337
+ // that can locate the throw. Declared 5xx (502/504) are included too: their
1338
+ // body carries code+message, but frame[0] is still the throw site inside
1339
+ // provider code. 4xx failures keep the lean record.
1340
+ const stack = status >= 500 ? providerErrorStackFrames(error, redact) : undefined;
1223
1341
  const details = observabilityDetails;
1224
1342
  const isUnregisteredProviderErrorCode = status === 500 &&
1225
1343
  isProviderError(error) &&
@@ -1261,6 +1379,7 @@ function logProviderError(logger, provider, kind, route, requestId, error, statu
1261
1379
  errorClass: redactDiagnosticText(errorClass, redact),
1262
1380
  message: sanitizeDiagnosticText(redactDiagnosticText(message, redact)),
1263
1381
  ...(causeChain ? { causeChain } : {}),
1382
+ ...(stack ? { stack } : {}),
1264
1383
  ...(providerObservability ? { providerObservability } : {}),
1265
1384
  ...(details.upstreamStatus ? { upstreamStatus: details.upstreamStatus } : {}),
1266
1385
  errorCategory: details.category,
@@ -1462,6 +1581,7 @@ function createRequestScope(input) {
1462
1581
  observe: createDiagnosticEnvObserver(input.provider, sensitiveRegistry.add, sensitiveRegistry.has),
1463
1582
  finished: false,
1464
1583
  staticValues: input.staticSensitiveValues,
1584
+ register: sensitiveRegistry.add,
1465
1585
  };
1466
1586
  const traceConfig = withDiagnosticEnv(observeEnv, () => resolveTraceConfigFromEnv());
1467
1587
  const details = {
@@ -1492,11 +1612,23 @@ function createRequestScope(input) {
1492
1612
  const resolverCollector = new ResolverTelemetryCollector({ redact });
1493
1613
  const nativeCollector = new NativeTelemetryCollector({ redact });
1494
1614
  const httpCollector = new HttpTelemetryCollector({ redact });
1615
+ const stealthCollector = new StealthTelemetryCollector({ redact });
1616
+ const ocrCollector = new OcrTelemetryCollector({ redact });
1617
+ const sttCollector = new SttTelemetryCollector({ redact });
1618
+ const browserCollector = new BrowserTelemetryCollector({ redact });
1619
+ const cacheTelemetry = new CacheTelemetryCollector({ redact });
1620
+ const stateTelemetry = new StateTelemetryCollector({ redact });
1495
1621
  const telemetry = new RequestTelemetry(trace);
1496
1622
  telemetry.register(proxyCollector);
1497
1623
  telemetry.register(resolverCollector);
1498
1624
  telemetry.register(nativeCollector);
1499
1625
  telemetry.register(httpCollector);
1626
+ telemetry.register(stealthCollector);
1627
+ telemetry.register(browserCollector);
1628
+ telemetry.register(ocrCollector);
1629
+ telemetry.register(sttCollector);
1630
+ telemetry.register(cacheTelemetry);
1631
+ telemetry.register(stateTelemetry);
1500
1632
  let rootRunner = (fn) => fn();
1501
1633
  let resolveRoot;
1502
1634
  const rootTerminal = new Promise((resolve) => {
@@ -1575,6 +1707,12 @@ function createRequestScope(input) {
1575
1707
  resolverTelemetry: resolverCollector,
1576
1708
  nativeTelemetry: nativeCollector,
1577
1709
  httpTelemetry: httpCollector,
1710
+ stealthTelemetry: stealthCollector,
1711
+ ocrTelemetry: ocrCollector,
1712
+ sttTelemetry: sttCollector,
1713
+ browserTelemetry: browserCollector,
1714
+ cacheTelemetry,
1715
+ stateTelemetry,
1578
1716
  seedCredentials(rawBody, kind) {
1579
1717
  const harvested = rawRequestCredentials(rawBody, kind);
1580
1718
  sensitiveRegistry.add(harvested.values);
@@ -2015,50 +2153,49 @@ function toAuthFlowResponse(result, contextPatch, engine) {
2015
2153
  ...(engine ? { engine } : {}),
2016
2154
  };
2017
2155
  }
2018
- function authFlowLocaleFromHeaders(headers) {
2019
- const header = Object.entries(headers ?? {}).find(([key]) => key.toLowerCase() === "accept-language")?.[1];
2020
- for (const token of (header ?? "").split(",")) {
2021
- const language = token.trim().split(";")[0]?.split("-")[0]?.toLowerCase();
2022
- if (isAuthFlowLocale(language)) {
2023
- return language;
2024
- }
2025
- }
2026
- return "en";
2027
- }
2028
- function isAuthFlowLocale(value) {
2029
- return value === "en" || value === "ko" || value === "ja";
2030
- }
2031
2156
  function isAuthTurn(value) {
2032
2157
  return !!value && typeof value === "object" && "kind" in value && "turnId" in value;
2033
2158
  }
2034
- function loadAuthFlowLocaleCatalogs(provider) {
2159
+ /**
2160
+ * Loads the provider's locale catalogs once, at `createServerApp` time.
2161
+ *
2162
+ * Only the locales that actually exist on disk are read: a provider with
2163
+ * `en`+`ko` but no `ja.json` used to make the whole load throw and silently
2164
+ * disable localization for every turn. Absence of catalogs is not an error —
2165
+ * every localized field falls back to its English literal — but a catalog that
2166
+ * exists and cannot be read is logged once so a packaging mistake is visible at
2167
+ * boot instead of per request.
2168
+ */
2169
+ function resolveProviderLocaleCatalogs(provider, options, logger) {
2170
+ if (options.localeCatalogs)
2171
+ return options.localeCatalogs;
2035
2172
  for (const providerDir of [
2036
2173
  process.cwd(),
2037
2174
  join(process.cwd(), "providers", provider.id),
2038
2175
  join(process.cwd(), "providers-staging", provider.id),
2039
2176
  ]) {
2040
- if (!existsSync(join(providerDir, "locales", "en.json")))
2177
+ const locales = PROVIDER_ERROR_LOCALES.filter((locale) => existsSync(join(providerDir, "locales", `${locale}.json`)));
2178
+ if (!locales.includes(DEFAULT_PROVIDER_ERROR_LOCALE))
2041
2179
  continue;
2042
2180
  try {
2043
- return loadProviderLocaleCatalogs({
2044
- providerDir,
2045
- locales: AUTH_FLOW_LOCALES,
2046
- });
2181
+ return loadProviderLocaleCatalogs({ providerDir, locales });
2047
2182
  }
2048
- catch {
2183
+ catch (error) {
2184
+ logger({
2185
+ level: "warn",
2186
+ event: "provider_locale_catalogs_unavailable",
2187
+ providerId: provider.id,
2188
+ reason: sanitizeDiagnosticText(error instanceof Error ? error.message : String(error)),
2189
+ });
2049
2190
  return undefined;
2050
2191
  }
2051
2192
  }
2052
2193
  return undefined;
2053
2194
  }
2054
- function materializeAuthFlowTurn(provider, request, turn) {
2055
- const catalogs = loadAuthFlowLocaleCatalogs(provider);
2195
+ function materializeAuthFlowTurn(turn, catalogs, locale) {
2056
2196
  if (!catalogs)
2057
2197
  return turn;
2058
- return localizeAuthTurn(turn, {
2059
- catalogs,
2060
- locale: authFlowLocaleFromHeaders(request.headers),
2061
- });
2198
+ return localizeAuthTurn(turn, { catalogs, locale });
2062
2199
  }
2063
2200
  function withAuthRequestHeaders(request, headers) {
2064
2201
  return {
@@ -2137,7 +2274,15 @@ async function handleOperation(provider, request, operationId, options, state =
2137
2274
  await cleanup();
2138
2275
  }
2139
2276
  }
2140
- async function handleAuthFlow(provider, request, route, options, state, scope, signal) {
2277
+ async function handleAuthFlow(provider, request, route, options, state, scope,
2278
+ /**
2279
+ * Negotiated once per request by the route so a successful turn and an error
2280
+ * envelope from the same request can never disagree. `withAuthRequestHeaders`
2281
+ * lets the HTTP header shadow the envelope's `Accept-Language`, but the
2282
+ * gateway stamps the flow's start locale into the envelope precisely so
2283
+ * continue/poll turns stay in one language, so the envelope wins here.
2284
+ */
2285
+ locale, signal) {
2141
2286
  const flow = provider.auth?.flow;
2142
2287
  if (!flow) {
2143
2288
  throw new ProviderError("Auth flow is not configured", {
@@ -2179,7 +2324,7 @@ async function handleAuthFlow(provider, request, route, options, state, scope, s
2179
2324
  !(result instanceof Response) &&
2180
2325
  !(result instanceof ReadableStream) &&
2181
2326
  isAuthTurn(result)
2182
- ? materializeAuthFlowTurn(provider, request, result)
2327
+ ? materializeAuthFlowTurn(result, options.localeCatalogs, locale)
2183
2328
  : result;
2184
2329
  return toAuthFlowResponse(materializedResult, getPatch(), getEngineState());
2185
2330
  }
@@ -2301,7 +2446,7 @@ function operationRequestFromForwardingEnvelope(envelope) {
2301
2446
  };
2302
2447
  }
2303
2448
  function parseStatefulForwardingEnvelope(rawBody, redact) {
2304
- const parsed = ProviderServerStatefulForwardEnvelopeSchema.safeParse(rawBody);
2449
+ const parsed = ReceivedStatefulForwardEnvelopeSchema.safeParse(rawBody);
2305
2450
  if (parsed.success)
2306
2451
  return parsed.data;
2307
2452
  throw new ProviderError("Stateful forwarding envelope is invalid.", {
@@ -2333,12 +2478,59 @@ export function createServerApp(provider, options = {}) {
2333
2478
  validateStatefulServerConfig(runtimeOptions);
2334
2479
  return createServerAppWithCapabilityModules(runtimeProvider, runtimeOptions, loadProviderCapabilityModulesSync(runtimeProvider));
2335
2480
  }
2481
+ /**
2482
+ * Resolve the engine once at boot, before any route exists. An explicit engine
2483
+ * object is used as given; otherwise the resolved mode selects the engine.
2484
+ *
2485
+ * Deliberately never throws for deployment-authored input: a mode this release
2486
+ * cannot serve yields an engine that fails every request and a `/readyz` 503,
2487
+ * because a boot crash would trade a structured signal for CrashLoopBackOff
2488
+ * across the fleet (same trade-off as `provider_secrets_missing` above).
2489
+ */
2490
+ function resolveServerEngine(serverOptions) {
2491
+ const resolution = resolveProviderEngineMode({
2492
+ option: serverOptions.engineMode,
2493
+ engine: serverOptions.engine,
2494
+ });
2495
+ if (serverOptions.engine !== undefined)
2496
+ return { engine: serverOptions.engine, resolution };
2497
+ return { engine: createEngineForMode(resolution.mode), resolution };
2498
+ }
2499
+ /**
2500
+ * `engine_env_ignored` is expected for the whole migration window: the manifest
2501
+ * generator projects the remote engine env fleet-wide before any mode flips, so
2502
+ * escalating on it would turn every healthy boot into a warn line. The
2503
+ * disagreement warnings still escalate.
2504
+ */
2505
+ const ENGINE_MODE_INFO_WARNINGS = new Set(["engine_env_ignored"]);
2506
+ function engineModeLogEvent(provider, resolution, attached) {
2507
+ const escalating = resolution.warnings.filter((warning) => !ENGINE_MODE_INFO_WARNINGS.has(warning));
2508
+ return {
2509
+ level: attached && escalating.length === 0 ? "info" : "warn",
2510
+ event: "provider_engine_mode",
2511
+ providerId: provider.id,
2512
+ mode: resolution.mode,
2513
+ source: resolution.source,
2514
+ deprecated: resolution.deprecated,
2515
+ attached,
2516
+ sdkVersion: SDK_VERSION,
2517
+ ...(provider.runtimeTarget === undefined ? {} : { runtimeTarget: provider.runtimeTarget }),
2518
+ ...(resolution.warnings.length === 0 ? {} : { warnings: resolution.warnings }),
2519
+ };
2520
+ }
2336
2521
  function createServerAppWithCapabilityModules(provider, serverOptions, capabilityModules) {
2522
+ const engineSelection = resolveServerEngine(serverOptions);
2523
+ // Read the provider's locale catalogs once here rather than per turn: the
2524
+ // previous auth-flow loader re-read all three JSON files on every turn and
2525
+ // threw the whole map away when one locale file was absent.
2526
+ const localeCatalogs = resolveProviderLocaleCatalogs(provider, serverOptions, serverOptions.logger ?? defaultProviderServerLogger);
2337
2527
  const options = {
2338
2528
  ...serverOptions,
2529
+ ...(localeCatalogs ? { localeCatalogs } : {}),
2339
2530
  capabilityModules,
2340
- engine: serverOptions.engine ?? createInProcessProviderEngine(),
2531
+ engine: engineSelection.engine,
2341
2532
  };
2533
+ const engineAttached = !isUnavailableProviderEngine(options.engine);
2342
2534
  const app = new Hono();
2343
2535
  // Compile the startup inventory once, composed with the shared outside-context fallback.
2344
2536
  const staticSensitiveValues = compileProcessDiagnosticSensitiveValues(collectStaticDiagnosticSensitiveValues(provider, {
@@ -2370,13 +2562,33 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
2370
2562
  app.notFound((c) => {
2371
2563
  const error = new ProviderError("Not found", { code: "not_found", retryable: false });
2372
2564
  const observabilityDetails = errorObservabilityDetails(error);
2373
- return responseWithErrorObservability(c.json(toErrorResponse(error, undefined, observabilityDetails), 404), observabilityDetails);
2565
+ return responseWithErrorObservability(c.json(toErrorResponse(error, undefined, observabilityDetails, undefined, errorEnvelopeLocalization({
2566
+ catalogs: localeCatalogs,
2567
+ requestHeaders: c.req.raw.headers,
2568
+ })), 404), observabilityDetails);
2374
2569
  });
2570
+ // Liveness. Deliberately static and engine-blind: generated manifests point
2571
+ // liveness at this route, so making it depend on engine reachability would
2572
+ // restart every provider pod on one engine incident. Engine state belongs to
2573
+ // /readyz, which manifests wire to readiness and startup only.
2375
2574
  app.get("/health", (c) => c.json({
2376
2575
  status: "ok",
2377
2576
  provider: provider.id,
2378
2577
  version: provider.version,
2379
2578
  }));
2579
+ // Readiness. Reports the boot-resolved attachment; does not probe the engine
2580
+ // per call, so it cannot amplify an engine incident into request load.
2581
+ app.get("/readyz", (c) => c.json({
2582
+ status: engineAttached ? "ok" : "unavailable",
2583
+ provider: provider.id,
2584
+ version: provider.version,
2585
+ engine: {
2586
+ // The resolution, not the raw engine `kind`: normalized, so this
2587
+ // body and the `provider_engine_mode` boot event never disagree.
2588
+ mode: engineSelection.resolution.mode,
2589
+ attached: engineAttached,
2590
+ },
2591
+ }, engineAttached ? 200 : 503));
2380
2592
  app.post(STATEFUL_INTERNAL_OPERATIONS_ROUTE, async (c) => {
2381
2593
  let rawBodyText = "";
2382
2594
  let rawBody;
@@ -2482,7 +2694,10 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
2482
2694
  requestId: request.requestId,
2483
2695
  operationId,
2484
2696
  headers: request.headers ?? {},
2485
- correlation: { connectionId: envelope.connectionId },
2697
+ correlation: {
2698
+ connectionId: envelope.connectionId,
2699
+ tenantId: resolveOperationTenantId(request),
2700
+ },
2486
2701
  });
2487
2702
  const response = await requestScope.run(async () => {
2488
2703
  const ctx = createProviderContext(provider, request, operationId, options, state, requestScope, signal);
@@ -2517,7 +2732,12 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
2517
2732
  ...(requestId ? { requestId } : {}),
2518
2733
  });
2519
2734
  const observabilityDetails = errorObservabilityDetails(error, declaredErrorCode);
2520
- const response = c.json(toErrorResponse(error, requestId, observabilityDetails, requestScope.redact), status);
2735
+ const response = c.json(toErrorResponse(error, requestId, observabilityDetails, requestScope.redact, errorEnvelopeLocalization({
2736
+ catalogs: localeCatalogs,
2737
+ requestHeaders: c.req.raw.headers,
2738
+ rawBody,
2739
+ ...(declaredErrorCode ? { declaredErrorCode } : {}),
2740
+ })), status);
2521
2741
  return finalizeRequestResponse(requestScope, response, {
2522
2742
  kind: "failed",
2523
2743
  status,
@@ -2551,7 +2771,10 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
2551
2771
  requestScope.enrich({
2552
2772
  requestId: body.requestId,
2553
2773
  headers: body.headers,
2554
- correlation: { connectionId: resolveOperationConnectionId(body) },
2774
+ correlation: {
2775
+ connectionId: resolveOperationConnectionId(body),
2776
+ tenantId: resolveOperationTenantId(body),
2777
+ },
2555
2778
  });
2556
2779
  const streaming = provider.operations[operation]?.transport?.kind
2557
2780
  ? provider.operations[operation]?.transport?.kind !== "json"
@@ -2581,7 +2804,12 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
2581
2804
  const requestId = extractRequestId(rawBody);
2582
2805
  requestScope.enrich({ ...(requestId ? { requestId } : {}) });
2583
2806
  const observabilityDetails = errorObservabilityDetails(error, declaredErrorCode);
2584
- const response = c.json(toErrorResponse(error, requestId, observabilityDetails, requestScope.redact), status);
2807
+ const response = c.json(toErrorResponse(error, requestId, observabilityDetails, requestScope.redact, errorEnvelopeLocalization({
2808
+ catalogs: localeCatalogs,
2809
+ requestHeaders: c.req.raw.headers,
2810
+ rawBody,
2811
+ ...(declaredErrorCode ? { declaredErrorCode } : {}),
2812
+ })), status);
2585
2813
  return finalizeRequestResponse(requestScope, response, {
2586
2814
  kind: "failed",
2587
2815
  status,
@@ -2599,6 +2827,12 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
2599
2827
  for (const { path, flowRoute, logRoute } of authRoutes) {
2600
2828
  app.post(path, async (c) => {
2601
2829
  let rawBody;
2830
+ // Negotiated once and shared by the success and failure paths so an
2831
+ // auth turn and an error envelope from the same request agree.
2832
+ let localization = errorEnvelopeLocalization({
2833
+ catalogs: localeCatalogs,
2834
+ requestHeaders: c.req.raw.headers,
2835
+ });
2602
2836
  const requestScope = createRequestScope({
2603
2837
  provider,
2604
2838
  staticSensitiveValues,
@@ -2613,6 +2847,11 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
2613
2847
  .clone()
2614
2848
  .json()
2615
2849
  .catch(() => undefined);
2850
+ localization = errorEnvelopeLocalization({
2851
+ catalogs: localeCatalogs,
2852
+ requestHeaders: c.req.raw.headers,
2853
+ rawBody,
2854
+ });
2616
2855
  requestScope.seedCredentials(rawBody, "auth");
2617
2856
  const body = withAuthRequestHeaders(AuthFlowRequestSchema.parse(rawBody), c.req.raw.headers);
2618
2857
  requestScope.enrich({
@@ -2622,14 +2861,14 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
2622
2861
  correlation: {
2623
2862
  connectionId: resolveOperationConnectionId(body),
2624
2863
  flowId: body.flowId,
2625
- tenantId: body.tenantId,
2864
+ tenantId: normalizeIdentifier(body.tenantId),
2626
2865
  requestedProviderId: body.providerId !== undefined && body.providerId !== provider.id
2627
2866
  ? body.providerId
2628
2867
  : undefined,
2629
2868
  },
2630
2869
  });
2631
2870
  const response = await requestScope.run(async () => {
2632
- const handled = await handleAuthFlow(provider, body, flowRoute, options, state, requestScope, c.req.raw.signal);
2871
+ const handled = await handleAuthFlow(provider, body, flowRoute, options, state, requestScope, localization.locale, c.req.raw.signal);
2633
2872
  return handled instanceof Response ? handled : c.json(handled);
2634
2873
  });
2635
2874
  return finalizeRequestResponse(requestScope, response, {
@@ -2642,7 +2881,7 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
2642
2881
  const requestId = extractRequestId(rawBody);
2643
2882
  requestScope.enrich({ ...(requestId ? { requestId } : {}) });
2644
2883
  const observabilityDetails = errorObservabilityDetails(error);
2645
- const response = c.json(toErrorResponse(error, requestId, observabilityDetails, requestScope.redact), status);
2884
+ const response = c.json(toErrorResponse(error, requestId, observabilityDetails, requestScope.redact, localization), status);
2646
2885
  return finalizeRequestResponse(requestScope, response, {
2647
2886
  kind: "failed",
2648
2887
  status,
@@ -2704,13 +2943,18 @@ export async function serve(provider, options = {}) {
2704
2943
  const configuredTimeoutMs = shutdownTimeout(options.shutdown?.timeoutMs ?? DEFAULT_SHUTDOWN_TIMEOUT_MS);
2705
2944
  const configuredSignals = resolveShutdownSignals(options.shutdown?.signals ?? true);
2706
2945
  const selfTestSecrets = resolveSelfTestMasterSecrets();
2946
+ // Resolve the engine here so the boot event reports the truthful source
2947
+ // (option/env/default) and the app is handed the engine already selected —
2948
+ // one resolution per process, not one per app construction.
2949
+ const engineSelection = resolveServerEngine(options);
2707
2950
  const serverAppOptions = {
2708
2951
  logger: options.logger,
2709
- engine: options.engine,
2952
+ engine: engineSelection.engine,
2710
2953
  files: options.files,
2711
2954
  ocr: options.ocr,
2712
2955
  stt: options.stt,
2713
2956
  resolver: options.resolver,
2957
+ localeCatalogs: options.localeCatalogs,
2714
2958
  state: options.state,
2715
2959
  allowMemoryStateFallback: options.allowMemoryStateFallback,
2716
2960
  operationExecutor: options.operationExecutor,
@@ -2721,6 +2965,9 @@ export async function serve(provider, options = {}) {
2721
2965
  createServerAppAsync(provider, serverAppOptions),
2722
2966
  selfTestSecrets ? import("./self-test.js") : undefined,
2723
2967
  ]);
2968
+ // One structured line per process boot, before the listener binds: the fleet
2969
+ // audit (ADR-0011 migration) counts these.
2970
+ logger(engineModeLogEvent(provider, engineSelection.resolution, !isUnavailableProviderEngine(engineSelection.engine)));
2724
2971
  const servers = [];
2725
2972
  try {
2726
2973
  servers.push(bunRuntime.serve({