@apifuse/provider-sdk 2.1.0-beta.9 → 2.2.0-beta.10

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 (263) hide show
  1. package/AUTHORING.md +240 -0
  2. package/CHANGELOG.md +93 -0
  3. package/README.md +26 -10
  4. package/SUBMISSION.md +11 -12
  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 +36 -0
  9. package/bin/apifuse-pack-smoke.ts +22 -81
  10. package/bin/apifuse-pack-types.ts +266 -0
  11. package/bin/apifuse-perf.ts +45 -127
  12. package/bin/apifuse-record.ts +53 -70
  13. package/bin/apifuse-submit-check.ts +2177 -353
  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/auth-turn.v1.schema.json +89 -0
  20. package/dist/auth-turn/fixtures/invalid/empty-kind.json +4 -0
  21. package/dist/auth-turn/fixtures/invalid/expires-at-not-string.json +5 -0
  22. package/dist/auth-turn/fixtures/invalid/missing-kind.json +3 -0
  23. package/dist/auth-turn/fixtures/invalid/missing-turn-id.json +3 -0
  24. package/dist/auth-turn/fixtures/invalid/timing-unknown-field.json +7 -0
  25. package/dist/auth-turn/fixtures/invalid/turn-id-snake-case.json +4 -0
  26. package/dist/auth-turn/fixtures/invalid/unknown-top-level-field.json +5 -0
  27. package/dist/auth-turn/fixtures/valid/abort.json +8 -0
  28. package/dist/auth-turn/fixtures/valid/challenge.json +17 -0
  29. package/dist/auth-turn/fixtures/valid/complete.json +13 -0
  30. package/dist/auth-turn/fixtures/valid/form.json +14 -0
  31. package/dist/auth-turn/fixtures/valid/message.json +13 -0
  32. package/dist/auth-turn/fixtures/valid/multi_choice.json +15 -0
  33. package/dist/auth-turn/fixtures/valid/pending.json +5 -0
  34. package/dist/auth-turn/fixtures/valid/poll.json +9 -0
  35. package/dist/auth-turn/fixtures/valid/redirect.json +16 -0
  36. package/dist/auth-turn/fixtures/valid/retry.json +8 -0
  37. package/dist/auth-turn/fixtures/valid/unknown-kind.json +7 -0
  38. package/dist/auth-turn/index.d.ts +195 -0
  39. package/dist/auth-turn/index.js +133 -0
  40. package/dist/auth.d.ts +76 -0
  41. package/dist/auth.js +427 -0
  42. package/dist/ceremonies/index.d.ts +1 -1
  43. package/dist/ceremonies/index.js +14 -48
  44. package/dist/cli/commands.d.ts +1 -1
  45. package/dist/cli/commands.js +8 -0
  46. package/dist/cli/create.d.ts +3 -0
  47. package/dist/cli/create.js +47 -33
  48. package/dist/cli/prompt-assets.d.ts +80 -0
  49. package/dist/cli/prompt-assets.js +743 -0
  50. package/dist/cli/templates/provider/.agents/skills/fixtures-and-recording/SKILL.md.tpl +58 -0
  51. package/dist/cli/templates/provider/.agents/skills/health-checks-and-fail-closed/SKILL.md.tpl +65 -0
  52. package/dist/cli/templates/provider/.agents/skills/normalization-standards/SKILL.md.tpl +57 -0
  53. package/dist/cli/templates/provider/.agents/skills/pagination-and-counts/SKILL.md.tpl +52 -0
  54. package/dist/cli/templates/provider/.agents/skills/upstream-contract-verification/SKILL.md.tpl +45 -0
  55. package/dist/cli/templates/provider/.agents/skills/upstream-notes/README.md.tpl +13 -0
  56. package/dist/cli/templates/provider/.dockerignore.tpl +22 -0
  57. package/dist/cli/templates/provider/.gitignore.tpl +22 -0
  58. package/dist/cli/templates/provider/AGENTS.md.tpl +96 -0
  59. package/dist/cli/templates/provider/Dockerfile.tpl +7 -0
  60. package/dist/cli/templates/provider/README.md.tpl +163 -0
  61. package/dist/cli/templates/provider/dev.ts.tpl +5 -0
  62. package/dist/cli/templates/provider/domain/README.md.tpl +3 -0
  63. package/dist/cli/templates/provider/index.test.ts.tpl +13 -0
  64. package/dist/cli/templates/provider/index.ts.tpl +15 -0
  65. package/dist/cli/templates/provider/mappers/README.md.tpl +3 -0
  66. package/dist/cli/templates/provider/meta.ts.tpl +7 -0
  67. package/dist/cli/templates/provider/operations/index.ts.tpl +5 -0
  68. package/dist/cli/templates/provider/operations/ping.ts.tpl +24 -0
  69. package/dist/cli/templates/provider/schemas/ping.ts.tpl +24 -0
  70. package/dist/cli/templates/provider/start.ts.tpl +5 -0
  71. package/dist/cli/templates/provider/upstream/README.md.tpl +3 -0
  72. package/dist/config/loader.d.ts +149 -8
  73. package/dist/config/loader.js +378 -89
  74. package/dist/contract-serialization.d.ts +2 -2
  75. package/dist/contract-serialization.js +3 -6
  76. package/dist/contract-types.d.ts +2 -2
  77. package/dist/contract.d.ts +3 -3
  78. package/dist/contract.js +5 -6
  79. package/dist/define.d.ts +13 -1
  80. package/dist/define.js +245 -178
  81. package/dist/dev.d.ts +1 -1
  82. package/dist/dev.js +1 -1
  83. package/dist/errors.d.ts +4 -1
  84. package/dist/errors.js +48 -0
  85. package/dist/i18n/catalog.d.ts +2 -2
  86. package/dist/i18n/catalog.js +4 -10
  87. package/dist/i18n/index.d.ts +2 -2
  88. package/dist/i18n/index.js +2 -2
  89. package/dist/i18n/keys.d.ts +2 -2
  90. package/dist/index.d.ts +44 -41
  91. package/dist/index.js +39 -36
  92. package/dist/lint.d.ts +1 -0
  93. package/dist/lint.js +35 -15
  94. package/dist/provider.d.ts +11 -9
  95. package/dist/provider.js +9 -8
  96. package/dist/public-schema-field-lint.d.ts +1 -1
  97. package/dist/recipes/gov-api.js +1 -1
  98. package/dist/runtime/auth-flow.d.ts +1 -1
  99. package/dist/runtime/auth-flow.js +4 -2
  100. package/dist/runtime/browser.d.ts +1 -1
  101. package/dist/runtime/browser.js +214 -25
  102. package/dist/runtime/cache.d.ts +1 -1
  103. package/dist/runtime/cache.js +4 -8
  104. package/dist/runtime/choice.d.ts +1 -1
  105. package/dist/runtime/choice.js +31 -35
  106. package/dist/runtime/credential.d.ts +1 -1
  107. package/dist/runtime/credential.js +1 -1
  108. package/dist/runtime/env.d.ts +1 -1
  109. package/dist/runtime/executor.d.ts +1 -1
  110. package/dist/runtime/executor.js +15 -3
  111. package/dist/runtime/http.d.ts +2 -2
  112. package/dist/runtime/http.js +160 -344
  113. package/dist/runtime/insights.d.ts +1 -1
  114. package/dist/runtime/insights.js +6 -13
  115. package/dist/runtime/instrumentation.d.ts +2 -2
  116. package/dist/runtime/instrumentation.js +56 -19
  117. package/dist/runtime/keyring.js +1 -1
  118. package/dist/runtime/namespace.js +1 -1
  119. package/dist/runtime/otlp.d.ts +1 -1
  120. package/dist/runtime/perf.d.ts +1 -1
  121. package/dist/runtime/provider.d.ts +1 -1
  122. package/dist/runtime/provider.js +1 -2
  123. package/dist/runtime/proxy-errors.d.ts +1 -1
  124. package/dist/runtime/proxy-errors.js +9 -7
  125. package/dist/runtime/proxy-nodemaven.d.ts +35 -0
  126. package/dist/runtime/proxy-nodemaven.js +128 -0
  127. package/dist/runtime/proxy-retry-policy.d.ts +40 -0
  128. package/dist/runtime/proxy-retry-policy.js +326 -0
  129. package/dist/runtime/proxy-telemetry.d.ts +2 -1
  130. package/dist/runtime/proxy-telemetry.js +55 -52
  131. package/dist/runtime/redis.d.ts +1 -1
  132. package/dist/runtime/redis.js +2 -4
  133. package/dist/runtime/request-options.d.ts +1 -1
  134. package/dist/runtime/secrets.d.ts +27 -0
  135. package/dist/runtime/secrets.js +51 -0
  136. package/dist/runtime/state.d.ts +2 -2
  137. package/dist/runtime/state.js +15 -4
  138. package/dist/runtime/stealth.d.ts +7 -4
  139. package/dist/runtime/stealth.js +257 -215
  140. package/dist/runtime/stt.d.ts +1 -1
  141. package/dist/runtime/stt.js +11 -15
  142. package/dist/runtime/trace.d.ts +2 -2
  143. package/dist/runtime/trace.js +2 -4
  144. package/dist/runtime/waterfall.d.ts +1 -1
  145. package/dist/schema.d.ts +1 -1
  146. package/dist/schema.js +7 -15
  147. package/dist/serve.d.ts +1 -1
  148. package/dist/serve.js +1 -1
  149. package/dist/server/index.d.ts +7 -3
  150. package/dist/server/index.js +6 -2
  151. package/dist/server/self-test-input-tokens.d.ts +1 -0
  152. package/dist/server/self-test-input-tokens.js +37 -0
  153. package/dist/server/self-test-redaction.d.ts +20 -0
  154. package/dist/server/self-test-redaction.js +70 -0
  155. package/dist/server/self-test-token.d.ts +30 -0
  156. package/dist/server/self-test-token.js +50 -0
  157. package/dist/server/self-test.d.ts +199 -0
  158. package/dist/server/self-test.js +1113 -0
  159. package/dist/server/serve.d.ts +14 -3
  160. package/dist/server/serve.js +135 -64
  161. package/dist/server/types.d.ts +10 -9
  162. package/dist/server/types.js +3 -7
  163. package/dist/stealth/profiles.d.ts +1 -1
  164. package/dist/stealth/profiles.js +5 -14
  165. package/dist/stream.d.ts +1 -1
  166. package/dist/testing/index.d.ts +2 -2
  167. package/dist/testing/index.js +2 -2
  168. package/dist/testing/run.d.ts +1 -1
  169. package/dist/testing/run.js +12 -15
  170. package/dist/types.d.ts +237 -1
  171. package/dist/user-input.d.ts +30 -0
  172. package/dist/user-input.js +66 -0
  173. package/package.json +16 -5
  174. package/src/auth-turn/auth-turn.v1.schema.json +89 -0
  175. package/src/auth-turn/fixtures/invalid/empty-kind.json +4 -0
  176. package/src/auth-turn/fixtures/invalid/expires-at-not-string.json +5 -0
  177. package/src/auth-turn/fixtures/invalid/missing-kind.json +3 -0
  178. package/src/auth-turn/fixtures/invalid/missing-turn-id.json +3 -0
  179. package/src/auth-turn/fixtures/invalid/timing-unknown-field.json +7 -0
  180. package/src/auth-turn/fixtures/invalid/turn-id-snake-case.json +4 -0
  181. package/src/auth-turn/fixtures/invalid/unknown-top-level-field.json +5 -0
  182. package/src/auth-turn/fixtures/valid/abort.json +8 -0
  183. package/src/auth-turn/fixtures/valid/challenge.json +17 -0
  184. package/src/auth-turn/fixtures/valid/complete.json +13 -0
  185. package/src/auth-turn/fixtures/valid/form.json +14 -0
  186. package/src/auth-turn/fixtures/valid/message.json +13 -0
  187. package/src/auth-turn/fixtures/valid/multi_choice.json +15 -0
  188. package/src/auth-turn/fixtures/valid/pending.json +5 -0
  189. package/src/auth-turn/fixtures/valid/poll.json +9 -0
  190. package/src/auth-turn/fixtures/valid/redirect.json +16 -0
  191. package/src/auth-turn/fixtures/valid/retry.json +8 -0
  192. package/src/auth-turn/fixtures/valid/unknown-kind.json +7 -0
  193. package/src/auth-turn/index.ts +177 -0
  194. package/src/auth.ts +728 -0
  195. package/src/ceremonies/index.ts +33 -121
  196. package/src/cli/commands.ts +10 -0
  197. package/src/cli/create.ts +69 -99
  198. package/src/cli/prompt-assets.ts +865 -0
  199. package/src/cli/templates/provider/.agents/skills/fixtures-and-recording/SKILL.md.tpl +58 -0
  200. package/src/cli/templates/provider/.agents/skills/health-checks-and-fail-closed/SKILL.md.tpl +65 -0
  201. package/src/cli/templates/provider/.agents/skills/normalization-standards/SKILL.md.tpl +57 -0
  202. package/src/cli/templates/provider/.agents/skills/pagination-and-counts/SKILL.md.tpl +52 -0
  203. package/src/cli/templates/provider/.agents/skills/upstream-contract-verification/SKILL.md.tpl +45 -0
  204. package/src/cli/templates/provider/.agents/skills/upstream-notes/README.md.tpl +13 -0
  205. package/src/cli/templates/provider/AGENTS.md.tpl +96 -0
  206. package/src/cli/templates/provider/README.md.tpl +7 -4
  207. package/src/config/loader.ts +543 -208
  208. package/src/contract-serialization.ts +5 -11
  209. package/src/contract-types.ts +2 -2
  210. package/src/contract.ts +13 -28
  211. package/src/define.ts +397 -528
  212. package/src/dev.ts +4 -9
  213. package/src/errors.ts +58 -5
  214. package/src/i18n/catalog.ts +10 -32
  215. package/src/i18n/index.ts +2 -2
  216. package/src/i18n/keys.ts +5 -11
  217. package/src/index.ts +64 -41
  218. package/src/lint.ts +122 -159
  219. package/src/provider.ts +40 -9
  220. package/src/public-schema-field-lint.ts +7 -33
  221. package/src/recipes/gov-api.ts +2 -5
  222. package/src/runtime/auth-flow.ts +6 -6
  223. package/src/runtime/browser.ts +320 -151
  224. package/src/runtime/cache.ts +20 -67
  225. package/src/runtime/choice.ts +79 -132
  226. package/src/runtime/credential.ts +2 -2
  227. package/src/runtime/env.ts +1 -1
  228. package/src/runtime/executor.ts +23 -20
  229. package/src/runtime/http.ts +216 -539
  230. package/src/runtime/insights.ts +15 -53
  231. package/src/runtime/instrumentation.ts +78 -65
  232. package/src/runtime/keyring.ts +7 -19
  233. package/src/runtime/namespace.ts +2 -7
  234. package/src/runtime/otlp.ts +12 -23
  235. package/src/runtime/perf.ts +1 -1
  236. package/src/runtime/provider.ts +4 -9
  237. package/src/runtime/proxy-errors.ts +29 -42
  238. package/src/runtime/proxy-nodemaven.ts +178 -0
  239. package/src/runtime/proxy-retry-policy.ts +469 -0
  240. package/src/runtime/proxy-telemetry.ts +79 -77
  241. package/src/runtime/redis.ts +4 -12
  242. package/src/runtime/request-options.ts +4 -13
  243. package/src/runtime/secrets.ts +64 -0
  244. package/src/runtime/state.ts +41 -110
  245. package/src/runtime/stealth.ts +331 -369
  246. package/src/runtime/stt.ts +38 -94
  247. package/src/runtime/trace.ts +14 -44
  248. package/src/runtime/waterfall.ts +5 -18
  249. package/src/schema.ts +23 -84
  250. package/src/serve.ts +1 -1
  251. package/src/server/index.ts +44 -3
  252. package/src/server/self-test-input-tokens.ts +46 -0
  253. package/src/server/self-test-redaction.ts +97 -0
  254. package/src/server/self-test-token.ts +70 -0
  255. package/src/server/self-test.ts +1450 -0
  256. package/src/server/serve.ts +206 -216
  257. package/src/server/types.ts +7 -19
  258. package/src/stealth/profiles.ts +10 -26
  259. package/src/stream.ts +8 -19
  260. package/src/testing/index.ts +2 -2
  261. package/src/testing/run.ts +24 -64
  262. package/src/types.ts +274 -1
  263. package/src/user-input.ts +118 -0
