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

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 (290) hide show
  1. package/AUTHORING.md +487 -0
  2. package/CHANGELOG.md +85 -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 +1 -1
  23. package/dist/ceremonies/index.js +8 -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 +208 -125
  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.js +8 -15
  56. package/dist/native-address.d.ts +43 -0
  57. package/dist/native-address.js +281 -0
  58. package/dist/native-egress-policy.d.ts +31 -0
  59. package/dist/native-egress-policy.js +288 -0
  60. package/dist/observability.d.ts +5 -2
  61. package/dist/observability.js +48 -1
  62. package/dist/provider.d.ts +12 -11
  63. package/dist/provider.js +10 -9
  64. package/dist/public-schema-field-lint.d.ts +1 -1
  65. package/dist/recipes/gov-api.js +1 -1
  66. package/dist/runtime/auth-flow.d.ts +1 -1
  67. package/dist/runtime/auth-flow.js +3 -3
  68. package/dist/runtime/browser.d.ts +1 -1
  69. package/dist/runtime/browser.js +15 -29
  70. package/dist/runtime/cache.d.ts +1 -1
  71. package/dist/runtime/cache.js +4 -8
  72. package/dist/runtime/choice.d.ts +1 -1
  73. package/dist/runtime/choice.js +31 -35
  74. package/dist/runtime/credential.d.ts +1 -1
  75. package/dist/runtime/credential.js +1 -1
  76. package/dist/runtime/env.d.ts +1 -1
  77. package/dist/runtime/executor.d.ts +1 -1
  78. package/dist/runtime/executor.js +31 -4
  79. package/dist/runtime/http.d.ts +2 -2
  80. package/dist/runtime/http.js +387 -47
  81. package/dist/runtime/insights.d.ts +1 -1
  82. package/dist/runtime/insights.js +6 -13
  83. package/dist/runtime/instrumentation.d.ts +2 -2
  84. package/dist/runtime/instrumentation.js +345 -22
  85. package/dist/runtime/keyring.js +1 -1
  86. package/dist/runtime/namespace.js +1 -1
  87. package/dist/runtime/native-network.d.ts +127 -0
  88. package/dist/runtime/native-network.js +1298 -0
  89. package/dist/runtime/otlp.d.ts +1 -1
  90. package/dist/runtime/perf.d.ts +1 -1
  91. package/dist/runtime/provider.d.ts +1 -1
  92. package/dist/runtime/provider.js +1 -2
  93. package/dist/runtime/proxy-errors.d.ts +1 -1
  94. package/dist/runtime/proxy-errors.js +9 -7
  95. package/dist/runtime/proxy-nodemaven.d.ts +56 -0
  96. package/dist/runtime/proxy-nodemaven.js +146 -0
  97. package/dist/runtime/proxy-retry-policy.d.ts +2 -2
  98. package/dist/runtime/proxy-retry-policy.js +2 -2
  99. package/dist/runtime/proxy-telemetry.d.ts +2 -1
  100. package/dist/runtime/proxy-telemetry.js +55 -52
  101. package/dist/runtime/redirects.d.ts +29 -0
  102. package/dist/runtime/redirects.js +36 -0
  103. package/dist/runtime/redis.d.ts +1 -1
  104. package/dist/runtime/redis.js +2 -4
  105. package/dist/runtime/request-options.d.ts +68 -1
  106. package/dist/runtime/request-options.js +548 -0
  107. package/dist/runtime/secrets.d.ts +27 -0
  108. package/dist/runtime/secrets.js +51 -0
  109. package/dist/runtime/state.d.ts +2 -2
  110. package/dist/runtime/state.js +15 -4
  111. package/dist/runtime/stealth.d.ts +5 -3
  112. package/dist/runtime/stealth.js +423 -143
  113. package/dist/runtime/stt.d.ts +1 -1
  114. package/dist/runtime/stt.js +11 -15
  115. package/dist/runtime/trace.d.ts +2 -2
  116. package/dist/runtime/trace.js +2 -4
  117. package/dist/runtime/waterfall.d.ts +1 -1
  118. package/dist/schema.d.ts +1 -1
  119. package/dist/schema.js +7 -15
  120. package/dist/serve.d.ts +1 -1
  121. package/dist/serve.js +1 -1
  122. package/dist/server/index.d.ts +7 -7
  123. package/dist/server/index.js +6 -6
  124. package/dist/server/self-test-input-tokens.d.ts +2 -1
  125. package/dist/server/self-test-input-tokens.js +18 -14
  126. package/dist/server/self-test-redaction.d.ts +1 -1
  127. package/dist/server/self-test-redaction.js +1 -1
  128. package/dist/server/self-test.d.ts +104 -3
  129. package/dist/server/self-test.js +673 -115
  130. package/dist/server/serve.d.ts +115 -4
  131. package/dist/server/serve.js +792 -127
  132. package/dist/server/types.d.ts +30 -9
  133. package/dist/server/types.js +8 -7
  134. package/dist/stateful/errors.d.ts +14 -0
  135. package/dist/stateful/errors.js +14 -0
  136. package/dist/stateful/http-provider-event-emitter.d.ts +40 -0
  137. package/dist/stateful/http-provider-event-emitter.js +237 -0
  138. package/dist/stateful/http-session-owner-registry.d.ts +44 -0
  139. package/dist/stateful/http-session-owner-registry.js +210 -0
  140. package/dist/stateful/index.d.ts +18 -0
  141. package/dist/stateful/index.js +18 -0
  142. package/dist/stateful/provider-event-delivery-failures.d.ts +32 -0
  143. package/dist/stateful/provider-event-delivery-failures.js +43 -0
  144. package/dist/stateful/provider-event-pipeline-metrics.d.ts +46 -0
  145. package/dist/stateful/provider-event-pipeline-metrics.js +48 -0
  146. package/dist/stateful/provider-event-pipeline.d.ts +50 -0
  147. package/dist/stateful/provider-event-pipeline.js +1 -0
  148. package/dist/stateful/provider-events.d.ts +101 -0
  149. package/dist/stateful/provider-events.js +289 -0
  150. package/dist/stateful/session-key.d.ts +15 -0
  151. package/dist/stateful/session-key.js +86 -0
  152. package/dist/stateful/stateful-provider-adapter-context.d.ts +5 -0
  153. package/dist/stateful/stateful-provider-adapter-context.js +42 -0
  154. package/dist/stateful/stateful-provider-adapter-metrics.d.ts +15 -0
  155. package/dist/stateful/stateful-provider-adapter-metrics.js +21 -0
  156. package/dist/stateful/stateful-provider-adapter.d.ts +98 -0
  157. package/dist/stateful/stateful-provider-adapter.js +287 -0
  158. package/dist/stateful/stateful-provider-observability.d.ts +62 -0
  159. package/dist/stateful/stateful-provider-observability.js +161 -0
  160. package/dist/stateful/stateful-provider-owner-forwarder.d.ts +41 -0
  161. package/dist/stateful/stateful-provider-owner-forwarder.js +215 -0
  162. package/dist/stateful/stateful-provider-runtime-context.d.ts +32 -0
  163. package/dist/stateful/stateful-provider-runtime-context.js +60 -0
  164. package/dist/stateful/stateful-provider-runtime-executor.d.ts +34 -0
  165. package/dist/stateful/stateful-provider-runtime-executor.js +52 -0
  166. package/dist/stateful/stateful-provider-session-routing.d.ts +71 -0
  167. package/dist/stateful/stateful-provider-session-routing.js +353 -0
  168. package/dist/stateful/stateful-provider-session-runtime.d.ts +98 -0
  169. package/dist/stateful/stateful-provider-session-runtime.js +245 -0
  170. package/dist/stateful-signing.d.ts +18 -0
  171. package/dist/stateful-signing.js +27 -0
  172. package/dist/stealth/profiles.d.ts +1 -1
  173. package/dist/stealth/profiles.js +5 -14
  174. package/dist/stream-evidence.d.ts +74 -0
  175. package/dist/stream-evidence.js +785 -0
  176. package/dist/stream.d.ts +1 -1
  177. package/dist/testing/index.d.ts +2 -2
  178. package/dist/testing/index.js +2 -2
  179. package/dist/testing/run.d.ts +32 -2
  180. package/dist/testing/run.js +477 -33
  181. package/dist/types.d.ts +323 -11
  182. package/dist/types.js +1 -0
  183. package/dist/user-input.d.ts +30 -0
  184. package/dist/user-input.js +66 -0
  185. package/package.json +16 -5
  186. package/src/auth-turn/index.ts +1 -1
  187. package/src/auth.ts +28 -86
  188. package/src/ceremonies/index.ts +24 -78
  189. package/src/cli/commands.ts +10 -0
  190. package/src/cli/create.ts +42 -35
  191. package/src/cli/prompt-assets.ts +865 -0
  192. package/src/cli/templates/provider/AGENTS.md.tpl +17 -8
  193. package/src/config/loader.ts +652 -204
  194. package/src/contract-serialization.ts +5 -11
  195. package/src/contract-types.ts +2 -2
  196. package/src/contract.ts +12 -28
  197. package/src/define.ts +368 -504
  198. package/src/dev.ts +4 -9
  199. package/src/error-resolution.ts +91 -0
  200. package/src/errors.ts +110 -5
  201. package/src/fixture-sanitization.ts +247 -0
  202. package/src/i18n/catalog.ts +10 -32
  203. package/src/i18n/index.ts +2 -2
  204. package/src/i18n/keys.ts +5 -11
  205. package/src/index.ts +111 -42
  206. package/src/lint.ts +89 -159
  207. package/src/native-address.ts +340 -0
  208. package/src/native-egress-policy.ts +358 -0
  209. package/src/observability.ts +51 -1
  210. package/src/provider.ts +65 -11
  211. package/src/public-schema-field-lint.ts +7 -33
  212. package/src/recipes/gov-api.ts +2 -5
  213. package/src/runtime/auth-flow.ts +5 -7
  214. package/src/runtime/browser.ts +64 -187
  215. package/src/runtime/cache.ts +20 -67
  216. package/src/runtime/choice.ts +79 -132
  217. package/src/runtime/credential.ts +2 -2
  218. package/src/runtime/env.ts +1 -1
  219. package/src/runtime/executor.ts +43 -20
  220. package/src/runtime/http.ts +494 -57
  221. package/src/runtime/insights.ts +15 -53
  222. package/src/runtime/instrumentation.ts +495 -66
  223. package/src/runtime/keyring.ts +7 -19
  224. package/src/runtime/namespace.ts +2 -7
  225. package/src/runtime/native-network.ts +1686 -0
  226. package/src/runtime/otlp.ts +12 -23
  227. package/src/runtime/perf.ts +1 -1
  228. package/src/runtime/provider.ts +4 -9
  229. package/src/runtime/proxy-errors.ts +29 -42
  230. package/src/runtime/proxy-nodemaven.ts +221 -0
  231. package/src/runtime/proxy-retry-policy.ts +3 -3
  232. package/src/runtime/proxy-telemetry.ts +79 -77
  233. package/src/runtime/redirects.ts +66 -0
  234. package/src/runtime/redis.ts +4 -12
  235. package/src/runtime/request-options.ts +679 -9
  236. package/src/runtime/secrets.ts +64 -0
  237. package/src/runtime/state.ts +41 -110
  238. package/src/runtime/stealth.ts +505 -154
  239. package/src/runtime/stt.ts +38 -94
  240. package/src/runtime/trace.ts +14 -44
  241. package/src/runtime/waterfall.ts +5 -18
  242. package/src/schema.ts +23 -84
  243. package/src/serve.ts +1 -1
  244. package/src/server/index.ts +29 -7
  245. package/src/server/self-test-input-tokens.ts +29 -14
  246. package/src/server/self-test-redaction.ts +2 -2
  247. package/src/server/self-test.ts +857 -132
  248. package/src/server/serve.ts +1141 -324
  249. package/src/server/types.ts +12 -19
  250. package/src/stateful/README.md +146 -0
  251. package/src/stateful/errors.ts +23 -0
  252. package/src/stateful/http-provider-event-emitter.ts +314 -0
  253. package/src/stateful/http-session-owner-registry.ts +306 -0
  254. package/src/stateful/index.ts +18 -0
  255. package/src/stateful/provider-event-delivery-failures.ts +80 -0
  256. package/src/stateful/provider-event-pipeline-metrics.ts +95 -0
  257. package/src/stateful/provider-event-pipeline.ts +61 -0
  258. package/src/stateful/provider-events.ts +462 -0
  259. package/src/stateful/session-key.ts +111 -0
  260. package/src/stateful/stateful-provider-adapter-context.ts +59 -0
  261. package/src/stateful/stateful-provider-adapter-metrics.ts +48 -0
  262. package/src/stateful/stateful-provider-adapter.ts +562 -0
  263. package/src/stateful/stateful-provider-observability.ts +261 -0
  264. package/src/stateful/stateful-provider-owner-forwarder.ts +287 -0
  265. package/src/stateful/stateful-provider-runtime-context.ts +92 -0
  266. package/src/stateful/stateful-provider-runtime-executor.ts +96 -0
  267. package/src/stateful/stateful-provider-session-routing.ts +555 -0
  268. package/src/stateful/stateful-provider-session-runtime.ts +403 -0
  269. package/src/stateful-signing.ts +46 -0
  270. package/src/stealth/profiles.ts +10 -26
  271. package/src/stream-evidence.ts +988 -0
  272. package/src/stream.ts +8 -19
  273. package/src/testing/index.ts +10 -2
  274. package/src/testing/run.ts +652 -79
  275. package/src/types.ts +369 -11
  276. package/src/user-input.ts +118 -0
  277. package/dist/cli/templates/provider/CLAUDE.md.tpl +0 -1
  278. package/src/cli/templates/provider/CLAUDE.md.tpl +0 -1
  279. /package/dist/cli/templates/provider/{skills → .agents/skills}/fixtures-and-recording/SKILL.md.tpl +0 -0
  280. /package/dist/cli/templates/provider/{skills → .agents/skills}/health-checks-and-fail-closed/SKILL.md.tpl +0 -0
  281. /package/dist/cli/templates/provider/{skills → .agents/skills}/normalization-standards/SKILL.md.tpl +0 -0
  282. /package/dist/cli/templates/provider/{skills → .agents/skills}/pagination-and-counts/SKILL.md.tpl +0 -0
  283. /package/dist/cli/templates/provider/{skills → .agents/skills}/upstream-contract-verification/SKILL.md.tpl +0 -0
  284. /package/dist/cli/templates/provider/{skills → .agents/skills}/upstream-notes/README.md.tpl +0 -0
  285. /package/src/cli/templates/provider/{skills → .agents/skills}/fixtures-and-recording/SKILL.md.tpl +0 -0
  286. /package/src/cli/templates/provider/{skills → .agents/skills}/health-checks-and-fail-closed/SKILL.md.tpl +0 -0
  287. /package/src/cli/templates/provider/{skills → .agents/skills}/normalization-standards/SKILL.md.tpl +0 -0
  288. /package/src/cli/templates/provider/{skills → .agents/skills}/pagination-and-counts/SKILL.md.tpl +0 -0
  289. /package/src/cli/templates/provider/{skills → .agents/skills}/upstream-contract-verification/SKILL.md.tpl +0 -0
  290. /package/src/cli/templates/provider/{skills → .agents/skills}/upstream-notes/README.md.tpl +0 -0
