@apifuse/provider-sdk 2.2.0-beta.2 → 2.2.0-beta.21

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 (291) hide show
  1. package/AUTHORING.md +487 -0
  2. package/CHANGELOG.md +90 -0
  3. package/README.md +49 -3
  4. package/SUBMISSION.md +1 -1
  5. package/bin/apifuse-check.ts +44 -59
  6. package/bin/apifuse-create.ts +1 -1
  7. package/bin/apifuse-dev.ts +27 -52
  8. package/bin/apifuse-pack-check.ts +14 -0
  9. package/bin/apifuse-pack-smoke.ts +36 -81
  10. package/bin/apifuse-pack-types.ts +305 -0
  11. package/bin/apifuse-perf.ts +45 -127
  12. package/bin/apifuse-record.ts +659 -111
  13. package/bin/apifuse-submit-check.ts +546 -48
  14. package/bin/apifuse-sync-assets.ts +117 -0
  15. package/bin/apifuse.ts +1 -1
  16. package/bin/submit-check-delimited-text.ts +50 -0
  17. package/bin/submit-check-xml-semantics.ts +204 -0
  18. package/bin/submit-check-xml.ts +134 -0
  19. package/dist/auth-turn/index.d.ts +3 -3
  20. package/dist/auth.d.ts +2 -2
  21. package/dist/auth.js +9 -18
  22. package/dist/ceremonies/index.d.ts +9 -1
  23. package/dist/ceremonies/index.js +65 -18
  24. package/dist/cli/commands.d.ts +1 -1
  25. package/dist/cli/commands.js +8 -0
  26. package/dist/cli/create.d.ts +3 -0
  27. package/dist/cli/create.js +34 -35
  28. package/dist/cli/prompt-assets.d.ts +80 -0
  29. package/dist/cli/prompt-assets.js +743 -0
  30. package/dist/cli/templates/provider/AGENTS.md.tpl +17 -8
  31. package/dist/config/loader.d.ts +176 -8
  32. package/dist/config/loader.js +424 -95
  33. package/dist/contract-serialization.d.ts +2 -2
  34. package/dist/contract-serialization.js +3 -6
  35. package/dist/contract-types.d.ts +2 -2
  36. package/dist/contract.d.ts +3 -3
  37. package/dist/contract.js +4 -6
  38. package/dist/define.d.ts +9 -1
  39. package/dist/define.js +304 -119
  40. package/dist/dev.d.ts +1 -1
  41. package/dist/dev.js +1 -1
  42. package/dist/error-resolution.d.ts +2 -0
  43. package/dist/error-resolution.js +90 -0
  44. package/dist/errors.d.ts +22 -1
  45. package/dist/errors.js +88 -0
  46. package/dist/fixture-sanitization.d.ts +26 -0
  47. package/dist/fixture-sanitization.js +216 -0
  48. package/dist/i18n/catalog.d.ts +2 -2
  49. package/dist/i18n/catalog.js +4 -10
  50. package/dist/i18n/index.d.ts +2 -2
  51. package/dist/i18n/index.js +2 -2
  52. package/dist/i18n/keys.d.ts +2 -2
  53. package/dist/index.d.ts +46 -42
  54. package/dist/index.js +41 -37
  55. package/dist/lint.d.ts +1 -1
  56. package/dist/lint.js +8 -15
  57. package/dist/native-address.d.ts +43 -0
  58. package/dist/native-address.js +281 -0
  59. package/dist/native-egress-policy.d.ts +31 -0
  60. package/dist/native-egress-policy.js +288 -0
  61. package/dist/observability.d.ts +5 -2
  62. package/dist/observability.js +48 -1
  63. package/dist/provider.d.ts +12 -11
  64. package/dist/provider.js +10 -9
  65. package/dist/public-schema-field-lint.d.ts +1 -1
  66. package/dist/recipes/gov-api.js +1 -1
  67. package/dist/runtime/auth-flow.d.ts +2 -1
  68. package/dist/runtime/auth-flow.js +4 -3
  69. package/dist/runtime/browser.d.ts +1 -1
  70. package/dist/runtime/browser.js +15 -29
  71. package/dist/runtime/cache.d.ts +1 -1
  72. package/dist/runtime/cache.js +4 -8
  73. package/dist/runtime/choice.d.ts +1 -1
  74. package/dist/runtime/choice.js +31 -35
  75. package/dist/runtime/credential.d.ts +1 -1
  76. package/dist/runtime/credential.js +1 -1
  77. package/dist/runtime/env.d.ts +1 -1
  78. package/dist/runtime/executor.d.ts +1 -1
  79. package/dist/runtime/executor.js +31 -4
  80. package/dist/runtime/http.d.ts +2 -2
  81. package/dist/runtime/http.js +387 -47
  82. package/dist/runtime/insights.d.ts +1 -1
  83. package/dist/runtime/insights.js +6 -13
  84. package/dist/runtime/instrumentation.d.ts +2 -2
  85. package/dist/runtime/instrumentation.js +345 -22
  86. package/dist/runtime/keyring.js +1 -1
  87. package/dist/runtime/namespace.js +1 -1
  88. package/dist/runtime/native-network.d.ts +127 -0
  89. package/dist/runtime/native-network.js +1298 -0
  90. package/dist/runtime/otlp.d.ts +1 -1
  91. package/dist/runtime/perf.d.ts +1 -1
  92. package/dist/runtime/provider.d.ts +1 -1
  93. package/dist/runtime/provider.js +1 -2
  94. package/dist/runtime/proxy-errors.d.ts +1 -1
  95. package/dist/runtime/proxy-errors.js +9 -7
  96. package/dist/runtime/proxy-nodemaven.d.ts +56 -0
  97. package/dist/runtime/proxy-nodemaven.js +146 -0
  98. package/dist/runtime/proxy-retry-policy.d.ts +2 -2
  99. package/dist/runtime/proxy-retry-policy.js +2 -2
  100. package/dist/runtime/proxy-telemetry.d.ts +2 -1
  101. package/dist/runtime/proxy-telemetry.js +55 -52
  102. package/dist/runtime/redirects.d.ts +29 -0
  103. package/dist/runtime/redirects.js +36 -0
  104. package/dist/runtime/redis.d.ts +1 -1
  105. package/dist/runtime/redis.js +2 -4
  106. package/dist/runtime/request-options.d.ts +68 -1
  107. package/dist/runtime/request-options.js +548 -0
  108. package/dist/runtime/secrets.d.ts +27 -0
  109. package/dist/runtime/secrets.js +51 -0
  110. package/dist/runtime/state.d.ts +2 -2
  111. package/dist/runtime/state.js +238 -26
  112. package/dist/runtime/stealth.d.ts +5 -3
  113. package/dist/runtime/stealth.js +423 -143
  114. package/dist/runtime/stt.d.ts +1 -1
  115. package/dist/runtime/stt.js +11 -15
  116. package/dist/runtime/trace.d.ts +2 -2
  117. package/dist/runtime/trace.js +2 -4
  118. package/dist/runtime/waterfall.d.ts +1 -1
  119. package/dist/schema.d.ts +1 -1
  120. package/dist/schema.js +7 -15
  121. package/dist/serve.d.ts +1 -1
  122. package/dist/serve.js +1 -1
  123. package/dist/server/index.d.ts +7 -7
  124. package/dist/server/index.js +6 -6
  125. package/dist/server/self-test-input-tokens.d.ts +2 -1
  126. package/dist/server/self-test-input-tokens.js +18 -14
  127. package/dist/server/self-test-redaction.d.ts +1 -1
  128. package/dist/server/self-test-redaction.js +1 -1
  129. package/dist/server/self-test.d.ts +104 -3
  130. package/dist/server/self-test.js +673 -115
  131. package/dist/server/serve.d.ts +116 -4
  132. package/dist/server/serve.js +799 -128
  133. package/dist/server/types.d.ts +34 -9
  134. package/dist/server/types.js +8 -1
  135. package/dist/stateful/errors.d.ts +14 -0
  136. package/dist/stateful/errors.js +14 -0
  137. package/dist/stateful/http-provider-event-emitter.d.ts +40 -0
  138. package/dist/stateful/http-provider-event-emitter.js +237 -0
  139. package/dist/stateful/http-session-owner-registry.d.ts +44 -0
  140. package/dist/stateful/http-session-owner-registry.js +210 -0
  141. package/dist/stateful/index.d.ts +18 -0
  142. package/dist/stateful/index.js +18 -0
  143. package/dist/stateful/provider-event-delivery-failures.d.ts +32 -0
  144. package/dist/stateful/provider-event-delivery-failures.js +43 -0
  145. package/dist/stateful/provider-event-pipeline-metrics.d.ts +46 -0
  146. package/dist/stateful/provider-event-pipeline-metrics.js +48 -0
  147. package/dist/stateful/provider-event-pipeline.d.ts +50 -0
  148. package/dist/stateful/provider-event-pipeline.js +1 -0
  149. package/dist/stateful/provider-events.d.ts +101 -0
  150. package/dist/stateful/provider-events.js +289 -0
  151. package/dist/stateful/session-key.d.ts +15 -0
  152. package/dist/stateful/session-key.js +86 -0
  153. package/dist/stateful/stateful-provider-adapter-context.d.ts +5 -0
  154. package/dist/stateful/stateful-provider-adapter-context.js +42 -0
  155. package/dist/stateful/stateful-provider-adapter-metrics.d.ts +15 -0
  156. package/dist/stateful/stateful-provider-adapter-metrics.js +21 -0
  157. package/dist/stateful/stateful-provider-adapter.d.ts +98 -0
  158. package/dist/stateful/stateful-provider-adapter.js +287 -0
  159. package/dist/stateful/stateful-provider-observability.d.ts +62 -0
  160. package/dist/stateful/stateful-provider-observability.js +161 -0
  161. package/dist/stateful/stateful-provider-owner-forwarder.d.ts +41 -0
  162. package/dist/stateful/stateful-provider-owner-forwarder.js +215 -0
  163. package/dist/stateful/stateful-provider-runtime-context.d.ts +32 -0
  164. package/dist/stateful/stateful-provider-runtime-context.js +60 -0
  165. package/dist/stateful/stateful-provider-runtime-executor.d.ts +34 -0
  166. package/dist/stateful/stateful-provider-runtime-executor.js +52 -0
  167. package/dist/stateful/stateful-provider-session-routing.d.ts +71 -0
  168. package/dist/stateful/stateful-provider-session-routing.js +353 -0
  169. package/dist/stateful/stateful-provider-session-runtime.d.ts +98 -0
  170. package/dist/stateful/stateful-provider-session-runtime.js +245 -0
  171. package/dist/stateful-signing.d.ts +18 -0
  172. package/dist/stateful-signing.js +27 -0
  173. package/dist/stealth/profiles.d.ts +1 -1
  174. package/dist/stealth/profiles.js +5 -14
  175. package/dist/stream-evidence.d.ts +74 -0
  176. package/dist/stream-evidence.js +785 -0
  177. package/dist/stream.d.ts +1 -1
  178. package/dist/testing/index.d.ts +2 -2
  179. package/dist/testing/index.js +2 -2
  180. package/dist/testing/run.d.ts +32 -2
  181. package/dist/testing/run.js +478 -28
  182. package/dist/types.d.ts +342 -14
  183. package/dist/types.js +1 -0
  184. package/dist/user-input.d.ts +30 -0
  185. package/dist/user-input.js +66 -0
  186. package/package.json +16 -5
  187. package/src/auth-turn/index.ts +1 -1
  188. package/src/auth.ts +28 -86
  189. package/src/ceremonies/index.ts +103 -78
  190. package/src/cli/commands.ts +10 -0
  191. package/src/cli/create.ts +42 -35
  192. package/src/cli/prompt-assets.ts +865 -0
  193. package/src/cli/templates/provider/AGENTS.md.tpl +17 -8
  194. package/src/config/loader.ts +652 -204
  195. package/src/contract-serialization.ts +5 -11
  196. package/src/contract-types.ts +2 -2
  197. package/src/contract.ts +12 -28
  198. package/src/define.ts +499 -498
  199. package/src/dev.ts +4 -9
  200. package/src/error-resolution.ts +91 -0
  201. package/src/errors.ts +110 -5
  202. package/src/fixture-sanitization.ts +247 -0
  203. package/src/i18n/catalog.ts +10 -32
  204. package/src/i18n/index.ts +2 -2
  205. package/src/i18n/keys.ts +5 -11
  206. package/src/index.ts +112 -42
  207. package/src/lint.ts +88 -152
  208. package/src/native-address.ts +340 -0
  209. package/src/native-egress-policy.ts +358 -0
  210. package/src/observability.ts +51 -1
  211. package/src/provider.ts +66 -11
  212. package/src/public-schema-field-lint.ts +7 -33
  213. package/src/recipes/gov-api.ts +2 -5
  214. package/src/runtime/auth-flow.ts +7 -7
  215. package/src/runtime/browser.ts +64 -187
  216. package/src/runtime/cache.ts +20 -67
  217. package/src/runtime/choice.ts +79 -132
  218. package/src/runtime/credential.ts +2 -2
  219. package/src/runtime/env.ts +1 -1
  220. package/src/runtime/executor.ts +43 -20
  221. package/src/runtime/http.ts +494 -57
  222. package/src/runtime/insights.ts +15 -53
  223. package/src/runtime/instrumentation.ts +495 -66
  224. package/src/runtime/keyring.ts +7 -19
  225. package/src/runtime/namespace.ts +2 -7
  226. package/src/runtime/native-network.ts +1686 -0
  227. package/src/runtime/otlp.ts +12 -23
  228. package/src/runtime/perf.ts +1 -1
  229. package/src/runtime/provider.ts +4 -9
  230. package/src/runtime/proxy-errors.ts +29 -42
  231. package/src/runtime/proxy-nodemaven.ts +221 -0
  232. package/src/runtime/proxy-retry-policy.ts +3 -3
  233. package/src/runtime/proxy-telemetry.ts +79 -77
  234. package/src/runtime/redirects.ts +66 -0
  235. package/src/runtime/redis.ts +4 -12
  236. package/src/runtime/request-options.ts +679 -9
  237. package/src/runtime/secrets.ts +64 -0
  238. package/src/runtime/state.ts +353 -133
  239. package/src/runtime/stealth.ts +505 -154
  240. package/src/runtime/stt.ts +38 -94
  241. package/src/runtime/trace.ts +14 -44
  242. package/src/runtime/waterfall.ts +5 -18
  243. package/src/schema.ts +23 -84
  244. package/src/serve.ts +1 -1
  245. package/src/server/index.ts +29 -7
  246. package/src/server/self-test-input-tokens.ts +29 -14
  247. package/src/server/self-test-redaction.ts +2 -2
  248. package/src/server/self-test.ts +857 -132
  249. package/src/server/serve.ts +1151 -328
  250. package/src/server/types.ts +12 -13
  251. package/src/stateful/README.md +146 -0
  252. package/src/stateful/errors.ts +23 -0
  253. package/src/stateful/http-provider-event-emitter.ts +314 -0
  254. package/src/stateful/http-session-owner-registry.ts +306 -0
  255. package/src/stateful/index.ts +18 -0
  256. package/src/stateful/provider-event-delivery-failures.ts +80 -0
  257. package/src/stateful/provider-event-pipeline-metrics.ts +95 -0
  258. package/src/stateful/provider-event-pipeline.ts +61 -0
  259. package/src/stateful/provider-events.ts +462 -0
  260. package/src/stateful/session-key.ts +111 -0
  261. package/src/stateful/stateful-provider-adapter-context.ts +59 -0
  262. package/src/stateful/stateful-provider-adapter-metrics.ts +48 -0
  263. package/src/stateful/stateful-provider-adapter.ts +562 -0
  264. package/src/stateful/stateful-provider-observability.ts +261 -0
  265. package/src/stateful/stateful-provider-owner-forwarder.ts +287 -0
  266. package/src/stateful/stateful-provider-runtime-context.ts +92 -0
  267. package/src/stateful/stateful-provider-runtime-executor.ts +96 -0
  268. package/src/stateful/stateful-provider-session-routing.ts +555 -0
  269. package/src/stateful/stateful-provider-session-runtime.ts +403 -0
  270. package/src/stateful-signing.ts +46 -0
  271. package/src/stealth/profiles.ts +10 -26
  272. package/src/stream-evidence.ts +988 -0
  273. package/src/stream.ts +8 -19
  274. package/src/testing/index.ts +10 -2
  275. package/src/testing/run.ts +653 -74
  276. package/src/types.ts +408 -28
  277. package/src/user-input.ts +118 -0
  278. package/dist/cli/templates/provider/CLAUDE.md.tpl +0 -1
  279. package/src/cli/templates/provider/CLAUDE.md.tpl +0 -1
  280. /package/dist/cli/templates/provider/{skills → .agents/skills}/fixtures-and-recording/SKILL.md.tpl +0 -0
  281. /package/dist/cli/templates/provider/{skills → .agents/skills}/health-checks-and-fail-closed/SKILL.md.tpl +0 -0
  282. /package/dist/cli/templates/provider/{skills → .agents/skills}/normalization-standards/SKILL.md.tpl +0 -0
  283. /package/dist/cli/templates/provider/{skills → .agents/skills}/pagination-and-counts/SKILL.md.tpl +0 -0
  284. /package/dist/cli/templates/provider/{skills → .agents/skills}/upstream-contract-verification/SKILL.md.tpl +0 -0
  285. /package/dist/cli/templates/provider/{skills → .agents/skills}/upstream-notes/README.md.tpl +0 -0
  286. /package/src/cli/templates/provider/{skills → .agents/skills}/fixtures-and-recording/SKILL.md.tpl +0 -0
  287. /package/src/cli/templates/provider/{skills → .agents/skills}/health-checks-and-fail-closed/SKILL.md.tpl +0 -0
  288. /package/src/cli/templates/provider/{skills → .agents/skills}/normalization-standards/SKILL.md.tpl +0 -0
  289. /package/src/cli/templates/provider/{skills → .agents/skills}/pagination-and-counts/SKILL.md.tpl +0 -0
  290. /package/src/cli/templates/provider/{skills → .agents/skills}/upstream-contract-verification/SKILL.md.tpl +0 -0
  291. /package/src/cli/templates/provider/{skills → .agents/skills}/upstream-notes/README.md.tpl +0 -0
