@apifuse/provider-sdk 2.2.0-beta.5 → 2.2.0-beta.51

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 (368) hide show
  1. package/AUTHORING.md +537 -33
  2. package/CHANGELOG.md +205 -1
  3. package/README.md +83 -22
  4. package/SUBMISSION.md +2 -2
  5. package/bin/apifuse-check.ts +165 -11
  6. package/bin/apifuse-dev.ts +57 -13
  7. package/bin/apifuse-migrate-operation-declaration.ts +55 -0
  8. package/bin/apifuse-migrate-shape.ts +202 -0
  9. package/bin/apifuse-pack-check.ts +22 -2
  10. package/bin/apifuse-pack-smoke.ts +57 -2
  11. package/bin/apifuse-pack-types.ts +357 -38
  12. package/bin/apifuse-perf.ts +14 -13
  13. package/bin/apifuse-record.ts +709 -72
  14. package/bin/apifuse-submit-check.ts +2344 -324
  15. package/bin/apifuse-sync-assets.ts +117 -0
  16. package/bin/submit-check-delimited-text.ts +50 -0
  17. package/dist/auth-turn/index.d.ts +3 -3
  18. package/dist/auth-turn/index.js +1 -1
  19. package/dist/auth.d.ts +14 -0
  20. package/dist/auth.js +67 -0
  21. package/dist/ceremonies/index.d.ts +16 -0
  22. package/dist/ceremonies/index.js +141 -36
  23. package/dist/cli/commands.d.ts +1 -1
  24. package/dist/cli/commands.js +27 -0
  25. package/dist/cli/create.d.ts +4 -1
  26. package/dist/cli/create.js +42 -37
  27. package/dist/cli/migrate-operation-declaration.d.ts +59 -0
  28. package/dist/cli/migrate-operation-declaration.js +1249 -0
  29. package/dist/cli/migrate-operation-shape.d.ts +44 -0
  30. package/dist/cli/migrate-operation-shape.js +113 -0
  31. package/dist/cli/migrate-provider-shape.d.ts +52 -0
  32. package/dist/cli/migrate-provider-shape.js +578 -0
  33. package/dist/cli/prompt-assets.d.ts +80 -0
  34. package/dist/cli/prompt-assets.js +743 -0
  35. package/dist/cli/templates/provider/AGENTS.md.tpl +17 -8
  36. package/dist/cli/templates/provider/Dockerfile.tpl +1 -1
  37. package/dist/cli/templates/provider/README.md.tpl +5 -5
  38. package/dist/cli/templates/provider/index.ts.tpl +6 -3
  39. package/dist/cli/templates/provider/operations/ping.ts.tpl +4 -1
  40. package/dist/cli/templates/provider/provider.json.tpl +6 -0
  41. package/dist/config/loader.d.ts +179 -16
  42. package/dist/config/loader.js +435 -127
  43. package/dist/contract-serialization.js +4 -8
  44. package/dist/contract-types.d.ts +12 -5
  45. package/dist/contract.js +24 -10
  46. package/dist/declaration-validation.d.ts +33 -0
  47. package/dist/declaration-validation.js +266 -0
  48. package/dist/define.d.ts +66 -29
  49. package/dist/define.js +778 -69
  50. package/dist/dev.d.ts +3 -0
  51. package/dist/dev.js +1 -1
  52. package/dist/engine.d.ts +78 -0
  53. package/dist/engine.js +133 -0
  54. package/dist/error-observability.d.ts +7 -0
  55. package/dist/error-observability.js +61 -0
  56. package/dist/error-resolution.d.ts +4 -0
  57. package/dist/error-resolution.js +122 -0
  58. package/dist/errors.d.ts +33 -0
  59. package/dist/errors.js +40 -0
  60. package/dist/fixture-sanitization.d.ts +28 -0
  61. package/dist/fixture-sanitization.js +227 -0
  62. package/dist/health-scenario.d.ts +1842 -0
  63. package/dist/health-scenario.js +624 -0
  64. package/dist/index.d.ts +21 -10
  65. package/dist/index.js +12 -7
  66. package/dist/lint.d.ts +23 -11
  67. package/dist/lint.js +572 -69
  68. package/dist/native-address.d.ts +43 -0
  69. package/dist/native-address.js +281 -0
  70. package/dist/native-egress-policy.d.ts +31 -0
  71. package/dist/native-egress-policy.js +288 -0
  72. package/dist/observability.d.ts +5 -2
  73. package/dist/observability.js +48 -1
  74. package/dist/provider.d.ts +10 -2
  75. package/dist/provider.js +4 -1
  76. package/dist/runtime/auth-flow.d.ts +5 -1
  77. package/dist/runtime/auth-flow.js +6 -0
  78. package/dist/runtime/browser.d.ts +1 -0
  79. package/dist/runtime/browser.js +492 -49
  80. package/dist/runtime/cache.d.ts +1 -0
  81. package/dist/runtime/cache.js +169 -15
  82. package/dist/runtime/choice-wordlist.d.ts +9 -0
  83. package/dist/runtime/choice-wordlist.js +138 -0
  84. package/dist/runtime/choice.d.ts +13 -1
  85. package/dist/runtime/choice.js +490 -102
  86. package/dist/runtime/chrome149-header-order.d.ts +58 -0
  87. package/dist/runtime/chrome149-header-order.js +289 -0
  88. package/dist/runtime/env.js +12 -0
  89. package/dist/runtime/executor.d.ts +3 -2
  90. package/dist/runtime/executor.js +26 -25
  91. package/dist/runtime/http.d.ts +1 -0
  92. package/dist/runtime/http.js +515 -53
  93. package/dist/runtime/insights.js +2 -2
  94. package/dist/runtime/instrumentation.d.ts +2 -2
  95. package/dist/runtime/instrumentation.js +366 -8
  96. package/dist/runtime/native-network-errors.d.ts +33 -0
  97. package/dist/runtime/native-network-errors.js +69 -0
  98. package/dist/runtime/native-network.d.ts +96 -0
  99. package/dist/runtime/native-network.js +1232 -0
  100. package/dist/runtime/ocr.d.ts +29 -0
  101. package/dist/runtime/ocr.js +440 -0
  102. package/dist/runtime/otlp.d.ts +71 -2
  103. package/dist/runtime/otlp.js +397 -16
  104. package/dist/runtime/proxy-errors.js +6 -2
  105. package/dist/runtime/proxy-nodemaven.d.ts +56 -0
  106. package/dist/runtime/proxy-nodemaven.js +146 -0
  107. package/dist/runtime/proxy-telemetry.d.ts +80 -1
  108. package/dist/runtime/proxy-telemetry.js +154 -47
  109. package/dist/runtime/redirects.d.ts +29 -0
  110. package/dist/runtime/redirects.js +36 -0
  111. package/dist/runtime/redis.d.ts +1 -1
  112. package/dist/runtime/redis.js +4 -2
  113. package/dist/runtime/request-options.d.ts +68 -1
  114. package/dist/runtime/request-options.js +548 -0
  115. package/dist/runtime/resolver-config.d.ts +6 -0
  116. package/dist/runtime/resolver-config.js +6 -0
  117. package/dist/runtime/resolver-public.d.ts +1 -0
  118. package/dist/runtime/resolver-public.js +1 -0
  119. package/dist/runtime/resolver-shared.d.ts +14 -0
  120. package/dist/runtime/resolver-shared.js +12 -0
  121. package/dist/runtime/resolver-vendors/bindings.d.ts +48 -0
  122. package/dist/runtime/resolver-vendors/bindings.js +40 -0
  123. package/dist/runtime/resolver-vendors/browser.d.ts +22 -0
  124. package/dist/runtime/resolver-vendors/browser.js +377 -0
  125. package/dist/runtime/resolver-vendors/capsolver.d.ts +22 -0
  126. package/dist/runtime/resolver-vendors/capsolver.js +531 -0
  127. package/dist/runtime/resolver-vendors/hosts.d.ts +2 -0
  128. package/dist/runtime/resolver-vendors/hosts.js +33 -0
  129. package/dist/runtime/resolver-vendors/twocaptcha.d.ts +24 -0
  130. package/dist/runtime/resolver-vendors/twocaptcha.js +408 -0
  131. package/dist/runtime/resolver-vendors/types.d.ts +94 -0
  132. package/dist/runtime/resolver-vendors/types.js +96 -0
  133. package/dist/runtime/resolver.d.ts +72 -0
  134. package/dist/runtime/resolver.js +771 -0
  135. package/dist/runtime/secrets.d.ts +27 -0
  136. package/dist/runtime/secrets.js +51 -0
  137. package/dist/runtime/state.d.ts +3 -0
  138. package/dist/runtime/state.js +277 -71
  139. package/dist/runtime/stealth-cookies.d.ts +20 -0
  140. package/dist/runtime/stealth-cookies.js +111 -0
  141. package/dist/runtime/stealth.d.ts +41 -5
  142. package/dist/runtime/stealth.js +997 -291
  143. package/dist/runtime/stt.js +1 -12
  144. package/dist/runtime/timeout.d.ts +5 -0
  145. package/dist/runtime/timeout.js +12 -0
  146. package/dist/runtime/trace-config.d.ts +12 -0
  147. package/dist/runtime/trace-config.js +62 -0
  148. package/dist/runtime/trace.d.ts +5 -0
  149. package/dist/runtime/trace.js +43 -10
  150. package/dist/serve.d.ts +1 -1
  151. package/dist/serve.js +1 -1
  152. package/dist/server/error-observability.d.ts +1 -0
  153. package/dist/server/error-observability.js +1 -0
  154. package/dist/server/index.d.ts +5 -2
  155. package/dist/server/index.js +2 -2
  156. package/dist/server/self-test-input-tokens.d.ts +2 -1
  157. package/dist/server/self-test-input-tokens.js +18 -14
  158. package/dist/server/self-test.d.ts +14 -3
  159. package/dist/server/self-test.js +129 -58
  160. package/dist/server/serve-implementation.d.ts +230 -0
  161. package/dist/server/serve-implementation.js +2265 -0
  162. package/dist/server/serve.d.ts +1 -70
  163. package/dist/server/serve.js +1 -1131
  164. package/dist/server/trace-output.d.ts +6 -0
  165. package/dist/server/trace-output.js +79 -0
  166. package/dist/server/types.d.ts +30 -5
  167. package/dist/server/types.js +13 -1
  168. package/dist/stateful/errors.d.ts +19 -0
  169. package/dist/stateful/errors.js +24 -0
  170. package/dist/stateful/http-provider-event-emitter.d.ts +40 -0
  171. package/dist/stateful/http-provider-event-emitter.js +237 -0
  172. package/dist/stateful/http-session-owner-registry.d.ts +44 -0
  173. package/dist/stateful/http-session-owner-registry.js +210 -0
  174. package/dist/stateful/index.d.ts +18 -0
  175. package/dist/stateful/index.js +18 -0
  176. package/dist/stateful/provider-event-delivery-failures.d.ts +32 -0
  177. package/dist/stateful/provider-event-delivery-failures.js +43 -0
  178. package/dist/stateful/provider-event-pipeline-metrics.d.ts +46 -0
  179. package/dist/stateful/provider-event-pipeline-metrics.js +48 -0
  180. package/dist/stateful/provider-event-pipeline.d.ts +50 -0
  181. package/dist/stateful/provider-event-pipeline.js +1 -0
  182. package/dist/stateful/provider-events.d.ts +101 -0
  183. package/dist/stateful/provider-events.js +289 -0
  184. package/dist/stateful/session-key.d.ts +15 -0
  185. package/dist/stateful/session-key.js +86 -0
  186. package/dist/stateful/stateful-provider-adapter-context.d.ts +5 -0
  187. package/dist/stateful/stateful-provider-adapter-context.js +42 -0
  188. package/dist/stateful/stateful-provider-adapter-metrics.d.ts +15 -0
  189. package/dist/stateful/stateful-provider-adapter-metrics.js +21 -0
  190. package/dist/stateful/stateful-provider-adapter.d.ts +98 -0
  191. package/dist/stateful/stateful-provider-adapter.js +287 -0
  192. package/dist/stateful/stateful-provider-observability.d.ts +62 -0
  193. package/dist/stateful/stateful-provider-observability.js +161 -0
  194. package/dist/stateful/stateful-provider-owner-forwarder.d.ts +41 -0
  195. package/dist/stateful/stateful-provider-owner-forwarder.js +215 -0
  196. package/dist/stateful/stateful-provider-runtime-context.d.ts +32 -0
  197. package/dist/stateful/stateful-provider-runtime-context.js +60 -0
  198. package/dist/stateful/stateful-provider-runtime-executor.d.ts +34 -0
  199. package/dist/stateful/stateful-provider-runtime-executor.js +52 -0
  200. package/dist/stateful/stateful-provider-session-routing.d.ts +67 -0
  201. package/dist/stateful/stateful-provider-session-routing.js +345 -0
  202. package/dist/stateful/stateful-provider-session-runtime.d.ts +98 -0
  203. package/dist/stateful/stateful-provider-session-runtime.js +245 -0
  204. package/dist/stateful-signing.d.ts +18 -0
  205. package/dist/stateful-signing.js +27 -0
  206. package/dist/stealth/profiles.d.ts +9 -4
  207. package/dist/stealth/profiles.js +160 -211
  208. package/dist/stream-evidence.d.ts +74 -0
  209. package/dist/stream-evidence.js +785 -0
  210. package/dist/stream.js +7 -1
  211. package/dist/testing/index.d.ts +2 -1
  212. package/dist/testing/index.js +2 -1
  213. package/dist/testing/run.d.ts +32 -2
  214. package/dist/testing/run.js +489 -21
  215. package/dist/trace-sanitization.d.ts +5 -0
  216. package/dist/trace-sanitization.js +45 -0
  217. package/dist/types.d.ts +673 -131
  218. package/dist/types.js +1 -0
  219. package/package.json +45 -5
  220. package/src/auth-turn/index.ts +1 -1
  221. package/src/auth.ts +118 -0
  222. package/src/ceremonies/index.ts +189 -46
  223. package/src/cli/__tests__/fixtures/migrate-operation-declaration/approval-override.ts.txt +6 -0
  224. package/src/cli/__tests__/fixtures/migrate-operation-declaration/codemod-syntax.ts.txt +3 -0
  225. package/src/cli/__tests__/fixtures/migrate-operation-declaration/connection-precedence.ts.txt +10 -0
  226. package/src/cli/__tests__/fixtures/migrate-operation-declaration/docs-conflict.ts.txt +8 -0
  227. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-hoisted-array-spread.ts.txt +15 -0
  228. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-imported-array-spread.ts.txt +9 -0
  229. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-map.ts.txt +5 -0
  230. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-mixed-array-spread.ts.txt +24 -0
  231. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-operation.ts.txt +16 -0
  232. package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-map.ts.txt +3 -0
  233. package/src/cli/__tests__/fixtures/migrate-operation-declaration/hoist-all.ts.txt +31 -0
  234. package/src/cli/__tests__/fixtures/migrate-operation-declaration/hoisted-const.ts.txt +11 -0
  235. package/src/cli/__tests__/fixtures/migrate-operation-declaration/imported-spread.ts.txt +11 -0
  236. package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-map.ts.txt +11 -0
  237. package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-cast-tail.ts.txt +21 -0
  238. package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-ekitan.ts.txt +11 -0
  239. package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-override.ts.txt +14 -0
  240. package/src/cli/__tests__/fixtures/migrate-operation-declaration/missing-english-locale.ts.txt +7 -0
  241. package/src/cli/__tests__/fixtures/migrate-operation-declaration/no-safety.ts.txt +6 -0
  242. package/src/cli/__tests__/fixtures/migrate-operation-declaration/non-literal.ts.txt +7 -0
  243. package/src/cli/__tests__/fixtures/migrate-operation-declaration/redundant-approval.ts.txt +6 -0
  244. package/src/cli/__tests__/fixtures/migrate-operation-declaration/safety-conflict.ts.txt +7 -0
  245. package/src/cli/__tests__/fixtures/migrate-operation-declaration/stream.ts.txt +7 -0
  246. package/src/cli/__tests__/fixtures/migrate-operation-declaration/tool-router-spread.ts.txt +15 -0
  247. package/src/cli/__tests__/fixtures/migrate-operation-declaration/unparseable.ts.txt +4 -0
  248. package/src/cli/__tests__/fixtures/migrate-operation-declaration/verbatim-template.ts.txt +12 -0
  249. package/src/cli/commands.ts +33 -0
  250. package/src/cli/create.ts +50 -37
  251. package/src/cli/migrate-operation-declaration.ts +1751 -0
  252. package/src/cli/migrate-operation-shape.ts +184 -0
  253. package/src/cli/migrate-provider-shape.ts +772 -0
  254. package/src/cli/prompt-assets.ts +865 -0
  255. package/src/cli/templates/provider/AGENTS.md.tpl +17 -8
  256. package/src/cli/templates/provider/Dockerfile.tpl +1 -1
  257. package/src/cli/templates/provider/README.md.tpl +5 -5
  258. package/src/cli/templates/provider/index.ts.tpl +6 -3
  259. package/src/cli/templates/provider/operations/ping.ts.tpl +4 -1
  260. package/src/cli/templates/provider/provider.json.tpl +6 -0
  261. package/src/config/loader.ts +690 -163
  262. package/src/contract-serialization.ts +5 -7
  263. package/src/contract-types.ts +12 -5
  264. package/src/contract.ts +24 -10
  265. package/src/declaration-validation.ts +330 -0
  266. package/src/define.ts +1048 -131
  267. package/src/dev.ts +4 -1
  268. package/src/engine.ts +279 -0
  269. package/src/error-observability.ts +64 -0
  270. package/src/error-resolution.ts +127 -0
  271. package/src/errors.ts +68 -0
  272. package/src/fixture-sanitization.ts +264 -0
  273. package/src/health-scenario.ts +875 -0
  274. package/src/index.ts +220 -13
  275. package/src/lint.ts +698 -90
  276. package/src/native-address.ts +340 -0
  277. package/src/native-egress-policy.ts +358 -0
  278. package/src/observability.ts +51 -1
  279. package/src/provider.ts +161 -3
  280. package/src/runtime/auth-flow.ts +12 -0
  281. package/src/runtime/browser.ts +661 -63
  282. package/src/runtime/cache.ts +189 -14
  283. package/src/runtime/choice-wordlist.ts +145 -0
  284. package/src/runtime/choice.ts +631 -120
  285. package/src/runtime/chrome149-header-order.ts +330 -0
  286. package/src/runtime/env.ts +13 -0
  287. package/src/runtime/executor.ts +43 -31
  288. package/src/runtime/http.ts +641 -61
  289. package/src/runtime/insights.ts +2 -2
  290. package/src/runtime/instrumentation.ts +520 -15
  291. package/src/runtime/native-network-errors.ts +99 -0
  292. package/src/runtime/native-network.ts +1605 -0
  293. package/src/runtime/ocr.ts +523 -0
  294. package/src/runtime/otlp.ts +467 -21
  295. package/src/runtime/proxy-errors.ts +12 -4
  296. package/src/runtime/proxy-nodemaven.ts +221 -0
  297. package/src/runtime/proxy-telemetry.ts +244 -75
  298. package/src/runtime/redirects.ts +66 -0
  299. package/src/runtime/redis.ts +7 -2
  300. package/src/runtime/request-options.ts +680 -1
  301. package/src/runtime/resolver-config.ts +6 -0
  302. package/src/runtime/resolver-public.ts +23 -0
  303. package/src/runtime/resolver-shared.ts +33 -0
  304. package/src/runtime/resolver-vendors/bindings.ts +56 -0
  305. package/src/runtime/resolver-vendors/browser.ts +533 -0
  306. package/src/runtime/resolver-vendors/capsolver.ts +707 -0
  307. package/src/runtime/resolver-vendors/hosts.ts +38 -0
  308. package/src/runtime/resolver-vendors/twocaptcha.ts +540 -0
  309. package/src/runtime/resolver-vendors/types.ts +212 -0
  310. package/src/runtime/resolver.ts +1157 -0
  311. package/src/runtime/secrets.ts +64 -0
  312. package/src/runtime/state.ts +394 -77
  313. package/src/runtime/stealth-cookies.ts +132 -0
  314. package/src/runtime/stealth.ts +1280 -336
  315. package/src/runtime/stt.ts +1 -19
  316. package/src/runtime/timeout.ts +18 -0
  317. package/src/runtime/trace-config.ts +78 -0
  318. package/src/runtime/trace.ts +57 -17
  319. package/src/serve.ts +6 -1
  320. package/src/server/error-observability.ts +1 -0
  321. package/src/server/index.ts +34 -2
  322. package/src/server/self-test-input-tokens.ts +29 -14
  323. package/src/server/self-test.ts +191 -68
  324. package/src/server/serve-implementation.ts +3378 -0
  325. package/src/server/serve.ts +1 -1632
  326. package/src/server/trace-output.ts +129 -0
  327. package/src/server/types.ts +13 -1
  328. package/src/stateful/README.md +146 -0
  329. package/src/stateful/errors.ts +35 -0
  330. package/src/stateful/http-provider-event-emitter.ts +314 -0
  331. package/src/stateful/http-session-owner-registry.ts +306 -0
  332. package/src/stateful/index.ts +18 -0
  333. package/src/stateful/provider-event-delivery-failures.ts +80 -0
  334. package/src/stateful/provider-event-pipeline-metrics.ts +95 -0
  335. package/src/stateful/provider-event-pipeline.ts +61 -0
  336. package/src/stateful/provider-events.ts +462 -0
  337. package/src/stateful/session-key.ts +111 -0
  338. package/src/stateful/stateful-provider-adapter-context.ts +59 -0
  339. package/src/stateful/stateful-provider-adapter-metrics.ts +48 -0
  340. package/src/stateful/stateful-provider-adapter.ts +562 -0
  341. package/src/stateful/stateful-provider-observability.ts +261 -0
  342. package/src/stateful/stateful-provider-owner-forwarder.ts +287 -0
  343. package/src/stateful/stateful-provider-runtime-context.ts +92 -0
  344. package/src/stateful/stateful-provider-runtime-executor.ts +96 -0
  345. package/src/stateful/stateful-provider-session-routing.ts +546 -0
  346. package/src/stateful/stateful-provider-session-runtime.ts +403 -0
  347. package/src/stateful-signing.ts +46 -0
  348. package/src/stealth/profiles.ts +202 -231
  349. package/src/stream-evidence.ts +988 -0
  350. package/src/stream.ts +8 -1
  351. package/src/testing/index.ts +10 -1
  352. package/src/testing/run.ts +658 -15
  353. package/src/trace-sanitization.ts +63 -0
  354. package/src/types.ts +788 -174
  355. package/dist/cli/templates/provider/CLAUDE.md.tpl +0 -1
  356. package/src/cli/templates/provider/CLAUDE.md.tpl +0 -1
  357. /package/dist/cli/templates/provider/{skills → .agents/skills}/fixtures-and-recording/SKILL.md.tpl +0 -0
  358. /package/dist/cli/templates/provider/{skills → .agents/skills}/health-checks-and-fail-closed/SKILL.md.tpl +0 -0
  359. /package/dist/cli/templates/provider/{skills → .agents/skills}/normalization-standards/SKILL.md.tpl +0 -0
  360. /package/dist/cli/templates/provider/{skills → .agents/skills}/pagination-and-counts/SKILL.md.tpl +0 -0
  361. /package/dist/cli/templates/provider/{skills → .agents/skills}/upstream-contract-verification/SKILL.md.tpl +0 -0
  362. /package/dist/cli/templates/provider/{skills → .agents/skills}/upstream-notes/README.md.tpl +0 -0
  363. /package/src/cli/templates/provider/{skills → .agents/skills}/fixtures-and-recording/SKILL.md.tpl +0 -0
  364. /package/src/cli/templates/provider/{skills → .agents/skills}/health-checks-and-fail-closed/SKILL.md.tpl +0 -0
  365. /package/src/cli/templates/provider/{skills → .agents/skills}/normalization-standards/SKILL.md.tpl +0 -0
  366. /package/src/cli/templates/provider/{skills → .agents/skills}/pagination-and-counts/SKILL.md.tpl +0 -0
  367. /package/src/cli/templates/provider/{skills → .agents/skills}/upstream-contract-verification/SKILL.md.tpl +0 -0
  368. /package/src/cli/templates/provider/{skills → .agents/skills}/upstream-notes/README.md.tpl +0 -0