package/src/types.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type ms from "ms";
2
+ import type { SerializedCookieJar } from "tough-cookie";
2
3
 
3
4
  import type { infer as ZodInfer, ZodType } from "zod";
4
5
 
@@ -718,9 +719,13 @@ export interface HealthMonitorProbeOverride {
718
719
  degradedThresholdMs?: number;
719
720
  }
720
721
 
722
+ export const VALID_OPERATION_ERROR_STATUSES = [400, 401, 404, 409, 410, 422, 429, 500, 502, 503, 504] as const;
723
+
724
+ export type ProviderErrorStatus = (typeof VALID_OPERATION_ERROR_STATUSES)[number];
725
+
721
726
  export interface OperationErrorCode {
722
727
  code: string;
723
- status?: number;
728
+ status?: ProviderErrorStatus;
724
729
  description: string;
725
730
  retryable?: boolean;
726
731
  }
@@ -769,7 +774,25 @@ export type ProviderAccessVisibility = "public" | "early_access";
769
774
 
770
775
  export type ProviderProxyMode = "disabled" | "optional" | "required";
771
776
 
772
- export type ProviderProxyProvider = "smartproxy" | "decodo" | "custom";
777
+ /**
778
+ * Proxy egress vendors. These are FOUR DISTINCT services — do not conflate them
779
+ * (a common mistake because the names collide with a well-known rebrand):
780
+ *
781
+ * - `smartproxy` — **api.smartproxy.org**, a residential proxy with an IP
782
+ * *extraction/allocation* API (app_key → a pool of raw `ip:port` CONNECT
783
+ * endpoints). This is our own vendor. It is NOT the company formerly named
784
+ * "Smartproxy". Credentials: `APIFUSE__PROXY__SMARTPROXY_APP_KEY`.
785
+ * - `nodemaven` — **gate.nodemaven.com**, a *gateway* proxy with static
786
+ * credentials; geo/session encoded in the username, no allocation API.
787
+ * - `decodo` — **decodo.com**, the *gateway* proxy that was named "Smartproxy"
788
+ * (smartproxy.com) before its 2025 rebrand to Decodo. Sticky sessions via
789
+ * username params. A different company from `smartproxy` above.
790
+ * **@deprecated** — unused; no managed adapter. Use `smartproxy`/`nodemaven`,
791
+ * or the `APIFUSE__PROXY__URL` bring-your-own escape hatch.
792
+ * - `custom` — **@deprecated** bring-your-own static proxy URL marker. The
793
+ * `APIFUSE__PROXY__URL` env still works without declaring this value.
794
+ */
795
+ export type ProviderProxyProvider = "smartproxy" | "nodemaven" | "decodo" | "custom";
773
796
 