package/src/types.ts CHANGED
@@ -331,9 +331,20 @@ export interface HealthJourneySchedule {
331
331
  kind: "interval";
332
332
  /** ISO 8601 duration, for example PT8H. */
333
333
  interval: Iso8601Duration;
334
+ randomize?: HealthScheduleRandomization;
334
335
  jitter?: Iso8601Duration;
335
336
  }
336
337
 
338
+ export type HealthScheduleRandomization =
339
+ | {
340
+ mode: "centered";
341
+ maxOffset: Iso8601Duration;
342
+ }
343
+ | {
344
+ mode: "delayed";
345
+ maxDelay: Iso8601Duration;
346
+ };
347
+
337
348
  export interface HealthJourneyStep {
338
349
  id: string;
339
350
  description?: string;
@@ -612,6 +623,9 @@ export interface HealthCheckCase<TInput = unknown, TOutput = unknown> {
612
623
  export interface HealthCheckSuite<TInput = unknown, TOutput = unknown> {
613
624
  /** Polling interval for the suite. All cases share this cadence. */
614
625
  interval: ProbeInterval;
626
+ schedule?: {
627
+ randomize?: HealthScheduleRandomization;
628
+ };
615
629
  /** Per-case timeout in milliseconds. Default: 30000. */
616
630
  timeoutMs?: number;
617
631
  /** Default degradation threshold for cases in this suite. Default: runtime threshold. */
@@ -687,6 +701,14 @@ export interface ProviderHealthMonitorConfig {
687
701
  serviceAccount?: string;
688
702
  }
689
703
 
704
+ /**
705
+ * New name for `ProviderHealthMonitorConfig` as part of the health dependency
706
+ * inversion (`healthMonitor` → `healthProbe`). Both provider-level fields are
707
+ * accepted transitionally with identical validation; declaring both is a
708
+ * `ValidationError`.
709
+ */
710
+ export type ProviderHealthProbeConfig = ProviderHealthMonitorConfig;
711
+
690
712
  export interface HealthMonitorProbeOverride {
691
713
  /** Optional runtime interval override as a positive `ms`-style duration string. */
692
714
  interval?: ProbeInterval;
@@ -747,7 +769,25 @@ export type ProviderAccessVisibility = "public" | "early_access";
747
769
 
748
770
  export type ProviderProxyMode = "disabled" | "optional" | "required";
749
771
 
750
- export type ProviderProxyProvider = "smartproxy" | "decodo" | "custom";
772
+ /**
773
+ * Proxy egress vendors. These are FOUR DISTINCT services — do not conflate them
774
+ * (a common mistake because the names collide with a well-known rebrand):
775
+ *
776
+ * - `smartproxy` — **api.smartproxy.org**, a residential proxy with an IP
777
+ * *extraction/allocation* API (app_key → a pool of raw `ip:port` CONNECT
778
+ * endpoints). This is our own vendor. It is NOT the company formerly named
779
+ * "Smartproxy". Credentials: `APIFUSE__PROXY__SMARTPROXY_APP_KEY`.
780
+ * - `nodemaven` — **gate.nodemaven.com**, a *gateway* proxy with static
781
+ * credentials; geo/session encoded in the username, no allocation API.
782
+ * - `decodo` — **decodo.com**, the *gateway* proxy that was named "Smartproxy"
783
+ * (smartproxy.com) before its 2025 rebrand to Decodo. Sticky sessions via
784
+ * username params. A different company from `smartproxy` above.
785
+ * **@deprecated** — unused; no managed adapter. Use `smartproxy`/`nodemaven`,
786
+ * or the `APIFUSE__PROXY__URL` bring-your-own escape hatch.
787
+ * - `custom` — **@deprecated** bring-your-own static proxy URL marker. The
788
+ * `APIFUSE__PROXY__URL` env still works without declaring this value.
789
+ */
790
+ export type ProviderProxyProvider = "smartproxy" | "nodemaven" | "decodo" | "custom";
751
791
 
752
792
  export type ProviderProxySessionAffinity =
753
793
  | "request"
@@ -761,7 +801,18 @@ export interface ProviderProxyPolicy {
761
801
  * certificate verification, and vendor allocator endpoints are SDK-owned.
762
802
  */
763
803
  mode: ProviderProxyMode;
804
+ /**
805
+ * @deprecated Use `providers: [...]` to declare an ordered vendor fallback
806
+ * chain. A single-element `providers` list is equivalent to this field.
807
+ */
764
808
  provider?: ProviderProxyProvider;
809
+ /**
810
+ * Ordered proxy-vendor fallback chain. The SDK tries each vendor in order and
811
+ * fails over to the next when a vendor lacks credentials or its allocation /
812
+ * transport is exhausted. When omitted, `provider` (or the platform default)
813
+ * is used as a single-vendor chain.
814
+ */
815
+ providers?: ProviderProxyProvider[];
765
816
  geo?: {
766
817
  /** ISO 3166-1 alpha-2 country code, for example KR or US. */
767
818
  country?: Iso3166Alpha2CountryCode;
@@ -979,6 +1030,7 @@ export type HttpMethod =
979
1030
  export interface StealthFetchOptions extends RequestOptions {
980
1031
  method?: HttpMethod;
981
1032
  body?: string | Buffer;
1033
+ redirect?: "follow" | "manual" | "error";
982
1034
  /**
983
1035
  * Offsets policy-managed proxy pool selection for caller-managed retries.
984
1036
  * Use when a request receives an upstream challenge page rather than a
@@ -1005,9 +1057,20 @@ export interface CookieJar {
1005
1057
  find?(predicate: (cookie: string) => boolean): string | undefined;
1006
1058
  }
1007
1059
 
1060
+ export interface StealthSessionCookies extends CookieJar {
1061
+ has(name: string): boolean;
1062
+ setFromCookieStrings(cookieStrings: readonly string[]): void;
1063
+ toHeader(): string;
1064
+ snapshot(): Record<string, string>;
1065
+ restore(cookies: Record<string, string>): void;
1066
+ clear(): void;
1067
+ }
1068
+
1008
1069
  export interface DeclarativeStealthResponse {
1009
1070
  status: number;
1010
1071
  ok: boolean;
1072
+ url?: string;
1073
+ redirected?: boolean;
1011
1074
  headers: Record<string, string>;
1012
1075
  rawHeaders: [string, string][];
1013
1076
  body: string;
@@ -1026,8 +1089,34 @@ export type RequestWithMethodOptions = RequestOptions & {
1026
1089
  body?: unknown;
1027
1090
  };
1028
1091
 
1092
+ export interface StealthRedirectHop {
1093
+ url: string;
1094
+ status: number;
1095
+ method: string;
1096
+ location?: string;
1097
+ nextUrl?: string;
1098
+ }
1099
+
1100
+ export interface StealthRedirectRunOptions
1101
+ extends Omit<StealthFetchOptions, "redirect"> {
1102
+ url: string;
1103
+ maxHops?: number;
1104
+ stopWhen?: (hop: StealthRedirectHop) => boolean | Promise<boolean>;
1105
+ }
1106
+
1107
+ export interface StealthRedirectRunResult {
1108
+ final: StealthResponse;
1109
+ hops: StealthRedirectHop[];
1110
+ reason: "completed" | "stopped" | "max_hops" | "missing_location" | "loop";
1111
+ cookies: Record<string, string>;
1112
+ }
1113
+
1029
1114
  export interface StealthSession {
1030
1115
  fetch(url: string, options?: StealthFetchOptions): Promise<StealthResponse>;
1116
+ cookies: StealthSessionCookies;
1117
+ redirects: {
1118
+ run(options: StealthRedirectRunOptions): Promise<StealthRedirectRunResult>;
1119
+ };
1031
1120
  close(): void;
1032
1121
  }
1033
1122
 
@@ -1050,6 +1139,8 @@ export interface HttpResponse<T = unknown> {
1050
1139
  data: T;
1051
1140
  json<U = T>(): Promise<U>;
1052
1141
  text(): Promise<string>;
1142
+ arrayBuffer(): Promise<ArrayBuffer>;
1143
+ bytes(): Promise<Uint8Array>;
1053
1144
  }
1054
1145
 
1055
1146
  export interface HttpStreamResponse {
@@ -1206,6 +1297,45 @@ export interface BrowserFrame {
1206
1297
  locator(selector: string): BrowserLocator;
1207
1298
  }
1208
1299
 
1300
+ export type BrowserResourceMethod = "GET" | "HEAD";
1301
+
1302
+ export type BrowserResourceRequest = {
1303
+ readonly url: string;
1304
+ readonly method: BrowserResourceMethod;
1305
+ readonly resourceType?: string;
1306
+ readonly headers: Readonly<Record<string, string>>;
1307
+ };
1308
+
1309
+ export type BrowserResourceBody = Buffer | Uint8Array | ArrayBuffer | string;
1310
+
1311
+ export type BrowserResourceDecision =
1312
+ | {
1313
+ readonly action: "fulfill";
1314
+ readonly status?: number;
1315
+ readonly headers?: Readonly<Record<string, string>>;
1316
+ readonly body?: BrowserResourceBody;
1317
+ }
1318
+ | {
1319
+ readonly action: "block";
1320
+ readonly reason?: string;
1321
+ };
1322
+
1323
+ export type BrowserResourceRoute = {
1324
+ readonly match:
1325
+ | string
1326
+ | RegExp
1327
+ | ((request: BrowserResourceRequest) => boolean);
1328
+ readonly handle: (
1329
+ request: BrowserResourceRequest,
1330
+ ) => Promise<BrowserResourceDecision> | BrowserResourceDecision;
1331
+ };
1332
+
1333
+ export type BrowserResourcePolicy = {
1334
+ readonly defaultAction?: "block";
1335
+ readonly allowedMethods?: readonly BrowserResourceMethod[];
1336
+ readonly routes: readonly BrowserResourceRoute[];
1337
+ };
1338
+
1209
1339
  export interface BrowserPage extends BrowserFrame {
1210
1340
  close(): Promise<void>;
1211
1341
  fill(selector: string, text: string): Promise<void>;
@@ -1219,6 +1349,10 @@ export interface BrowserPage extends BrowserFrame {
1219
1349
  options?: { timeout?: number },
1220
1350
  ): Promise<void>;
1221
1351
  frames(): Promise<BrowserFrame[]>;
1352
+ withResourcePolicy<T>(
1353
+ policy: BrowserResourcePolicy,
1354
+ run: () => Promise<T>,
1355
+ ): Promise<T>;
1222
1356
  }
1223
1357
 
1224
1358
  export type BrowserChallengeRequest = {
@@ -1397,6 +1531,84 @@ export interface ContextScratchpad {
1397
1531
 
1398
1532
  export type FlowContextStore = ContextScratchpad;
1399
1533
 
1534
+ export type AuthSafeJson =
1535
+ | string
1536
+ | number
1537
+ | boolean
1538
+ | null
1539
+ | readonly AuthSafeJson[]
1540
+ | { readonly [key: string]: AuthSafeJson };
1541
+
1542
+ export type AuthSafeData = { readonly [key: string]: AuthSafeJson };
1543
+
1544
+ export type AuthAbortRetry = "never" | "retry" | "after_user_action";
1545
+
1546
+ export type AuthAbortData = Record<string, unknown> & {
1547
+ readonly code: string;
1548
+ readonly message?: string;
1549
+ readonly retry?: AuthAbortRetry;
1550
+ readonly actionHint?: AuthSafeJson;
1551
+ readonly fieldErrors?: { readonly [field: string]: string };
1552
+ readonly details?: AuthSafeData;
1553
+ };
1554
+
1555
+ export interface AuthFlowTerminalContext {
1556
+ readonly signal?: AbortSignal;
1557
+ readonly deadline?: string;
1558
+ complete<TCredential extends Record<string, string>>(options: {
1559
+ readonly credential: TCredential;
1560
+ readonly metadata?: AuthSafeData;
1561
+ readonly data?: AuthSafeData;
1562
+ readonly turnId?: string;
1563
+ readonly expiresAt?: string;
1564
+ }): AuthTurn;
1565
+ abort(options: {
1566
+ readonly code: string;
1567
+ readonly message?: string;
1568
+ readonly retry?: AuthAbortRetry;
1569
+ readonly actionHint?: AuthSafeJson;
1570
+ readonly fieldErrors?: { readonly [field: string]: string };
1571
+ readonly data?: AuthSafeData;
1572
+ readonly turnId?: string;
1573
+ readonly expiresAt?: string;
1574
+ }): AuthTurn;
1575
+ nextForm(
1576
+ options: {
1577
+ readonly hintKey?: ProviderLocaleKeyInput;
1578
+ readonly data?: AuthSafeData;
1579
+ readonly turnId?: string;
1580
+ readonly expiresAt?: string;
1581
+ readonly timing?: AuthTurn["timing"];
1582
+ } & (
1583
+ | {
1584
+ readonly fields: Record<
1585
+ string,
1586
+ {
1587
+ readonly type?: "string" | "email" | "password" | "otp";
1588
+ readonly labelKey?: ProviderLocaleKeyInput;
1589
+ readonly descriptionKey?: ProviderLocaleKeyInput;
1590
+ readonly placeholderKey?: ProviderLocaleKeyInput;
1591
+ readonly required?: boolean;
1592
+ readonly sensitive?: boolean;
1593
+ }
1594
+ >;
1595
+ readonly expectedInput?: never;
1596
+ }
1597
+ | {
1598
+ readonly expectedInput: Record<string, unknown>;
1599
+ readonly fields?: never;
1600
+ }
1601
+ ),
1602
+ ): AuthTurn;
1603
+ nextPoll(options?: {
1604
+ readonly hintKey?: ProviderLocaleKeyInput;
1605
+ readonly data?: AuthSafeData;
1606
+ readonly turnId?: string;
1607
+ readonly expiresAt?: string;
1608
+ readonly timing?: AuthTurn["timing"];
1609
+ }): AuthTurn;
1610
+ }
1611
+
1400
1612
  export interface FlowContext {
1401
1613
  connectionId?: string;
1402
1614
  externalRef?: string;
@@ -1408,6 +1620,7 @@ export interface FlowContext {
1408
1620
  credential?: CredentialContext;
1409
1621
  context: ContextScratchpad;
1410
1622
  stt: SttContext;
1623
+ auth: AuthFlowTerminalContext;
1411
1624
  }
1412
1625
 
1413
1626
  export interface AuthTurn {
@@ -1575,6 +1788,19 @@ export interface OperationDefinition<
1575
1788
  TInput extends SchemaLike = SchemaLike,
1576
1789
  TOutput extends SchemaLike = SchemaLike,
1577
1790
  > {
1791
+ /**
1792
+ * Short English display title for the operation. The SDK passes it through
1793
+ * verbatim; the APIFuse registry derives the operation's en locale title
1794
+ * baseline from it (`docs.titleKey` remains available as an explicit key
1795
+ * override).
1796
+ */
1797
+ title?: string;
1798
+ /**
1799
+ * Raw English operation description. The SDK passes it through verbatim;
1800
+ * keyed descriptions via `descriptionKey` remain the primary flow, and
1801
+ * provider lint rules for raw prose descriptions still apply.
1802
+ */
1803
+ description?: string;
1578
1804
  descriptionKey?: ProviderLocaleKeyInput;
1579
1805
  docs?: OperationDocMeta;
1580
1806
  whenToUseKeys?: readonly ProviderLocaleKeyInput[];
@@ -1613,10 +1839,55 @@ export interface OperationDefinition<
1613
1839
  healthCheckUnsupported?: HealthCheckUnsupported;
1614
1840
  }
1615
1841
 
1842
+ /**
1843
+ * Author-declared deployment overrides accepted as the optional top-level
1844
+ * `deployment` key on `defineProvider()`.
1845
+ *
1846
+ * The SDK passes this object through VERBATIM onto the returned provider
1847
+ * definition — it is typed here but deliberately not deep-validated: the
1848
+ * APIFuse registry builder owns deployment validation and resolves omitted
1849
+ * fields against the runtime deployment profiles. The shape mirrors the
1850
+ * registry's `ProviderDeploymentOverrides` contract; every field is
1851
+ * optional. Note that the deployment `runtime` axis
1852
+ * (`shared`/`dedicated`/`browser`) is distinct from the provider execution
1853
+ * `runtime` (`standard`/`shared`/`browser`).
1854
+ */
1855
+ export interface ProviderDeploymentOverrides {
1856
+ runtime?: "shared" | "dedicated" | "browser";
1857
+ language?: "typescript" | "python";
1858
+ replicas?: number;
1859
+ hpa?: {
1860
+ enabled: boolean;
1861
+ minReplicas?: number;
1862
+ maxReplicas?: number;
1863
+ targetCPUUtilizationPercentage?: number;
1864
+ };
1865
+ resources?: {
1866
+ cpu: string;
1867
+ memory: string;
1868
+ };
1869
+ cache?: {
1870
+ redis?: {
1871
+ enabled: boolean;
1872
+ url?: string;
1873
+ };
1874
+ };
1875
+ network?: {
1876
+ additionalTcpPorts?: number[];
1877
+ };
1878
+ buildContext?: string;
1879
+ }
1880
+
1616
1881
  export interface ProviderDefinition {
1617
1882
  id: string;
1618
1883
  version: string;
1619
1884
  runtime: "standard" | "shared" | "browser";
1885
+ /**
1886
+ * Optional deployment overrides, passed through verbatim from
1887
+ * `defineProvider({ deployment })`. Validation and profile resolution are
1888
+ * owned by the APIFuse registry builder, not the SDK.
1889
+ */
1890
+ deployment?: ProviderDeploymentOverrides;
1620
1891
  allowedHosts?: string[];
1621
1892
  stealth?: {
1622
1893
  profile: string;
@@ -1636,5 +1907,7 @@ export interface ProviderDefinition {
1636
1907
  meta: ProviderMeta;
1637
1908
  operations: Record<string, OperationDefinition<SchemaLike, SchemaLike>>;
1638
1909
  healthMonitor?: ProviderHealthMonitorConfig;
1910
+ /** Transitional alias for `healthMonitor`; `defineProvider` mirrors both. */
1911
+ healthProbe?: ProviderHealthProbeConfig;
1639
1912
  healthJourneys?: readonly HealthJourneyDefinition[];
1640
1913
  }
@@ -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
+ }