@@ -1,14 +1,52 @@
1
- import { createCipheriv, createDecipheriv, createHash, createHmac, randomBytes, timingSafeEqual, } from "node:crypto";
1
+ import { createCipheriv, createDecipheriv, createHash, createHmac, randomBytes, randomInt, timingSafeEqual, } from "node:crypto";
2
2
  import { assertFreshProviderChoiceIssuedAt, ProviderChoiceTokenError, } from "../choice-token.js";
3
- import { ProviderError } from "../errors.js";
3
+ import { isProviderError, ProviderError } from "../errors.js";
4
+ import { CHOICE_WORDLIST_SIZE, choiceWordAt, HIGH_CHOICE_WORD_COUNT, isChoiceWord, STANDARD_CHOICE_WORD_COUNT, } from "./choice-wordlist.js";
4
5
  export const PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV = "APIFUSE__PROVIDER_RUNTIME__CHOICE_TOKEN_MASTER_SECRET";
5
6
  const PRIMARY_CHOICE_TOKEN_KID = "v1";
6
7
  const MANAGED_CHOICE_TOKEN_VERSION = 1;
8
+ const SERVER_STORED_CHOICE_RECORD_VERSION = 1;
9
+ const SERVER_STORED_CHOICE_ISSUE_ATTEMPTS = 5;
10
+ const WORD_CHOICE_NOT_FOUND_MESSAGE = "Provider choice token was not found.";
7
11
  export function createProviderChoiceContext(options) {
8
12
  const kid = options.kid ?? PRIMARY_CHOICE_TOKEN_KID;
9
13
  const resolveMasterSecret = () => resolveChoiceMasterSecret(options);
10
14
  function issue(issueOptions) {
11
15
  const issuedAtMs = issueOptions.nowMs ?? Date.now();
16
+ const resolvedStorage = resolveIssueStorage(issueOptions.storage, issueOptions.payload);
17
+ if (resolvedStorage.mode === "server") {
18
+ const keys = hasRequestedChoiceBinding(issueOptions.bind)
19
+ ? deriveManagedChoiceKeys({
20
+ masterSecret: resolveMasterSecret(),
21
+ providerId: options.providerId,
22
+ purpose: issueOptions.purpose,
23
+ kid,
24
+ })
25
+ : undefined;
26
+ const binding = hasRequestedChoiceBinding(issueOptions.bind)
27
+ ? createChoiceBinding({
28
+ keys: keys,
29
+ options: issueOptions.bind,
30
+ request: options.request,
31
+ credential: options.credential,
32
+ required: true,
33
+ })
34
+ : undefined;
35
+ const baseEnvelope = {
36
+ v: MANAGED_CHOICE_TOKEN_VERSION,
37
+ provider_id: options.providerId,
38
+ purpose: issueOptions.purpose,
39
+ issued_at_ms: issuedAtMs,
40
+ ttl_ms: issueOptions.ttlMs,
41
+ binding,
42
+ };
43
+ return issueWordServerStoredChoice({
44
+ baseEnvelope,
45
+ issueOptions,
46
+ storage: resolvedStorage.storage,
47
+ contextState: options.state,
48
+ });
49
+ }
12
50
  const keys = deriveManagedChoiceKeys({
13
51
  masterSecret: resolveMasterSecret(),
14
52
  providerId: options.providerId,
@@ -29,18 +67,6 @@ export function createProviderChoiceContext(options) {
29
67
  required: true,
30
68
  }),
31
69
  };
32
- const resolvedStorage = resolveIssueStorage(issueOptions.storage, issueOptions.payload);
33
- if (resolvedStorage.mode === "server") {
34
- return issueServerStoredChoice({
35
- baseEnvelope,
36
- issueOptions,
37
- storage: resolvedStorage.storage,
38
- contextState: options.state,
39
- kid,
40
- keys,
41
- issuedAtMs,
42
- });
43
- }
44
70
  const envelope = {
45
71
  ...baseEnvelope,
46
72
  payload: issueOptions.payload,
@@ -53,58 +79,129 @@ export function createProviderChoiceContext(options) {
53
79
  });
54
80
  }
55
81
  function parse(parseOptions) {
56
- const [actualPrefix, tokenKid, encodedIv, encryptedPayload, authTag, signature] = parseManagedChoiceTokenParts(parseOptions.token);
57
- if (actualPrefix !== parseOptions.prefix ||
58
- tokenKid !== kid ||
59
- !encodedIv ||
60
- !encryptedPayload ||
61
- !authTag ||
62
- !signature) {
63
- throw new ProviderChoiceTokenError("invalid_shape", "Provider choice token shape is invalid.");
64
- }
65
- const keys = deriveManagedChoiceKeys({
66
- masterSecret: resolveMasterSecret(),
67
- providerId: options.providerId,
68
- purpose: parseOptions.purpose,
69
- kid: tokenKid,
70
- });
71
- const signedBody = [parseOptions.prefix, tokenKid, encodedIv, encryptedPayload, authTag].join(".");
72
- assertManagedChoiceSignature({
73
- signedBody,
74
- signature,
75
- signingKey: keys.signing,
76
- });
77
- const envelope = decryptManagedChoiceToken({
78
- encodedIv,
79
- encryptedPayload,
80
- authTag,
81
- encryptionKey: keys.encryption,
82
+ const consumeMode = parseOptions.consume ?? "never";
83
+ const wordStateKey = parseWordChoiceStateKey({
84
+ token: parseOptions.token,
85
+ prefix: parseOptions.prefix,
82
86
  });
83
- assertManagedChoiceEnvelope(envelope, {
84
- providerId: options.providerId,
85
- purpose: parseOptions.purpose,
86
- ttlMs: parseOptions.ttlMs,
87
- nowMs: parseOptions.nowMs,
88
- futureToleranceMs: parseOptions.futureToleranceMs,
89
- });
90
- assertChoiceBindingMatches({
91
- actual: envelope.binding,
92
- expected: createChoiceBinding({
93
- keys,
94
- options: parseOptions.bind,
87
+ if (wordStateKey) {
88
+ const parsed = parseWordServerStoredChoice({
89
+ stateKey: wordStateKey,
90
+ parseOptions,
91
+ contextState: options.state,
92
+ providerId: options.providerId,
95
93
  request: options.request,
96
94
  credential: options.credential,
97
- required: true,
98
- }),
99
- });
100
- if (isServerChoiceHandlePayload(envelope.payload)) {
101
- return parseServerStoredChoice({
102
- handle: envelope.payload,
103
- storage: parseOptions.storage,
104
- contextState: options.state,
95
+ resolveBindingKeys: () => deriveManagedChoiceKeys({
96
+ masterSecret: resolveMasterSecret(),
97
+ providerId: options.providerId,
98
+ purpose: parseOptions.purpose,
99
+ kid,
100
+ }),
101
+ onConsume: (result) => emitChoiceTelemetry(options.onTelemetry, {
102
+ providerId: options.providerId,
103
+ purpose: parseOptions.purpose,
104
+ operation: "consume",
105
+ format: "word",
106
+ outcome: "success",
107
+ consumeMode,
108
+ consumed: result.status === "consumed",
109
+ replay: result.status === "already-consumed",
110
+ }),
111
+ });
112
+ return observeChoiceParse(parsed, {
113
+ onTelemetry: options.onTelemetry,
114
+ providerId: options.providerId,
115
+ purpose: parseOptions.purpose,
116
+ format: "word",
117
+ consumeMode,
105
118
  });
106
119
  }
107
- return envelope.payload;
120
+ // Inline choices continue to use the encrypted envelope. A structurally
121
+ // valid word token returns above, so lookup, expiry, consumption, and
122
+ // binding failures can never enter this branch.
123
+ try {
124
+ const [actualPrefix, tokenKid, encodedIv, encryptedPayload, authTag, signature] = parseManagedChoiceTokenParts(parseOptions.token);
125
+ if (actualPrefix !== parseOptions.prefix ||
126
+ tokenKid !== kid ||
127
+ !encodedIv ||
128
+ !encryptedPayload ||
129
+ !authTag ||
130
+ !signature) {
131
+ throw new ProviderChoiceTokenError("invalid_shape", "Provider choice token shape is invalid.");
132
+ }
133
+ const keys = deriveManagedChoiceKeys({
134
+ masterSecret: resolveMasterSecret(),
135
+ providerId: options.providerId,
136
+ purpose: parseOptions.purpose,
137
+ kid: tokenKid,
138
+ });
139
+ const signedBody = [parseOptions.prefix, tokenKid, encodedIv, encryptedPayload, authTag].join(".");
140
+ assertManagedChoiceSignature({
141
+ signedBody,
142
+ signature,
143
+ signingKey: keys.signing,
144
+ });
145
+ const envelope = decryptManagedChoiceToken({
146
+ encodedIv,
147
+ encryptedPayload,
148
+ authTag,
149
+ encryptionKey: keys.encryption,
150
+ });
151
+ assertManagedChoiceEnvelope(envelope, {
152
+ providerId: options.providerId,
153
+ purpose: parseOptions.purpose,
154
+ ttlMs: parseOptions.ttlMs,
155
+ nowMs: parseOptions.nowMs,
156
+ futureToleranceMs: parseOptions.futureToleranceMs,
157
+ });
158
+ assertChoiceBindingMatches({
159
+ actual: envelope.binding,
160
+ expected: createChoiceBinding({
161
+ keys,
162
+ options: parseOptions.bind,
163
+ request: options.request,
164
+ credential: options.credential,
165
+ required: true,
166
+ }),
167
+ });
168
+ if (isServerChoiceHandlePayload(envelope.payload)) {
169
+ throw wordChoiceNotFoundError();
170
+ }
171
+ const payload = envelope.payload;
172
+ const parsed = consumeMode === "explicit"
173
+ ? Promise.resolve(payload).then((resolvedPayload) => createInlineExplicitParseResult({
174
+ payload: resolvedPayload,
175
+ replayKey: digestChoiceReplayKey(parseOptions.token),
176
+ onConsume: () => emitChoiceTelemetry(options.onTelemetry, {
177
+ providerId: options.providerId,
178
+ purpose: parseOptions.purpose,
179
+ operation: "consume",
180
+ format: "legacy",
181
+ outcome: "unsupported",
182
+ consumeMode,
183
+ consumed: false,
184
+ replay: false,
185
+ }),
186
+ }))
187
+ : payload;
188
+ return observeChoiceParse(parsed, {
189
+ onTelemetry: options.onTelemetry,
190
+ providerId: options.providerId,
191
+ purpose: parseOptions.purpose,
192
+ format: "legacy",
193
+ consumeMode,
194
+ });
195
+ }
196
+ catch (error) {
197
+ emitChoiceParseFailure(options.onTelemetry, error, {
198
+ providerId: options.providerId,
199
+ purpose: parseOptions.purpose,
200
+ format: "legacy",
201
+ consumeMode,
202
+ });
203
+ throw error;
204
+ }
108
205
  }
109
206
  return { issue, parse };
110
207
  }
@@ -114,6 +211,76 @@ export function createTestProviderChoiceContext(options) {
114
211
  masterSecret: options.masterSecret ?? "apifuse-test-provider-runtime-choice-token-master-secret",
115
212
  });
116
213
  }
214
+ function observeChoiceParse(result, base) {
215
+ if (result instanceof Promise) {
216
+ return result.then((value) => {
217
+ emitChoiceParseSuccess(base, value);
218
+ return value;
219
+ }, (error) => {
220
+ emitChoiceParseFailure(base.onTelemetry, error, base);
221
+ throw error;
222
+ });
223
+ }
224
+ emitChoiceParseSuccess(base, result);
225
+ return result;
226
+ }
227
+ function emitChoiceParseSuccess(base, result) {
228
+ const replay = isConsumedChoiceReplay(result);
229
+ emitChoiceTelemetry(base.onTelemetry, {
230
+ providerId: base.providerId,
231
+ purpose: base.purpose,
232
+ operation: "parse",
233
+ format: base.format,
234
+ outcome: "success",
235
+ consumeMode: base.consumeMode,
236
+ consumed: replay || (base.format === "word" && base.consumeMode === "on-parse"),
237
+ replay,
238
+ });
239
+ }
240
+ function isConsumedChoiceReplay(value) {
241
+ return (value !== null &&
242
+ typeof value === "object" &&
243
+ "status" in value &&
244
+ value.status === "consumed" &&
245
+ "replayKey" in value &&
246
+ typeof value.replayKey === "string");
247
+ }
248
+ function emitChoiceParseFailure(onTelemetry, error, base) {
249
+ const outcome = error instanceof ProviderChoiceTokenError
250
+ ? base.format === "word" && error.message === WORD_CHOICE_NOT_FOUND_MESSAGE
251
+ ? "not-found"
252
+ : "invalid"
253
+ : "error";
254
+ emitChoiceTelemetry(onTelemetry, {
255
+ providerId: base.providerId,
256
+ purpose: base.purpose,
257
+ operation: "parse",
258
+ format: base.format,
259
+ outcome,
260
+ consumeMode: base.consumeMode,
261
+ consumed: false,
262
+ replay: false,
263
+ });
264
+ }
265
+ function emitChoiceTelemetry(onTelemetry, event) {
266
+ try {
267
+ onTelemetry?.(event);
268
+ }
269
+ catch {
270
+ // Observability must never change provider token semantics.
271
+ }
272
+ }
273
+ function createInlineExplicitParseResult(options) {
274
+ return {
275
+ status: "active",
276
+ payload: options.payload,
277
+ replayKey: options.replayKey,
278
+ consume: async () => {
279
+ options.onConsume();
280
+ return { status: "unsupported" };
281
+ },
282
+ };
283
+ }
117
284
  function resolveChoiceMasterSecret(options) {
118
285
  const configured = options.masterSecret ?? options.env?.get(PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV);
119
286
  const trimmed = configured?.trim();
@@ -163,62 +330,255 @@ function encryptManagedChoiceToken(options) {
163
330
  .digest("base64url");
164
331
  return `${signedBody}.${signature}`;
165
332
  }
166
- async function issueServerStoredChoice(options) {
333
+ async function issueWordServerStoredChoice(options) {
167
334
  const serializedPayload = serializeChoicePayload(options.issueOptions.payload);
168
- const payloadBytes = Buffer.byteLength(serializedPayload, "utf8");
169
- if (payloadBytes > options.storage.maxValueBytes) {
170
- throw new ProviderError("Provider choice payload exceeds state storage policy.", {
171
- code: "CHOICE_STATE_PAYLOAD_TOO_LARGE",
172
- category: "input_validation",
173
- retryable: false,
174
- details: {
175
- maxValueBytes: options.storage.maxValueBytes,
176
- payloadBytes,
177
- },
178
- });
179
- }
180
- const stateId = `choice_${randomBytes(16).toString("base64url")}`;
181
- const digest = digestChoicePayload(serializedPayload);
335
+ const payloadDigest = digestChoicePayload(serializedPayload);
182
336
  const namespace = resolveChoiceStateNamespace({
183
337
  storage: options.storage,
184
338
  contextState: options.contextState,
185
339
  ttlMs: options.issueOptions.ttlMs,
186
340
  });
187
- await namespace.set(optionsStateKey(stateId), options.issueOptions.payload, {
188
- ttl: stateTtl(options.storage, options.issueOptions.ttlMs),
189
- });
190
- const envelope = {
191
- ...options.baseEnvelope,
192
- payload: {
341
+ const wordCount = options.issueOptions.strength === "high" ? HIGH_CHOICE_WORD_COUNT : STANDARD_CHOICE_WORD_COUNT;
342
+ for (let attempt = 0; attempt < SERVER_STORED_CHOICE_ISSUE_ATTEMPTS; attempt += 1) {
343
+ const stateKey = generateChoiceWordSequence(wordCount);
344
+ const token = `${options.issueOptions.prefix}${stateKey}`;
345
+ const record = {
346
+ v: SERVER_STORED_CHOICE_RECORD_VERSION,
193
347
  storage: "server",
194
- state_id: stateId,
195
- payload_digest: digest,
196
- created_at_ms: options.issuedAtMs,
197
- },
198
- };
199
- return encryptManagedChoiceToken({
200
- prefix: options.issueOptions.prefix,
201
- kid: options.kid,
202
- envelope,
203
- keys: options.keys,
348
+ status: "active",
349
+ provider_id: options.baseEnvelope.provider_id,
350
+ purpose: options.baseEnvelope.purpose,
351
+ issued_at_ms: options.baseEnvelope.issued_at_ms,
352
+ ttl_ms: options.baseEnvelope.ttl_ms,
353
+ binding: options.baseEnvelope.binding,
354
+ prefix: options.issueOptions.prefix,
355
+ payload: options.issueOptions.payload,
356
+ payload_digest: payloadDigest,
357
+ replay_key: digestChoiceReplayKey(token),
358
+ };
359
+ const valueBytes = Buffer.byteLength(JSON.stringify(record), "utf8");
360
+ if (valueBytes > options.storage.maxValueBytes) {
361
+ throw new ProviderError("Provider choice payload exceeds state storage policy.", {
362
+ code: "CHOICE_STATE_PAYLOAD_TOO_LARGE",
363
+ category: "input_validation",
364
+ retryable: false,
365
+ details: {
366
+ maxValueBytes: options.storage.maxValueBytes,
367
+ valueBytes,
368
+ },
369
+ });
370
+ }
371
+ const result = await namespace.compareAndSet(optionsStateKey(stateKey), 0, record, {
372
+ ttl: stateTtl(options.storage, options.issueOptions.ttlMs),
373
+ });
374
+ if (result.ok)
375
+ return token;
376
+ }
377
+ throw new ProviderError("Provider choice state storage is not available.", {
378
+ code: "CHOICE_STATE_UNAVAILABLE",
379
+ category: "internal_error",
380
+ retryable: false,
204
381
  });
205
382
  }
206
- async function parseServerStoredChoice(options) {
207
- const storage = resolveParseStorage(options.storage);
383
+ async function parseWordServerStoredChoice(options) {
384
+ const storage = resolveParseStorage(options.parseOptions.storage);
208
385
  const namespace = resolveChoiceStateNamespace({
209
386
  storage,
210
387
  contextState: options.contextState,
388
+ ttlMs: options.parseOptions.ttlMs,
211
389
  });
212
- const record = await namespace.get(optionsStateKey(options.handle.state_id));
213
- if (!record) {
214
- throw new ProviderChoiceTokenError("invalid_payload", "Provider choice token state payload is missing.");
390
+ let stored;
391
+ try {
392
+ stored = await namespace.get(optionsStateKey(options.stateKey));
393
+ }
394
+ catch (error) {
395
+ if (isProviderError(error))
396
+ throw error;
397
+ throw wordChoiceNotFoundError();
398
+ }
399
+ if (!stored || !isServerStoredChoiceRecord(stored.value)) {
400
+ throw wordChoiceNotFoundError();
401
+ }
402
+ const record = stored.value;
403
+ const expectedReplayKey = digestChoiceReplayKey(`${options.parseOptions.prefix}${options.stateKey}`);
404
+ try {
405
+ if (record.provider_id !== options.providerId ||
406
+ record.purpose !== options.parseOptions.purpose ||
407
+ record.prefix !== options.parseOptions.prefix) {
408
+ throw wordChoiceNotFoundError();
409
+ }
410
+ assertPayloadDigestMatches({
411
+ actual: digestChoicePayload(serializeChoicePayload(record.payload)),
412
+ expected: record.payload_digest,
413
+ });
414
+ assertPayloadDigestMatches({ actual: expectedReplayKey, expected: record.replay_key });
415
+ assertWordChoiceBindingMatches({
416
+ actual: record.binding,
417
+ requested: options.parseOptions.bind,
418
+ request: options.request,
419
+ credential: options.credential,
420
+ resolveKeys: options.resolveBindingKeys,
421
+ });
422
+ }
423
+ catch (error) {
424
+ if (error instanceof ProviderChoiceTokenError ||
425
+ (isProviderError(error) && error.code === "CHOICE_CONTEXT_REQUIRED")) {
426
+ throw wordChoiceNotFoundError();
427
+ }
428
+ throw error;
215
429
  }
216
- const serializedPayload = serializeChoicePayload(record.value);
217
- assertPayloadDigestMatches({
218
- actual: digestChoicePayload(serializedPayload),
219
- expected: options.handle.payload_digest,
430
+ // Freshness is classified last, reachable only after every identity,
431
+ // integrity, and binding check above has passed (ADR 0006, amended
432
+ // 2026-08-20): a caller that proved the record's binding may observe the
433
+ // canonical stale error, while an unbound record keeps the collapsed
434
+ // not-found error so expiry never becomes an existence signal for
435
+ // guessable tokens.
436
+ try {
437
+ assertFreshProviderChoiceIssuedAt(record.issued_at_ms, {
438
+ ttlMs: options.parseOptions.ttlMs != null
439
+ ? Math.min(options.parseOptions.ttlMs, record.ttl_ms)
440
+ : record.ttl_ms,
441
+ nowMs: options.parseOptions.nowMs,
442
+ futureToleranceMs: options.parseOptions.futureToleranceMs,
443
+ });
444
+ }
445
+ catch (error) {
446
+ const recordIsBound = Boolean(record.binding?.connection_hash || record.binding?.credential_hash);
447
+ if (recordIsBound && error instanceof ProviderChoiceTokenError && error.reason === "stale") {
448
+ throw error;
449
+ }
450
+ if (error instanceof ProviderChoiceTokenError)
451
+ throw wordChoiceNotFoundError();
452
+ throw error;
453
+ }
454
+ const consumeMode = options.parseOptions.consume ?? "never";
455
+ if (record.status === "consumed") {
456
+ if (consumeMode === "explicit") {
457
+ return { status: "consumed", replayKey: record.replay_key };
458
+ }
459
+ throw wordChoiceNotFoundError();
460
+ }
461
+ if (consumeMode === "never")
462
+ return record.payload;
463
+ if (consumeMode === "explicit") {
464
+ return {
465
+ status: "active",
466
+ payload: record.payload,
467
+ replayKey: record.replay_key,
468
+ consume: async () => {
469
+ const result = await consumeWordServerStoredChoice({
470
+ stateKey: options.stateKey,
471
+ stored,
472
+ record,
473
+ storage,
474
+ contextState: options.contextState,
475
+ });
476
+ options.onConsume(result);
477
+ return result;
478
+ },
479
+ };
480
+ }
481
+ const consumed = await consumeWordServerStoredChoice({
482
+ stateKey: options.stateKey,
483
+ stored,
484
+ record,
485
+ storage,
486
+ contextState: options.contextState,
487
+ });
488
+ if (consumed.status !== "consumed")
489
+ throw wordChoiceNotFoundError();
490
+ return record.payload;
491
+ }
492
+ async function consumeWordServerStoredChoice(options) {
493
+ const namespace = resolveChoiceStateNamespace({
494
+ storage: options.storage,
495
+ contextState: options.contextState,
496
+ ttlMs: options.record.ttl_ms,
220
497
  });
221
- return record.value;
498
+ try {
499
+ const consumed = await namespace.compareAndSet(optionsStateKey(options.stateKey), options.stored.version, { ...options.record, status: "consumed" }, { ttl: remainingStateTtl(options.stored.expiresAt) });
500
+ if (consumed.ok)
501
+ return { status: "consumed" };
502
+ if (consumed.current &&
503
+ isServerStoredChoiceRecord(consumed.current.value) &&
504
+ consumed.current.value.status === "consumed" &&
505
+ consumed.current.value.replay_key === options.record.replay_key) {
506
+ return { status: "already-consumed" };
507
+ }
508
+ throw wordChoiceNotFoundError();
509
+ }
510
+ catch (error) {
511
+ if (isProviderError(error))
512
+ throw error;
513
+ if (error instanceof ProviderChoiceTokenError)
514
+ throw error;
515
+ throw wordChoiceNotFoundError();
516
+ }
517
+ }
518
+ function generateChoiceWordSequence(wordCount) {
519
+ return Array.from({ length: wordCount }, () => choiceWordAt(randomInt(CHOICE_WORDLIST_SIZE))).join("-");
520
+ }
521
+ function parseWordChoiceStateKey(options) {
522
+ if (!options.token.startsWith(options.prefix))
523
+ return null;
524
+ const body = options.token.slice(options.prefix.length);
525
+ // The official list contains one hyphenated entry (`yo-yo`), so structural
526
+ // recognition uses dictionary-aware segmentation instead of assuming every
527
+ // hyphen is a word boundary.
528
+ if (!/^[a-z]+(?:-[a-z]+){3,9}$/.test(body))
529
+ return null;
530
+ const segments = body.split("-");
531
+ if (!canSegmentChoiceWords(segments, 0, STANDARD_CHOICE_WORD_COUNT) &&
532
+ !canSegmentChoiceWords(segments, 0, HIGH_CHOICE_WORD_COUNT)) {
533
+ return null;
534
+ }
535
+ return body;
536
+ }
537
+ function canSegmentChoiceWords(segments, segmentIndex, wordsRemaining) {
538
+ if (wordsRemaining === 0)
539
+ return segmentIndex === segments.length;
540
+ const segmentsRemaining = segments.length - segmentIndex;
541
+ if (segmentsRemaining < wordsRemaining)
542
+ return false;
543
+ for (let end = segmentIndex + 1; end <= segments.length - (wordsRemaining - 1); end += 1) {
544
+ const candidate = segments.slice(segmentIndex, end).join("-");
545
+ if (candidate.length > 10)
546
+ break;
547
+ if (isChoiceWord(candidate) && canSegmentChoiceWords(segments, end, wordsRemaining - 1)) {
548
+ return true;
549
+ }
550
+ }
551
+ return false;
552
+ }
553
+ function isServerStoredChoiceRecord(value) {
554
+ if (!value || typeof value !== "object" || Array.isArray(value))
555
+ return false;
556
+ return ("v" in value &&
557
+ value.v === SERVER_STORED_CHOICE_RECORD_VERSION &&
558
+ "storage" in value &&
559
+ value.storage === "server" &&
560
+ "status" in value &&
561
+ (value.status === "active" || value.status === "consumed") &&
562
+ "provider_id" in value &&
563
+ typeof value.provider_id === "string" &&
564
+ "purpose" in value &&
565
+ typeof value.purpose === "string" &&
566
+ "issued_at_ms" in value &&
567
+ typeof value.issued_at_ms === "number" &&
568
+ "ttl_ms" in value &&
569
+ typeof value.ttl_ms === "number" &&
570
+ (!("binding" in value) || value.binding === undefined || isChoiceBinding(value.binding)) &&
571
+ "prefix" in value &&
572
+ typeof value.prefix === "string" &&
573
+ "payload" in value &&
574
+ isChoicePayload(value.payload) &&
575
+ "payload_digest" in value &&
576
+ typeof value.payload_digest === "string" &&
577
+ "replay_key" in value &&
578
+ typeof value.replay_key === "string");
579
+ }
580
+ function wordChoiceNotFoundError() {
581
+ return new ProviderChoiceTokenError("invalid_payload", WORD_CHOICE_NOT_FOUND_MESSAGE);
222
582
  }
223
583
  function resolveIssueStorage(storage, payload) {
224
584
  if (!storage || storage.mode === "inline")
@@ -255,6 +615,10 @@ function resolveChoiceStateNamespace(options) {
255
615
  function stateTtl(storage, ttlMs) {
256
616
  return storage.ttl ?? `${ttlMs ?? 1}ms`;
257
617
  }
618
+ function remainingStateTtl(expiresAt) {
619
+ const remainingMs = Date.parse(expiresAt) - Date.now();
620
+ return `${Number.isFinite(remainingMs) ? Math.max(1, Math.floor(remainingMs)) : 1}ms`;
621
+ }
258
622
  function optionsStateKey(stateId) {
259
623
  return stateId;
260
624
  }
@@ -264,6 +628,9 @@ function serializeChoicePayload(payload) {
264
628
  function digestChoicePayload(serializedPayload) {
265
629
  return createHash("sha256").update(serializedPayload).digest("base64url");
266
630
  }
631
+ function digestChoiceReplayKey(token) {
632
+ return createHash("sha256").update(token).digest("hex");
633
+ }
267
634
  function isServerChoiceHandlePayload(value) {
268
635
  return (value.storage === "server" &&
269
636
  typeof value.state_id === "string" &&
@@ -366,6 +733,27 @@ function createChoiceBinding(options) {
366
733
  ...(credentialHash ? { credential_hash: credentialHash } : {}),
367
734
  };
368
735
  }
736
+ function hasRequestedChoiceBinding(options) {
737
+ return options?.connection === true || Boolean(options?.credentialKeys?.length);
738
+ }
739
+ function assertWordChoiceBindingMatches(options) {
740
+ const hasStoredBinding = Boolean(options.actual?.connection_hash || options.actual?.credential_hash);
741
+ const hasRequestedBinding = hasRequestedChoiceBinding(options.requested);
742
+ if (!hasStoredBinding && !hasRequestedBinding)
743
+ return;
744
+ if (hasStoredBinding !== hasRequestedBinding)
745
+ throw wordChoiceNotFoundError();
746
+ assertChoiceBindingMatches({
747
+ actual: options.actual,
748
+ expected: createChoiceBinding({
749
+ keys: options.resolveKeys(),
750
+ options: options.requested,
751
+ request: options.request,
752
+ credential: options.credential,
753
+ required: true,
754
+ }),
755
+ });
756
+ }
369
757
  function hashRequiredConnection(options) {
370
758
  const connectionId = options.request?.connectionId;
371
759
  if (!connectionId) {