774
797
  export type ProviderProxySessionAffinity =
775
798
  | "request"
@@ -783,7 +806,18 @@ export interface ProviderProxyPolicy {
783
806
  * certificate verification, and vendor allocator endpoints are SDK-owned.
784
807
  */
785
808
  mode: ProviderProxyMode;
809
+ /**
810
+ * @deprecated Use `providers: [...]` to declare an ordered vendor fallback
811
+ * chain. A single-element `providers` list is equivalent to this field.
812
+ */
786
813
  provider?: ProviderProxyProvider;
814
+ /**
815
+ * Ordered proxy-vendor fallback chain. The SDK tries each vendor in order and
816
+ * fails over to the next when a vendor lacks credentials or its allocation /
817
+ * transport is exhausted. When omitted, `provider` (or the platform default)
818
+ * is used as a single-vendor chain.
819
+ */
820
+ providers?: ProviderProxyProvider[];
787
821
  geo?: {
788
822
  /** ISO 3166-1 alpha-2 country code, for example KR or US. */
789
823
  country?: Iso3166Alpha2CountryCode;
@@ -794,6 +828,12 @@ export interface ProviderProxyPolicy {
794
828
  affinity?: ProviderProxySessionAffinity;
795
829
  lifetimeMinutes?: number;
796
830
  poolSize?: number;
831
+ /**
832
+ * Seconds before hard sticky expiry at which native connections receive
833
+ * the `expiring` event so the provider can drain and reconnect cleanly.
834
+ * Declared by the provider; the SDK does not assume a default cut point.
835
+ */
836
+ drainLeadSeconds?: number;
797
837
  };
798
838
  }
799
839
 
@@ -970,6 +1010,11 @@ export interface HttpRetrySummary {
970
1010
  export interface RequestOptions {
971
1011
  headers?: Record<string, string>;
972
1012
  params?: RequestParams;
1013
+ /**
1014
+ * Query parameters whose values contain credentials or other secret material.
1015
+ * They are sent like `params`, but redacted from SDK errors, traces, and recorded fixtures.
1016
+ */
1017
+ sensitiveParams?: Record<string, string>;
973
1018
  proxy?: string;
974
1019
  timeout?: number;
975
1020
  /**
@@ -978,8 +1023,32 @@ export interface RequestOptions {
978
1023
  */
979
1024
  throwOnHttpError?: boolean;
980
1025
  retry?: boolean | HttpRetryPreset | HttpRetryOptions;
1026
+ /**
1027
+ * Opt-in redirect-hop enforcement for ctx.http. When present, redirects are
1028
+ * evaluated before the next request is issued. Existing callers that omit
1029
+ * this policy retain the native fetch redirect behavior.
1030
+ */
1031
+ redirectPolicy?: HttpRedirectPolicy;
981
1032
  }
982
1033
 
1034
+ export type RedirectRunReason =
1035
+ | "completed"
1036
+ | "stopped"
1037
+ | "max_hops"
1038
+ | "missing_location"
1039
+ | "loop";
1040
+
1041
+ export type HttpRedirectPolicyMode = "same-origin";
1042
+
1043
+ export interface HttpRedirectPolicy {
1044
+ /** Only follow redirects whose canonical scheme, host, and port match the initial URL. */
1045
+ mode: HttpRedirectPolicyMode;
1046
+ /** Maximum number of redirect hops that may be followed. Must be an integer from 0 to 20. */
1047
+ maxHops: number;
1048
+ }
1049
+
1050
+ export type HttpRedirectFailureReason = Exclude<RedirectRunReason, "completed">;
1051
+
983
1052
  export type HttpMethod =
984
1053
  | "HEAD"
985
1054
  | "head"
@@ -998,10 +1067,16 @@ export type HttpMethod =
998
1067
  | "PATCH"
999
1068
  | "patch";
1000
1069
 
1001
- export interface StealthFetchOptions extends RequestOptions {
1070
+ export interface StealthFetchOptions extends Omit<RequestOptions, "redirectPolicy"> {
1002
1071
  method?: HttpMethod;
1003
1072
  body?: string | Buffer;
1004
1073
  redirect?: "follow" | "manual" | "error";
1074
+ /**
1075
+ * Maximum decoded response-body bytes to buffer. When set, the stealth
1076
+ * transport aborts the response and throws `response_too_large` if the
1077
+ * declared or streamed body exceeds this limit.
1078
+ */
1079
+ maxBodyBytes?: number;
1005
1080
  /**
1006
1081
  * Offsets policy-managed proxy pool selection for caller-managed retries.
1007
1082
  * Use when a request receives an upstream challenge page rather than a
@@ -1022,18 +1097,45 @@ export interface StealthFetchOptions extends RequestOptions {
1022
1097
  }
1023
1098
 
1024
1099
  export interface CookieJar {
1025
- get(name: string): string | undefined;
1026
- getAll(): Record<string, string>;
1027
- toString(): string;
1028
- find?(predicate: (cookie: string) => boolean): string | undefined;
1100
+ /** URL-less reads use the jar's response URL or session base URL. */
1101
+ get(name: string, url?: string): string | undefined;
1102
+ getAll(url?: string): Record<string, string>;
1103
+ toString(url?: string): string;
1104
+ find?(predicate: (cookie: string) => boolean, url?: string): string | undefined;
1105
+ }
1106
+
1107
+ /**
1108
+ * Version 1 of the JSON-safe, attribute-preserving stealth cookie store.
1109
+ * The nested jar is tough-cookie's serialized form and retains cookie origin,
1110
+ * Path, Secure, expiry, host-only, and other RFC attributes.
1111
+ */
1112
+ export interface StealthCookieStoreV1 {
1113
+ readonly version: 1;
1114
+ readonly jar: SerializedCookieJar;
1029
1115
  }
1030
1116
 
1117
+ /** Cookie persistence formats understood by this SDK version. */
1118
+ export type StealthCookieStore = StealthCookieStoreV1;
1119
+
1031
1120
  export interface StealthSessionCookies extends CookieJar {
1032
- has(name: string): boolean;
1033
- setFromCookieStrings(cookieStrings: readonly string[]): void;
1034
- toHeader(): string;
1121
+ has(name: string, url?: string): boolean;
1122
+ /** URL-less writes are scoped to the session base URL. */
1123
+ setFromCookieStrings(cookieStrings: readonly string[], url?: string): void;
1124
+ toHeader(url?: string): string;
1125
+ /**
1126
+ * Returns every cookie as a flat name/value map, collapsing duplicate names.
1127
+ * @deprecated Use serialize() for lossless, attribute-preserving persistence.
1128
+ */
1035
1129
  snapshot(): Record<string, string>;
1130
+ /**
1131
+ * Restores flat values as host-only, Path=/ cookies on the session base URL.
1132
+ * @deprecated Use deserialize() with state produced by serialize().
1133
+ */
1036
1134
  restore(cookies: Record<string, string>): void;
1135
+ /** Returns a versioned, JSON-safe, attribute-preserving representation of every cookie. */
1136
+ serialize(): StealthCookieStoreV1;
1137
+ /** Replaces the jar with a previously serialized, attribute-preserving cookie store. */
1138
+ deserialize(state: StealthCookieStore): void;
1037
1139
  clear(): void;
1038
1140
  }
1039
1141
 
@@ -1078,8 +1180,14 @@ export interface StealthRedirectRunOptions
1078
1180
  export interface StealthRedirectRunResult {
1079
1181
  final: StealthResponse;
1080
1182
  hops: StealthRedirectHop[];
1081
- reason: "completed" | "stopped" | "max_hops" | "missing_location" | "loop";
1183
+ reason: RedirectRunReason;
1184
+ /**
1185
+ * Complete flat view across all redirect hosts. Attributes and duplicate names are lost.
1186
+ * @deprecated Use cookieStore for lossless persistence.
1187
+ */
1082
1188
  cookies: Record<string, string>;
1189
+ /** Versioned, attribute-preserving cookie state accumulated across the redirect chain. */
1190
+ cookieStore: StealthCookieStoreV1;
1083
1191
  }
1084
1192
 
1085
1193
  export interface StealthSession {
@@ -1169,6 +1277,186 @@ export interface HttpClient {
1169
1277
  ): Promise<AsyncIterable<SseMessage>>;
1170
1278
  }
1171
1279
 
1280
+ /** Request-scoped file reference accepted by provider operation inputs. */
1281
+ export interface ProviderFileRef {
1282
+ readonly type: "request_file";
1283
+ readonly id: string;
1284
+ readonly filename: string;
1285
+ readonly mime_type?: string;
1286
+ readonly size: number;
1287
+ readonly sha256?: string;
1288
+ }
1289
+
1290
+ /** File body resolved from a request-scoped {@link ProviderFileRef}. */
1291
+ export type ProviderResolvedFile = Omit<ProviderFileRef, "mime_type"> & {
1292
+ readonly mimeType?: string;
1293
+ arrayBuffer(): Promise<ArrayBuffer>;
1294
+ bytes(): Promise<Uint8Array>;
1295
+ stream(): ReadableStream<Uint8Array>;
1296
+ };
1297
+
1298
+ /** Resolver supplied by runtimes that accept request-scoped file inputs. */
1299
+ export interface ProviderFilesContext {
1300
+ has(input: string | ProviderFileRef): boolean;
1301
+ resolve(input: string | ProviderFileRef): Promise<ProviderResolvedFile>;
1302
+ }
1303
+
1304
+ export type NativeTcpTlsMode = "required" | "allowed" | "disabled";
1305
+
1306
+ export interface NativeTcpPortRange {
1307
+ readonly start: number;
1308
+ readonly end: number;
1309
+ }
1310
+
1311
+ /** Static native TCP egress declared by a provider. */
1312
+ export interface NativeTcpEgressRule {
1313
+ readonly host: string;
1314
+ readonly ports: readonly number[];
1315
+ readonly tls: NativeTcpTlsMode;
1316
+ }
1317
+
1318
+ /**
1319
+ * Bounded native TCP egress discovered through a declared bootstrap endpoint.
1320
+ * Host suffixes are exact DNS suffixes, not wildcard patterns.
1321
+ * Dynamic rules must declare at least one target host selector through
1322
+ * targetHostSuffixes, targetIpv4Cidrs, and/or targetIpv6Cidrs. Literal grant
1323
+ * sources and targets match only same-family selectors (except exact
1324
+ * sourceHost); DNS names match only host/suffix selectors. IPv4-mapped and
1325
+ * IPv4-compatible IPv6 literals are authorized only by IPv4 CIDRs.
1326
+ *
1327
+ * Dynamic rules are ordered. The first rule whose source, target, port, and TLS
1328
+ * selectors match exclusively owns the grant; its ttlMs and maxGrants bounds
1329
+ * apply, and an exhausted/shorter rule never falls through to a later overlap.
1330
+ * Every rule must declare a source host selector, source port list/range, and
1331
+ * target port list/range; omitted ttlMs/maxGrants remain unbounded.
1332
+ */
1333
+ export interface NativeTcpDynamicEgressRule {
1334
+ readonly sourceHost?: string;
1335
+ readonly sourceHostSuffixes?: readonly string[];
1336
+ readonly sourceIpv4Cidrs?: readonly string[];
1337
+ readonly sourceIpv6Cidrs?: readonly string[];
1338
+ readonly sourcePorts?: readonly number[];
1339
+ readonly sourcePortRanges?: readonly NativeTcpPortRange[];
1340
+ readonly targetHostSuffixes?: readonly string[];
1341
+ readonly targetIpv4Cidrs?: readonly string[];
1342
+ readonly targetIpv6Cidrs?: readonly string[];
1343
+ readonly targetPorts?: readonly number[];
1344
+ readonly targetPortRanges?: readonly NativeTcpPortRange[];
1345
+ readonly tls: NativeTcpTlsMode;
1346
+ readonly ttlMs?: number;
1347
+ readonly maxGrants?: number;
1348
+ }
1349
+
1350
+ /** Common TCP/TLS connection input supported by the native runtime. */
1351
+ export interface NativeNetworkConnectInput {
1352
+ readonly host: string;
1353
+ readonly port: number;
1354
+ readonly serverName?: string;
1355
+ readonly rejectUnauthorized?: boolean;
1356
+ /**
1357
+ * Maximum time without a successful socket read before the connection is
1358
+ * closed. Opt-in; when absent, reads can remain pending indefinitely.
1359
+ */
1360
+ readonly idleTimeoutMs?: number;
1361
+ /** Maximum time allowed to establish the TCP/SOCKS/TLS connection. */
1362
+ readonly timeoutMs?: number;
1363
+ readonly signal?: AbortSignal;
1364
+ /** Overrides the credential-derived sticky affinity key. */
1365
+ readonly affinityKey?: string;
1366
+ }
1367
+
1368
+ export type NativeNetworkConnectOptions = Omit<
1369
+ NativeNetworkConnectInput,
1370
+ "serverName" | "rejectUnauthorized"
1371
+ >;
1372
+
1373
+ export type NativeTlsConnectOptions = NativeNetworkConnectInput;
1374
+
1375
+ export interface NativeNetworkDynamicGrantOptions {
1376
+ readonly sourceHost: string;
1377
+ readonly sourcePort: number;
1378
+ readonly host: string;
1379
+ readonly port: number;
1380
+ readonly tls: NativeTcpTlsMode;
1381
+ readonly ttlMs?: number;
1382
+ }
1383
+
1384
+ export interface NativeNetworkEgressGrant {
1385
+ revoke(): void;
1386
+ }
1387
+
1388
+ /** Consumer-facing alias used by native TCP providers. */
1389
+ export type NativeTcpEgressGrant = NativeNetworkEgressGrant;
1390
+
1391
+ /** Resolved egress identity for a native connection routed through a proxy. */
1392
+ export interface NativeProxyEgressInfo {
1393
+ readonly vendor: ProviderProxyProvider;
1394
+ readonly sticky: boolean;
1395
+ /** Vendor sticky session id (sid). Absent for rotating sessions. */
1396
+ readonly sessionId?: string;
1397
+ /** Hard expiry of the sticky binding, ISO 8601. */
1398
+ readonly expiresAt?: string;
1399
+ }
1400
+
1401
+ export type NativeProxyExpiringReason = "sticky_expiry";
1402
+
1403
+ export interface NativeProxyExpiringEvent {
1404
+ readonly expiresAt: string;
1405
+ readonly leadSeconds: number;
1406
+ readonly reason: NativeProxyExpiringReason;
1407
+ }
1408
+
1409
+ /**
1410
+ * Cooperative drain handler. The SDK awaits this before closing a socket whose
1411
+ * sticky proxy binding is about to expire, then force-closes at hard expiry.
1412
+ */
1413
+ export type NativeProxyDrainHandler = (
1414
+ event: NativeProxyExpiringEvent,
1415
+ ) => void | Promise<void>;
1416
+
1417
+ /** Typed reason recorded when the SDK closes a native connection intentionally. */
1418
+ export interface NativeNetworkCloseReason {
1419
+ readonly code: string;
1420
+ readonly message: string;
1421
+ }
1422
+
1423
+ /** Byte-oriented connection returned by the native TCP/TLS runtime. */
1424
+ export interface NativeNetworkConnection {
1425
+ /** Present when the connection was routed through a proxy. */
1426
+ readonly proxy?: NativeProxyEgressInfo;
1427
+ /** Present after an SDK-planned close, such as sticky proxy expiry. */
1428
+ readonly closeReason?: NativeNetworkCloseReason;
1429
+ /** Register a cooperative drain handler for sticky-expiry reconnects. */
1430
+ onExpiring?(handler: NativeProxyDrainHandler): void;
1431
+ read(): Promise<Uint8Array | null>;
1432
+ write(data: Uint8Array): Promise<void>;
1433
+ close(): Promise<void>;
1434
+ }
1435
+
1436
+ export interface NativeNetworkClient {
1437
+ connectTcp(
1438
+ input: NativeNetworkConnectOptions,
1439
+ ): Promise<NativeNetworkConnection>;
1440
+ connectTls(input: NativeTlsConnectOptions): Promise<NativeNetworkConnection>;
1441
+ grantTcpEgress(
1442
+ input: NativeNetworkDynamicGrantOptions,
1443
+ ): NativeNetworkEgressGrant;
1444
+ }
1445
+
1446
+ export interface NativeContext {
1447
+ readonly network: NativeNetworkClient;
1448
+ }
1449
+
1450
+ /** Consumer-facing alias for the native capability on provider contexts. */
1451
+ export type NativeProviderContext = NativeContext;
1452
+
1453
+ export interface NativeProviderConfig {
1454
+ readonly network?: {
1455
+ readonly tcp?: readonly NativeTcpEgressRule[];
1456
+ readonly dynamicTcp?: readonly NativeTcpDynamicEgressRule[];
1457
+ };
1458
+ }
1459
+
1172
1460
  export interface ProviderCacheKeyOptions {
1173
1461
  /**
1174
1462
  * Additional field names to omit from stable key material. The SDK always
@@ -1586,6 +1874,8 @@ export interface FlowContext {
1586
1874
  tenantId: string;
1587
1875
  providerId: string;
1588
1876
  http: HttpClient;
1877
+ /** Present when the selected runtime supplies native network capabilities. */
1878
+ readonly native?: NativeProviderContext;
1589
1879
  stealth: StealthClient;
1590
1880
  env: EnvContext;
1591
1881
  credential?: CredentialContext;
@@ -1705,6 +1995,10 @@ export interface ProviderContext {
1705
1995
  credential: CredentialContext;
1706
1996
  request?: ProviderRequestContext;
1707
1997
  http: HttpClient;
1998
+ /** Present for requests carrying runtime-resolvable file references. */
1999
+ readonly files?: ProviderFilesContext;
2000
+ /** Present when the selected runtime supplies native network capabilities. */
2001
+ readonly native?: NativeProviderContext;
1708
2002
  cache: ProviderCache;
1709
2003
  state: ProviderRuntimeState;
1710
2004
  stealth: StealthClient;
@@ -1759,6 +2053,19 @@ export interface OperationDefinition<
1759
2053
  TInput extends SchemaLike = SchemaLike,
1760
2054
  TOutput extends SchemaLike = SchemaLike,
1761
2055
  > {
2056
+ /**
2057
+ * Short English display title for the operation. The SDK passes it through
2058
+ * verbatim; the APIFuse registry derives the operation's en locale title
2059
+ * baseline from it (`docs.titleKey` remains available as an explicit key
2060
+ * override).
2061
+ */
2062
+ title?: string;
2063
+ /**
2064
+ * Raw English operation description. The SDK passes it through verbatim;
2065
+ * keyed descriptions via `descriptionKey` remain the primary flow, and
2066
+ * provider lint rules for raw prose descriptions still apply.
2067
+ */
2068
+ description?: string;
1762
2069
  descriptionKey?: ProviderLocaleKeyInput;
1763
2070
  docs?: OperationDocMeta;
1764
2071
  whenToUseKeys?: readonly ProviderLocaleKeyInput[];
@@ -1784,6 +2091,11 @@ export interface OperationDefinition<
1784
2091
  fixtures?: {
1785
2092
  request: InferSchemaOutput<TInput>;
1786
2093
  response: InferSchemaOutput<TOutput>;
2094
+ /**
2095
+ * KST calendar date when `response` evidence was captured. Date fields in
2096
+ * the response align with this date, not a resolved relative request date.
2097
+ */
2098
+ recordedAt?: string;
1787
2099
  };
1788
2100
  upstream?: {
1789
2101
  baseUrl?: string;
@@ -1797,11 +2109,57 @@ export interface OperationDefinition<
1797
2109
  healthCheckUnsupported?: HealthCheckUnsupported;
1798
2110
  }
1799
2111
 
2112
+ /**
2113
+ * Author-declared deployment overrides accepted as the optional top-level
2114
+ * `deployment` key on `defineProvider()`.
2115
+ *
2116
+ * The SDK passes this object through VERBATIM onto the returned provider
2117
+ * definition — it is typed here but deliberately not deep-validated: the
2118
+ * APIFuse registry builder owns deployment validation and resolves omitted
2119
+ * fields against the runtime deployment profiles. The shape mirrors the
2120
+ * registry's `ProviderDeploymentOverrides` contract; every field is
2121
+ * optional. Note that the deployment `runtime` axis
2122
+ * (`shared`/`dedicated`/`browser`) is distinct from the provider execution
2123
+ * `runtime` (`standard`/`shared`/`browser`).
2124
+ */
2125
+ export interface ProviderDeploymentOverrides {
2126
+ runtime?: "shared" | "dedicated" | "browser";
2127
+ language?: "typescript" | "python";
2128
+ replicas?: number;
2129
+ hpa?: {
2130
+ enabled: boolean;
2131
+ minReplicas?: number;
2132
+ maxReplicas?: number;
2133
+ targetCPUUtilizationPercentage?: number;
2134
+ };
2135
+ resources?: {
2136
+ cpu: string;
2137
+ memory: string;
2138
+ };
2139
+ cache?: {
2140
+ redis?: {
2141
+ enabled: boolean;
2142
+ url?: string;
2143
+ };
2144
+ };
2145
+ network?: {
2146
+ additionalTcpPorts?: number[];
2147
+ };
2148
+ buildContext?: string;
2149
+ }
2150
+
1800
2151
  export interface ProviderDefinition {
1801
2152
  id: string;
1802
2153
  version: string;
1803
2154
  runtime: "standard" | "shared" | "browser";
2155
+ /**
2156
+ * Optional deployment overrides, passed through verbatim from
2157
+ * `defineProvider({ deployment })`. Validation and profile resolution are
2158
+ * owned by the APIFuse registry builder, not the SDK.
2159
+ */
2160
+ deployment?: ProviderDeploymentOverrides;
1804
2161
  allowedHosts?: string[];
2162
+ native?: NativeProviderConfig;
1805
2163
  stealth?: {
1806
2164
  profile: string;
1807
2165
  platform: StealthPlatform;
@@ -0,0 +1,118 @@
1
+ // Official cross-provider contract for user-input round-trips.
2
+ //
3
+ // Doctrine (born from the 2026-07-20 CatchTable reserve incident): an
4
+ // operation MUST NOT dead-end on a problem the end user can resolve by
5
+ // choosing among live options. Instead of throwing, the provider returns a
6
+ // SUCCESSFUL payload with `status: "needs_input"` that carries everything a
7
+ // consumer-side agent needs to relay the choice verbatim and retry:
8
+ //
9
+ // - `required_selections`: only the still-pending questions, each with
10
+ // human-readable `label`s and `valid_options` the agent shows the user
11
+ // as-is. The agent never chooses on the user's behalf.
12
+ // - `selected_options`: selections already settled, echoed so the follow-up
13
+ // call keeps them without the agent reconstructing anything.
14
+ // - a fresh provider-specific state token (e.g. `reservation_state`) minted
15
+ // at response time, so the retry never races an expired token.
16
+ //
17
+ // Keeping this success-shaped (instead of error `details`) is deliberate:
18
+ // consumer error-shaping layers routinely strip error metadata, and a model
19
+ // that only sees "error" narrates failure to the user. Complex recovery
20
+ // logic belongs to the system, not the model.
21
+ //
22
+ // Deliberately ABSENT: retry templates, next-action routing, or any other
23
+ // agent choreography. Provider payloads carry upstream-backed data only; how
24
+ // to phrase the ask and shape the retry call is the consumer's contract
25
+ // (apifuse#1030). The retry is fully determined by data: copy the echoed
26
+ // selected_options, add the user's new answers, resend with the fresh state.
27
+
28
+ export const NEEDS_INPUT_STATUS = "needs_input" as const;
29
+
30
+ export interface ProviderSelectionOption {
31
+ readonly selection_value: string;
32
+ readonly label: string;
33
+ readonly [extra: string]: unknown;
34
+ }
35
+
36
+ export interface ProviderRequiredSelection {
37
+ readonly selection_key: string;
38
+ readonly label: string;
39
+ readonly required: boolean;
40
+ readonly selection_type?: string;
41
+ readonly valid_options: readonly ProviderSelectionOption[];
42
+ readonly [extra: string]: unknown;
43
+ }
44
+
45
+ export interface ProviderSelectedOption {
46
+ readonly selection_key: string;
47
+ readonly selection_value: string;
48
+ /** Free-text answer for text-kind selections. */
49
+ readonly text?: string;
50
+ /** Quantity for quantity-kind selections. */
51
+ readonly quantity?: number;
52
+ }
53
+
54
+ export interface ProviderNeedsInputPayload {
55
+ readonly status: typeof NEEDS_INPUT_STATUS;
56
+ readonly required_selections: readonly ProviderRequiredSelection[];
57
+ readonly selected_options?: readonly ProviderSelectedOption[];
58
+ /** Provider-specific fresh state token(s), e.g. `reservation_state`. */
59
+ readonly [extra: string]: unknown;
60
+ }
61
+
62
+ function isRecord(value: unknown): value is Record<string, unknown> {
63
+ return typeof value === "object" && value !== null && !Array.isArray(value);
64
+ }
65
+
66
+ function isSelectionOption(value: unknown): value is ProviderSelectionOption {
67
+ return (
68
+ isRecord(value) &&
69
+ typeof value.selection_value === "string" &&
70
+ typeof value.label === "string"
71
+ );
72
+ }
73
+
74
+ function isRequiredSelection(
75
+ value: unknown,
76
+ ): value is ProviderRequiredSelection {
77
+ return (
78
+ isRecord(value) &&
79
+ typeof value.selection_key === "string" &&
80
+ typeof value.label === "string" &&
81
+ typeof value.required === "boolean" &&
82
+ Array.isArray(value.valid_options) &&
83
+ value.valid_options.every(isSelectionOption)
84
+ );
85
+ }
86
+
87
+ function isSelectedOption(value: unknown): value is ProviderSelectedOption {
88
+ return (
89
+ isRecord(value) &&
90
+ typeof value.selection_key === "string" &&
91
+ typeof value.selection_value === "string"
92
+ );
93
+ }
94
+
95
+ export function isProviderNeedsInputPayload(
96
+ value: unknown,
97
+ ): value is ProviderNeedsInputPayload {
98
+ if (!isRecord(value)) {
99
+ return false;
100
+ }
101
+ if (
102
+ value.status !== NEEDS_INPUT_STATUS ||
103
+ !Array.isArray(value.required_selections) ||
104
+ !value.required_selections.every(isRequiredSelection)
105
+ ) {
106
+ return false;
107
+ }
108
+ // A needs_input with nothing to ask AND nothing settled to echo is a
109
+ // no-op dead end — reject it so providers cannot ship it accidentally.
110
+ if (value.required_selections.length > 0) {
111
+ return true;
112
+ }
113
+ return (
114
+ Array.isArray(value.selected_options) &&
115
+ value.selected_options.length > 0 &&
116
+ value.selected_options.every(isSelectedOption)
117
+ );
118
+ }
@@ -1 +0,0 @@
1
- @AGENTS.md
@@ -1 +0,0 @@
1
- @AGENTS.md