package/AUTHORING.md CHANGED
@@ -18,6 +18,59 @@
18
18
 
19
19
  Provider code is the declaration input to the internal platform registry. The public SDK owns provider authoring/runtime ergonomics; internal docs, deploy, and discovery projections are built downstream from those declarations. `bun run lint:providers` enforces provider authoring standards.
20
20
 
21
+ ### User-input round-trips: never dead-end (`needs_input`)
22
+
23
+ A mutation MUST NOT fail for a problem the end user can resolve by choosing among live options (a required menu/course selection, a form question, a stale-but-recoverable state token). Throwing an error there strands the consuming agent: consumer error-shaping layers routinely strip error metadata, and a model that only sees "error" narrates failure to the user instead of relaying the choice.
24
+
25
+ Return the official success-shaped contract from `src/user-input.ts` instead (`ProviderNeedsInputPayload`, guard `isProviderNeedsInputPayload`):
26
+
27
+ - `status: "needs_input"` plus `required_selections` — only the still-pending questions, with human-readable `label`s and `valid_options` the agent relays verbatim. The agent never chooses for the user; anything with no real choice (agreement checkboxes, single-option required groups) is the provider's job to auto-answer.
28
+ - `selected_options` — selections already settled, echoed so the retry keeps them (copy them back and add the user's new answers).
29
+ - a freshly minted provider state token in the same response, so the retry never races an expired token.
30
+
31
+ No retry templates, next-action routing, or other agent choreography: provider payloads carry upstream-backed data only, and the consumer owns how the ask is phrased and how the retry call is shaped.
32
+
33
+ Declare the union in the operation `output` schema (`z.union([CreatedSchema, NeedsInputSchema])`). Reserve hard errors for genuinely unrecoverable flows (payment-gated, unsupported input kinds) and state the concrete reason in the error `message` itself, not only in `details`. Reference implementation: `providers/catchtable` `reserve` in the platform monorepo.
34
+
35
+ ### Attempt tokens: the server carries the decisions
36
+
37
+ When a mutation needs more than one user decision (or one decision plus a
38
+ final go/no-go), do not make the agent re-send accumulated state across
39
+ rounds — weak models drop or corrupt it. Split the operation into a
40
+ **prepare/confirm pair** driven by a server-held attempt record:
41
+
42
+ - The prepare operation is non-destructive. A start call takes only the
43
+ scalar intent fields; every response returns a fresh `attempt_token`
44
+ referencing a server-side record (`ctx.choice.issue` with
45
+ `storage.mode: "server"`) that stores every settled decision. Continue
46
+ calls take `attempt_token` plus only the NEW answers.
47
+ - `needs_input` rounds list only the still-pending selections; settled
48
+ decisions may ride along in a display-only field but are never re-sent.
49
+ - When nothing is pending, the prepare operation returns `status: "ready"`
50
+ with a human-readable summary — the consumer's user-facing confirmation.
51
+ - The confirm operation is the only mutation and takes exactly
52
+ `{attempt_token}`. It re-validates everything live before executing and
53
+ returns `needs_input` (fresh token) instead of proceeding when upstream
54
+ drift invalidates a stored decision — never substitute a different option
55
+ for what the user picked.
56
+ - Expired or foreign tokens fail factually (nothing happened; start a new
57
+ attempt with the scalar fields) — no answer salvage from a dead token.
58
+ - **The provider must enforce consumption itself.** `ctx.choice` server
59
+ storage keeps tokens parseable until TTL — `parse` does not invalidate
60
+ them, so a confirm handler that only parses can be replayed into a second
61
+ booking or payment. After a successful execution, record the result under
62
+ the token's digest in `ctx.state` and make replays idempotent: a repeated
63
+ confirm returns the original created payload without touching upstream,
64
+ and later prepare rounds on the consumed token fail factually with the
65
+ existing reference. Record the result only after upstream success, so an
66
+ interrupted confirm stays retryable.
67
+
68
+ The invariant behind all of it: complex flow state is the system's job, not
69
+ the model's. The model carries exactly one opaque key between calls.
70
+ Reference implementations: `providers/catchtable` `reserve`/`reserve-confirm`
71
+ (including the consume-on-success guard) and `providers/modu-parking`
72
+ payment state tokens in the platform monorepo.
73
+
21
74
  ### Description template
22
75
 
23
76
  Every operation `description` MUST be at least 150 characters and follow this structure:
@@ -53,6 +106,90 @@ description:
53
106
 
54
107
  Use `defineOperation()` when an operation is large enough to live beside helper functions or in a separate module. It preserves the same type inference as inline `defineProvider()` operations and can be placed directly in the provider `operations` map. `defineProvider()` accepts Zod and Standard Schema v1-compatible schemas. If config validation fails, the SDK names the field to fix, for example `runtime`, `auth.mode`, `operations.<id>.handler`, or `operations.<id>.fixtures.response`.
55
108
 
109
+ ### Replay-safe fixtures
110
+
111
+ Keep public operation schemas strict: date fields should accept absolute dates,
112
+ not relative tokens. Inside `fixtures.request` only, the SDK resolves `+Nd` and
113
+ `+Nd:YYYYMMDD` (1–365 days ahead) before import-time schema validation and
114
+ stores the resolved request in provider metadata. Health-check case inputs use
115
+ the same resolver when a probe runs. The default calendar is **KST**, including
116
+ the 15:00–23:59 UTC window when KST is already on the next day.
117
+
118
+ `fixtures.recordedAt` is the KST `YYYY-MM-DD` date when the response evidence
119
+ was captured. It must be a real, non-future calendar date. Response date fields
120
+ are expected to align with `recordedAt`, not with the newly resolved request;
121
+ this permits stable recorded evidence alongside a replay-safe request.
122
+
123
+ ```ts
124
+ const FlightInput = z.object({
125
+ departureDate: z.string().date(), // public calls remain absolute-date only
126
+ });
127
+
128
+ const searchFlights = {
129
+ input: FlightInput,
130
+ output: FlightOutput,
131
+ async handler(ctx, input) {
132
+ return fetchAndNormalizeFlights(ctx, input);
133
+ },
134
+ fixtures: {
135
+ request: { departureDate: "+45d" },
136
+ response: recordedFlightResponse, // dates reflect the capture below
137
+ recordedAt: "2026-07-15",
138
+ },
139
+ healthCheckUnsupported: { reason: "Upstream search is cost-bearing." },
140
+ };
141
+ ```
142
+
143
+ For code that explicitly calls the shared resolver, omit the third argument to
144
+ use KST or pass `"UTC"` deliberately:
145
+
146
+ ```ts
147
+ import { resolveHealthCheckInputDateTokens } from "@apifuse/provider-sdk/server";
148
+
149
+ const kstInput = resolveHealthCheckInputDateTokens({ date: "+45d" });
150
+ const utcInput = resolveHealthCheckInputDateTokens({ date: "+45d" }, new Date(), "UTC");
151
+ ```
152
+
153
+ Do not re-resolve a health assertion's dates in UTC when its case input used the
154
+ default KST calendar.
155
+
156
+ ### Real-handler E2E in standard tests
157
+
158
+ `runStandardTests(provider)` validates declarations and fixtures but reports a
159
+ per-operation warning because it has no handler E2E coverage. Opt in with an
160
+ `upstreamStub`: the runner calls each fixture-backed real handler with its
161
+ already-resolved fixture request, routes ProviderContext upstream transports to
162
+ the stub, and validates the result against the output schema. It never compares
163
+ the result to the recorded response because that evidence belongs to
164
+ `recordedAt`.
165
+
166
+ ```ts
167
+ import { runStandardTests } from "@apifuse/provider-sdk/testing";
168
+ import provider from "../index.js";
169
+
170
+ runStandardTests(provider, {
171
+ upstreamStub: ({ transport, method, url }) => {
172
+ if (
173
+ transport === "http" &&
174
+ method === "GET" &&
175
+ url === "https://api.example.test/flights"
176
+ ) {
177
+ return Response.json({ flights: [{ id: "fixture-flight" }] });
178
+ }
179
+ return undefined; // fails the test: live-network passthrough is forbidden
180
+ },
181
+ });
182
+ ```
183
+
184
+ The stub also identifies `stealth`, `browser`, and `native` interactions. Return
185
+ a Web `Response` or `{ status, headers, body }`; an unmatched call fails with
186
+ the operation, transport, and method named in the error. Browser handlers expose
187
+ method-level calls such as `goto`, `evaluate`, and `locator.click`, so provide a
188
+ canned result for each method the handler uses. Native connections similarly
189
+ identify `connectTcp`/`connectTls` and subsequent `write` calls. Direct global
190
+ `fetch` or socket usage is outside this ProviderContext seam and should not be
191
+ used by provider handlers.
192
+
56
193
  ### Health assertion context
57
194
 
58
195
  `healthCheck.cases[].assertions` receives a `HealthCheckAssertionContext` with
@@ -256,6 +393,200 @@ External contributors are expected to submit standalone Provider source plus:
256
393
  Maintainers own monorepo import under `providers/<id>/`, registry generation,
257
394
  deployment projection checks, and release workflows.
258
395
 
396
+ ### Error responses
397
+
398
+ Provider-server failures use a stable public envelope:
399
+
400
+ ```json
401
+ {
402
+ "error": {
403
+ "code": "UPSTREAM_ERROR",
404
+ "message": "The upstream service failed",
405
+ "requestId": "req_123",
406
+ "retryable": true,
407
+ "details": { "providerReason": "temporarily_unavailable" }
408
+ }
409
+ }
410
+ ```
411
+
412
+ `retryable` is always present on responses emitted by the current SDK. Set
413
+ `retryable` in the `ProviderError` options when the provider knows the answer;
414
+ an explicit `true` or `false` wins over the matching operation declaration and
415
+ SDK derivation. When it is omitted, `operations.<id>.docs.errorCodes[].retryable`
416
+ is used for a matching provider-owned code, followed by SDK derivation (which
417
+ defaults ordinary `ProviderError` values to `false`). During stateful rolling
418
+ upgrades, the forwarding client also accepts an older owner response that omits
419
+ `retryable` and treats it as `false` without loosening the emitted response
420
+ contract. Existing optional `fix` guidance is also preserved when a
421
+ `ProviderError` supplies it.
422
+
423
+ `details` belongs exclusively to the provider. The server passes
424
+ `ProviderError.options.details` through verbatim, including strings and arrays,
425
+ and never merges, overwrites, or wraps it. Do not put SDK taxonomy fields there.
426
+ SDK-owned validation and masked-internal-error paths retain their own diagnostic
427
+ details.
428
+
429
+ SDK observability is emitted separately in the
430
+ `X-ApiFuse-Error-Observability` response header as compact, single-line JSON:
431
+
432
+ ```json
433
+ {"category":"upstream_http","taxonomyVersion":"2026-05-26","retryable":true,"upstreamStatus":502}
434
+ ```
435
+
436
+ Treat this header as telemetry, not as provider-controlled public error detail.
437
+ Its category, taxonomy version, retryability, and optional upstream status match
438
+ the structured `provider_request_failed` log event.
439
+
440
+ Declare provider-owned operation failures next to their documentation. The
441
+ server builds a lookup once at startup and applies it to failures from that
442
+ operation:
443
+
444
+ ```ts
445
+ docs: {
446
+ errorCodes: [{
447
+ code: "UPSTREAM_SCHEMA_ERROR",
448
+ status: 502,
449
+ retryable: true,
450
+ description: "The upstream response no longer matches its schema.",
451
+ }],
452
+ },
453
+ handler: async () => {
454
+ throw new ProviderError("Upstream schema changed", {
455
+ code: "UPSTREAM_SCHEMA_ERROR",
456
+ });
457
+ },
458
+ ```
459
+
460
+ `defineProvider` accepts only statuses the server can emit: 400, 401, 404, 429,
461
+ 500, 502, 503, and 504. Invalid declared statuses fail provider definition,
462
+ not a live request. Status selection uses this order:
463
+
464
+ 1. SDK-owned errors retain SDK status semantics. Operation declarations cannot
465
+ override SDK-owned codes, stateful-forwarding failures, Zod/deadline errors,
466
+ or `TransportError` values.
467
+ 2. A matching operation `errorCodes` entry with `status` supplies the status.
468
+ This slot applies to `ValidationError` as well as ordinary `ProviderError`.
469
+ 3. The registered mappings below apply.
470
+ 4. Existing fallbacks apply: `TransportError` 502/504, unregistered input
471
+ `ValidationError` 400 (output validation 500), and other unregistered
472
+ `ProviderError` values 500.
473
+
474
+ The registered mappings are:
475
+
476
+ | Error code or fallback | HTTP status |
477
+ | --- | ---: |
478
+ | `AUTH_REQUIRED`, `reauth_required` | 401 |
479
+ | `MISSING_SECRET` | 400 |
480
+ | `NOT_FOUND`, `not_found`, `NO_DATA` | 404 |
481
+ | `RATE_LIMITED`, `UPSTREAM_RATE_LIMIT`, `LIMITED_NUMBER_OF_SERVICE_REQUESTS_EXCEEDS_ERROR` | 429 |
482
+ | `UPSTREAM_ERROR`, `BLOCKED` | 502 |
483
+ | `STT_UNAVAILABLE`, `UNSUPPORTED_STT_BACKEND`, `STATEFUL_FORWARDING_REPLAY_CACHE_FULL` | 503 |
484
+ | Unregistered input `ValidationError` code | 400 |
485
+ | Other unregistered `ProviderError` code | 500 |
486
+
487
+ An unregistered non-validation `ProviderError` code returns HTTP 500 and emits
488
+ the greppable `unregistered_provider_error_code` signal with the code in the
489
+ structured failure log. A matching operation declaration, including one that
490
+ omits `status`, makes the code registered for this signal and may independently
491
+ supply `retryable`. The HTTP 400 `ValidationError` behavior is only the fallback
492
+ when neither an operation status nor a registered mapping applies.
493
+
494
+ Throw the domain `ProviderError` directly. Subclassing or wrapping it as a
495
+ `TransportError` solely to preserve a 5xx response is obsolete; declare the
496
+ domain code's `status` instead. Genuine `TransportError` values remain
497
+ SDK-owned and keep their 502/504 mapping.
498
+
499
+ ### Declared secrets are SDK-enforced
500
+
501
+ Environment/secret presence validation is single-sourced in the SDK. Declare
502
+ every env secret the provider needs in `defineProvider`:
503
+
504
+ ```ts
505
+ secrets: [
506
+ {
507
+ name: "APIFUSE__PROVIDER__MY_PROVIDER__API_KEY",
508
+ required: true,
509
+ description: "Upstream API key from the vendor portal",
510
+ },
511
+ ],
512
+ ```
513
+
514
+ The runtime validates every `required: true` declaration before any operation
515
+ handler or auth-flow handler (except `abort`) runs. When a required secret is
516
+ unset or whitespace-only, the invocation fails with the canonical structured
517
+ error — code `MISSING_SECRET`, HTTP 400, top-level `retryable: false`, and a
518
+ `fix` naming every missing secret — across `/v1/{operation}`, self-test probes,
519
+ `apifuse perf`, and `apifuse record`. Its error-observability header carries the
520
+ `credential_unavailable` category. The server also emits a
521
+ `provider_secrets_missing` warn log at boot so unprovisioned deployments are
522
+ visible immediately without crashing the pod.
523
+
524
+ Provider-local presence re-validation is **deprecated**: do not write
525
+ `requireServiceKey`/`requireApiKey`-style guards that re-check `ctx.env.get()`
526
+ and throw a hand-rolled `CONFIGURATION_ERROR`/`MISSING_SECRET`. Those guards
527
+ are dead weight (the SDK gate runs first) and historically diverged into
528
+ inconsistent error shapes. The `sdk-owned-secret-presence` submit-check rule
529
+ flags them at warn level; acknowledge a deliberate exception with
530
+ `// @apifuse-allow sdk-owned-secret-presence: <reason>`.
531
+
532
+ ```ts
533
+ // Before (deprecated): provider-local double validation
534
+ function requireServiceKey(ctx: ProviderContext): string {
535
+ const value = ctx.env.get(SERVICE_KEY_ENV);
536
+ if (!value?.trim()) {
537
+ throw new ProviderError(`Missing required provider secret: ${SERVICE_KEY_ENV}`, {
538
+ code: "CONFIGURATION_ERROR",
539
+ });
540
+ }
541
+ return value;
542
+ }
543
+
544
+ // After: declare { name: SERVICE_KEY_ENV, required: true } and read directly.
545
+ const serviceKey = ctx.env.get(SERVICE_KEY_ENV);
546
+ ```
547
+
548
+ Note the asymmetry: the gate treats whitespace-only values as missing, but
549
+ `ctx.env.get()` still returns the raw value to handlers — trim at the point of
550
+ use if the upstream is whitespace-sensitive.
551
+
552
+ ### Credentials forced into query parameters
553
+
554
+ Prefer an authorization header or request body whenever the upstream supports
555
+ one. When the upstream requires a credential in the URL query (for example
556
+ `serviceKey`, `confmKey`, or `crtfc_key`), use `sensitiveParams`:
557
+
558
+ ```ts
559
+ const response = await ctx.http.get("/openapi/lookup", {
560
+ params: { pageNo: 1, numOfRows: 100 },
561
+ sensitiveParams: {
562
+ serviceKey: ctx.env.get("APIFUSE__PROVIDER__EXAMPLE__SERVICE_KEY")!,
563
+ },
564
+ });
565
+ ```
566
+
567
+ `sensitiveParams` is merged into the outgoing query like `params`, while its
568
+ values are redacted from SDK transport errors, traces, and `apifuse record`
569
+ fixtures. Do not put query credentials in `params`, and do not hand-build a URL
570
+ containing a key; those paths cannot declare which query values are secret.
571
+
572
+ #### Residual risks
573
+
574
+ Redaction is unconditional in structural positions (declared query keys and
575
+ exact scalar fixture/error fields) for values of every length. In unstructured
576
+ free text, values of four or more characters are replaced as substrings; shorter
577
+ values are replaced only at token boundaries to avoid corrupting unrelated text
578
+ (for example, a secret `api` must not rewrite `rapid`). The residual risk is that
579
+ a sub-four-character secret embedded directly inside a larger alphanumeric token
580
+ can remain in free text. Prefer a higher-entropy credential, or a header/body
581
+ credential channel, whenever the upstream permits it. An empty
582
+ `sensitiveParams: {}` is treated exactly as if the option were omitted.
583
+
584
+ For `session.redirects.run()`, returned hop URLs are diagnostic metadata and
585
+ therefore keep declared query values and common response-only credential keys
586
+ redacted. If a login flow must consume a rotated credential from `Location`,
587
+ inspect it inside `stopWhen`; that callback receives the real hop while callback
588
+ failures are sanitized before propagation.
589
+
259
590
  ### Public local debugging checklist
260
591
 
261
592
  - Operation smoke requests use the provider server envelope:
@@ -411,6 +742,162 @@ const credentialsAuth = defineCredentialsAuth({
411
742
  `bunx playwright install chromium`, or set
412
743
  `APIFUSE__CDP_POOL__URL` for remote browser debugging.
413
744
 
745
+ ### Native gateway adapter migration
746
+
747
+ Native proxy resolution supports both HTTP CONNECT and SOCKS5 without
748
+ terminating origin TLS. The built-in vendor order follows `proxy.providers`
749
+ exactly: `smartproxy` means the `api.smartproxy.org` allocation vendor (raw
750
+ `ip:port` endpoints), while `nodemaven` is the credentialed gateway. It is not
751
+ the company formerly called Smartproxy; that separate company is represented
752
+ by the deprecated `decodo` name.
753
+
754
+ Custom `gatewaySynthesizers` must now accept the selected `protocol` and the
755
+ injected `credentials` resolver on `NativeGatewayProxySynthesisInput`.
756
+ Synthesizers may be async because allocation vendors perform network I/O. They
757
+ may return a proxy, `undefined` when they do not implement the offered vendor,
758
+ or `{ kind: "skipped", reason }` so an exhausted required chain can explain an
759
+ absent credential, unsupported protocol, or allocation failure. Accordingly,
760
+ `resolveNativeGatewayProxy(...)` must now be awaited.
761
+
762
+ Callers that do not supply custom synthesizers or credentials keep env-backed
763
+ behavior. Hosts that already have an allowlisted `EnvContext` can inject it
764
+ explicitly, and vault-backed or per-tenant hosts can supply their own resolver:
765
+
766
+ ```ts
767
+ import {
768
+ createEnvVendorCredentialResolver,
769
+ createNativeNetworkClient,
770
+ } from "@apifuse/provider-sdk";
771
+
772
+ const network = createNativeNetworkClient({
773
+ proxyPolicy: { mode: "required", providers: ["smartproxy", "nodemaven"] },
774
+ credentials: createEnvVendorCredentialResolver(ctx.env),
775
+ // Optional advanced override; omit for each vendor's default.
776
+ proxyProtocol: "socks5",
777
+ });
778
+ ```
779
+
780
+ Never include credential values in adapter skip messages or thrown errors. The
781
+ SDK redacts built-in proxy URL userinfo, CONNECT authentication, and allocator
782
+ causes, but a custom adapter remains responsible for not publishing secrets in
783
+ its own diagnostics.
784
+
785
+ ### Limiting stealth response bodies
786
+
787
+ Set `maxBodyBytes` on `ctx.stealth.fetch()` or `session.redirects.run()` when an
788
+ upstream response has a known safe maximum. The limit is opt-in and counts
789
+ decoded bytes as impit streams them. It applies to every redirect hop, uses a
790
+ parseable `Content-Length` for an early rejection, and still enforces the limit
791
+ incrementally when the header is absent or inaccurate. Exceeding the limit
792
+ aborts the response and throws a non-retryable `TransportError` with code
793
+ `response_too_large`.
794
+
795
+ Pass the limit to the transport instead of checking `Content-Length` in provider
796
+ code:
797
+
798
+ ```ts
799
+ const response = await ctx.stealth.fetch("/api/search", {
800
+ params: { query: input.query },
801
+ maxBodyBytes: 2 * 1024 * 1024,
802
+ })
803
+ ```
804
+
805
+ ### Persisting stealth session cookies
806
+
807
+ Persist `session.cookies.serialize()` as JSON when an authenticated session must
808
+ survive a restart or move to another replica. The returned
809
+ `StealthCookieStoreV1` has an explicit version and retains every cookie together
810
+ with its Domain, Path, Secure, expiry, host-only, and other cookie attributes.
811
+ Restore it with `session.cookies.deserialize()`. Unsupported future versions
812
+ fail explicitly instead of being accepted as a partial cookie jar.
813
+
814
+ Credential values are strings, so stringify the store at the credential
815
+ boundary and parse it when rebuilding the session:
816
+
817
+ ```ts
818
+ // After login (including any redirects across sibling hosts):
819
+ const result = await session.redirects.run({ url: loginUrl });
820
+ return {
821
+ credential: {
822
+ cookieStore: JSON.stringify(result.cookieStore),
823
+ },
824
+ };
825
+
826
+ // In a later operation or replica:
827
+ const persisted = ctx.credential.get("cookieStore");
828
+ if (persisted) {
829
+ session.cookies.deserialize(JSON.parse(persisted));
830
+ }
831
+ ```
832
+
833
+ `snapshot()` and `restore()` remain only for backward compatibility with flat
834
+ `Record<string, string>` credentials. `snapshot()` enumerates cookies across all
835
+ hosts and paths, but the flat shape is inherently lossy: duplicate names
836
+ collapse and Domain, Path, Secure, expiry, and host-only attributes cannot be
837
+ represented. `restore()` therefore recreates host-only `Path=/` cookies on the
838
+ session base origin. Do not use the flat form for new persistence code. Cookie
839
+ headers remain origin-filtered: use `toHeader(url)` for a particular request and
840
+ never build a request header from serialized or snapshotted persistence data.
841
+
842
+ ### Recording and replaying streaming responses
843
+
844
+ `apifuse record` passes responses returned by `ctx.http.stream()` directly to the operation
845
+ handler while incrementally capturing a bounded preview. If the handler returns or cancels its
846
+ reader before EOF, the recorder drains the retained upstream reader before writing a JSON evidence
847
+ record to `__fixtures__/raw.json`. The record contains the status, success
848
+ flag, `content-type`/`content-length`/`content-disposition` headers when present, the
849
+ full body SHA-256 and byte count, and a base64 preview up to the configured stream preview limit.
850
+ Textual previews are decoded and passed through the fixture sanitizer before base64 encoding.
851
+ Classification uses both the declared content type and the preview bytes, so missing or incorrect
852
+ content-type headers do not bypass sanitization. PEM private-key blocks and long high-entropy
853
+ tokens in otherwise unstructured text are redacted as well. If the full preview is not valid UTF-8,
854
+ the entire lossy-decoded preview is scanned and matching decodable byte windows are sanitized. Only a
855
+ magic-number-confirmed binary preview with no textual-secret pattern anywhere in the preview bypasses
856
+ sanitization; other undecodable data fails closed.
857
+ Sanitized previews carry `preview_sanitized: true`, plus a
858
+ `preview_redaction_reason` when capture had to fail closed. The original hash and byte count always
859
+ describe upstream bytes, not a sanitized preview.
860
+
861
+ Each record includes query-free request provenance (`method`, `path`, and a one-based stream call
862
+ ordinal). Provenance never stores the origin, URL userinfo, query, or fragment. Every retained path
863
+ segment is scrubbed before persistence: credential-key segments, values following those keys,
864
+ known token shapes, and long high-entropy opaque segments become `[REDACTED]`. If an operation
865
+ opens multiple streams, the recorder finalizes every retained reader and
866
+ writes all evidence records in stream call order. Stream invocations use a tagged capture envelope
867
+ whose items distinguish stream evidence from ordinary JSON responses. Evidence-only snapshot replay
868
+ consumes that exact call order and fails immediately when evidence is exhausted or a call kind is
869
+ reordered. When request provenance is present, replay also rejects method or path changes (relative
870
+ URLs are resolved against the recorded path prefix); ordinals remain diagnostic and are not matched.
871
+ Appended fixtures replay a stream envelope only when it is the latest invocation. SSE
872
+ recording remains unsupported and fails explicitly instead of retaining an unrelated earlier
873
+ response.
874
+
875
+ #### Residual risks
876
+
877
+ - Credential-path sanitization decodes each URL path segment once. Double-encoded separators or
878
+ values such as `%252F` are not decoded recursively, so they can conceal a credential-shaped
879
+ segment from the recorder. This single-pass policy keeps path handling deterministic and avoids
880
+ interpreting ambiguous or intentionally layered encodings differently from the upstream. Never
881
+ place credentials in URL paths, and review recorded provenance before committing fixtures.
882
+ - Primitive strings embedded in prose are redacted only when they match the current PEM,
883
+ credential-assignment, known-token, or entropy heuristics. Other secret formats can remain because
884
+ blanket redaction of ordinary strings would destroy useful fixture content and create broad false
885
+ positives. Keep secrets under credential-named structured fields where possible and manually
886
+ inspect sanitized fixture text before committing it.
887
+
888
+ Stream fixture replay in `runStandardTests(..., { snapshot: true })` is evidence-only:
889
+ `ctx.http.stream()` returns a usable stream containing exactly the recorded preview,
890
+ not a fabricated full body. The replay response also carries runtime metadata
891
+ `evidence_only: true`, `body_sha256`, `body_bytes`, and the optional preview sanitization fields
892
+ for assertions about the original capture. Do not assert that the replay body hashes to
893
+ `body_sha256` when `body_bytes`
894
+ exceeds the decoded preview length or `preview_sanitized` is present; use the metadata for
895
+ full-body integrity and limit body-content assertions to the preview.
896
+
897
+ Golden snapshot suites can set `requireSnapshot: true` so a missing committed snapshot fails instead
898
+ of being created implicitly. Regenerate intentional changes with
899
+ `bun test --update-snapshots`; review and commit the resulting `transform.snap.json` file.
900
+
414
901
  ### Running the pre-submission report
415
902
 
416
903
  ```bash
package/CHANGELOG.md CHANGED
@@ -1,5 +1,81 @@
1
1
  # @apifuse/provider-sdk Changelog
2
2
 
3
+ ## 2.2.0-beta.21
4
+
5
+ - Release candidate for main commit 00f61024fb18db39711dc5076c4621508baa49f5.
6
+
7
+ ## 2.2.0-beta.20
8
+
9
+ - Release candidate for main commit 8043d2ef0047e431aace750d8abca2e1149ec1d6.
10
+
11
+ ## 2.2.0-beta.19
12
+
13
+ - Release candidate for main commit fd93ae68d0472fae68d908312249163373ec5d22.
14
+
15
+ ## 2.2.0-beta.18
16
+
17
+ - Release candidate for main commit cebaf4b994918d2641c0fe6681fbffd3a3284c5d.
18
+
19
+ ## 2.2.0-beta.17
20
+
21
+ - Release candidate for main commit 5a0f9127a5861992d5c144b07ad379a085756544.
22
+
23
+ ## 2.2.0-beta.16
24
+
25
+ - Release candidate for main commit c5deb2bc31a4a4a236d27a2ce3d214b533ed358f.
26
+
27
+ ## 2.2.0-beta.15
28
+
29
+ - Release candidate for main commit b5ebd25e48f6502e4ddb775d4e0a25f5c8276712.
30
+
31
+ ## 2.2.0-beta.14
32
+
33
+ - Release candidate for main commit 3491acd253ca17b517985e8a618f1c2904a664a9.
34
+
35
+ ## 2.2.0-beta.13
36
+
37
+ - Release candidate for main commit 75e840d0614aea3b99a1e5cef4f93f8cdccf0507.
38
+
39
+ ## 2.2.0-beta.12
40
+
41
+ - Release candidate for main commit c66789c4745c72fc94ad3c10b3e0d7e5ed83fd25.
42
+
43
+ ## 2.2.0-beta.11
44
+
45
+ - Release candidate for main commit f6f739bd5265afe714bbace9900edc2695fcf826.
46
+
47
+ ## 2.2.0-beta.10
48
+
49
+ - Release candidate for main commit c41bd919739e0293ae8fa4d72a8a32f034cef4b8.
50
+
51
+ ## 2.2.0-beta.9
52
+
53
+ - Release candidate for main commit 5c78c8b (bundles #67 nodemaven required-secret + #68 transport vendor-advance).
54
+
55
+ ## 2.2.0-beta.8
56
+
57
+ - Release candidate for main commit 9e8a3f028ee78b9cab29d4aa3f5494ac9cffa65f.
58
+
59
+ ## 2.2.0-beta.7
60
+
61
+ - Release candidate for main commit 2ce4ea4bd36ce333eba8b3b474bf6e82b5e9216c.
62
+
63
+ ## 2.2.0-beta.6
64
+
65
+ - Release candidate for main commit 17f4e41d44efe7c148ef875b950be4f2c7df1294.
66
+
67
+ ## 2.2.0-beta.5
68
+
69
+ - Release candidate for main commit 82fa14e99a9af7edd44e3196aa3f4e87b4699edf.
70
+
71
+ ## 2.2.0-beta.4
72
+
73
+ - Release candidate for main commit 73f2c6ec429c2fbce8ac458a67111e4844b99178.
74
+
75
+ ## 2.2.0-beta.3
76
+
77
+ - Release candidate for main commit 74e8e18b502dd9b02dbf0d3e702f917570312fc0.
78
+
3
79
  ## 2.2.0-beta.2
4
80
 
5
81
  - Release candidate for main commit ceefad020a1038eade542fd3b128667b39625f6f.
@@ -38,8 +114,22 @@
38
114
 
39
115
  ## Unreleased
40
116
 
117
+ - Upstream the platform monorepo's beta.16 dist patch: OAuth2-proxied auth ceremony (`createOAuth2ProxiedStart`, `OAUTH2_PROXIED_PKCE_VERIFIER_KEY`, `APIFUSE__AUTH_PROXY__URL` origin key, proxied redirect/callback turns) and the provider runtime state upgrades it depends on (in-memory compareAndSet with quota-aware write policy, Redis CAS/set Lua scripts with entry quotas and legacy index migration). The monorepo drops `patchedDependencies` once it pins this release.
118
+ - **honest-provider-error-contract (phase 2):** `UPSTREAM_REJECTED` is a registered code family serving HTTP 409 with `retryable: false` — deterministic upstream business refusals are no longer 502s. Operation-declared `docs.errorCodes` may now use 409/410/422. Public error envelopes carry a `source` field (`client` | `upstream_rule` | `upstream_failure` | `apifuse`) derived from the observability category. Taxonomy version bumps to `2026-08-07` with the `upstream_rejected`, `dependency_unavailable`, `unsupported_transport`, and `client_cancelled` categories (409/410/422 map to `upstream_rejected`), matching the platform monorepo SoT. The `unregistered_provider_error_code` signal now carries a `signalFix` pointing at `docs.errorCodes` declaration.
119
+ - **Breaking for custom native gateway adapters:** `NativeGatewayProxySynthesisInput` now includes an injected `credentials` resolver and selected `protocol`; synthesizers may return promises and structured skip reasons, and `resolveNativeGatewayProxy` is async. Default callers retain env-backed behavior. Native transport now supports both HTTP CONNECT and SOCKS5, defaults per vendor with an explicit runtime override, registers smartproxy allocation ahead of nodemaven when declared in that order, and reports every exhausted vendor reason without exposing proxy credentials.
120
+ - Honor operation `docs.errorCodes` at runtime: declared provider-owned statuses and retryability now drive the HTTP envelope, observability header, and structured log; invalid statuses fail `defineProvider`, declared codes no longer emit the unregistered-code signal, and `TransportError` status-preservation workarounds are obsolete.
121
+ - Add an opt-in same-origin redirect hop policy to `ctx.http`, with bounded manual following and typed failures before a refused target is requested.
122
+ - Enforce provider-declared native TCP/TLS egress before proxy or socket setup, with revocable and expiring dynamic grants plus typed authorization failures; providers without a native egress declaration retain legacy behavior.
123
+ - **Breaking:** Provider error `details` is now passed through verbatim; SDK observability fields (`category`, `taxonomyVersion`, `upstreamStatus`, and derived `retryable`) are no longer merged into the public body. Emitted error envelopes now require top-level `retryable`, while inbound stateful forwarding tolerates an older owner response that omits it and defaults it to `false`. The removed observability metadata is available in the new `X-ApiFuse-Error-Observability` response header.
124
+ - Unregistered `ProviderError` codes now default to HTTP 500 instead of 400 and emit an `unregistered_provider_error_code` structured-log signal; registered mappings remain unchanged and take precedence over the HTTP 400 fallback for unregistered input `ValidationError` codes.
125
+ - Add an opt-in native connection idle read timeout with a typed error, independently from TCP/SOCKS/TLS establishment deadlines.
126
+ - Add opt-in `maxBodyBytes` enforcement to stealth fetches and redirect hops, aborting oversized decoded response streams with `response_too_large`.
127
+ - Resolve relative date tokens in fixture requests before input-schema validation, add KST capture-date `fixtures.recordedAt` metadata, and support explicit KST/UTC calendars in the shared health-input resolver.
128
+ - Add opt-in `runStandardTests(provider, { upstreamStub })` real-handler E2E coverage with strict offline transport stubs, output-schema validation, and per-operation warnings when handler E2E is not enabled.
129
+ - Export native-network and request-file TypeScript contracts from the package root and `./provider`, including typed native provider declarations and optional runtime capabilities on provider/auth contexts.
41
130
  - Add `arrayBuffer()` and `bytes()` to `HttpResponse` so `ctx.http` consumers can read binary-safe upstream bodies; internal response handling is now byte-first.
42
131
  - Preserve identity-only operation `connectionId` values in `ProviderContext` without requiring credential material.
132
+ - Accept and validate `proxy.session.drainLeadSeconds` in `defineProvider`, so providers can actually declare the native sticky-expiry drain lead time the type surface already exposed; a non-positive value, or one that meets or exceeds the sticky lifetime, is rejected at define time.
43
133
 
44
134
  ## 2.1.0-beta.15
45
135