@copilotkit/aimock 1.45.0 → 1.46.0

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 (426) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +24 -0
  4. package/README.md +7 -1
  5. package/dist/agui-mock.cjs +25 -25
  6. package/dist/agui-mock.cjs.map +1 -1
  7. package/dist/agui-mock.d.cts.map +1 -1
  8. package/dist/agui-mock.d.ts.map +1 -1
  9. package/dist/agui-mock.js +25 -25
  10. package/dist/agui-mock.js.map +1 -1
  11. package/dist/agui-types.d.ts.map +1 -1
  12. package/dist/aimock-cli.cjs +15 -1
  13. package/dist/aimock-cli.cjs.map +1 -1
  14. package/dist/aimock-cli.d.cts.map +1 -1
  15. package/dist/aimock-cli.d.ts.map +1 -1
  16. package/dist/aimock-cli.js +16 -2
  17. package/dist/aimock-cli.js.map +1 -1
  18. package/dist/api-key-auth.cjs +10 -9
  19. package/dist/api-key-auth.cjs.map +1 -1
  20. package/dist/api-key-auth.d.cts.map +1 -1
  21. package/dist/api-key-auth.d.ts.map +1 -1
  22. package/dist/api-key-auth.js +11 -10
  23. package/dist/api-key-auth.js.map +1 -1
  24. package/dist/batches.cjs +2 -4
  25. package/dist/batches.cjs.map +1 -1
  26. package/dist/batches.d.cts.map +1 -1
  27. package/dist/batches.d.ts.map +1 -1
  28. package/dist/batches.js +2 -4
  29. package/dist/batches.js.map +1 -1
  30. package/dist/bedrock-converse.cjs +61 -80
  31. package/dist/bedrock-converse.cjs.map +1 -1
  32. package/dist/bedrock-converse.d.cts.map +1 -1
  33. package/dist/bedrock-converse.d.ts.map +1 -1
  34. package/dist/bedrock-converse.js +58 -77
  35. package/dist/bedrock-converse.js.map +1 -1
  36. package/dist/bedrock.cjs +65 -97
  37. package/dist/bedrock.cjs.map +1 -1
  38. package/dist/bedrock.d.cts.map +1 -1
  39. package/dist/bedrock.d.ts.map +1 -1
  40. package/dist/bedrock.js +62 -94
  41. package/dist/bedrock.js.map +1 -1
  42. package/dist/byteplus-video.cjs +11 -10
  43. package/dist/byteplus-video.cjs.map +1 -1
  44. package/dist/byteplus-video.d.cts.map +1 -1
  45. package/dist/byteplus-video.d.ts.map +1 -1
  46. package/dist/byteplus-video.js +5 -4
  47. package/dist/byteplus-video.js.map +1 -1
  48. package/dist/chaos.cjs +5 -4
  49. package/dist/chaos.cjs.map +1 -1
  50. package/dist/chaos.d.cts.map +1 -1
  51. package/dist/chaos.d.ts.map +1 -1
  52. package/dist/chaos.js +5 -4
  53. package/dist/chaos.js.map +1 -1
  54. package/dist/cli.cjs +15 -0
  55. package/dist/cli.cjs.map +1 -1
  56. package/dist/cli.js +15 -0
  57. package/dist/cli.js.map +1 -1
  58. package/dist/cohere.cjs +49 -74
  59. package/dist/cohere.cjs.map +1 -1
  60. package/dist/cohere.d.cts.map +1 -1
  61. package/dist/cohere.d.ts.map +1 -1
  62. package/dist/cohere.js +47 -72
  63. package/dist/cohere.js.map +1 -1
  64. package/dist/config-loader.cjs +29 -1
  65. package/dist/config-loader.cjs.map +1 -1
  66. package/dist/config-loader.d.cts +7 -1
  67. package/dist/config-loader.d.cts.map +1 -1
  68. package/dist/config-loader.d.ts +7 -1
  69. package/dist/config-loader.d.ts.map +1 -1
  70. package/dist/config-loader.js +29 -2
  71. package/dist/config-loader.js.map +1 -1
  72. package/dist/count-tokens.cjs +146 -0
  73. package/dist/count-tokens.cjs.map +1 -0
  74. package/dist/count-tokens.js +146 -0
  75. package/dist/count-tokens.js.map +1 -0
  76. package/dist/elevenlabs-audio.cjs +36 -34
  77. package/dist/elevenlabs-audio.cjs.map +1 -1
  78. package/dist/elevenlabs-audio.d.cts.map +1 -1
  79. package/dist/elevenlabs-audio.d.ts.map +1 -1
  80. package/dist/elevenlabs-audio.js +33 -31
  81. package/dist/elevenlabs-audio.js.map +1 -1
  82. package/dist/elevenlabs-voice.cjs +38 -37
  83. package/dist/elevenlabs-voice.cjs.map +1 -1
  84. package/dist/elevenlabs-voice.d.cts.map +1 -1
  85. package/dist/elevenlabs-voice.d.ts.map +1 -1
  86. package/dist/elevenlabs-voice.js +33 -32
  87. package/dist/elevenlabs-voice.js.map +1 -1
  88. package/dist/embedding-request.cjs +16 -0
  89. package/dist/embedding-request.cjs.map +1 -0
  90. package/dist/embedding-request.js +14 -0
  91. package/dist/embedding-request.js.map +1 -0
  92. package/dist/embeddings.cjs +30 -20
  93. package/dist/embeddings.cjs.map +1 -1
  94. package/dist/embeddings.d.cts.map +1 -1
  95. package/dist/embeddings.d.ts.map +1 -1
  96. package/dist/embeddings.js +30 -20
  97. package/dist/embeddings.js.map +1 -1
  98. package/dist/fal-audio.cjs +45 -43
  99. package/dist/fal-audio.cjs.map +1 -1
  100. package/dist/fal-audio.d.cts.map +1 -1
  101. package/dist/fal-audio.d.ts.map +1 -1
  102. package/dist/fal-audio.js +42 -40
  103. package/dist/fal-audio.js.map +1 -1
  104. package/dist/fal.cjs +17 -15
  105. package/dist/fal.cjs.map +1 -1
  106. package/dist/fal.js +16 -14
  107. package/dist/fal.js.map +1 -1
  108. package/dist/files.cjs +2 -4
  109. package/dist/files.cjs.map +1 -1
  110. package/dist/files.d.cts.map +1 -1
  111. package/dist/files.d.ts.map +1 -1
  112. package/dist/files.js +2 -4
  113. package/dist/files.js.map +1 -1
  114. package/dist/fine-tuning.cjs +2 -4
  115. package/dist/fine-tuning.cjs.map +1 -1
  116. package/dist/fine-tuning.d.cts.map +1 -1
  117. package/dist/fine-tuning.d.ts.map +1 -1
  118. package/dist/fine-tuning.js +2 -4
  119. package/dist/fine-tuning.js.map +1 -1
  120. package/dist/fixture-loader.cjs +37 -42
  121. package/dist/fixture-loader.cjs.map +1 -1
  122. package/dist/fixture-loader.d.cts.map +1 -1
  123. package/dist/fixture-loader.d.ts.map +1 -1
  124. package/dist/fixture-loader.js +37 -42
  125. package/dist/fixture-loader.js.map +1 -1
  126. package/dist/gemini-embeddings.cjs +26 -24
  127. package/dist/gemini-embeddings.cjs.map +1 -1
  128. package/dist/gemini-embeddings.js +26 -24
  129. package/dist/gemini-embeddings.js.map +1 -1
  130. package/dist/gemini-interactions.cjs +50 -35
  131. package/dist/gemini-interactions.cjs.map +1 -1
  132. package/dist/gemini-interactions.d.cts +0 -1
  133. package/dist/gemini-interactions.d.cts.map +1 -1
  134. package/dist/gemini-interactions.d.ts +0 -1
  135. package/dist/gemini-interactions.d.ts.map +1 -1
  136. package/dist/gemini-interactions.js +49 -34
  137. package/dist/gemini-interactions.js.map +1 -1
  138. package/dist/gemini.cjs +57 -67
  139. package/dist/gemini.cjs.map +1 -1
  140. package/dist/gemini.d.cts.map +1 -1
  141. package/dist/gemini.d.ts.map +1 -1
  142. package/dist/gemini.js +56 -67
  143. package/dist/gemini.js.map +1 -1
  144. package/dist/grok-video.cjs +14 -12
  145. package/dist/grok-video.cjs.map +1 -1
  146. package/dist/grok-video.d.cts.map +1 -1
  147. package/dist/grok-video.d.ts.map +1 -1
  148. package/dist/grok-video.js +8 -6
  149. package/dist/grok-video.js.map +1 -1
  150. package/dist/helpers.cjs +11 -68
  151. package/dist/helpers.cjs.map +1 -1
  152. package/dist/helpers.d.cts.map +1 -1
  153. package/dist/helpers.d.ts.map +1 -1
  154. package/dist/helpers.js +12 -67
  155. package/dist/helpers.js.map +1 -1
  156. package/dist/images.cjs +30 -28
  157. package/dist/images.cjs.map +1 -1
  158. package/dist/images.d.cts.map +1 -1
  159. package/dist/images.d.ts.map +1 -1
  160. package/dist/images.js +27 -25
  161. package/dist/images.js.map +1 -1
  162. package/dist/index.d.cts +2 -2
  163. package/dist/index.d.ts +2 -2
  164. package/dist/journal.cjs +3 -1
  165. package/dist/journal.cjs.map +1 -1
  166. package/dist/journal.d.cts.map +1 -1
  167. package/dist/journal.d.ts.map +1 -1
  168. package/dist/journal.js +2 -1
  169. package/dist/journal.js.map +1 -1
  170. package/dist/llmock.cjs +3 -0
  171. package/dist/llmock.cjs.map +1 -1
  172. package/dist/llmock.d.cts.map +1 -1
  173. package/dist/llmock.d.ts.map +1 -1
  174. package/dist/llmock.js +3 -0
  175. package/dist/llmock.js.map +1 -1
  176. package/dist/mcp-fakes.d.cts.map +1 -1
  177. package/dist/mcp-handler.cjs +14 -4
  178. package/dist/mcp-handler.cjs.map +1 -1
  179. package/dist/mcp-handler.js +14 -4
  180. package/dist/mcp-handler.js.map +1 -1
  181. package/dist/mcp-recorder.cjs +2 -1
  182. package/dist/mcp-recorder.cjs.map +1 -1
  183. package/dist/mcp-recorder.js +3 -2
  184. package/dist/mcp-recorder.js.map +1 -1
  185. package/dist/messages.cjs +111 -96
  186. package/dist/messages.cjs.map +1 -1
  187. package/dist/messages.d.cts.map +1 -1
  188. package/dist/messages.d.ts.map +1 -1
  189. package/dist/messages.js +109 -95
  190. package/dist/messages.js.map +1 -1
  191. package/dist/metrics.cjs +5 -2
  192. package/dist/metrics.cjs.map +1 -1
  193. package/dist/metrics.d.cts.map +1 -1
  194. package/dist/metrics.d.ts.map +1 -1
  195. package/dist/metrics.js +6 -3
  196. package/dist/metrics.js.map +1 -1
  197. package/dist/model-utils.cjs +1 -0
  198. package/dist/model-utils.cjs.map +1 -1
  199. package/dist/model-utils.js +1 -1
  200. package/dist/model-utils.js.map +1 -1
  201. package/dist/moderation.cjs +15 -12
  202. package/dist/moderation.cjs.map +1 -1
  203. package/dist/moderation.js +14 -11
  204. package/dist/moderation.js.map +1 -1
  205. package/dist/ollama.cjs +128 -68
  206. package/dist/ollama.cjs.map +1 -1
  207. package/dist/ollama.d.cts.map +1 -1
  208. package/dist/ollama.d.ts.map +1 -1
  209. package/dist/ollama.js +124 -64
  210. package/dist/ollama.js.map +1 -1
  211. package/dist/openapi.cjs +323 -3
  212. package/dist/openapi.cjs.map +1 -1
  213. package/dist/openapi.d.cts.map +1 -1
  214. package/dist/openapi.d.ts.map +1 -1
  215. package/dist/openapi.js +324 -4
  216. package/dist/openapi.js.map +1 -1
  217. package/dist/openrouter-chat.cjs +11 -155
  218. package/dist/openrouter-chat.cjs.map +1 -1
  219. package/dist/openrouter-chat.js +11 -155
  220. package/dist/openrouter-chat.js.map +1 -1
  221. package/dist/openrouter-video.cjs +76 -71
  222. package/dist/openrouter-video.cjs.map +1 -1
  223. package/dist/openrouter-video.d.cts.map +1 -1
  224. package/dist/openrouter-video.d.ts.map +1 -1
  225. package/dist/openrouter-video.js +63 -58
  226. package/dist/openrouter-video.js.map +1 -1
  227. package/dist/recorder.cjs +176 -95
  228. package/dist/recorder.cjs.map +1 -1
  229. package/dist/recorder.d.cts.map +1 -1
  230. package/dist/recorder.d.ts.map +1 -1
  231. package/dist/recorder.js +176 -95
  232. package/dist/recorder.js.map +1 -1
  233. package/dist/rerank.cjs +13 -11
  234. package/dist/rerank.cjs.map +1 -1
  235. package/dist/rerank.js +12 -10
  236. package/dist/rerank.js.map +1 -1
  237. package/dist/response/catalog/anthropic-page.cjs +39 -0
  238. package/dist/response/catalog/anthropic-page.cjs.map +1 -0
  239. package/dist/response/catalog/anthropic-page.js +38 -0
  240. package/dist/response/catalog/anthropic-page.js.map +1 -0
  241. package/dist/response/catalog/collect.cjs +61 -0
  242. package/dist/response/catalog/collect.cjs.map +1 -0
  243. package/dist/response/catalog/collect.js +59 -0
  244. package/dist/response/catalog/collect.js.map +1 -0
  245. package/dist/response/catalog/gemini-page.cjs +25 -0
  246. package/dist/response/catalog/gemini-page.cjs.map +1 -0
  247. package/dist/response/catalog/gemini-page.js +24 -0
  248. package/dist/response/catalog/gemini-page.js.map +1 -0
  249. package/dist/response/catalog/render.cjs +182 -0
  250. package/dist/response/catalog/render.cjs.map +1 -0
  251. package/dist/response/catalog/render.js +171 -0
  252. package/dist/response/catalog/render.js.map +1 -0
  253. package/dist/response/errors.cjs +225 -0
  254. package/dist/response/errors.cjs.map +1 -0
  255. package/dist/response/errors.js +224 -0
  256. package/dist/response/errors.js.map +1 -0
  257. package/dist/response/finish.cjs +39 -0
  258. package/dist/response/finish.cjs.map +1 -0
  259. package/dist/response/finish.js +35 -0
  260. package/dist/response/finish.js.map +1 -0
  261. package/dist/response/policy.cjs +126 -0
  262. package/dist/response/policy.cjs.map +1 -0
  263. package/dist/response/policy.js +120 -0
  264. package/dist/response/policy.js.map +1 -0
  265. package/dist/response/profiles.cjs +69 -0
  266. package/dist/response/profiles.cjs.map +1 -0
  267. package/dist/response/profiles.js +65 -0
  268. package/dist/response/profiles.js.map +1 -0
  269. package/dist/response/usage/codecs/anthropic.cjs +52 -0
  270. package/dist/response/usage/codecs/anthropic.cjs.map +1 -0
  271. package/dist/response/usage/codecs/anthropic.js +50 -0
  272. package/dist/response/usage/codecs/anthropic.js.map +1 -0
  273. package/dist/response/usage/codecs/cohere.cjs +36 -0
  274. package/dist/response/usage/codecs/cohere.cjs.map +1 -0
  275. package/dist/response/usage/codecs/cohere.js +35 -0
  276. package/dist/response/usage/codecs/cohere.js.map +1 -0
  277. package/dist/response/usage/codecs/converse.cjs +16 -0
  278. package/dist/response/usage/codecs/converse.cjs.map +1 -0
  279. package/dist/response/usage/codecs/converse.js +15 -0
  280. package/dist/response/usage/codecs/converse.js.map +1 -0
  281. package/dist/response/usage/codecs/gemini.cjs +31 -0
  282. package/dist/response/usage/codecs/gemini.cjs.map +1 -0
  283. package/dist/response/usage/codecs/gemini.js +31 -0
  284. package/dist/response/usage/codecs/gemini.js.map +1 -0
  285. package/dist/response/usage/codecs/interactions.cjs +22 -0
  286. package/dist/response/usage/codecs/interactions.cjs.map +1 -0
  287. package/dist/response/usage/codecs/interactions.js +21 -0
  288. package/dist/response/usage/codecs/interactions.js.map +1 -0
  289. package/dist/response/usage/codecs/ollama.cjs +17 -0
  290. package/dist/response/usage/codecs/ollama.cjs.map +1 -0
  291. package/dist/response/usage/codecs/ollama.js +17 -0
  292. package/dist/response/usage/codecs/ollama.js.map +1 -0
  293. package/dist/response/usage/codecs/openai.cjs +52 -0
  294. package/dist/response/usage/codecs/openai.cjs.map +1 -0
  295. package/dist/response/usage/codecs/openai.js +50 -0
  296. package/dist/response/usage/codecs/openai.js.map +1 -0
  297. package/dist/response/usage/codecs/openrouter.cjs +68 -0
  298. package/dist/response/usage/codecs/openrouter.cjs.map +1 -0
  299. package/dist/response/usage/codecs/openrouter.js +67 -0
  300. package/dist/response/usage/codecs/openrouter.js.map +1 -0
  301. package/dist/response/usage/codecs/realtime.cjs +19 -0
  302. package/dist/response/usage/codecs/realtime.cjs.map +1 -0
  303. package/dist/response/usage/codecs/realtime.js +18 -0
  304. package/dist/response/usage/codecs/realtime.js.map +1 -0
  305. package/dist/response/usage/codecs/responses.cjs +21 -0
  306. package/dist/response/usage/codecs/responses.cjs.map +1 -0
  307. package/dist/response/usage/codecs/responses.js +20 -0
  308. package/dist/response/usage/codecs/responses.js.map +1 -0
  309. package/dist/response/usage/estimate.cjs +23 -0
  310. package/dist/response/usage/estimate.cjs.map +1 -0
  311. package/dist/response/usage/estimate.js +21 -0
  312. package/dist/response/usage/estimate.js.map +1 -0
  313. package/dist/response/usage/fields.cjs +72 -0
  314. package/dist/response/usage/fields.cjs.map +1 -0
  315. package/dist/response/usage/fields.js +70 -0
  316. package/dist/response/usage/fields.js.map +1 -0
  317. package/dist/response/usage/recording.cjs +52 -0
  318. package/dist/response/usage/recording.cjs.map +1 -0
  319. package/dist/response/usage/recording.js +52 -0
  320. package/dist/response/usage/recording.js.map +1 -0
  321. package/dist/responses.cjs +45 -58
  322. package/dist/responses.cjs.map +1 -1
  323. package/dist/responses.d.cts.map +1 -1
  324. package/dist/responses.d.ts.map +1 -1
  325. package/dist/responses.js +44 -57
  326. package/dist/responses.js.map +1 -1
  327. package/dist/route-registry.cjs +45 -0
  328. package/dist/route-registry.cjs.map +1 -1
  329. package/dist/route-registry.d.cts.map +1 -1
  330. package/dist/route-registry.d.ts.map +1 -1
  331. package/dist/route-registry.js +41 -1
  332. package/dist/route-registry.js.map +1 -1
  333. package/dist/router.cjs +4 -0
  334. package/dist/router.cjs.map +1 -1
  335. package/dist/router.d.cts.map +1 -1
  336. package/dist/router.d.ts.map +1 -1
  337. package/dist/router.js +3 -0
  338. package/dist/router.js.map +1 -1
  339. package/dist/search.cjs +13 -11
  340. package/dist/search.cjs.map +1 -1
  341. package/dist/search.js +12 -10
  342. package/dist/search.js.map +1 -1
  343. package/dist/server.cjs +222 -146
  344. package/dist/server.cjs.map +1 -1
  345. package/dist/server.d.cts.map +1 -1
  346. package/dist/server.d.ts.map +1 -1
  347. package/dist/server.js +223 -147
  348. package/dist/server.js.map +1 -1
  349. package/dist/speech.cjs +20 -18
  350. package/dist/speech.cjs.map +1 -1
  351. package/dist/speech.d.cts.map +1 -1
  352. package/dist/speech.d.ts.map +1 -1
  353. package/dist/speech.js +19 -17
  354. package/dist/speech.js.map +1 -1
  355. package/dist/sse-writer.cjs +7 -0
  356. package/dist/sse-writer.cjs.map +1 -1
  357. package/dist/sse-writer.d.cts.map +1 -1
  358. package/dist/sse-writer.d.ts.map +1 -1
  359. package/dist/sse-writer.js +7 -1
  360. package/dist/sse-writer.js.map +1 -1
  361. package/dist/stream-collapse.cjs +118 -11
  362. package/dist/stream-collapse.cjs.map +1 -1
  363. package/dist/stream-collapse.d.cts +10 -9
  364. package/dist/stream-collapse.d.cts.map +1 -1
  365. package/dist/stream-collapse.d.ts +10 -9
  366. package/dist/stream-collapse.d.ts.map +1 -1
  367. package/dist/stream-collapse.js +118 -11
  368. package/dist/stream-collapse.js.map +1 -1
  369. package/dist/transcription.cjs +12 -10
  370. package/dist/transcription.cjs.map +1 -1
  371. package/dist/transcription.d.cts.map +1 -1
  372. package/dist/transcription.d.ts.map +1 -1
  373. package/dist/transcription.js +11 -9
  374. package/dist/transcription.js.map +1 -1
  375. package/dist/types.cjs.map +1 -1
  376. package/dist/types.d.cts +62 -3
  377. package/dist/types.d.cts.map +1 -1
  378. package/dist/types.d.ts +62 -3
  379. package/dist/types.d.ts.map +1 -1
  380. package/dist/types.js.map +1 -1
  381. package/dist/vector-mock.cjs +3 -8
  382. package/dist/vector-mock.cjs.map +1 -1
  383. package/dist/vector-mock.d.cts.map +1 -1
  384. package/dist/vector-mock.d.ts.map +1 -1
  385. package/dist/vector-mock.js +3 -8
  386. package/dist/vector-mock.js.map +1 -1
  387. package/dist/vector-stores.cjs +2 -4
  388. package/dist/vector-stores.cjs.map +1 -1
  389. package/dist/vector-stores.d.cts.map +1 -1
  390. package/dist/vector-stores.d.ts.map +1 -1
  391. package/dist/vector-stores.js +2 -4
  392. package/dist/vector-stores.js.map +1 -1
  393. package/dist/veo-video.cjs +16 -14
  394. package/dist/veo-video.cjs.map +1 -1
  395. package/dist/veo-video.js +10 -8
  396. package/dist/veo-video.js.map +1 -1
  397. package/dist/video.cjs +21 -19
  398. package/dist/video.cjs.map +1 -1
  399. package/dist/video.d.cts.map +1 -1
  400. package/dist/video.d.ts.map +1 -1
  401. package/dist/video.js +20 -18
  402. package/dist/video.js.map +1 -1
  403. package/dist/ws-gemini-live.cjs +11 -3
  404. package/dist/ws-gemini-live.cjs.map +1 -1
  405. package/dist/ws-gemini-live.d.cts +0 -1
  406. package/dist/ws-gemini-live.d.cts.map +1 -1
  407. package/dist/ws-gemini-live.d.ts +0 -1
  408. package/dist/ws-gemini-live.d.ts.map +1 -1
  409. package/dist/ws-gemini-live.js +10 -2
  410. package/dist/ws-gemini-live.js.map +1 -1
  411. package/dist/ws-live.cjs +6 -5
  412. package/dist/ws-live.cjs.map +1 -1
  413. package/dist/ws-live.js +5 -4
  414. package/dist/ws-live.js.map +1 -1
  415. package/dist/ws-realtime.cjs +28 -54
  416. package/dist/ws-realtime.cjs.map +1 -1
  417. package/dist/ws-realtime.d.cts.map +1 -1
  418. package/dist/ws-realtime.d.ts.map +1 -1
  419. package/dist/ws-realtime.js +25 -51
  420. package/dist/ws-realtime.js.map +1 -1
  421. package/dist/ws-responses.cjs +8 -6
  422. package/dist/ws-responses.cjs.map +1 -1
  423. package/dist/ws-responses.js +7 -5
  424. package/dist/ws-responses.js.map +1 -1
  425. package/package.json +4 -1
  426. package/skills/write-fixtures/SKILL.md +25 -16
@@ -1 +1 @@
1
- {"version":3,"file":"fixture-loader.js","names":[],"sources":["../src/fixture-loader.ts"],"sourcesContent":["import type { LiveOptions } from \"./live-types.js\";\nimport { normalizeLiveFixture } from \"./live-fixture.js\";\nimport { readFileSync, readdirSync, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type {\n ChatCompletionRequest,\n Fixture,\n FixtureFile,\n FixtureFileEntry,\n FixtureFileResponse,\n FixtureMatch,\n FixtureResponse,\n ResponseOverrides,\n} from \"./types.js\";\nimport {\n isLiveResponse,\n isTextResponse,\n isToolCallResponse,\n isCombinedFixtureResponse,\n isErrorResponse,\n isEmbeddingResponse,\n isImageResponse,\n isAudioResponse,\n isTranscriptionResponse,\n isVideoResponse,\n isJSONResponse,\n fixtureListTarget,\n} from \"./helpers.js\";\nimport type { Logger } from \"./logger.js\";\nimport { CHAOS_FIELDS, CHAOS_FIELD_NAMES, parseChaosField } from \"./chaos.js\";\nimport { Message, build, fixed, jsonText, msg, quote, within } from \"./message-text.js\";\nimport { MCP_FAKES_ECHO_LIMIT } from \"./constants.js\";\nimport {\n copyFixtureMisbehaviorPosition,\n parseMisbehavior,\n setFixtureMisbehaviorPosition,\n validateFixtureMisbehavior,\n type MisbehaviorRule,\n} from \"./misbehavior.js\";\n\n/**\n * Auto-stringify object-valued `content` and `toolCalls[].arguments` fields.\n * This lets fixture authors write plain JSON objects instead of escaped strings.\n * All other fields (including ResponseOverrides, and the ErrorResponse\n * `fallthrough` OpenRouter-failover flag) pass through unmodified via the\n * shallow clone below.\n */\nexport function normalizeResponse(\n raw: FixtureFileResponse,\n liveOptions?: LiveOptions,\n): FixtureResponse {\n if (isLiveResponse(raw)) return normalizeLiveFixture(raw, liveOptions);\n // Shallow-clone so we don't mutate the parsed JSON input.\n const response = { ...raw } as Record<string, unknown>;\n\n // Auto-stringify object content (e.g. structured output)\n if (typeof response.content === \"object\" && response.content !== null) {\n response.content = JSON.stringify(response.content);\n }\n\n // Auto-stringify object arguments in toolCalls\n if (Array.isArray(response.toolCalls)) {\n response.toolCalls = (response.toolCalls as Array<Record<string, unknown>>).map((tc) => {\n if (typeof tc.arguments === \"object\" && tc.arguments !== null) {\n return { ...tc, arguments: JSON.stringify(tc.arguments) };\n }\n return tc;\n });\n }\n\n // Carry the optional ordered `blocks` array through, mirroring the\n // toolCalls[].arguments idiom above: auto-stringify object `arguments` on\n // each `toolCall` block. Gated on Array.isArray so a malformed (non-array)\n // `blocks` value passes through untouched rather than crashing — downstream\n // validation/builders own shape rejection. Absent `blocks` → key absent.\n if (Array.isArray(response.blocks)) {\n response.blocks = (response.blocks as Array<Record<string, unknown>>).map((block) => {\n if (\n block != null &&\n block.type === \"toolCall\" &&\n typeof block.arguments === \"object\" &&\n block.arguments !== null\n ) {\n return { ...block, arguments: JSON.stringify(block.arguments) };\n }\n return block;\n });\n }\n\n return response as unknown as FixtureResponse;\n}\n\n/**\n * @internal Normalize `responsesBlocks` and `customToolCalls` in place, for a\n * server with `responsesTools: \"extended\"`: stringify object `arguments` on\n * `toolCall` blocks, and give a `customToolCalls` entry with no `type` the\n * type `\"custom\"`. A custom call's free-text `input` is never stringified.\n * {@link normalizeResponse} leaves both keys as written, as 1.44.0 did.\n */\nexport function normalizeResponsesToolsKeys(response: Record<string, unknown>): void {\n if (Array.isArray(response.responsesBlocks)) {\n response.responsesBlocks = (response.responsesBlocks as unknown[]).map((block) => {\n const b = block as Record<string, unknown> | null;\n if (\n b !== null &&\n typeof b === \"object\" &&\n b.type === \"toolCall\" &&\n typeof b.arguments === \"object\" &&\n b.arguments !== null\n ) {\n return { ...b, arguments: JSON.stringify(b.arguments) };\n }\n return block;\n });\n }\n if (Array.isArray(response.customToolCalls)) {\n response.customToolCalls = (response.customToolCalls as unknown[]).map((tc) => {\n const entry = tc as Record<string, unknown> | null;\n return entry !== null && typeof entry === \"object\" && entry.type === undefined\n ? { ...entry, type: \"custom\" }\n : tc;\n });\n }\n}\n\nclass InvalidFixtureMatchError extends TypeError {}\n\nexport function entryToFixture(\n entry: FixtureFileEntry,\n logger?: Logger,\n liveOptions?: LiveOptions,\n source?: { file: string; index: number },\n): Fixture {\n if (entry === null || typeof entry !== \"object\" || Array.isArray(entry)) {\n throw new TypeError(\"Fixture entry must be an object\");\n }\n if (entry.match == null) {\n throw new TypeError(\"Fixture match must be an object\");\n }\n if (typeof entry.match !== \"object\" || Array.isArray(entry.match)) {\n throw new InvalidFixtureMatchError(\"Fixture match must be an object\");\n }\n const fixture: Fixture = {\n match: {\n userMessage: entry.match.userMessage,\n systemMessage: entry.match.systemMessage,\n inputText: entry.match.inputText,\n toolCallId: entry.match.toolCallId,\n toolResultContains: entry.match.toolResultContains,\n toolName: entry.match.toolName,\n model: entry.match.model,\n responseFormat: entry.match.responseFormat,\n endpoint: entry.match.endpoint,\n ...(entry.match.sequenceIndex !== undefined && { sequenceIndex: entry.match.sequenceIndex }),\n ...(entry.match.turnIndex !== undefined && {\n turnIndex: entry.match.turnIndex,\n }),\n ...(entry.match.hasToolResult !== undefined && {\n hasToolResult: entry.match.hasToolResult,\n }),\n ...(entry.match.context !== undefined && { context: entry.match.context }),\n },\n response:\n entry.match.endpoint === \"openai-live\"\n ? normalizeLiveFixture(entry.response, liveOptions)\n : normalizeResponse(entry.response, liveOptions),\n ...(entry.latency !== undefined && { latency: entry.latency }),\n ...(entry.chunkSize !== undefined && { chunkSize: entry.chunkSize }),\n ...(entry.truncateAfterChunks !== undefined && {\n truncateAfterChunks: entry.truncateAfterChunks,\n }),\n ...(entry.disconnectAfterMs !== undefined && { disconnectAfterMs: entry.disconnectAfterMs }),\n ...(entry.streamingProfile !== undefined && { streamingProfile: entry.streamingProfile }),\n ...(entry.recordedTimings !== undefined && { recordedTimings: entry.recordedTimings }),\n ...(entry.replaySpeed != null && { replaySpeed: entry.replaySpeed }),\n ...(entry.chaos !== undefined && { chaos: entry.chaos }),\n ...(entry.openRouterProcessing !== undefined && {\n openRouterProcessing: entry.openRouterProcessing,\n }),\n ...(entry.metadata !== undefined && { metadata: entry.metadata }),\n };\n\n if (source) setFixtureMisbehaviorPosition(fixture, `${source.file}#${source.index}`);\n\n // `match.toolNamespace`, `customToolCalls` and `responsesBlocks` are held,\n // not read: 1.44.0 dropped the first and kept the others as written, so the\n // fixture looks exactly as it did then. Only a server with responsesTools\n // \"extended\" applies them (markFixtureResponsesToolsExtended).\n const heldToolNamespace = entry.match.toolNamespace !== undefined;\n const response: unknown = fixture.response;\n const heldResponseKeys =\n entry.match.endpoint !== \"openai-live\" &&\n !isLiveResponse(entry.response) &&\n typeof response === \"object\" &&\n response !== null &&\n (\"customToolCalls\" in response || \"responsesBlocks\" in response);\n if (heldToolNamespace || heldResponseKeys) {\n heldResponsesTools.set(fixture, {\n ...(heldToolNamespace && { toolNamespace: entry.match.toolNamespace }),\n normalize: heldResponseKeys,\n });\n }\n\n // A `misbehavior` key is held, not read: only a server with misbehavior\n // enabled recognizes it (enableHeldFixtureMisbehavior). Otherwise it is\n // unused data, as in 1.44.0.\n if (entry.misbehavior !== undefined) {\n heldFixtureMisbehavior.set(fixture, {\n value: entry.misbehavior,\n path: source ? `fixtures[${source.index}].misbehavior` : \"misbehavior\",\n file: source?.file ?? null,\n });\n }\n\n // Sanitize recordedTimings to guard against NaN or negative values that\n // would silently degrade replay timing calculations.\n if (fixture.recordedTimings) {\n const rt = fixture.recordedTimings;\n if (!Number.isFinite(rt.ttftMs) || rt.ttftMs < 0) rt.ttftMs = 0;\n rt.interChunkDelaysMs = Array.isArray(rt.interChunkDelaysMs)\n ? rt.interChunkDelaysMs.filter((d) => Number.isFinite(d) && d >= 0)\n : [];\n if (!Number.isFinite(rt.totalDurationMs) || rt.totalDurationMs < 0) rt.totalDurationMs = 0;\n }\n\n if (fixture.replaySpeed != null && fixture.replaySpeed <= 0) {\n logger?.warn(`Fixture replaySpeed must be positive, got ${fixture.replaySpeed}. Ignoring.`);\n delete fixture.replaySpeed;\n }\n\n return fixture;\n}\n\n/**\n * Serialise a match value (string | string[] | RegExp | undefined) into a\n * dedup-key fragment that is kind-aware, so a string, a RegExp with the same\n * source, and an array containing that string do not collide. A plain template\n * literal would coerce `/foo/` and `\"foo\"` to the same text and join `[\"foo\"]`\n * to `\"foo\"`, wrongly deduping fixtures that the router would treat as distinct.\n * Used by the duplicate-userMessage dedup key in validateFixtures.\n */\nfunction serializeMatcher(value: string | string[] | RegExp | undefined): string {\n if (value === undefined) return \"\";\n if (value instanceof RegExp) return `re:${value.source}\\u0000${value.flags}`;\n if (Array.isArray(value)) return `arr:${JSON.stringify(value)}`;\n return `str:${value}`;\n}\n\n// Logging helper — uses logger if provided, falls back to console.warn.\nfunction warn(logger: Logger | undefined, msg: string, ...rest: unknown[]): void {\n if (logger) {\n logger.warn(msg, ...rest);\n } else {\n console.warn(`[fixture-loader] ${msg}`, ...rest);\n }\n}\n\n/**\n * A parsed JSON value, or `null` when the file could not be read or parsed (warned).\n *\n * @internal Shared with fixture-loader-services.ts.\n */\nexport function readFixtureJson(\n filePath: string,\n logger: Logger | undefined,\n): { value: unknown } | null {\n let raw: string;\n try {\n raw = readFileSync(filePath, \"utf-8\");\n } catch (err) {\n warn(logger, `Could not read file ${filePath}:`, err);\n return null;\n }\n\n try {\n return { value: JSON.parse(raw) };\n } catch (err) {\n warn(logger, `Invalid JSON in ${filePath}:`, err);\n return null;\n }\n}\n\n/**\n * `parsed` is a JSON object with an own top-level `mcpFakes` key.\n *\n * @internal Shared with fixture-loader-services.ts.\n */\nexport function hasMcpFakesKey(parsed: unknown): parsed is Record<string, unknown> {\n return (\n typeof parsed === \"object\" &&\n parsed !== null &&\n !Array.isArray(parsed) &&\n Object.prototype.hasOwnProperty.call(parsed, \"mcpFakes\")\n );\n}\n\n/**\n * The LLM fixtures of a parsed file. A missing or non-array `fixtures` is\n * warned and gives none, except that a file with `mcpFakes` may leave\n * `fixtures` out (`fixturesOptional`, spec 5.4). An entry whose `match` is\n * not an object is skipped with a warning; any other bad entry throws.\n *\n * @internal Shared with fixture-loader-services.ts.\n */\nexport function fixturesOf(\n parsed: unknown,\n filePath: string,\n logger: Logger | undefined,\n liveOptions: LiveOptions | undefined,\n fixturesOptional: boolean,\n source = filePath,\n): Fixture[] {\n if (\n fixturesOptional &&\n typeof parsed === \"object\" &&\n parsed !== null &&\n !Object.prototype.hasOwnProperty.call(parsed, \"fixtures\")\n ) {\n return [];\n }\n if (\n typeof parsed !== \"object\" ||\n parsed === null ||\n !Array.isArray((parsed as FixtureFile).fixtures)\n ) {\n warn(logger, `Missing or invalid \"fixtures\" array in ${filePath}`);\n return [];\n }\n\n const fixtures: Fixture[] = [];\n for (const [index, entry] of (parsed as FixtureFile).fixtures.entries()) {\n try {\n fixtures.push(entryToFixture(entry, logger, liveOptions, { file: source, index }));\n } catch (error) {\n if (!(error instanceof InvalidFixtureMatchError)) throw error;\n warn(logger, `Skipping fixture at index ${index} in ${filePath}: ${error.message}`);\n }\n }\n return fixtures;\n}\n\n/**\n * B13: the old loaders return LLM fixtures only, so a file with `mcpFakes`\n * fails loud instead of losing its fakes.\n */\nfunction cannotCarryFakes(filePath: string): FixtureLoadError {\n return new FixtureLoadError({\n rule: \"mcp-fakes/loader-cannot-carry-fakes\",\n file: filePath,\n blockId: null,\n entryId: null,\n detail: msg`top-level \"mcpFakes\" key: loadFixtureFile and loadFixturesFromDir cannot carry MCP fakes; use loadFixtureFileWithServices or loadFixturesFromDirWithServices, or LLMock.loadFixtureFile or LLMock.loadFixtureDir`,\n });\n}\n\n/**\n * Load the LLM fixtures of one file. Throws a `FixtureLoadError` (rule\n * `mcp-fakes/loader-cannot-carry-fakes`) when the file has a top-level\n * `mcpFakes` key; use `loadFixtureFileWithServices` (fixture-loader-services.ts) for such files.\n */\nexport function loadFixtureFile(\n filePath: string,\n logger?: Logger,\n liveOptions?: LiveOptions,\n): Fixture[] {\n return loadLlmFixtureFile(filePath, filePath, logger, liveOptions);\n}\n\nfunction loadLlmFixtureFile(\n filePath: string,\n source: string,\n logger: Logger | undefined,\n liveOptions: LiveOptions | undefined,\n): Fixture[] {\n const read = readFixtureJson(filePath, logger);\n if (read === null) return [];\n if (hasMcpFakesKey(read.value)) throw cannotCarryFakes(filePath);\n return fixturesOf(read.value, filePath, logger, liveOptions, false, source);\n}\n\n/**\n * The `.json` files (sorted) and the subdirectories (sorted) of `dirPath`,\n * or `null` when it cannot be read (warned).\n *\n * @internal Shared with fixture-loader-services.ts.\n */\nexport function listFixtureDir(\n dirPath: string,\n logger: Logger | undefined,\n): { jsonFiles: string[]; subdirs: string[] } | null {\n let entries: string[];\n try {\n entries = readdirSync(dirPath);\n } catch (err) {\n warn(logger, `Could not read directory ${dirPath}:`, err);\n return null;\n }\n\n const jsonFiles: string[] = [];\n const subdirs: string[] = [];\n for (const name of entries) {\n const fullPath = join(dirPath, name);\n try {\n if (statSync(fullPath).isDirectory()) {\n subdirs.push(name);\n continue;\n }\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code !== \"ENOENT\") {\n warn(logger, `Could not stat ${fullPath}:`, err);\n }\n continue;\n }\n if (name.endsWith(\".json\")) {\n jsonFiles.push(name);\n }\n }\n jsonFiles.sort();\n subdirs.sort();\n return { jsonFiles, subdirs };\n}\n\n/**\n * Load the LLM fixtures of every `.json` file under `dirPath`. Throws a\n * `FixtureLoadError` (rule `mcp-fakes/loader-cannot-carry-fakes`) on the\n * first file with a top-level `mcpFakes` key; use\n * `loadFixturesFromDirWithServices` (fixture-loader-services.ts) for such\n * directories.\n */\nexport function loadFixturesFromDir(\n dirPath: string,\n logger?: Logger,\n liveOptions?: LiveOptions,\n): Fixture[] {\n return loadLlmFixtureDir(dirPath, \"\", logger, liveOptions);\n}\n\nfunction loadLlmFixtureDir(\n dirPath: string,\n relDir: string,\n logger: Logger | undefined,\n liveOptions: LiveOptions | undefined,\n): Fixture[] {\n const listing = listFixtureDir(dirPath, logger);\n if (listing === null) return [];\n\n const fixtures: Fixture[] = [];\n for (const name of listing.jsonFiles) {\n const filePath = join(dirPath, name);\n fixtures.push(...loadLlmFixtureFile(filePath, relDir + name, logger, liveOptions));\n }\n\n // Recurse into all subdirectories (full depth) to support nested layouts\n // like showcase/aimock/d6/<integration>/<feature>.json.\n for (const sub of listing.subdirs) {\n fixtures.push(\n ...loadLlmFixtureDir(join(dirPath, sub), `${relDir}${sub}/`, logger, liveOptions),\n );\n }\n\n return fixtures;\n}\n\n// ---------------------------------------------------------------------------\n// Fixture validation\n// ---------------------------------------------------------------------------\n\n/**\n * The fixture indices a finding's message NAMES, carried as data.\n *\n * Two rules (`duplicate userMessage` and the catch-all ordering check) name\n * OTHER fixtures in the array they were given, so their message text embeds an\n * index. A consumer that validates a different array than the one the reader\n * sees — `aimock validate` runs the rules per file and then over the union of\n * every file — has to renumber those indices, and the only safe way to do that\n * is to re-render from the numbers rather than to rewrite the rendered prose:\n * `duplicate userMessage` interpolates the fixture's OWN `userMessage`, so a\n * regex over the message corrupts user-authored text that happens to read\n * \"fixture 3\".\n */\nexport type ValidationRef =\n | { rule: \"duplicate-user-message\"; userMessage: string; shadows: number }\n | { rule: \"catch-all-not-last\"; shadowsFrom: number; shadowsThrough: number };\n\nconst INVALID_ARGUMENTS_HINT =\n \"to send invalid JSON on purpose, use `misbehavior: tool-args-invalid-json`\";\n\nexport interface ValidationResult {\n severity: \"error\" | \"warning\";\n fixtureIndex: number;\n message: string;\n /** Present only on the rules whose message names other fixtures. */\n ref?: ValidationRef;\n}\n\n/**\n * Render a ref-carrying finding's message with each named index passed through\n * `renderIndex`. With `String` it reproduces the message `validateFixtures`\n * itself emits; `aimock validate` passes a renderer that resolves a union\n * index to `<file> #<index-in-that-file>`.\n */\nexport function renderValidationRef(\n ref: ValidationRef,\n renderIndex: (fixtureIndex: number) => string,\n): string {\n return ref.rule === \"duplicate-user-message\"\n ? `duplicate userMessage '${ref.userMessage}' — shadows fixture ${renderIndex(ref.shadows)}`\n : `empty match acts as catch-all but is not the last fixture — shadows fixtures ${renderIndex(\n ref.shadowsFrom,\n )}+`;\n}\n\nfunction validateReasoning(\n response: { reasoning?: unknown },\n fixtureIndex: number,\n results: ValidationResult[],\n): void {\n if (response.reasoning !== undefined) {\n if (typeof response.reasoning !== \"string\") {\n results.push({\n severity: \"error\",\n fixtureIndex,\n message: \"reasoning must be a string\",\n });\n } else if (response.reasoning === \"\") {\n results.push({\n severity: \"warning\",\n fixtureIndex,\n message: \"reasoning is empty string — no reasoning events will be emitted\",\n });\n }\n }\n}\n\nfunction validateWebSearches(\n response: { webSearches?: unknown },\n fixtureIndex: number,\n results: ValidationResult[],\n): void {\n if (response.webSearches !== undefined) {\n if (!Array.isArray(response.webSearches)) {\n results.push({\n severity: \"error\",\n fixtureIndex,\n message: \"webSearches must be an array of strings\",\n });\n } else if (response.webSearches.length === 0) {\n results.push({\n severity: \"warning\",\n fixtureIndex,\n message: \"webSearches is empty array — no web search events will be emitted\",\n });\n } else {\n for (let j = 0; j < response.webSearches.length; j++) {\n if (typeof response.webSearches[j] !== \"string\") {\n results.push({\n severity: \"error\",\n fixtureIndex,\n message: `webSearches[${j}] is not a string`,\n });\n break;\n }\n if (response.webSearches[j] === \"\") {\n results.push({\n severity: \"warning\",\n fixtureIndex,\n message: `webSearches[${j}] is empty string`,\n });\n }\n }\n }\n }\n}\n\n/**\n * Path (`customToolCalls[j]` or `responsesBlocks[j]`) of the first custom\n * tool call a static fixture response carries, or undefined. Factory\n * responses are skipped.\n */\nfunction firstCustomToolCallPath(response: unknown): string | undefined {\n if (response === null || typeof response !== \"object\") return undefined;\n const r = response as { customToolCalls?: unknown; responsesBlocks?: unknown };\n if (Array.isArray(r.customToolCalls) && r.customToolCalls.length > 0) {\n return \"customToolCalls[0]\";\n }\n if (Array.isArray(r.responsesBlocks)) {\n const j = r.responsesBlocks.findIndex(\n (b) =>\n b !== null && typeof b === \"object\" && (b as { type?: unknown }).type === \"customToolCall\",\n );\n if (j >= 0) return `responsesBlocks[${j}]`;\n }\n return undefined;\n}\n\n/**\n * Checks for the OpenAI Responses keys `customToolCalls` and\n * `responsesBlocks`. Both are new, so their findings never touch a fixture an\n * earlier release accepted. A custom tool call takes a non-empty string\n * `name`, a string `input` (empty: warning), no `arguments`, a string `id`\n * when present and a non-empty string `namespace` when present.\n * `responsesBlocks` follows the `blocks` rules plus `customToolCall` blocks,\n * and may not be combined with `blocks`.\n */\nfunction validateResponsesToolKeys(\n response: unknown,\n fixtureIndex: number,\n results: ValidationResult[],\n): void {\n if (response === null || typeof response !== \"object\") return;\n const r = response as { customToolCalls?: unknown; responsesBlocks?: unknown; blocks?: unknown };\n const error = (message: string) => results.push({ severity: \"error\", fixtureIndex, message });\n const warning = (message: string) => results.push({ severity: \"warning\", fixtureIndex, message });\n const checkCustom = (tc: Record<string, unknown>, path: string) => {\n if (typeof tc.name !== \"string\" || tc.name === \"\") {\n error(`${path}.name must be a non-empty string`);\n }\n if (typeof tc.input !== \"string\") {\n error(`${path}.input must be a string for a custom tool call`);\n } else if (tc.input === \"\") {\n warning(`${path}.input is empty`);\n }\n if (tc.arguments !== undefined) {\n error(`${path}.arguments is not valid on a custom tool call; use input`);\n }\n if (tc.id !== undefined && typeof tc.id !== \"string\") {\n error(`${path}.id must be a string, got ${typeof tc.id}`);\n }\n if (tc.namespace !== undefined && (typeof tc.namespace !== \"string\" || tc.namespace === \"\")) {\n error(`${path}.namespace must be a non-empty string`);\n }\n };\n if (r.customToolCalls !== undefined) {\n if (!Array.isArray(r.customToolCalls)) {\n error(`customToolCalls must be an array, got ${typeof r.customToolCalls}`);\n } else {\n r.customToolCalls.forEach((entry: unknown, j) => {\n if (entry === null || typeof entry !== \"object\" || Array.isArray(entry)) {\n error(`customToolCalls[${j}] must be an object`);\n return;\n }\n const tc = entry as Record<string, unknown>;\n if (tc.type !== undefined && tc.type !== \"custom\") {\n error(`customToolCalls[${j}].type must be \"custom\" when present`);\n }\n checkCustom(tc, `customToolCalls[${j}]`);\n });\n }\n }\n if (r.responsesBlocks === undefined) return;\n if (!Array.isArray(r.responsesBlocks)) {\n error(`responsesBlocks must be an array, got ${typeof r.responsesBlocks}`);\n return;\n }\n if (Array.isArray(r.blocks) && r.blocks.length > 0 && r.responsesBlocks.length > 0) {\n error(\"blocks and responsesBlocks cannot both be set; use responsesBlocks alone\");\n }\n r.responsesBlocks.forEach((entry: unknown, j) => {\n const path = `responsesBlocks[${j}]`;\n if (entry === null || typeof entry !== \"object\" || Array.isArray(entry)) {\n error(`${path} must be an object`);\n return;\n }\n const block = entry as Record<string, unknown>;\n if (block.type === \"text\") {\n if (typeof block.text !== \"string\") error(`${path}.text must be a string`);\n return;\n }\n if (block.type === \"customToolCall\") {\n checkCustom(block, path);\n return;\n }\n if (block.type !== \"toolCall\") {\n error(\n `${path}.type must be \"text\", \"toolCall\" or \"customToolCall\", got ${JSON.stringify(block.type)}`,\n );\n return;\n }\n if (typeof block.name !== \"string\" || block.name === \"\") {\n error(`${path}.name must be a non-empty string`);\n }\n if (block.id !== undefined && typeof block.id !== \"string\") {\n error(`${path}.id must be a string, got ${typeof block.id}`);\n }\n if (block.input !== undefined) {\n error(`${path}.input is only valid on a \"customToolCall\" block`);\n }\n if (\n block.namespace !== undefined &&\n (typeof block.namespace !== \"string\" || block.namespace === \"\")\n ) {\n error(`${path}.namespace must be a non-empty string`);\n }\n const args = block.arguments;\n if (typeof args === \"string\") {\n try {\n JSON.parse(args);\n } catch {\n error(`${path}.arguments is not valid JSON: ${args}; ${INVALID_ARGUMENTS_HINT}`);\n }\n } else if (args === null || typeof args !== \"object\") {\n error(`${path}.arguments must be a string or an object`);\n }\n });\n}\n\nfunction validateBlocks(\n response: { blocks?: unknown; content?: unknown; toolCalls?: unknown },\n fixtureIndex: number,\n results: ValidationResult[],\n): void {\n if (response.blocks === undefined) return;\n\n // Mirrors the toolCalls checks: reject malformed `blocks` at LOAD time so a\n // bad blocks array never reaches the dispatch/builder (where\n // resolveFixtureBlocks throws AFTER the journal has already recorded\n // status:200, yielding a journal-200/client-500 mismatch). #274 F3+F8.\n if (!Array.isArray(response.blocks)) {\n results.push({\n severity: \"error\",\n fixtureIndex,\n message: `blocks must be an array, got ${typeof response.blocks}`,\n });\n return;\n }\n\n for (let j = 0; j < response.blocks.length; j++) {\n const block = response.blocks[j] as Record<string, unknown> | null | undefined;\n if (typeof block !== \"object\" || block === null) {\n results.push({\n severity: \"error\",\n fixtureIndex,\n message: `blocks[${j}] must be an object`,\n });\n continue;\n }\n if (block.type !== \"text\" && block.type !== \"toolCall\") {\n results.push({\n severity: \"error\",\n fixtureIndex,\n message: `blocks[${j}].type must be \"text\" or \"toolCall\", got ${JSON.stringify(block.type)}`,\n });\n continue;\n }\n if (block.type === \"text\") {\n if (typeof block.text !== \"string\") {\n results.push({\n severity: \"error\",\n fixtureIndex,\n message: `blocks[${j}].text must be a string, got ${typeof block.text}`,\n });\n } else if (block.text === \"\") {\n // Mirror the content/toolCalls \"empty string\" rejection: an empty-text\n // block produces a meaningless/spurious wire chunk on replay.\n results.push({\n severity: \"error\",\n fixtureIndex,\n message: `blocks[${j}].text is empty string`,\n });\n }\n } else {\n // toolCall block — mirror toolCalls[] name + arguments checks.\n if (typeof block.name !== \"string\" || block.name === \"\") {\n results.push({\n severity: \"error\",\n fixtureIndex,\n message: `blocks[${j}].name must be a non-empty string`,\n });\n }\n // `arguments` is JSON-string in runtime form (normalizeResponse already\n // stringified object/array args); accept a valid-JSON string or an object.\n if (typeof block.arguments === \"string\") {\n try {\n JSON.parse(block.arguments);\n } catch {\n results.push({\n severity: \"error\",\n fixtureIndex,\n message: `blocks[${j}].arguments is not valid JSON: ${block.arguments}; ${INVALID_ARGUMENTS_HINT}`,\n });\n }\n } else if (typeof block.arguments !== \"object\" || block.arguments === null) {\n results.push({\n severity: \"error\",\n fixtureIndex,\n message: `blocks[${j}].arguments must be a JSON string or object, got ${typeof block.arguments}`,\n });\n }\n if (block.id !== undefined && typeof block.id !== \"string\") {\n results.push({\n severity: \"error\",\n fixtureIndex,\n message: `blocks[${j}].id must be a string, got ${typeof block.id}`,\n });\n }\n }\n }\n\n // blocks-vs-content/toolCalls divergence (#274 P2). When a fixture carries\n // BOTH `blocks` AND legacy `content`/`toolCalls` (allowed but unusual now\n // that blocks-only is first-class), builders stream `blocks` and IGNORE the\n // redundant `content`/`toolCalls`. If those disagree it is a silent footgun,\n // so WARN (not a hard error). Stay silent on the clean blocks-only path\n // (neither legacy field present) — that is the intended shape.\n const hasLegacyContent = typeof response.content === \"string\";\n const hasLegacyToolCalls = Array.isArray(response.toolCalls);\n if ((hasLegacyContent || hasLegacyToolCalls) && Array.isArray(response.blocks)) {\n const textBlocks = response.blocks.filter(\n (b): b is { type: \"text\"; text: string } =>\n b != null &&\n typeof b === \"object\" &&\n (b as { type?: unknown }).type === \"text\" &&\n typeof (b as { text?: unknown }).text === \"string\",\n );\n const toolCallBlockNames = response.blocks\n .filter(\n (b): b is { type: \"toolCall\"; name: string } =>\n b != null &&\n typeof b === \"object\" &&\n (b as { type?: unknown }).type === \"toolCall\" &&\n typeof (b as { name?: unknown }).name === \"string\",\n )\n .map((b) => b.name);\n\n // Text divergence: blocks' concatenated text vs legacy `content`.\n if (hasLegacyContent) {\n const blocksText = textBlocks.map((b) => b.text).join(\"\");\n if (blocksText !== response.content) {\n results.push({\n severity: \"warning\",\n fixtureIndex,\n message:\n \"blocks text diverges from content — builders stream blocks and ignore the redundant content field\",\n });\n }\n }\n\n // ToolCall divergence: blocks' ordered toolCall names vs legacy `toolCalls`.\n if (hasLegacyToolCalls) {\n const legacyNames = (response.toolCalls as Array<{ name?: unknown }>).map((tc) =>\n typeof tc?.name === \"string\" ? tc.name : undefined,\n );\n const sameNames =\n legacyNames.length === toolCallBlockNames.length &&\n legacyNames.every((n, k) => n === toolCallBlockNames[k]);\n if (!sameNames) {\n results.push({\n severity: \"warning\",\n fixtureIndex,\n message:\n \"blocks toolCalls diverge from toolCalls — builders stream blocks and ignore the redundant toolCalls field\",\n });\n }\n }\n }\n}\n\n/**\n * Human-readable form of the status range an injected error may carry.\n * Measured on Node v26: `res.writeHead` throws outside 100-999 (the injected\n * error is then lost and the caller sees a generic 500), and a 1xx status is\n * accepted but cannot terminate a response, so it hangs the request until the\n * client times out. 600-999 is unassigned, NOT invalid — Node sends it and a\n * client sees it — so a mock server allows it.\n */\nexport const INJECTED_STATUS_RANGE =\n \"an integer between 200 and 999 (1xx cannot terminate a response and would hang the client)\";\n\n/**\n * Shared by the three doors that queue an error whose `status` reaches\n * `res.writeHead`: a fixture's `response.status` (below), `POST\n * /__aimock/error`, and `LLMock.nextRequestError`.\n */\nexport function isInjectableStatus(status: unknown): status is number {\n // The typeof is for the COMPILER — it narrows `unknown` so the comparisons\n // below type-check. At runtime `Number.isInteger` already excludes every\n // non-number, so mutating it away fails nothing.\n if (typeof status !== \"number\") return false;\n return Number.isInteger(status) && status >= 200 && status <= 999;\n}\n\n/**\n * An injected error is only a valid response for endpoints that can carry an\n * error envelope. Mirrors the router's endpoint-compat table\n * (matchFixtureDiagnostic in router.ts): error responses are compatible with\n * chat / embedding / realtime* / fal / the four elevenlabs-voice* slots and\n * with requests that carry no endpoint type, but NOT with multimedia endpoints\n * (image, speech, video, transcription, …). Whether files / fine-tuning /\n * batches belong here is an open design question — not widened yet.\n */\nfunction isErrorEndpointCompatible(req: ChatCompletionRequest): boolean {\n const reqEndpoint = req._endpointType as string | undefined;\n return (\n reqEndpoint === undefined ||\n reqEndpoint === \"chat\" ||\n reqEndpoint === \"embedding\" ||\n reqEndpoint.startsWith(\"realtime\") ||\n reqEndpoint === \"fal\" ||\n reqEndpoint === \"elevenlabs-voice-design\" ||\n reqEndpoint === \"elevenlabs-voice\" ||\n reqEndpoint === \"elevenlabs-voice-get\" ||\n reqEndpoint === \"elevenlabs-voice-delete\"\n );\n}\n\n/**\n * Queue a one-shot injected error at the FRONT of `fixtures` — shared by\n * `LLMock.nextRequestError` and `POST /__aimock/error` so both doors gate and\n * consume identically. The caller validates `status` (`isInjectableStatus`).\n *\n * The fixture is consumed exactly when its error body is actually WRITTEN to a\n * client, not when its predicate is evaluated and not when its factory happens\n * to run. Handlers that await chaos claim it synchronously at selection\n * (`claimOneShotError`) so two concurrent requests cannot both pick it across\n * the chaos-latency await, and RELEASES the claim (`releaseOneShotError`) on\n * every exit that ends the request without writing that body — a terminal\n * chaos action, or a client that left during the chaos-latency delay — unless\n * the queue was cleared (`clearFixtureQueue`) in between, in which case the\n * stale claim is dropped rather than re-armed into the reset queue. The\n * claim cannot simply be deferred past the chaos gate instead: the chaos\n * config is resolved FROM the selected fixture, and a claim taken after the\n * latency await re-opens the concurrency race the claim-at-selection closes.\n * The OpenRouter `models[]` loop cannot burn it by resolving a candidate it\n * then fails past — the error is `fallthrough: false` (terminal) so the\n * candidate that selects it serves it. The factory still performs the same\n * idempotent claim for handlers that only reach the fixture via\n * `resolveResponse` without an intervening await (realtime and fal). Consuming at\n * predicate time burned the error unserved whenever a later fixture won\n * selection (e.g. a behind-the-count `turnIndex` fixture) and spliced the\n * array while the router was still iterating it, skipping the very fixture\n * that then won. The predicate gate keeps an incompatible request (an image\n * call) from selecting it at all, so the error stays pending for its intended\n * endpoint.\n */\nconst oneShotErrorFixtures = new WeakSet<Fixture>();\n\n/** True for a fixture queued by `queueOneShotError` (still armed or not). */\nexport function isOneShotError(fixture: Fixture): boolean {\n return oneShotErrorFixtures.has(fixture);\n}\n\n/**\n * Claim (consume) a one-shot error fixture. Returns true when this call\n * removed it from `fixtures`; false when another request already claimed it,\n * in which case the caller must NOT serve it and must re-select.\n */\nexport function claimOneShotError(fixtures: Fixture[], fixture: Fixture): boolean {\n const idx = fixtures.indexOf(fixture);\n if (idx === -1) return false;\n fixtures.splice(idx, 1);\n oneShotClaimGeneration.set(fixture, queueGeneration(fixtures));\n return true;\n}\n\n/**\n * Generation of each fixture queue, bumped by `clearFixtureQueue`. A one-shot\n * claim records the generation it was taken under; a release under a LATER\n * generation means the queue was cleared or reset while the claim was parked\n * (in the chaos-latency await) and the claim is stale — re-arming it would\n * plant the previous test's injection in the next test's freshly reset queue.\n * Keyed by the caller's array (a server's own list resolves to it): every\n * clear path preserves the array reference (`length = 0`), so the counter\n * follows the live queue.\n */\nconst fixtureQueueGeneration = new WeakMap<Fixture[], number>();\nconst oneShotClaimGeneration = new WeakMap<Fixture, number>();\n\nfunction queueGeneration(fixtures: Fixture[]): number {\n return fixtureQueueGeneration.get(fixtureListTarget(fixtures)) ?? 0;\n}\n\n/**\n * Empty a fixture queue in place (array identity preserved — the running\n * server reads this same array on every request) and invalidate every\n * outstanding one-shot claim against it, so a release that lands after the\n * clear is a no-op instead of re-arming into the emptied queue. The ONE door\n * for `POST /__aimock/reset`, `DELETE /__aimock/fixtures` and\n * `LLMock.clearFixtures`; a bare `fixtures.length = 0` misses the second half.\n */\nexport function clearFixtureQueue(fixtures: Fixture[]): void {\n fixtures.length = 0;\n fixtureQueueGeneration.set(fixtureListTarget(fixtures), queueGeneration(fixtures) + 1);\n}\n\n/**\n * Re-arm a one-shot error that was claimed for a request which then ended\n * WITHOUT writing the error body — a terminal chaos action (drop / disconnect\n * / rateLimit / malformed) or a client that hung up during the chaos-latency\n * delay. Without this the injection is burned unserved and the NEXT request\n * gets a 200, which is exactly the outcome `nextRequestError` promises not to\n * produce. A no-op for a fixture that is not a one-shot, and for one still\n * present in `fixtures` (never claimed, or already re-armed) so a double\n * release cannot duplicate the injection — and for a claim taken before the\n * queue was last cleared, so a reset that raced the request stays a reset.\n */\nexport function releaseOneShotError(fixtures: Fixture[], fixture: Fixture | null): void {\n if (fixture === null || !oneShotErrorFixtures.has(fixture)) return;\n if (fixtures.includes(fixture)) return;\n // Claimed under an earlier generation: the queue was cleared/reset while\n // this request was parked. The clear disarmed the injection — stay cleared.\n if (oneShotClaimGeneration.get(fixture) !== queueGeneration(fixtures)) return;\n // Front, matching `queueOneShotError`: the one-shot wins registration-order\n // ties against everything else, exactly as it did before it was claimed.\n fixtures.unshift(fixture);\n}\n\nexport function queueOneShotError(\n fixtures: Fixture[],\n status: number,\n errorBody?: { message?: string; type?: string; code?: string },\n): Fixture {\n const errorResponse: FixtureResponse = {\n error: {\n message: errorBody?.message ?? \"Injected error\",\n type: errorBody?.type ?? \"server_error\",\n code: errorBody?.code,\n },\n status,\n // Terminal in the OpenRouter `models[]` loop: the candidate that selects\n // (and thereby claims) the one-shot serves it instead of failing past it.\n fallthrough: false,\n };\n const fixture: Fixture = {\n match: { predicate: isErrorEndpointCompatible },\n response: () => {\n // Idempotent: a no-op when the handler already claimed at selection.\n claimOneShotError(fixtures, fixture);\n return errorResponse;\n },\n };\n oneShotErrorFixtures.add(fixture);\n // Insert at front so it wins registration-order ties against everything else.\n fixtures.unshift(fixture);\n return fixture;\n}\n\nexport function validateFixtures(\n fixtures: Fixture[],\n liveOptions?: LiveOptions,\n): ValidationResult[] {\n const results: ValidationResult[] = [];\n\n const seenUserMessages = new Map<string, number>();\n\n for (let i = 0; i < fixtures.length; i++) {\n const original = fixtures[i];\n // Only a fixture loaded for a server with responsesTools \"extended\" has its\n // `match.toolNamespace`, `customToolCalls` and `responsesBlocks` checked;\n // for any other fixture they are unused data, as in 1.44.0.\n const f = responsesToolsExtendedFixtures.has(original)\n ? original\n : withoutResponsesToolsKeys(original);\n const response = f.response;\n\n // Only a fixture recognized under enabled misbehavior is checked; for any\n // other fixture a `misbehavior` key is unused data, as in 1.44.0.\n const misbehaviorIssue = misbehaviorEnabledFixtures.has(original)\n ? validateFixtureMisbehavior(original, `fixtures[${i}].misbehavior`)\n : undefined;\n if (misbehaviorIssue) {\n results.push({ severity: \"error\", fixtureIndex: i, message: misbehaviorIssue.message });\n } else if (misbehaviorEnabledFixtures.has(original) && f.misbehavior !== undefined) {\n const parsed = parseMisbehavior(f.misbehavior);\n if (parsed.ok && parsed.config.seed === undefined) {\n for (const fault of parsed.config.faults) {\n if (fault.rate !== undefined && fault.rate < 1 && fault.times === undefined) {\n results.push({\n severity: \"warning\",\n fixtureIndex: i,\n message:\n \"probabilistic fault without a seed is reproducible only per test id order; set seed to pin it\",\n });\n }\n }\n }\n }\n\n if (\n typeof response !== \"function\" &&\n (isLiveResponse(response) || f.match.endpoint === \"openai-live\")\n ) {\n try {\n normalizeLiveFixture(response, liveOptions);\n } catch (error) {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n // Skip response-shape validation for function responses — they are\n // evaluated at runtime so we cannot statically inspect them.\n if (typeof response === \"function\") {\n // Still validate match fields and numeric options below.\n } else {\n // --- Error checks ---\n\n // Response type recognition\n // Note: isCombinedFixtureResponse must be checked before isTextResponse\n // and isToolCallResponse since it is a structural superset of both.\n if (\n !isCombinedFixtureResponse(response) &&\n !isTextResponse(response) &&\n !isToolCallResponse(response) &&\n !isErrorResponse(response) &&\n !isEmbeddingResponse(response) &&\n !isImageResponse(response) &&\n !isAudioResponse(response) &&\n !isTranscriptionResponse(response) &&\n !isVideoResponse(response) &&\n !isJSONResponse(response) &&\n !isLiveResponse(response)\n ) {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message:\n \"response is not a recognized type (must have content, toolCalls, error, embedding, image, audio, transcription, video, json, or live)\",\n });\n }\n\n // When a non-empty ordered `blocks` array is present, the builders stream\n // `blocks` and IGNORE the legacy `content` mirror (see validateBlocks's\n // divergence note + isCombinedFixtureResponse's BLOCKS-ONLY clause).\n // So an empty-string `content` is harmless in that case and must NOT raise\n // the \"content is empty string\" hard error. Fixtures WITHOUT blocks keep\n // the error (an empty content with no blocks produces no output).\n const hasNonEmptyBlocks =\n (Array.isArray((response as { blocks?: unknown }).blocks) &&\n (response as { blocks: unknown[] }).blocks.length > 0) ||\n (Array.isArray((response as { responsesBlocks?: unknown }).responsesBlocks) &&\n (response as { responsesBlocks: unknown[] }).responsesBlocks.length > 0);\n const hasCustomToolCalls =\n Array.isArray((response as { customToolCalls?: unknown }).customToolCalls) &&\n (response as { customToolCalls: unknown[] }).customToolCalls.length > 0;\n\n // Text response checks\n if (isTextResponse(response)) {\n if (response.content === \"\" && !hasNonEmptyBlocks) {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: \"content is empty string\",\n });\n }\n validateReasoning(response, i, results);\n validateWebSearches(response, i, results);\n }\n\n // ContentWithToolCalls response checks\n if (isCombinedFixtureResponse(response)) {\n // The guard now also matches a BLOCKS-ONLY fixture (non-empty `blocks`,\n // no `content`/`toolCalls`). For that shape the content/toolCalls checks\n // below don't apply (and `content`/`toolCalls` are undefined) — the\n // ordered `blocks` array is validated separately by `validateBlocks`\n // immediately after this block. So gate the legacy field checks on the\n // fields actually being present, mirroring the builders' branch-on-blocks.\n if (typeof response.content === \"string\") {\n if (response.content === \"\" && !hasNonEmptyBlocks) {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: \"content is empty string\",\n });\n }\n }\n if (Array.isArray(response.toolCalls)) {\n if (response.toolCalls.length === 0 && !hasCustomToolCalls) {\n results.push({\n severity: \"warning\",\n fixtureIndex: i,\n message: \"toolCalls array is empty — fixture will never produce tool calls\",\n });\n }\n for (let j = 0; j < response.toolCalls.length; j++) {\n const tc = response.toolCalls[j];\n if (!tc.name) {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `toolCalls[${j}].name is empty`,\n });\n }\n try {\n JSON.parse(tc.arguments);\n } catch {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `toolCalls[${j}].arguments is not valid JSON: ${tc.arguments}; ${INVALID_ARGUMENTS_HINT}`,\n });\n }\n }\n }\n validateReasoning(response, i, results);\n validateWebSearches(response, i, results);\n }\n\n // Optional ordered `blocks` checks — validated whenever present on the\n // response, regardless of which content/toolCalls guard matched, so a\n // malformed blocks array is rejected at LOAD rather than at dispatch.\n validateBlocks(\n response as { blocks?: unknown; content?: unknown; toolCalls?: unknown },\n i,\n results,\n );\n // OpenAI Responses keys (new): customToolCalls and responsesBlocks.\n validateResponsesToolKeys(response, i, results);\n\n // Tool call response checks\n if (isToolCallResponse(response)) {\n if (response.toolCalls.length === 0 && !hasCustomToolCalls) {\n results.push({\n severity: \"warning\",\n fixtureIndex: i,\n message: \"toolCalls array is empty — fixture will never produce tool calls\",\n });\n }\n for (let j = 0; j < response.toolCalls.length; j++) {\n const tc = response.toolCalls[j];\n if (!tc.name) {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `toolCalls[${j}].name is empty`,\n });\n }\n try {\n JSON.parse(tc.arguments);\n } catch {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `toolCalls[${j}].arguments is not valid JSON: ${tc.arguments}; ${INVALID_ARGUMENTS_HINT}`,\n });\n }\n }\n validateWebSearches(response, i, results);\n }\n\n // Error response checks\n if (isErrorResponse(response)) {\n if (!response.error.message) {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: \"error.message is empty\",\n });\n }\n // Shared with `POST /__aimock/error` and `LLMock.nextRequestError`\n // (see `isInjectableStatus` above): a fixture's status lands on the same\n // `res.writeHead`, so it is accepted or rejected identically.\n if (response.status !== undefined && !isInjectableStatus(response.status)) {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `error status ${response.status} is not a valid HTTP status code — must be ${INJECTED_STATUS_RANGE}`,\n });\n }\n // `fallthrough` gates OpenRouter `models[]` failover and is read as\n // `!== false`, so a non-boolean (`\"false\"`, 0, null) would silently\n // fall through — the OPPOSITE of the intended terminal behavior. Reject\n // it at load so the fail-closed half cannot quietly fail open.\n if (response.fallthrough !== undefined && typeof response.fallthrough !== \"boolean\") {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `error.fallthrough must be a boolean, got ${typeof response.fallthrough}`,\n });\n }\n }\n\n // Embedding response checks\n if (isEmbeddingResponse(response)) {\n if (response.embedding.length === 0) {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: \"embedding array is empty\",\n });\n }\n for (let j = 0; j < response.embedding.length; j++) {\n if (typeof response.embedding[j] !== \"number\") {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `embedding[${j}] is not a number`,\n });\n break; // one error is enough\n }\n }\n }\n\n // Audio response checks — validate object-form audio\n if (isAudioResponse(response) && typeof response.audio === \"object\") {\n const audioObj = response.audio;\n if (typeof audioObj.b64Json !== \"string\" || audioObj.b64Json === \"\") {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: \"audio.b64Json must be a non-empty string\",\n });\n }\n if (audioObj.contentType !== undefined && typeof audioObj.contentType !== \"string\") {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `audio.contentType must be a string, got ${typeof audioObj.contentType}`,\n });\n }\n }\n\n // Validate ResponseOverrides fields\n if (\n isTextResponse(response) ||\n isToolCallResponse(response) ||\n isCombinedFixtureResponse(response)\n ) {\n const r = response as ResponseOverrides;\n if (r.id !== undefined && typeof r.id !== \"string\") {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `override \"id\" must be a string, got ${typeof r.id}`,\n });\n }\n if (r.created !== undefined && (typeof r.created !== \"number\" || r.created < 0)) {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `override \"created\" must be a non-negative number`,\n });\n }\n if (r.model !== undefined && typeof r.model !== \"string\") {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `override \"model\" must be a string, got ${typeof r.model}`,\n });\n }\n if (r.finishReason !== undefined && typeof r.finishReason !== \"string\") {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `override \"finishReason\" must be a string, got ${typeof r.finishReason}`,\n });\n }\n if (r.role !== undefined && typeof r.role !== \"string\") {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `override \"role\" must be a string, got ${typeof r.role}`,\n });\n }\n if (r.systemFingerprint !== undefined && typeof r.systemFingerprint !== \"string\") {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `override \"systemFingerprint\" must be a string, got ${typeof r.systemFingerprint}`,\n });\n }\n if (r.usage !== undefined) {\n if (typeof r.usage !== \"object\" || r.usage === null || Array.isArray(r.usage)) {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `override \"usage\" must be an object`,\n });\n } else {\n // Numeric token fields must be numbers, but the OpenRouter cost\n // sub-fields carry non-numeric shapes (see ResponseOverrides.usage\n // in types.ts): cost_details / prompt_tokens_details /\n // completion_tokens_details are objects and is_byok is a boolean.\n // Validate each per its documented type so cost-scripting fixtures\n // are not rejected with a misleading \"must be a number\" error.\n // Known numeric inner scalars per object field (see\n // OpenRouterUsageExtras in types.ts). Validating only these keeps\n // the index-signature ([key: string]: unknown) escape hatch open\n // for forward-compat fields while still catching malformed values\n // on the documented ones — otherwise a bad inner scalar (e.g.\n // cost_details.upstream_inference_cost: \"abc\") passes load-time\n // validation and only blows up at replay in the canonical SDK.\n const numericInnerFields: Record<string, readonly string[]> = {\n cost_details: [\n \"upstream_inference_cost\",\n \"upstream_inference_prompt_cost\",\n \"upstream_inference_completions_cost\",\n ],\n prompt_tokens_details: [\"cached_tokens\", \"cache_write_tokens\", \"audio_tokens\"],\n completion_tokens_details: [\"reasoning_tokens\"],\n };\n const objectUsageFields = new Set(Object.keys(numericInnerFields));\n for (const key of Object.keys(r.usage)) {\n const val = (r.usage as Record<string, unknown>)[key];\n if (val === undefined) continue;\n if (objectUsageFields.has(key)) {\n if (typeof val !== \"object\" || val === null || Array.isArray(val)) {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `override \"usage.${key}\" must be an object, got ${\n Array.isArray(val) ? \"array\" : typeof val\n }`,\n });\n } else {\n // Type-check the documented inner scalars; each is optional\n // (required-if-present), so only validate keys that are set.\n const inner = val as Record<string, unknown>;\n for (const innerKey of numericInnerFields[key]) {\n const innerVal = inner[innerKey];\n if (innerVal !== undefined && typeof innerVal !== \"number\") {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `override \"usage.${key}.${innerKey}\" must be a number, got ${typeof innerVal}`,\n });\n }\n }\n }\n } else if (key === \"is_byok\") {\n if (typeof val !== \"boolean\") {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `override \"usage.is_byok\" must be a boolean, got ${typeof val}`,\n });\n }\n } else if (typeof val !== \"number\") {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `override \"usage.${key}\" must be a number, got ${typeof val}`,\n });\n }\n }\n }\n }\n }\n } // end: skip response-shape validation for function responses\n\n // Numeric sanity checks\n if (f.latency !== undefined && f.latency < 0) {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: \"latency must be >= 0\",\n });\n }\n if (f.chunkSize !== undefined && f.chunkSize < 1) {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: \"chunkSize must be >= 1\",\n });\n }\n if (f.truncateAfterChunks !== undefined && f.truncateAfterChunks < 1) {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: \"truncateAfterChunks must be >= 1\",\n });\n }\n if (f.disconnectAfterMs !== undefined && f.disconnectAfterMs < 0) {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: \"disconnectAfterMs must be >= 0\",\n });\n }\n if (f.streamingProfile !== undefined) {\n const sp = f.streamingProfile;\n if (sp.ttft !== undefined && sp.ttft < 0) {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: \"streamingProfile.ttft must be >= 0\",\n });\n }\n if (sp.tps !== undefined && sp.tps <= 0) {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: \"streamingProfile.tps must be > 0\",\n });\n }\n if (sp.jitter !== undefined && (sp.jitter < 0 || sp.jitter > 1)) {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: \"streamingProfile.jitter must be between 0 and 1\",\n });\n }\n }\n if (f.chaos !== undefined) {\n // EVERY chaos field is routed through the ONE chaos table, so a fixture\n // cannot accept a value the CLI, the header and the runtime reject. The\n // hand-rolled `< 0 || > 1` checks this replaces let `NaN`, `-0` and a\n // numeric string (`dropRate: \"0.5\"`) validate clean, and `latencyMs: 1.5`\n // through; the runtime then rejected the fixture value on every request.\n // A fixture is typed data, like the control API's JSON body: the field\n // must be a number. A numeric STRING is a type error here, not a wire\n // spelling to be parsed — the header API is the surface that takes text.\n const ch = f.chaos as Record<string, unknown>;\n for (const field of CHAOS_FIELD_NAMES) {\n const value = ch[field];\n if (value === undefined) continue;\n const accepted = typeof value === \"number\" ? parseChaosField(field, value) : undefined;\n if (accepted === undefined) {\n const shape = CHAOS_FIELDS[field].integer ? \"a whole number of ms\" : \"a number\";\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `chaos.${field} must be ${shape} between 0 and ${CHAOS_FIELDS[field].max}`,\n });\n }\n }\n }\n\n // Match field type checks\n if (f.match.turnIndex !== undefined) {\n if (\n typeof f.match.turnIndex !== \"number\" ||\n f.match.turnIndex < 0 ||\n !Number.isInteger(f.match.turnIndex)\n ) {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: \"match.turnIndex must be a non-negative integer\",\n });\n }\n }\n if (f.match.sequenceIndex !== undefined) {\n if (\n typeof f.match.sequenceIndex !== \"number\" ||\n f.match.sequenceIndex < 0 ||\n !Number.isInteger(f.match.sequenceIndex)\n ) {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: \"match.sequenceIndex must be a non-negative integer\",\n });\n }\n }\n if (f.match.hasToolResult !== undefined && typeof f.match.hasToolResult !== \"boolean\") {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `match.hasToolResult must be a boolean, got ${typeof f.match.hasToolResult}`,\n });\n }\n if (f.match.toolResultContains !== undefined) {\n if (typeof f.match.toolResultContains !== \"string\") {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `match.toolResultContains must be a string, got ${typeof f.match.toolResultContains}`,\n });\n } else if (f.match.toolResultContains.length === 0) {\n // An empty substring is always contained, so the gate would silently\n // act as \"last message is any tool result\" — reject it as an authoring\n // mistake rather than let it shadow later fixtures.\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: \"match.toolResultContains must be a non-empty string\",\n });\n }\n }\n if (f.match.toolNamespace !== undefined) {\n if (typeof f.match.toolNamespace !== \"string\") {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `match.toolNamespace must be a string, got ${typeof f.match.toolNamespace}`,\n });\n } else if (f.match.toolNamespace.length === 0) {\n // OpenAI's spec gives a namespace tool a non-empty name\n // (openai/openai-openapi openapi.yaml 2.3.0, NamespaceToolParam: `name`\n // has `minLength: 1`), and aimock drops a namespace tool named \"\", so\n // this value never matches. Reject it as an authoring mistake.\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: \"match.toolNamespace must be a non-empty string\",\n });\n }\n }\n if (\n typeof f.match.toolNamespace === \"string\" &&\n f.match.toolNamespace !== \"\" &&\n f.match.endpoint !== undefined &&\n f.match.endpoint !== \"chat\"\n ) {\n // Only the OpenAI Responses adapter attaches tool namespaces. Its\n // requests carry endpoint \"chat\", which every chat-style adapter shares,\n // so any other endpoint never sees a namespace.\n results.push({\n severity: \"warning\",\n fixtureIndex: i,\n message: `match.toolNamespace never matches with endpoint ${JSON.stringify(f.match.endpoint)} — only OpenAI Responses requests (endpoint \"chat\") carry tool namespaces`,\n });\n }\n const customCallPath =\n f.match.endpoint !== undefined && f.match.endpoint !== \"chat\"\n ? firstCustomToolCallPath(f.response)\n : undefined;\n if (customCallPath !== undefined) {\n // No non-\"chat\" endpoint can carry a custom tool call, but the failure\n // differs per wire, so the text names no status or error code.\n results.push({\n severity: \"warning\",\n fixtureIndex: i,\n message: `${customCallPath} is a custom tool call, which only OpenAI Responses requests (endpoint \"chat\") can carry — endpoint ${JSON.stringify(f.match.endpoint)} never serves it, so a request that matches this fixture gets an error instead`,\n });\n }\n if (f.match.systemMessage !== undefined) {\n const sm = f.match.systemMessage;\n if (typeof sm === \"string\") {\n // ok\n } else if (Array.isArray(sm)) {\n if (sm.length === 0) {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `match.systemMessage array must contain at least one substring`,\n });\n } else {\n for (let j = 0; j < sm.length; j++) {\n if (typeof sm[j] !== \"string\") {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `match.systemMessage[${j}] must be a string, got ${typeof sm[j]}`,\n });\n }\n }\n }\n } else {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `match.systemMessage must be a string or string[], got ${typeof sm}`,\n });\n }\n }\n if (f.match.context !== undefined && typeof f.match.context !== \"string\") {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `match.context must be a string, got ${typeof f.match.context}`,\n });\n }\n\n // --- Warning checks ---\n\n // Duplicate userMessage shadowing — two fixtures are only genuine\n // duplicates when they would match the SAME requests, so the dedup key must\n // include EVERY match discriminator the router (matchFixtureDiagnostic in\n // router.ts) actually gates on: userMessage, systemMessage, inputText,\n // toolCallId, toolResultContains, toolName, model, responseFormat, endpoint,\n // context, sequenceIndex, turnIndex, and hasToolResult. Omitting any of these\n // (the old key only carried turnIndex/hasToolResult/toolResultContains/\n // sequenceIndex/context) flags two legitimately-distinct fixtures — e.g. two\n // that differ ONLY in toolCallId or model — as false duplicates.\n //\n // `predicate` is a function and cannot be compared for equivalence, so it is\n // never folded into a shared key: a fixture carrying a predicate is treated\n // as unconditionally distinct (keyed by its own index) so it neither shadows\n // nor is shadowed by another fixture on the userMessage axis.\n //\n // Values are serialised kind-aware so RegExp / string[] matchers do not\n // collide (a template literal would coerce a RegExp to its source and an\n // array via join, losing the distinction) — mirroring describeMatch in\n // router.ts.\n const um = f.match.userMessage;\n if (typeof um === \"string\" && um) {\n const m = f.match;\n const dedupKey =\n m.predicate !== undefined\n ? `predicate:${i}`\n : [\n serializeMatcher(m.userMessage),\n serializeMatcher(m.systemMessage),\n serializeMatcher(m.inputText),\n m.toolCallId,\n m.toolResultContains,\n m.toolName,\n serializeMatcher(m.model),\n m.responseFormat,\n m.endpoint,\n m.context,\n m.sequenceIndex,\n m.turnIndex,\n m.hasToolResult,\n // Appended only when set, so every key without it is unchanged.\n ...(m.toolNamespace !== undefined ? [`toolNamespace=${m.toolNamespace}`] : []),\n ]\n .map((v) => (v === undefined ? \"\" : String(v)))\n .join(\"|\");\n const prev = seenUserMessages.get(dedupKey);\n if (prev !== undefined) {\n const ref: ValidationRef = {\n rule: \"duplicate-user-message\",\n userMessage: um,\n shadows: prev,\n };\n results.push({\n severity: \"warning\",\n fixtureIndex: i,\n message: renderValidationRef(ref, String),\n ref,\n });\n } else {\n seenUserMessages.set(dedupKey, i);\n }\n }\n\n // Catch-all not in last position\n const match = f.match;\n const hasDiscriminator =\n match.endpoint !== undefined ||\n match.context !== undefined ||\n match.userMessage !== undefined ||\n match.systemMessage !== undefined ||\n match.inputText !== undefined ||\n match.responseFormat !== undefined ||\n match.toolCallId !== undefined ||\n match.toolResultContains !== undefined ||\n match.toolName !== undefined ||\n match.toolNamespace !== undefined ||\n match.model !== undefined ||\n match.predicate !== undefined ||\n match.turnIndex !== undefined ||\n match.sequenceIndex !== undefined ||\n match.hasToolResult !== undefined;\n\n if (!hasDiscriminator && i < fixtures.length - 1) {\n const ref: ValidationRef = {\n rule: \"catch-all-not-last\",\n shadowsFrom: i + 1,\n shadowsThrough: fixtures.length - 1,\n };\n results.push({\n severity: \"warning\",\n fixtureIndex: i,\n message: renderValidationRef(ref, String),\n ref,\n });\n }\n }\n\n return results;\n}\n\n// ---------------------------------------------------------------------------\n// FixtureLoadError — the error type for fail-loud fixture-load cases\n// ---------------------------------------------------------------------------\n\n/**\n * The `rule` values owned by MCP fakes (prefix `mcp-fakes/`). The\n * bad-block letters: (a) no scope, (b)\n * malformed scope, (c) shared + deny, (d) bad call entry, (e) duplicate tool,\n * (f) entry-id or block-id collision, (g) bad block/tool shape, (h) unknown\n * key on a block, tool or call entry, (i) mcpFakes, or one of its blocks, has\n * the wrong JSON type, mcpFakes holds no blocks, or an add has an invalid\n * origin or source item, (j) anyArgs not `true`.\n */\nexport const MCP_FAKES_LOAD_RULES = [\n \"mcp-fakes/bad-block:a\",\n \"mcp-fakes/bad-block:b\",\n \"mcp-fakes/bad-block:c\",\n \"mcp-fakes/bad-block:d\",\n \"mcp-fakes/bad-block:e\",\n \"mcp-fakes/bad-block:f\",\n \"mcp-fakes/bad-block:g\",\n \"mcp-fakes/bad-block:h\",\n \"mcp-fakes/bad-block:i\",\n \"mcp-fakes/bad-block:j\",\n \"mcp-fakes/mount-conflict\",\n \"mcp-fakes/loader-cannot-carry-fakes\",\n \"mcp-fakes/watch-reload-changed\",\n] as const;\n\nexport type McpFakesLoadRule = (typeof MCP_FAKES_LOAD_RULES)[number];\n\n/**\n * Every known `FixtureLoadError` rule, by prefix. A feature that throws\n * `FixtureLoadError` adds its own prefix here, with its rule values as a\n * union of string literals; a module can add one by declaration merging.\n */\nexport interface FixtureLoadRuleRegistry {\n \"mcp-fakes\": McpFakesLoadRule;\n}\n\n/**\n * A known `FixtureLoadError` rule, namespaced `<prefix>/<name>`. Each\n * registered value must start with its own prefix and `/` (any other value\n * drops out of the union), so a misspelled or unregistered rule fails to\n * compile.\n */\nexport type FixtureLoadRule = {\n [P in keyof FixtureLoadRuleRegistry]: FixtureLoadRuleRegistry[P] & `${P}/${string}`;\n}[keyof FixtureLoadRuleRegistry];\n\nexport interface FixtureLoadErrorInit {\n /** Namespaced `<prefix>/<name>`, one of {@link FixtureLoadRuleRegistry}. */\n rule: FixtureLoadRule;\n /** Source of the offending input (path, URL, `control-api#<n>`, `code#<n>`), or null. */\n file: string | null;\n /** `<source><load>[<block>]` of the offending block; omit or null when not tied to a block. */\n blockId?: string | null;\n /**\n * Entry id when one entry caused it (bad-block cases d, f, j, and h on a\n * call entry); omit or null otherwise.\n */\n entryId?: string | null;\n /**\n * The offending key or value, in words: text, or a `Message` from the\n * message builder (message-text.ts), whose user parts are quoted and cut\n * once. Cut to `MCP_FAKES_ECHO_LIMIT` chars in the message (see `capPart`).\n */\n detail: string | Message;\n}\n\nexport interface FixtureLoadErrorJSON {\n /** `\"FixtureLoadError\"`, or a subclass's own name (e.g. `\"McpFakesAddError\"`). */\n name: string;\n rule: string;\n file: string | null;\n blockId?: string | null;\n entryId?: string | null;\n message: string;\n}\n\n/**\n * `text` with every run of line breaks, and the spaces around it, folded to\n * one space: CR, LF, VT, FF, NEL, and the Unicode line and paragraph\n * separators.\n */\nfunction oneLine(text: string): string {\n return text.replace(/\\s*[\\r\\n\\v\\f\\u0085\\u2028\\u2029]+\\s*/g, \" \");\n}\n\n/**\n * The `detail` as a `FixtureLoadError` message adds it: built in a budget of\n * its own, cut to at most `MCP_FAKES_ECHO_LIMIT` chars in all, with each of\n * its user parts cut once. A text detail is folded onto one line, gets the\n * escaping of every JSON text part (`jsonText`: each control char left, C0,\n * DEL or C1, and the line and paragraph separators as `\\uXXXX`) and is cut\n * as JSON text (it is\n * made of fixed text, paths and quoted values, where a backslash only starts\n * a JSON escape; a stray one only moves the cut back a few chars).\n */\nfunction capPart(detail: string | Message): Message {\n const message = typeof detail === \"string\" ? msg`${jsonText(oneLine(detail))}` : detail;\n return within(MCP_FAKES_ECHO_LIMIT, message);\n}\n\n/**\n * One `file`, `blockId` or `entryId` as the message names it: JSON-quoted\n * (control chars escaped, so a name can neither break the line nor pose as\n * the message layout), and cut once to `MCP_FAKES_ECHO_LIMIT` chars at most.\n * An empty string is `\"\"`, so the message names every part `toJSON()` keeps.\n */\nfunction namePart(text: string): Message {\n return msg`${quote(text)}`;\n}\n\n/**\n * The message of a `FixtureLoadError` or `MisbehaviorConfigError`: the\n * `file`, every non-null `blockId` and `entryId`, then `[rule] detail`.\n */\nfunction loadErrorMessage(init: {\n rule: string;\n file: string | null;\n blockId?: string | null;\n entryId?: string | null;\n detail: string | Message;\n}): string {\n const file = init.file === null ? msg`<no file>` : namePart(init.file);\n const block = init.blockId == null ? msg`` : msg`, block ${namePart(init.blockId)}`;\n const entry = init.entryId == null ? msg`` : msg`, entry ${namePart(init.entryId)}`;\n return oneLine(\n build(msg`${file}${block}${entry}: [${fixed(init.rule)}] ${capPart(init.detail)}`),\n );\n}\n\n/**\n * Thrown for a fixture-load case aimock cannot honor (fail-loud rule). Only\n * MCP fakes use it (misbehavior throws its own `MisbehaviorConfigError`);\n * other fixture-loader paths log warnings.\n * `blockId` and `entryId` are own properties only when supplied, so\n * `toJSON()` (the control API's HTTP 400 `details`) omits them otherwise.\n * The message names `file` and every non-null `blockId` and `entryId`, an\n * empty string as `\"\"`, so it agrees with `toJSON()`. `options.cause` (the\n * standard `ErrorOptions`) keeps the underlying error, such as a JSON parse\n * failure; `toJSON()` leaves it out.\n */\nexport class FixtureLoadError extends Error {\n /** Typed `string` so a subclass can set its own name. */\n override readonly name: string = \"FixtureLoadError\";\n readonly rule: FixtureLoadRule;\n readonly file: string | null;\n declare readonly blockId?: string | null;\n declare readonly entryId?: string | null;\n\n constructor(init: FixtureLoadErrorInit, options?: ErrorOptions) {\n super(loadErrorMessage(init), options);\n this.rule = init.rule;\n this.file = init.file;\n if (init.blockId !== undefined) {\n Object.defineProperty(this, \"blockId\", {\n value: init.blockId,\n enumerable: true,\n writable: false,\n });\n }\n if (init.entryId !== undefined) {\n Object.defineProperty(this, \"entryId\", {\n value: init.entryId,\n enumerable: true,\n writable: false,\n });\n }\n }\n\n toJSON(): FixtureLoadErrorJSON {\n const json: FixtureLoadErrorJSON = {\n name: this.name,\n rule: this.rule,\n file: this.file,\n message: this.message,\n };\n if (this.blockId !== undefined) json.blockId = this.blockId;\n if (this.entryId !== undefined) json.entryId = this.entryId;\n return json;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Misbehavior in fixtures: recognized only when misbehavior is enabled\n// ---------------------------------------------------------------------------\n\nexport interface MisbehaviorConfigErrorJSON {\n /** `\"MisbehaviorConfigError\"`. */\n name: string;\n rule: string;\n file: string | null;\n message: string;\n}\n\n/**\n * Thrown for an invalid fixture `misbehavior` key, or one that cannot apply\n * to its fixture, on a server with misbehavior enabled (`enableMisbehavior:\n * true`, CLI `--misbehavior`). Without that opt-in the key is unused data\n * and nothing throws. The message has the `FixtureLoadError` layout:\n * `\"<file>\": [<rule>] <detail>`.\n */\nexport class MisbehaviorConfigError extends Error {\n override readonly name: string = \"MisbehaviorConfigError\";\n readonly rule: MisbehaviorRule;\n readonly file: string | null;\n\n constructor(init: { rule: MisbehaviorRule; file: string | null; detail: string }) {\n super(loadErrorMessage(init));\n this.rule = init.rule;\n this.file = init.file;\n }\n\n toJSON(): MisbehaviorConfigErrorJSON {\n return { name: this.name, rule: this.rule, file: this.file, message: this.message };\n }\n}\n\n/** A fixture entry's `misbehavior` value, held until misbehavior is enabled. */\ninterface HeldMisbehavior {\n value: unknown;\n /** Path for messages, e.g. `fixtures[2].misbehavior`. */\n path: string;\n file: string | null;\n}\n\nconst heldFixtureMisbehavior = new WeakMap<Fixture, HeldMisbehavior>();\nconst responsesToolsExtendedFixtures = new WeakSet<Fixture>();\n\n/** The keys a loaded fixture holds until a server with responsesTools \"extended\" reads them. */\ninterface HeldResponsesTools {\n /** `match.toolNamespace` as written, when the entry set it. */\n toolNamespace?: unknown;\n /** Whether the response carries `customToolCalls` / `responsesBlocks` to normalize. */\n normalize: boolean;\n}\n\nconst heldResponsesTools = new WeakMap<Fixture, HeldResponsesTools>();\n\n/**\n * @internal Mark a fixture for a server with `responsesTools: \"extended\"`,\n * where that server made the fixture itself (its own load, `--validate`, the\n * control API): the held `match.toolNamespace` is applied and\n * `customToolCalls` / `responsesBlocks` are normalized in place, and\n * `validateFixtures` checks those keys. Idempotent. A fixture the caller\n * passed in is never marked: an extended server reads it through\n * {@link responsesToolsExtendedView}, so another server sharing it still sees\n * it as 1.44.0 loaded it.\n */\nexport function markFixtureResponsesToolsExtended(fixture: Fixture): void {\n responsesToolsExtendedFixtures.add(fixture);\n const held = heldResponsesTools.get(fixture);\n if (held === undefined) return;\n heldResponsesTools.delete(fixture);\n if (held.toolNamespace !== undefined) {\n fixture.match = withHeldToolNamespace(fixture.match, held.toolNamespace);\n }\n if (held.normalize) {\n normalizeResponsesToolsKeys(fixture.response as unknown as Record<string, unknown>);\n }\n}\n\n/**\n * `match` with `toolNamespace` after `toolName`, in the same key order the\n * listing and journal have always shown in extended mode.\n */\nfunction withHeldToolNamespace(match: FixtureMatch, toolNamespace: unknown): FixtureMatch {\n const rebuilt: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(match)) {\n rebuilt[key] = value;\n if (key === \"toolName\") rebuilt.toolNamespace = toolNamespace;\n }\n if (!(\"toolNamespace\" in rebuilt)) rebuilt.toolNamespace = toolNamespace;\n return rebuilt as FixtureMatch;\n}\n\n/**\n * The extended view of one held fixture. `match` and `response` record the\n * fixture's values the view was built from, so it is rebuilt only when the\n * fixture's own `match` or `response` is replaced.\n */\ninterface ExtendedView {\n view: Fixture;\n match?: FixtureMatch;\n viewMatch?: FixtureMatch;\n response?: Fixture[\"response\"];\n viewResponse?: Fixture[\"response\"];\n}\n\nconst extendedViews = new WeakMap<Fixture, ExtendedView>();\n/** Each extended view's fixture, so identity bookkeeping can name the caller's object. */\nconst extendedViewSources = new WeakMap<Fixture, Fixture>();\n\n/**\n * @internal The fixture an extended view was made from\n * ({@link responsesToolsExtendedView}); any other fixture is itself. An\n * extended server counts and journals the caller's fixture, not its view, so\n * the identity APIs (`getFixtureMatchCount`, `findByFixture`, the count-map\n * keys, `JournalEntry.response.fixture`) take the object the caller passed in.\n */\nexport function responsesToolsViewSource(fixture: Fixture): Fixture {\n return extendedViewSources.get(fixture) ?? fixture;\n}\n\n/**\n * @internal The fixture as a server with `responsesTools: \"extended\"` reads\n * it. A fixture with held keys gets a separate object, one per fixture, with\n * `match.toolNamespace` applied and `customToolCalls` / `responsesBlocks`\n * normalized, as {@link markFixtureResponsesToolsExtended} would leave it.\n * The fixture itself is not changed, so a legacy server that shares it is\n * unaffected. Every other property is read from the fixture on each call.\n * A fixture with nothing held is returned as it is.\n */\nexport function responsesToolsExtendedView(fixture: Fixture): Fixture {\n const held = heldResponsesTools.get(fixture);\n if (held === undefined) return fixture;\n let cached = extendedViews.get(fixture);\n if (cached === undefined) {\n cached = { view: {} as Fixture };\n responsesToolsExtendedFixtures.add(cached.view);\n extendedViewSources.set(cached.view, fixture);\n extendedViews.set(fixture, cached);\n }\n if (cached.viewMatch === undefined || cached.match !== fixture.match) {\n cached.match = fixture.match;\n cached.viewMatch =\n held.toolNamespace !== undefined\n ? withHeldToolNamespace(fixture.match, held.toolNamespace)\n : fixture.match;\n }\n if (!(\"viewResponse\" in cached) || cached.response !== fixture.response) {\n cached.response = fixture.response;\n let viewResponse = fixture.response;\n if (held.normalize && typeof viewResponse === \"object\" && viewResponse !== null) {\n const copy = { ...(viewResponse as unknown as Record<string, unknown>) };\n normalizeResponsesToolsKeys(copy);\n viewResponse = copy as unknown as FixtureResponse;\n }\n cached.viewResponse = viewResponse;\n }\n const view = cached.view as unknown as Record<string, unknown>;\n const source = fixture as unknown as Record<string, unknown>;\n for (const key of Object.keys(view)) if (!(key in source)) delete view[key];\n for (const key of Object.keys(source)) {\n view[key] =\n key === \"match\" ? cached.viewMatch : key === \"response\" ? cached.viewResponse : source[key];\n }\n copyFixtureMisbehaviorPosition(fixture, cached.view);\n return cached.view;\n}\n\n/**\n * The fixture as 1.44.0 saw it: without `match.toolNamespace`,\n * `customToolCalls` and `responsesBlocks`. Returns the same object when none\n * is set; factory responses are left as they are.\n */\nfunction withoutResponsesToolsKeys(fixture: Fixture): Fixture {\n let match = fixture.match;\n if (\"toolNamespace\" in match) {\n const copy = { ...match };\n delete copy.toolNamespace;\n match = copy;\n }\n let response = fixture.response;\n if (\n typeof response !== \"function\" &&\n response !== null &&\n typeof response === \"object\" &&\n (\"customToolCalls\" in response || \"responsesBlocks\" in response)\n ) {\n const copy = { ...(response as unknown as Record<string, unknown>) };\n delete copy.customToolCalls;\n delete copy.responsesBlocks;\n response = copy as unknown as FixtureResponse;\n }\n if (match === fixture.match && response === fixture.response) return fixture;\n return { ...fixture, match, response };\n}\nconst misbehaviorEnabledFixtures = new WeakSet<Fixture>();\n\n/**\n * @internal Recognize the `misbehavior` key a loaded fixture entry carried,\n * for a server with misbehavior enabled: parse it onto the fixture (in place;\n * the loader made the object) and check it applies, or throw\n * `MisbehaviorConfigError`. A fixture with no held key is left unchanged.\n */\nexport function enableHeldFixtureMisbehavior(fixture: Fixture): void {\n const held = heldFixtureMisbehavior.get(fixture);\n if (held === undefined) return;\n const parsed = parseMisbehavior(held.value, held.path);\n if (parsed.ok) fixture.misbehavior = parsed.config;\n const issue = parsed.ok ? validateFixtureMisbehavior(fixture, held.path) : parsed.issue;\n if (issue) {\n throw new MisbehaviorConfigError({ rule: issue.rule, file: held.file, detail: issue.message });\n }\n heldFixtureMisbehavior.delete(fixture);\n misbehaviorEnabledFixtures.add(fixture);\n}\n\n/**\n * @internal Mark a fixture whose `misbehavior` a server with misbehavior\n * enabled has recognized, so `validateFixtures` checks it.\n */\nexport function markFixtureMisbehaviorEnabled(fixture: Fixture): void {\n misbehaviorEnabledFixtures.add(fixture);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"fixture-loader.js","names":[],"sources":["../src/fixture-loader.ts"],"sourcesContent":["import type { LiveOptions } from \"./live-types.js\";\nimport { normalizeLiveFixture } from \"./live-fixture.js\";\nimport { readFileSync, readdirSync, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type {\n ChatCompletionRequest,\n Fixture,\n FixtureFile,\n FixtureFileEntry,\n FixtureFileResponse,\n FixtureMatch,\n FixtureResponse,\n ResponseOverrides,\n} from \"./types.js\";\nimport {\n isLiveResponse,\n isTextResponse,\n isToolCallResponse,\n isCombinedFixtureResponse,\n isErrorResponse,\n isEmbeddingResponse,\n isImageResponse,\n isAudioResponse,\n isTranscriptionResponse,\n isVideoResponse,\n isJSONResponse,\n fixtureListTarget,\n} from \"./helpers.js\";\nimport type { Logger } from \"./logger.js\";\nimport { CHAOS_FIELDS, CHAOS_FIELD_NAMES, parseChaosField } from \"./chaos.js\";\nimport { Message, build, fixed, jsonText, msg, quote, within } from \"./message-text.js\";\nimport { MCP_FAKES_ECHO_LIMIT } from \"./constants.js\";\nimport { getUsageFieldDefinition } from \"./response/usage/fields.js\";\nimport {\n copyFixtureMisbehaviorPosition,\n parseMisbehavior,\n setFixtureMisbehaviorPosition,\n validateFixtureMisbehavior,\n type MisbehaviorRule,\n} from \"./misbehavior.js\";\n\n/**\n * Auto-stringify object-valued `content` and `toolCalls[].arguments` fields.\n * This lets fixture authors write plain JSON objects instead of escaped strings.\n * All other fields (including ResponseOverrides, and the ErrorResponse\n * `fallthrough` OpenRouter-failover flag) pass through unmodified via the\n * shallow clone below.\n */\nexport function normalizeResponse(\n raw: FixtureFileResponse,\n liveOptions?: LiveOptions,\n): FixtureResponse {\n if (isLiveResponse(raw)) return normalizeLiveFixture(raw, liveOptions);\n // Shallow-clone so we don't mutate the parsed JSON input.\n const response = { ...raw } as Record<string, unknown>;\n\n // Auto-stringify object content (e.g. structured output)\n if (typeof response.content === \"object\" && response.content !== null) {\n response.content = JSON.stringify(response.content);\n }\n\n // Auto-stringify object arguments in toolCalls\n if (Array.isArray(response.toolCalls)) {\n response.toolCalls = (response.toolCalls as Array<Record<string, unknown>>).map((tc) => {\n if (typeof tc.arguments === \"object\" && tc.arguments !== null) {\n return { ...tc, arguments: JSON.stringify(tc.arguments) };\n }\n return tc;\n });\n }\n\n // Carry the optional ordered `blocks` array through, mirroring the\n // toolCalls[].arguments idiom above: auto-stringify object `arguments` on\n // each `toolCall` block. Gated on Array.isArray so a malformed (non-array)\n // `blocks` value passes through untouched rather than crashing — downstream\n // validation/builders own shape rejection. Absent `blocks` → key absent.\n if (Array.isArray(response.blocks)) {\n response.blocks = (response.blocks as Array<Record<string, unknown>>).map((block) => {\n if (\n block != null &&\n block.type === \"toolCall\" &&\n typeof block.arguments === \"object\" &&\n block.arguments !== null\n ) {\n return { ...block, arguments: JSON.stringify(block.arguments) };\n }\n return block;\n });\n }\n\n return response as unknown as FixtureResponse;\n}\n\n/**\n * @internal Normalize `responsesBlocks` and `customToolCalls` in place, for a\n * server with `responsesTools: \"extended\"`: stringify object `arguments` on\n * `toolCall` blocks, and give a `customToolCalls` entry with no `type` the\n * type `\"custom\"`. A custom call's free-text `input` is never stringified.\n * {@link normalizeResponse} leaves both keys as written, as 1.44.0 did.\n */\nexport function normalizeResponsesToolsKeys(response: Record<string, unknown>): void {\n if (Array.isArray(response.responsesBlocks)) {\n response.responsesBlocks = (response.responsesBlocks as unknown[]).map((block) => {\n const b = block as Record<string, unknown> | null;\n if (\n b !== null &&\n typeof b === \"object\" &&\n b.type === \"toolCall\" &&\n typeof b.arguments === \"object\" &&\n b.arguments !== null\n ) {\n return { ...b, arguments: JSON.stringify(b.arguments) };\n }\n return block;\n });\n }\n if (Array.isArray(response.customToolCalls)) {\n response.customToolCalls = (response.customToolCalls as unknown[]).map((tc) => {\n const entry = tc as Record<string, unknown> | null;\n return entry !== null && typeof entry === \"object\" && entry.type === undefined\n ? { ...entry, type: \"custom\" }\n : tc;\n });\n }\n}\n\nclass InvalidFixtureMatchError extends TypeError {}\n\nexport function entryToFixture(\n entry: FixtureFileEntry,\n logger?: Logger,\n liveOptions?: LiveOptions,\n source?: { file: string; index: number },\n): Fixture {\n if (entry === null || typeof entry !== \"object\" || Array.isArray(entry)) {\n throw new TypeError(\"Fixture entry must be an object\");\n }\n if (entry.match == null) {\n throw new TypeError(\"Fixture match must be an object\");\n }\n if (typeof entry.match !== \"object\" || Array.isArray(entry.match)) {\n throw new InvalidFixtureMatchError(\"Fixture match must be an object\");\n }\n const fixture: Fixture = {\n match: {\n userMessage: entry.match.userMessage,\n systemMessage: entry.match.systemMessage,\n inputText: entry.match.inputText,\n ...(entry.match.embeddingRequest !== undefined && {\n embeddingRequest: entry.match.embeddingRequest,\n }),\n toolCallId: entry.match.toolCallId,\n toolResultContains: entry.match.toolResultContains,\n toolName: entry.match.toolName,\n model: entry.match.model,\n responseFormat: entry.match.responseFormat,\n endpoint: entry.match.endpoint,\n ...(entry.match.sequenceIndex !== undefined && { sequenceIndex: entry.match.sequenceIndex }),\n ...(entry.match.turnIndex !== undefined && {\n turnIndex: entry.match.turnIndex,\n }),\n ...(entry.match.hasToolResult !== undefined && {\n hasToolResult: entry.match.hasToolResult,\n }),\n ...(entry.match.context !== undefined && { context: entry.match.context }),\n },\n response:\n entry.match.endpoint === \"openai-live\"\n ? normalizeLiveFixture(entry.response, liveOptions)\n : normalizeResponse(entry.response, liveOptions),\n ...(entry.latency !== undefined && { latency: entry.latency }),\n ...(entry.chunkSize !== undefined && { chunkSize: entry.chunkSize }),\n ...(entry.truncateAfterChunks !== undefined && {\n truncateAfterChunks: entry.truncateAfterChunks,\n }),\n ...(entry.disconnectAfterMs !== undefined && { disconnectAfterMs: entry.disconnectAfterMs }),\n ...(entry.streamingProfile !== undefined && { streamingProfile: entry.streamingProfile }),\n ...(entry.recordedTimings !== undefined && { recordedTimings: entry.recordedTimings }),\n ...(entry.replaySpeed != null && { replaySpeed: entry.replaySpeed }),\n ...(entry.chaos !== undefined && { chaos: entry.chaos }),\n ...(entry.openRouterProcessing !== undefined && {\n openRouterProcessing: entry.openRouterProcessing,\n }),\n ...(entry.nonStreamingTiming !== undefined && {\n nonStreamingTiming: entry.nonStreamingTiming,\n }),\n ...(entry.metadata !== undefined && { metadata: entry.metadata }),\n };\n\n if (source) setFixtureMisbehaviorPosition(fixture, `${source.file}#${source.index}`);\n\n // `match.toolNamespace`, `customToolCalls` and `responsesBlocks` are held,\n // not read: 1.44.0 dropped the first and kept the others as written, so the\n // fixture looks exactly as it did then. Only a server with responsesTools\n // \"extended\" applies them (markFixtureResponsesToolsExtended).\n const heldToolNamespace = entry.match.toolNamespace !== undefined;\n const response: unknown = fixture.response;\n const heldResponseKeys =\n entry.match.endpoint !== \"openai-live\" &&\n !isLiveResponse(entry.response) &&\n typeof response === \"object\" &&\n response !== null &&\n (\"customToolCalls\" in response || \"responsesBlocks\" in response);\n if (heldToolNamespace || heldResponseKeys) {\n heldResponsesTools.set(fixture, {\n ...(heldToolNamespace && { toolNamespace: entry.match.toolNamespace }),\n normalize: heldResponseKeys,\n });\n }\n\n // A `misbehavior` key is held, not read: only a server with misbehavior\n // enabled recognizes it (enableHeldFixtureMisbehavior). Otherwise it is\n // unused data, as in 1.44.0.\n if (entry.misbehavior !== undefined) {\n heldFixtureMisbehavior.set(fixture, {\n value: entry.misbehavior,\n path: source ? `fixtures[${source.index}].misbehavior` : \"misbehavior\",\n file: source?.file ?? null,\n });\n }\n\n // Sanitize recordedTimings to guard against NaN or negative values that\n // would silently degrade replay timing calculations.\n if (fixture.recordedTimings) {\n const rt = fixture.recordedTimings;\n if (!Number.isFinite(rt.ttftMs) || rt.ttftMs < 0) rt.ttftMs = 0;\n rt.interChunkDelaysMs = Array.isArray(rt.interChunkDelaysMs)\n ? rt.interChunkDelaysMs.filter((d) => Number.isFinite(d) && d >= 0)\n : [];\n if (!Number.isFinite(rt.totalDurationMs) || rt.totalDurationMs < 0) rt.totalDurationMs = 0;\n }\n\n if (fixture.replaySpeed != null && fixture.replaySpeed <= 0) {\n logger?.warn(`Fixture replaySpeed must be positive, got ${fixture.replaySpeed}. Ignoring.`);\n delete fixture.replaySpeed;\n }\n\n return fixture;\n}\n\n/**\n * Serialise a match value (string | string[] | RegExp | undefined) into a\n * dedup-key fragment that is kind-aware, so a string, a RegExp with the same\n * source, and an array containing that string do not collide. A plain template\n * literal would coerce `/foo/` and `\"foo\"` to the same text and join `[\"foo\"]`\n * to `\"foo\"`, wrongly deduping fixtures that the router would treat as distinct.\n * Used by the duplicate-userMessage dedup key in validateFixtures.\n */\nfunction serializeMatcher(value: string | string[] | RegExp | undefined): string {\n if (value === undefined) return \"\";\n if (value instanceof RegExp) return `re:${value.source}\\u0000${value.flags}`;\n if (Array.isArray(value)) return `arr:${JSON.stringify(value)}`;\n return `str:${value}`;\n}\n\n// Logging helper — uses logger if provided, falls back to console.warn.\nfunction warn(logger: Logger | undefined, msg: string, ...rest: unknown[]): void {\n if (logger) {\n logger.warn(msg, ...rest);\n } else {\n console.warn(`[fixture-loader] ${msg}`, ...rest);\n }\n}\n\n/**\n * A parsed JSON value, or `null` when the file could not be read or parsed (warned).\n *\n * @internal Shared with fixture-loader-services.ts.\n */\nexport function readFixtureJson(\n filePath: string,\n logger: Logger | undefined,\n): { value: unknown } | null {\n let raw: string;\n try {\n raw = readFileSync(filePath, \"utf-8\");\n } catch (err) {\n warn(logger, `Could not read file ${filePath}:`, err);\n return null;\n }\n\n try {\n return { value: JSON.parse(raw) };\n } catch (err) {\n warn(logger, `Invalid JSON in ${filePath}:`, err);\n return null;\n }\n}\n\n/**\n * `parsed` is a JSON object with an own top-level `mcpFakes` key.\n *\n * @internal Shared with fixture-loader-services.ts.\n */\nexport function hasMcpFakesKey(parsed: unknown): parsed is Record<string, unknown> {\n return (\n typeof parsed === \"object\" &&\n parsed !== null &&\n !Array.isArray(parsed) &&\n Object.prototype.hasOwnProperty.call(parsed, \"mcpFakes\")\n );\n}\n\n/**\n * The LLM fixtures of a parsed file. A missing or non-array `fixtures` is\n * warned and gives none, except that a file with `mcpFakes` may leave\n * `fixtures` out (`fixturesOptional`, spec 5.4). An entry whose `match` is\n * not an object is skipped with a warning; any other bad entry throws.\n *\n * @internal Shared with fixture-loader-services.ts.\n */\nexport function fixturesOf(\n parsed: unknown,\n filePath: string,\n logger: Logger | undefined,\n liveOptions: LiveOptions | undefined,\n fixturesOptional: boolean,\n source = filePath,\n): Fixture[] {\n if (\n fixturesOptional &&\n typeof parsed === \"object\" &&\n parsed !== null &&\n !Object.prototype.hasOwnProperty.call(parsed, \"fixtures\")\n ) {\n return [];\n }\n if (\n typeof parsed !== \"object\" ||\n parsed === null ||\n !Array.isArray((parsed as FixtureFile).fixtures)\n ) {\n warn(logger, `Missing or invalid \"fixtures\" array in ${filePath}`);\n return [];\n }\n\n const fixtures: Fixture[] = [];\n for (const [index, entry] of (parsed as FixtureFile).fixtures.entries()) {\n try {\n fixtures.push(entryToFixture(entry, logger, liveOptions, { file: source, index }));\n } catch (error) {\n if (!(error instanceof InvalidFixtureMatchError)) throw error;\n warn(logger, `Skipping fixture at index ${index} in ${filePath}: ${error.message}`);\n }\n }\n return fixtures;\n}\n\n/**\n * B13: the old loaders return LLM fixtures only, so a file with `mcpFakes`\n * fails loud instead of losing its fakes.\n */\nfunction cannotCarryFakes(filePath: string): FixtureLoadError {\n return new FixtureLoadError({\n rule: \"mcp-fakes/loader-cannot-carry-fakes\",\n file: filePath,\n blockId: null,\n entryId: null,\n detail: msg`top-level \"mcpFakes\" key: loadFixtureFile and loadFixturesFromDir cannot carry MCP fakes; use loadFixtureFileWithServices or loadFixturesFromDirWithServices, or LLMock.loadFixtureFile or LLMock.loadFixtureDir`,\n });\n}\n\n/**\n * Load the LLM fixtures of one file. Throws a `FixtureLoadError` (rule\n * `mcp-fakes/loader-cannot-carry-fakes`) when the file has a top-level\n * `mcpFakes` key; use `loadFixtureFileWithServices` (fixture-loader-services.ts) for such files.\n */\nexport function loadFixtureFile(\n filePath: string,\n logger?: Logger,\n liveOptions?: LiveOptions,\n): Fixture[] {\n return loadLlmFixtureFile(filePath, filePath, logger, liveOptions);\n}\n\nfunction loadLlmFixtureFile(\n filePath: string,\n source: string,\n logger: Logger | undefined,\n liveOptions: LiveOptions | undefined,\n): Fixture[] {\n const read = readFixtureJson(filePath, logger);\n if (read === null) return [];\n if (hasMcpFakesKey(read.value)) throw cannotCarryFakes(filePath);\n return fixturesOf(read.value, filePath, logger, liveOptions, false, source);\n}\n\n/**\n * The `.json` files (sorted) and the subdirectories (sorted) of `dirPath`,\n * or `null` when it cannot be read (warned).\n *\n * @internal Shared with fixture-loader-services.ts.\n */\nexport function listFixtureDir(\n dirPath: string,\n logger: Logger | undefined,\n): { jsonFiles: string[]; subdirs: string[] } | null {\n let entries: string[];\n try {\n entries = readdirSync(dirPath);\n } catch (err) {\n warn(logger, `Could not read directory ${dirPath}:`, err);\n return null;\n }\n\n const jsonFiles: string[] = [];\n const subdirs: string[] = [];\n for (const name of entries) {\n const fullPath = join(dirPath, name);\n try {\n if (statSync(fullPath).isDirectory()) {\n subdirs.push(name);\n continue;\n }\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code !== \"ENOENT\") {\n warn(logger, `Could not stat ${fullPath}:`, err);\n }\n continue;\n }\n if (name.endsWith(\".json\")) {\n jsonFiles.push(name);\n }\n }\n jsonFiles.sort();\n subdirs.sort();\n return { jsonFiles, subdirs };\n}\n\n/**\n * Load the LLM fixtures of every `.json` file under `dirPath`. Throws a\n * `FixtureLoadError` (rule `mcp-fakes/loader-cannot-carry-fakes`) on the\n * first file with a top-level `mcpFakes` key; use\n * `loadFixturesFromDirWithServices` (fixture-loader-services.ts) for such\n * directories.\n */\nexport function loadFixturesFromDir(\n dirPath: string,\n logger?: Logger,\n liveOptions?: LiveOptions,\n): Fixture[] {\n return loadLlmFixtureDir(dirPath, \"\", logger, liveOptions);\n}\n\nfunction loadLlmFixtureDir(\n dirPath: string,\n relDir: string,\n logger: Logger | undefined,\n liveOptions: LiveOptions | undefined,\n): Fixture[] {\n const listing = listFixtureDir(dirPath, logger);\n if (listing === null) return [];\n\n const fixtures: Fixture[] = [];\n for (const name of listing.jsonFiles) {\n const filePath = join(dirPath, name);\n fixtures.push(...loadLlmFixtureFile(filePath, relDir + name, logger, liveOptions));\n }\n\n // Recurse into all subdirectories (full depth) to support nested layouts\n // like showcase/aimock/d6/<integration>/<feature>.json.\n for (const sub of listing.subdirs) {\n fixtures.push(\n ...loadLlmFixtureDir(join(dirPath, sub), `${relDir}${sub}/`, logger, liveOptions),\n );\n }\n\n return fixtures;\n}\n\n// ---------------------------------------------------------------------------\n// Fixture validation\n// ---------------------------------------------------------------------------\n\n/**\n * The fixture indices a finding's message NAMES, carried as data.\n *\n * Two rules (`duplicate userMessage` and the catch-all ordering check) name\n * OTHER fixtures in the array they were given, so their message text embeds an\n * index. A consumer that validates a different array than the one the reader\n * sees — `aimock validate` runs the rules per file and then over the union of\n * every file — has to renumber those indices, and the only safe way to do that\n * is to re-render from the numbers rather than to rewrite the rendered prose:\n * `duplicate userMessage` interpolates the fixture's OWN `userMessage`, so a\n * regex over the message corrupts user-authored text that happens to read\n * \"fixture 3\".\n */\nexport type ValidationRef =\n | { rule: \"duplicate-user-message\"; userMessage: string; shadows: number }\n | { rule: \"catch-all-not-last\"; shadowsFrom: number; shadowsThrough: number };\n\nconst INVALID_ARGUMENTS_HINT =\n \"to send invalid JSON on purpose, use `misbehavior: tool-args-invalid-json`\";\n\nexport interface ValidationResult {\n severity: \"error\" | \"warning\";\n fixtureIndex: number;\n message: string;\n /** Present only on the rules whose message names other fixtures. */\n ref?: ValidationRef;\n}\n\n/**\n * Render a ref-carrying finding's message with each named index passed through\n * `renderIndex`. With `String` it reproduces the message `validateFixtures`\n * itself emits; `aimock validate` passes a renderer that resolves a union\n * index to `<file> #<index-in-that-file>`.\n */\nexport function renderValidationRef(\n ref: ValidationRef,\n renderIndex: (fixtureIndex: number) => string,\n): string {\n return ref.rule === \"duplicate-user-message\"\n ? `duplicate userMessage '${ref.userMessage}' — shadows fixture ${renderIndex(ref.shadows)}`\n : `empty match acts as catch-all but is not the last fixture — shadows fixtures ${renderIndex(\n ref.shadowsFrom,\n )}+`;\n}\n\nfunction validateReasoning(\n response: { reasoning?: unknown },\n fixtureIndex: number,\n results: ValidationResult[],\n): void {\n if (response.reasoning !== undefined) {\n if (typeof response.reasoning !== \"string\") {\n results.push({\n severity: \"error\",\n fixtureIndex,\n message: \"reasoning must be a string\",\n });\n } else if (response.reasoning === \"\") {\n results.push({\n severity: \"warning\",\n fixtureIndex,\n message: \"reasoning is empty string — no reasoning events will be emitted\",\n });\n }\n }\n}\n\nfunction validateWebSearches(\n response: { webSearches?: unknown },\n fixtureIndex: number,\n results: ValidationResult[],\n): void {\n if (response.webSearches !== undefined) {\n if (!Array.isArray(response.webSearches)) {\n results.push({\n severity: \"error\",\n fixtureIndex,\n message: \"webSearches must be an array of strings\",\n });\n } else if (response.webSearches.length === 0) {\n results.push({\n severity: \"warning\",\n fixtureIndex,\n message: \"webSearches is empty array — no web search events will be emitted\",\n });\n } else {\n for (let j = 0; j < response.webSearches.length; j++) {\n if (typeof response.webSearches[j] !== \"string\") {\n results.push({\n severity: \"error\",\n fixtureIndex,\n message: `webSearches[${j}] is not a string`,\n });\n break;\n }\n if (response.webSearches[j] === \"\") {\n results.push({\n severity: \"warning\",\n fixtureIndex,\n message: `webSearches[${j}] is empty string`,\n });\n }\n }\n }\n }\n}\n\n/**\n * Path (`customToolCalls[j]` or `responsesBlocks[j]`) of the first custom\n * tool call a static fixture response carries, or undefined. Factory\n * responses are skipped.\n */\nfunction firstCustomToolCallPath(response: unknown): string | undefined {\n if (response === null || typeof response !== \"object\") return undefined;\n const r = response as { customToolCalls?: unknown; responsesBlocks?: unknown };\n if (Array.isArray(r.customToolCalls) && r.customToolCalls.length > 0) {\n return \"customToolCalls[0]\";\n }\n if (Array.isArray(r.responsesBlocks)) {\n const j = r.responsesBlocks.findIndex(\n (b) =>\n b !== null && typeof b === \"object\" && (b as { type?: unknown }).type === \"customToolCall\",\n );\n if (j >= 0) return `responsesBlocks[${j}]`;\n }\n return undefined;\n}\n\n/**\n * Checks for the OpenAI Responses keys `customToolCalls` and\n * `responsesBlocks`. Both are new, so their findings never touch a fixture an\n * earlier release accepted. A custom tool call takes a non-empty string\n * `name`, a string `input` (empty: warning), no `arguments`, a string `id`\n * when present and a non-empty string `namespace` when present.\n * `responsesBlocks` follows the `blocks` rules plus `customToolCall` blocks,\n * and may not be combined with `blocks`.\n */\nfunction validateResponsesToolKeys(\n response: unknown,\n fixtureIndex: number,\n results: ValidationResult[],\n): void {\n if (response === null || typeof response !== \"object\") return;\n const r = response as { customToolCalls?: unknown; responsesBlocks?: unknown; blocks?: unknown };\n const error = (message: string) => results.push({ severity: \"error\", fixtureIndex, message });\n const warning = (message: string) => results.push({ severity: \"warning\", fixtureIndex, message });\n const checkCustom = (tc: Record<string, unknown>, path: string) => {\n if (typeof tc.name !== \"string\" || tc.name === \"\") {\n error(`${path}.name must be a non-empty string`);\n }\n if (typeof tc.input !== \"string\") {\n error(`${path}.input must be a string for a custom tool call`);\n } else if (tc.input === \"\") {\n warning(`${path}.input is empty`);\n }\n if (tc.arguments !== undefined) {\n error(`${path}.arguments is not valid on a custom tool call; use input`);\n }\n if (tc.id !== undefined && typeof tc.id !== \"string\") {\n error(`${path}.id must be a string, got ${typeof tc.id}`);\n }\n if (tc.namespace !== undefined && (typeof tc.namespace !== \"string\" || tc.namespace === \"\")) {\n error(`${path}.namespace must be a non-empty string`);\n }\n };\n if (r.customToolCalls !== undefined) {\n if (!Array.isArray(r.customToolCalls)) {\n error(`customToolCalls must be an array, got ${typeof r.customToolCalls}`);\n } else {\n r.customToolCalls.forEach((entry: unknown, j) => {\n if (entry === null || typeof entry !== \"object\" || Array.isArray(entry)) {\n error(`customToolCalls[${j}] must be an object`);\n return;\n }\n const tc = entry as Record<string, unknown>;\n if (tc.type !== undefined && tc.type !== \"custom\") {\n error(`customToolCalls[${j}].type must be \"custom\" when present`);\n }\n checkCustom(tc, `customToolCalls[${j}]`);\n });\n }\n }\n if (r.responsesBlocks === undefined) return;\n if (!Array.isArray(r.responsesBlocks)) {\n error(`responsesBlocks must be an array, got ${typeof r.responsesBlocks}`);\n return;\n }\n if (Array.isArray(r.blocks) && r.blocks.length > 0 && r.responsesBlocks.length > 0) {\n error(\"blocks and responsesBlocks cannot both be set; use responsesBlocks alone\");\n }\n r.responsesBlocks.forEach((entry: unknown, j) => {\n const path = `responsesBlocks[${j}]`;\n if (entry === null || typeof entry !== \"object\" || Array.isArray(entry)) {\n error(`${path} must be an object`);\n return;\n }\n const block = entry as Record<string, unknown>;\n if (block.type === \"text\") {\n if (typeof block.text !== \"string\") error(`${path}.text must be a string`);\n return;\n }\n if (block.type === \"customToolCall\") {\n checkCustom(block, path);\n return;\n }\n if (block.type !== \"toolCall\") {\n error(\n `${path}.type must be \"text\", \"toolCall\" or \"customToolCall\", got ${JSON.stringify(block.type)}`,\n );\n return;\n }\n if (typeof block.name !== \"string\" || block.name === \"\") {\n error(`${path}.name must be a non-empty string`);\n }\n if (block.id !== undefined && typeof block.id !== \"string\") {\n error(`${path}.id must be a string, got ${typeof block.id}`);\n }\n if (block.input !== undefined) {\n error(`${path}.input is only valid on a \"customToolCall\" block`);\n }\n if (\n block.namespace !== undefined &&\n (typeof block.namespace !== \"string\" || block.namespace === \"\")\n ) {\n error(`${path}.namespace must be a non-empty string`);\n }\n const args = block.arguments;\n if (typeof args === \"string\") {\n try {\n JSON.parse(args);\n } catch {\n error(`${path}.arguments is not valid JSON: ${args}; ${INVALID_ARGUMENTS_HINT}`);\n }\n } else if (args === null || typeof args !== \"object\") {\n error(`${path}.arguments must be a string or an object`);\n }\n });\n}\n\nfunction validateBlocks(\n response: { blocks?: unknown; content?: unknown; toolCalls?: unknown },\n fixtureIndex: number,\n results: ValidationResult[],\n): void {\n if (response.blocks === undefined) return;\n\n // Mirrors the toolCalls checks: reject malformed `blocks` at LOAD time so a\n // bad blocks array never reaches the dispatch/builder (where\n // resolveFixtureBlocks throws AFTER the journal has already recorded\n // status:200, yielding a journal-200/client-500 mismatch). #274 F3+F8.\n if (!Array.isArray(response.blocks)) {\n results.push({\n severity: \"error\",\n fixtureIndex,\n message: `blocks must be an array, got ${typeof response.blocks}`,\n });\n return;\n }\n\n for (let j = 0; j < response.blocks.length; j++) {\n const block = response.blocks[j] as Record<string, unknown> | null | undefined;\n if (typeof block !== \"object\" || block === null) {\n results.push({\n severity: \"error\",\n fixtureIndex,\n message: `blocks[${j}] must be an object`,\n });\n continue;\n }\n if (block.type !== \"text\" && block.type !== \"toolCall\") {\n results.push({\n severity: \"error\",\n fixtureIndex,\n message: `blocks[${j}].type must be \"text\" or \"toolCall\", got ${JSON.stringify(block.type)}`,\n });\n continue;\n }\n if (block.type === \"text\") {\n if (typeof block.text !== \"string\") {\n results.push({\n severity: \"error\",\n fixtureIndex,\n message: `blocks[${j}].text must be a string, got ${typeof block.text}`,\n });\n } else if (block.text === \"\") {\n // Mirror the content/toolCalls \"empty string\" rejection: an empty-text\n // block produces a meaningless/spurious wire chunk on replay.\n results.push({\n severity: \"error\",\n fixtureIndex,\n message: `blocks[${j}].text is empty string`,\n });\n }\n } else {\n // toolCall block — mirror toolCalls[] name + arguments checks.\n if (typeof block.name !== \"string\" || block.name === \"\") {\n results.push({\n severity: \"error\",\n fixtureIndex,\n message: `blocks[${j}].name must be a non-empty string`,\n });\n }\n // `arguments` is JSON-string in runtime form (normalizeResponse already\n // stringified object/array args); accept a valid-JSON string or an object.\n if (typeof block.arguments === \"string\") {\n try {\n JSON.parse(block.arguments);\n } catch {\n results.push({\n severity: \"error\",\n fixtureIndex,\n message: `blocks[${j}].arguments is not valid JSON: ${block.arguments}; ${INVALID_ARGUMENTS_HINT}`,\n });\n }\n } else if (typeof block.arguments !== \"object\" || block.arguments === null) {\n results.push({\n severity: \"error\",\n fixtureIndex,\n message: `blocks[${j}].arguments must be a JSON string or object, got ${typeof block.arguments}`,\n });\n }\n if (block.id !== undefined && typeof block.id !== \"string\") {\n results.push({\n severity: \"error\",\n fixtureIndex,\n message: `blocks[${j}].id must be a string, got ${typeof block.id}`,\n });\n }\n }\n }\n\n // blocks-vs-content/toolCalls divergence (#274 P2). When a fixture carries\n // BOTH `blocks` AND legacy `content`/`toolCalls` (allowed but unusual now\n // that blocks-only is first-class), builders stream `blocks` and IGNORE the\n // redundant `content`/`toolCalls`. If those disagree it is a silent footgun,\n // so WARN (not a hard error). Stay silent on the clean blocks-only path\n // (neither legacy field present) — that is the intended shape.\n const hasLegacyContent = typeof response.content === \"string\";\n const hasLegacyToolCalls = Array.isArray(response.toolCalls);\n if ((hasLegacyContent || hasLegacyToolCalls) && Array.isArray(response.blocks)) {\n const textBlocks = response.blocks.filter(\n (b): b is { type: \"text\"; text: string } =>\n b != null &&\n typeof b === \"object\" &&\n (b as { type?: unknown }).type === \"text\" &&\n typeof (b as { text?: unknown }).text === \"string\",\n );\n const toolCallBlockNames = response.blocks\n .filter(\n (b): b is { type: \"toolCall\"; name: string } =>\n b != null &&\n typeof b === \"object\" &&\n (b as { type?: unknown }).type === \"toolCall\" &&\n typeof (b as { name?: unknown }).name === \"string\",\n )\n .map((b) => b.name);\n\n // Text divergence: blocks' concatenated text vs legacy `content`.\n if (hasLegacyContent) {\n const blocksText = textBlocks.map((b) => b.text).join(\"\");\n if (blocksText !== response.content) {\n results.push({\n severity: \"warning\",\n fixtureIndex,\n message:\n \"blocks text diverges from content — builders stream blocks and ignore the redundant content field\",\n });\n }\n }\n\n // ToolCall divergence: blocks' ordered toolCall names vs legacy `toolCalls`.\n if (hasLegacyToolCalls) {\n const legacyNames = (response.toolCalls as Array<{ name?: unknown }>).map((tc) =>\n typeof tc?.name === \"string\" ? tc.name : undefined,\n );\n const sameNames =\n legacyNames.length === toolCallBlockNames.length &&\n legacyNames.every((n, k) => n === toolCallBlockNames[k]);\n if (!sameNames) {\n results.push({\n severity: \"warning\",\n fixtureIndex,\n message:\n \"blocks toolCalls diverge from toolCalls — builders stream blocks and ignore the redundant toolCalls field\",\n });\n }\n }\n }\n}\n\n/**\n * Human-readable form of the status range an injected error may carry.\n * Measured on Node v26: `res.writeHead` throws outside 100-999 (the injected\n * error is then lost and the caller sees a generic 500), and a 1xx status is\n * accepted but cannot terminate a response, so it hangs the request until the\n * client times out. 600-999 is unassigned, NOT invalid — Node sends it and a\n * client sees it — so a mock server allows it.\n */\nexport const INJECTED_STATUS_RANGE =\n \"an integer between 200 and 999 (1xx cannot terminate a response and would hang the client)\";\n\n/**\n * Shared by the three doors that queue an error whose `status` reaches\n * `res.writeHead`: a fixture's `response.status` (below), `POST\n * /__aimock/error`, and `LLMock.nextRequestError`.\n */\nexport function isInjectableStatus(status: unknown): status is number {\n // The typeof is for the COMPILER — it narrows `unknown` so the comparisons\n // below type-check. At runtime `Number.isInteger` already excludes every\n // non-number, so mutating it away fails nothing.\n if (typeof status !== \"number\") return false;\n return Number.isInteger(status) && status >= 200 && status <= 999;\n}\n\n/**\n * An injected error is only a valid response for endpoints that can carry an\n * error envelope. Mirrors the router's endpoint-compat table\n * (matchFixtureDiagnostic in router.ts): error responses are compatible with\n * chat / embedding / realtime* / fal / the four elevenlabs-voice* slots and\n * with requests that carry no endpoint type, but NOT with multimedia endpoints\n * (image, speech, video, transcription, …). Whether files / fine-tuning /\n * batches belong here is an open design question — not widened yet.\n */\nfunction isErrorEndpointCompatible(req: ChatCompletionRequest): boolean {\n const reqEndpoint = req._endpointType as string | undefined;\n return (\n reqEndpoint === undefined ||\n reqEndpoint === \"chat\" ||\n reqEndpoint === \"embedding\" ||\n reqEndpoint.startsWith(\"realtime\") ||\n reqEndpoint === \"fal\" ||\n reqEndpoint === \"elevenlabs-voice-design\" ||\n reqEndpoint === \"elevenlabs-voice\" ||\n reqEndpoint === \"elevenlabs-voice-get\" ||\n reqEndpoint === \"elevenlabs-voice-delete\"\n );\n}\n\n/**\n * Queue a one-shot injected error at the FRONT of `fixtures` — shared by\n * `LLMock.nextRequestError` and `POST /__aimock/error` so both doors gate and\n * consume identically. The caller validates `status` (`isInjectableStatus`).\n *\n * The fixture is consumed exactly when its error body is actually WRITTEN to a\n * client, not when its predicate is evaluated and not when its factory happens\n * to run. Handlers that await chaos claim it synchronously at selection\n * (`claimOneShotError`) so two concurrent requests cannot both pick it across\n * the chaos-latency await, and RELEASES the claim (`releaseOneShotError`) on\n * every exit that ends the request without writing that body — a terminal\n * chaos action, or a client that left during the chaos-latency delay — unless\n * the queue was cleared (`clearFixtureQueue`) in between, in which case the\n * stale claim is dropped rather than re-armed into the reset queue. The\n * claim cannot simply be deferred past the chaos gate instead: the chaos\n * config is resolved FROM the selected fixture, and a claim taken after the\n * latency await re-opens the concurrency race the claim-at-selection closes.\n * The OpenRouter `models[]` loop cannot burn it by resolving a candidate it\n * then fails past — the error is `fallthrough: false` (terminal) so the\n * candidate that selects it serves it. The factory still performs the same\n * idempotent claim for handlers that only reach the fixture via\n * `resolveResponse` without an intervening await (realtime and fal). Consuming at\n * predicate time burned the error unserved whenever a later fixture won\n * selection (e.g. a behind-the-count `turnIndex` fixture) and spliced the\n * array while the router was still iterating it, skipping the very fixture\n * that then won. The predicate gate keeps an incompatible request (an image\n * call) from selecting it at all, so the error stays pending for its intended\n * endpoint.\n */\nconst oneShotErrorFixtures = new WeakSet<Fixture>();\n\n/** True for a fixture queued by `queueOneShotError` (still armed or not). */\nexport function isOneShotError(fixture: Fixture): boolean {\n return oneShotErrorFixtures.has(fixture);\n}\n\n/**\n * Claim (consume) a one-shot error fixture. Returns true when this call\n * removed it from `fixtures`; false when another request already claimed it,\n * in which case the caller must NOT serve it and must re-select.\n */\nexport function claimOneShotError(fixtures: Fixture[], fixture: Fixture): boolean {\n const idx = fixtures.indexOf(fixture);\n if (idx === -1) return false;\n fixtures.splice(idx, 1);\n oneShotClaimGeneration.set(fixture, queueGeneration(fixtures));\n return true;\n}\n\n/**\n * Generation of each fixture queue, bumped by `clearFixtureQueue`. A one-shot\n * claim records the generation it was taken under; a release under a LATER\n * generation means the queue was cleared or reset while the claim was parked\n * (in the chaos-latency await) and the claim is stale — re-arming it would\n * plant the previous test's injection in the next test's freshly reset queue.\n * Keyed by the caller's array (a server's own list resolves to it): every\n * clear path preserves the array reference (`length = 0`), so the counter\n * follows the live queue.\n */\nconst fixtureQueueGeneration = new WeakMap<Fixture[], number>();\nconst oneShotClaimGeneration = new WeakMap<Fixture, number>();\n\nfunction queueGeneration(fixtures: Fixture[]): number {\n return fixtureQueueGeneration.get(fixtureListTarget(fixtures)) ?? 0;\n}\n\n/**\n * Empty a fixture queue in place (array identity preserved — the running\n * server reads this same array on every request) and invalidate every\n * outstanding one-shot claim against it, so a release that lands after the\n * clear is a no-op instead of re-arming into the emptied queue. The ONE door\n * for `POST /__aimock/reset`, `DELETE /__aimock/fixtures` and\n * `LLMock.clearFixtures`; a bare `fixtures.length = 0` misses the second half.\n */\nexport function clearFixtureQueue(fixtures: Fixture[]): void {\n fixtures.length = 0;\n fixtureQueueGeneration.set(fixtureListTarget(fixtures), queueGeneration(fixtures) + 1);\n}\n\n/**\n * Re-arm a one-shot error that was claimed for a request which then ended\n * WITHOUT writing the error body — a terminal chaos action (drop / disconnect\n * / rateLimit / malformed) or a client that hung up during the chaos-latency\n * delay. Without this the injection is burned unserved and the NEXT request\n * gets a 200, which is exactly the outcome `nextRequestError` promises not to\n * produce. A no-op for a fixture that is not a one-shot, and for one still\n * present in `fixtures` (never claimed, or already re-armed) so a double\n * release cannot duplicate the injection — and for a claim taken before the\n * queue was last cleared, so a reset that raced the request stays a reset.\n */\nexport function releaseOneShotError(fixtures: Fixture[], fixture: Fixture | null): void {\n if (fixture === null || !oneShotErrorFixtures.has(fixture)) return;\n if (fixtures.includes(fixture)) return;\n // Claimed under an earlier generation: the queue was cleared/reset while\n // this request was parked. The clear disarmed the injection — stay cleared.\n if (oneShotClaimGeneration.get(fixture) !== queueGeneration(fixtures)) return;\n // Front, matching `queueOneShotError`: the one-shot wins registration-order\n // ties against everything else, exactly as it did before it was claimed.\n fixtures.unshift(fixture);\n}\n\nexport function queueOneShotError(\n fixtures: Fixture[],\n status: number,\n errorBody?: { message?: string; type?: string; code?: string },\n): Fixture {\n const errorResponse: FixtureResponse = {\n error: {\n message: errorBody?.message ?? \"Injected error\",\n type: errorBody?.type ?? \"server_error\",\n code: errorBody?.code,\n },\n status,\n // Terminal in the OpenRouter `models[]` loop: the candidate that selects\n // (and thereby claims) the one-shot serves it instead of failing past it.\n fallthrough: false,\n };\n const fixture: Fixture = {\n match: { predicate: isErrorEndpointCompatible },\n response: () => {\n // Idempotent: a no-op when the handler already claimed at selection.\n claimOneShotError(fixtures, fixture);\n return errorResponse;\n },\n };\n oneShotErrorFixtures.add(fixture);\n // Insert at front so it wins registration-order ties against everything else.\n fixtures.unshift(fixture);\n return fixture;\n}\n\nexport function validateFixtures(\n fixtures: Fixture[],\n liveOptions?: LiveOptions,\n): ValidationResult[] {\n const results: ValidationResult[] = [];\n\n const seenUserMessages = new Map<string, number>();\n\n for (let i = 0; i < fixtures.length; i++) {\n const original = fixtures[i];\n // Only a fixture loaded for a server with responsesTools \"extended\" has its\n // `match.toolNamespace`, `customToolCalls` and `responsesBlocks` checked;\n // for any other fixture they are unused data, as in 1.44.0.\n const f = responsesToolsExtendedFixtures.has(original)\n ? original\n : withoutResponsesToolsKeys(original);\n const response = f.response;\n\n // Only a fixture recognized under enabled misbehavior is checked; for any\n // other fixture a `misbehavior` key is unused data, as in 1.44.0.\n const misbehaviorIssue = misbehaviorEnabledFixtures.has(original)\n ? validateFixtureMisbehavior(original, `fixtures[${i}].misbehavior`)\n : undefined;\n if (misbehaviorIssue) {\n results.push({ severity: \"error\", fixtureIndex: i, message: misbehaviorIssue.message });\n } else if (misbehaviorEnabledFixtures.has(original) && f.misbehavior !== undefined) {\n const parsed = parseMisbehavior(f.misbehavior);\n if (parsed.ok && parsed.config.seed === undefined) {\n for (const fault of parsed.config.faults) {\n if (fault.rate !== undefined && fault.rate < 1 && fault.times === undefined) {\n results.push({\n severity: \"warning\",\n fixtureIndex: i,\n message:\n \"probabilistic fault without a seed is reproducible only per test id order; set seed to pin it\",\n });\n }\n }\n }\n }\n\n if (\n typeof response !== \"function\" &&\n (isLiveResponse(response) || f.match.endpoint === \"openai-live\")\n ) {\n try {\n normalizeLiveFixture(response, liveOptions);\n } catch (error) {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n // Skip response-shape validation for function responses — they are\n // evaluated at runtime so we cannot statically inspect them.\n if (typeof response === \"function\") {\n // Still validate match fields and numeric options below.\n } else {\n // --- Error checks ---\n\n // Response type recognition\n // Note: isCombinedFixtureResponse must be checked before isTextResponse\n // and isToolCallResponse since it is a structural superset of both.\n if (\n !isCombinedFixtureResponse(response) &&\n !isTextResponse(response) &&\n !isToolCallResponse(response) &&\n !isErrorResponse(response) &&\n !isEmbeddingResponse(response) &&\n !isImageResponse(response) &&\n !isAudioResponse(response) &&\n !isTranscriptionResponse(response) &&\n !isVideoResponse(response) &&\n !isJSONResponse(response) &&\n !isLiveResponse(response)\n ) {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message:\n \"response is not a recognized type (must have content, toolCalls, error, embedding, image, audio, transcription, video, json, or live)\",\n });\n }\n\n // When a non-empty ordered `blocks` array is present, the builders stream\n // `blocks` and IGNORE the legacy `content` mirror (see validateBlocks's\n // divergence note + isCombinedFixtureResponse's BLOCKS-ONLY clause).\n // So an empty-string `content` is harmless in that case and must NOT raise\n // the \"content is empty string\" hard error. Fixtures WITHOUT blocks keep\n // the error (an empty content with no blocks produces no output).\n const hasNonEmptyBlocks =\n (Array.isArray((response as { blocks?: unknown }).blocks) &&\n (response as { blocks: unknown[] }).blocks.length > 0) ||\n (Array.isArray((response as { responsesBlocks?: unknown }).responsesBlocks) &&\n (response as { responsesBlocks: unknown[] }).responsesBlocks.length > 0);\n const hasCustomToolCalls =\n Array.isArray((response as { customToolCalls?: unknown }).customToolCalls) &&\n (response as { customToolCalls: unknown[] }).customToolCalls.length > 0;\n\n // Text response checks\n if (isTextResponse(response)) {\n if (response.content === \"\" && !hasNonEmptyBlocks) {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: \"content is empty string\",\n });\n }\n validateReasoning(response, i, results);\n validateWebSearches(response, i, results);\n }\n\n // ContentWithToolCalls response checks\n if (isCombinedFixtureResponse(response)) {\n // The guard now also matches a BLOCKS-ONLY fixture (non-empty `blocks`,\n // no `content`/`toolCalls`). For that shape the content/toolCalls checks\n // below don't apply (and `content`/`toolCalls` are undefined) — the\n // ordered `blocks` array is validated separately by `validateBlocks`\n // immediately after this block. So gate the legacy field checks on the\n // fields actually being present, mirroring the builders' branch-on-blocks.\n if (typeof response.content === \"string\") {\n if (response.content === \"\" && !hasNonEmptyBlocks) {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: \"content is empty string\",\n });\n }\n }\n if (Array.isArray(response.toolCalls)) {\n if (response.toolCalls.length === 0 && !hasCustomToolCalls) {\n results.push({\n severity: \"warning\",\n fixtureIndex: i,\n message: \"toolCalls array is empty — fixture will never produce tool calls\",\n });\n }\n for (let j = 0; j < response.toolCalls.length; j++) {\n const tc = response.toolCalls[j];\n if (!tc.name) {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `toolCalls[${j}].name is empty`,\n });\n }\n try {\n JSON.parse(tc.arguments);\n } catch {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `toolCalls[${j}].arguments is not valid JSON: ${tc.arguments}; ${INVALID_ARGUMENTS_HINT}`,\n });\n }\n }\n }\n validateReasoning(response, i, results);\n validateWebSearches(response, i, results);\n }\n\n // Optional ordered `blocks` checks — validated whenever present on the\n // response, regardless of which content/toolCalls guard matched, so a\n // malformed blocks array is rejected at LOAD rather than at dispatch.\n validateBlocks(\n response as { blocks?: unknown; content?: unknown; toolCalls?: unknown },\n i,\n results,\n );\n // OpenAI Responses keys (new): customToolCalls and responsesBlocks.\n validateResponsesToolKeys(response, i, results);\n\n // Tool call response checks\n if (isToolCallResponse(response)) {\n if (response.toolCalls.length === 0 && !hasCustomToolCalls) {\n results.push({\n severity: \"warning\",\n fixtureIndex: i,\n message: \"toolCalls array is empty — fixture will never produce tool calls\",\n });\n }\n for (let j = 0; j < response.toolCalls.length; j++) {\n const tc = response.toolCalls[j];\n if (!tc.name) {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `toolCalls[${j}].name is empty`,\n });\n }\n try {\n JSON.parse(tc.arguments);\n } catch {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `toolCalls[${j}].arguments is not valid JSON: ${tc.arguments}; ${INVALID_ARGUMENTS_HINT}`,\n });\n }\n }\n validateWebSearches(response, i, results);\n }\n\n // Error response checks\n if (isErrorResponse(response)) {\n if (!response.error.message) {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: \"error.message is empty\",\n });\n }\n // Shared with `POST /__aimock/error` and `LLMock.nextRequestError`\n // (see `isInjectableStatus` above): a fixture's status lands on the same\n // `res.writeHead`, so it is accepted or rejected identically.\n if (response.status !== undefined && !isInjectableStatus(response.status)) {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `error status ${response.status} is not a valid HTTP status code — must be ${INJECTED_STATUS_RANGE}`,\n });\n }\n // `fallthrough` gates OpenRouter `models[]` failover and is read as\n // `!== false`, so a non-boolean (`\"false\"`, 0, null) would silently\n // fall through — the OPPOSITE of the intended terminal behavior. Reject\n // it at load so the fail-closed half cannot quietly fail open.\n if (response.fallthrough !== undefined && typeof response.fallthrough !== \"boolean\") {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `error.fallthrough must be a boolean, got ${typeof response.fallthrough}`,\n });\n }\n }\n\n // Embedding response checks\n if (isEmbeddingResponse(response)) {\n if (response.embedding.length === 0) {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: \"embedding array is empty\",\n });\n }\n for (let j = 0; j < response.embedding.length; j++) {\n if (typeof response.embedding[j] !== \"number\") {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `embedding[${j}] is not a number`,\n });\n break; // one error is enough\n }\n }\n }\n\n // Audio response checks — validate object-form audio\n if (isAudioResponse(response) && typeof response.audio === \"object\") {\n const audioObj = response.audio;\n if (typeof audioObj.b64Json !== \"string\" || audioObj.b64Json === \"\") {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: \"audio.b64Json must be a non-empty string\",\n });\n }\n if (audioObj.contentType !== undefined && typeof audioObj.contentType !== \"string\") {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `audio.contentType must be a string, got ${typeof audioObj.contentType}`,\n });\n }\n }\n\n // Validate ResponseOverrides fields\n if (\n isTextResponse(response) ||\n isToolCallResponse(response) ||\n isCombinedFixtureResponse(response)\n ) {\n const r = response as ResponseOverrides;\n if (r.id !== undefined && typeof r.id !== \"string\") {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `override \"id\" must be a string, got ${typeof r.id}`,\n });\n }\n if (r.created !== undefined && (typeof r.created !== \"number\" || r.created < 0)) {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `override \"created\" must be a non-negative number`,\n });\n }\n if (r.model !== undefined && typeof r.model !== \"string\") {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `override \"model\" must be a string, got ${typeof r.model}`,\n });\n }\n if (r.finishReason !== undefined && typeof r.finishReason !== \"string\") {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `override \"finishReason\" must be a string, got ${typeof r.finishReason}`,\n });\n }\n if (r.role !== undefined && typeof r.role !== \"string\") {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `override \"role\" must be a string, got ${typeof r.role}`,\n });\n }\n if (r.phase !== undefined && r.phase !== \"commentary\" && r.phase !== \"final_answer\") {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: 'override \"phase\" must be \"commentary\" or \"final_answer\"',\n });\n }\n if (r.systemFingerprint !== undefined && typeof r.systemFingerprint !== \"string\") {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `override \"systemFingerprint\" must be a string, got ${typeof r.systemFingerprint}`,\n });\n }\n if (r.usage !== undefined) {\n if (typeof r.usage !== \"object\" || r.usage === null || Array.isArray(r.usage)) {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `override \"usage\" must be an object`,\n });\n } else {\n // Numeric token fields must be numbers, but the OpenRouter cost\n // sub-fields carry non-numeric shapes (see ResponseOverrides.usage\n // in types.ts): cost_details / prompt_tokens_details /\n // completion_tokens_details are objects and is_byok is a boolean.\n // Validate each per its documented type so cost-scripting fixtures\n // are not rejected with a misleading \"must be a number\" error.\n // Known numeric inner scalars per object field (see\n // OpenRouterUsageExtras in types.ts). Validating only these keeps\n // the index-signature ([key: string]: unknown) escape hatch open\n // for forward-compat fields while still catching malformed values\n // on the documented ones — otherwise a bad inner scalar (e.g.\n // cost_details.upstream_inference_cost: \"abc\") passes load-time\n // validation and only blows up at replay in the canonical SDK.\n for (const key of Object.keys(r.usage)) {\n const val = (r.usage as Record<string, unknown>)[key];\n if (val === undefined) continue;\n const definition = getUsageFieldDefinition(key);\n if (definition.kind === \"number-or-object\" && typeof val === \"number\") continue;\n if (definition.kind === \"object\" || definition.kind === \"number-or-object\") {\n if (typeof val !== \"object\" || val === null || Array.isArray(val)) {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `override \"usage.${key}\" must be ${definition.kind === \"number-or-object\" ? \"a number or object\" : \"an object\"}, got ${\n Array.isArray(val) ? \"array\" : typeof val\n }`,\n });\n } else {\n // Type-check the documented inner scalars; each is optional\n // (required-if-present), so only validate keys that are set.\n const inner = val as Record<string, unknown>;\n for (const innerKey of definition.numericMembers) {\n const innerVal = inner[innerKey];\n if (innerVal !== undefined && typeof innerVal !== \"number\") {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `override \"usage.${key}.${innerKey}\" must be a number, got ${typeof innerVal}`,\n });\n }\n }\n }\n } else if (definition.kind === \"boolean\") {\n if (typeof val !== \"boolean\") {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `override \"usage.is_byok\" must be a boolean, got ${typeof val}`,\n });\n }\n } else if (typeof val !== \"number\") {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `override \"usage.${key}\" must be a number, got ${typeof val}`,\n });\n }\n }\n }\n }\n }\n } // end: skip response-shape validation for function responses\n\n // Numeric sanity checks\n if (f.latency !== undefined && f.latency < 0) {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: \"latency must be >= 0\",\n });\n }\n if (f.chunkSize !== undefined && f.chunkSize < 1) {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: \"chunkSize must be >= 1\",\n });\n }\n if (f.truncateAfterChunks !== undefined && f.truncateAfterChunks < 1) {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: \"truncateAfterChunks must be >= 1\",\n });\n }\n if (f.disconnectAfterMs !== undefined && f.disconnectAfterMs < 0) {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: \"disconnectAfterMs must be >= 0\",\n });\n }\n if (f.streamingProfile !== undefined) {\n const sp = f.streamingProfile;\n if (sp.ttft !== undefined && sp.ttft < 0) {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: \"streamingProfile.ttft must be >= 0\",\n });\n }\n if (sp.tps !== undefined && sp.tps <= 0) {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: \"streamingProfile.tps must be > 0\",\n });\n }\n if (sp.jitter !== undefined && (sp.jitter < 0 || sp.jitter > 1)) {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: \"streamingProfile.jitter must be between 0 and 1\",\n });\n }\n }\n if (f.chaos !== undefined) {\n // EVERY chaos field is routed through the ONE chaos table, so a fixture\n // cannot accept a value the CLI, the header and the runtime reject. The\n // hand-rolled `< 0 || > 1` checks this replaces let `NaN`, `-0` and a\n // numeric string (`dropRate: \"0.5\"`) validate clean, and `latencyMs: 1.5`\n // through; the runtime then rejected the fixture value on every request.\n // A fixture is typed data, like the control API's JSON body: the field\n // must be a number. A numeric STRING is a type error here, not a wire\n // spelling to be parsed — the header API is the surface that takes text.\n const ch = f.chaos as Record<string, unknown>;\n for (const field of CHAOS_FIELD_NAMES) {\n const value = ch[field];\n if (value === undefined) continue;\n const accepted = typeof value === \"number\" ? parseChaosField(field, value) : undefined;\n if (accepted === undefined) {\n const shape = CHAOS_FIELDS[field].integer ? \"a whole number of ms\" : \"a number\";\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `chaos.${field} must be ${shape} between 0 and ${CHAOS_FIELDS[field].max}`,\n });\n }\n }\n }\n\n // Match field type checks\n if (f.match.turnIndex !== undefined) {\n if (\n typeof f.match.turnIndex !== \"number\" ||\n f.match.turnIndex < 0 ||\n !Number.isInteger(f.match.turnIndex)\n ) {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: \"match.turnIndex must be a non-negative integer\",\n });\n }\n }\n if (f.match.sequenceIndex !== undefined) {\n if (\n typeof f.match.sequenceIndex !== \"number\" ||\n f.match.sequenceIndex < 0 ||\n !Number.isInteger(f.match.sequenceIndex)\n ) {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: \"match.sequenceIndex must be a non-negative integer\",\n });\n }\n }\n if (f.match.hasToolResult !== undefined && typeof f.match.hasToolResult !== \"boolean\") {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `match.hasToolResult must be a boolean, got ${typeof f.match.hasToolResult}`,\n });\n }\n if (f.match.toolResultContains !== undefined) {\n if (typeof f.match.toolResultContains !== \"string\") {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `match.toolResultContains must be a string, got ${typeof f.match.toolResultContains}`,\n });\n } else if (f.match.toolResultContains.length === 0) {\n // An empty substring is always contained, so the gate would silently\n // act as \"last message is any tool result\" — reject it as an authoring\n // mistake rather than let it shadow later fixtures.\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: \"match.toolResultContains must be a non-empty string\",\n });\n }\n }\n if (f.match.toolNamespace !== undefined) {\n if (typeof f.match.toolNamespace !== \"string\") {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `match.toolNamespace must be a string, got ${typeof f.match.toolNamespace}`,\n });\n } else if (f.match.toolNamespace.length === 0) {\n // OpenAI's spec gives a namespace tool a non-empty name\n // (openai/openai-openapi openapi.yaml 2.3.0, NamespaceToolParam: `name`\n // has `minLength: 1`), and aimock drops a namespace tool named \"\", so\n // this value never matches. Reject it as an authoring mistake.\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: \"match.toolNamespace must be a non-empty string\",\n });\n }\n }\n if (\n typeof f.match.toolNamespace === \"string\" &&\n f.match.toolNamespace !== \"\" &&\n f.match.endpoint !== undefined &&\n f.match.endpoint !== \"chat\"\n ) {\n // Only the OpenAI Responses adapter attaches tool namespaces. Its\n // requests carry endpoint \"chat\", which every chat-style adapter shares,\n // so any other endpoint never sees a namespace.\n results.push({\n severity: \"warning\",\n fixtureIndex: i,\n message: `match.toolNamespace never matches with endpoint ${JSON.stringify(f.match.endpoint)} — only OpenAI Responses requests (endpoint \"chat\") carry tool namespaces`,\n });\n }\n const customCallPath =\n f.match.endpoint !== undefined && f.match.endpoint !== \"chat\"\n ? firstCustomToolCallPath(f.response)\n : undefined;\n if (customCallPath !== undefined) {\n // No non-\"chat\" endpoint can carry a custom tool call, but the failure\n // differs per wire, so the text names no status or error code.\n results.push({\n severity: \"warning\",\n fixtureIndex: i,\n message: `${customCallPath} is a custom tool call, which only OpenAI Responses requests (endpoint \"chat\") can carry — endpoint ${JSON.stringify(f.match.endpoint)} never serves it, so a request that matches this fixture gets an error instead`,\n });\n }\n if (f.match.systemMessage !== undefined) {\n const sm = f.match.systemMessage;\n if (typeof sm === \"string\") {\n // ok\n } else if (Array.isArray(sm)) {\n if (sm.length === 0) {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `match.systemMessage array must contain at least one substring`,\n });\n } else {\n for (let j = 0; j < sm.length; j++) {\n if (typeof sm[j] !== \"string\") {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `match.systemMessage[${j}] must be a string, got ${typeof sm[j]}`,\n });\n }\n }\n }\n } else {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `match.systemMessage must be a string or string[], got ${typeof sm}`,\n });\n }\n }\n if (f.match.context !== undefined && typeof f.match.context !== \"string\") {\n results.push({\n severity: \"error\",\n fixtureIndex: i,\n message: `match.context must be a string, got ${typeof f.match.context}`,\n });\n }\n\n // --- Warning checks ---\n\n // Duplicate userMessage shadowing — two fixtures are only genuine\n // duplicates when they would match the SAME requests, so the dedup key must\n // include EVERY match discriminator the router (matchFixtureDiagnostic in\n // router.ts) actually gates on: userMessage, systemMessage, inputText,\n // toolCallId, toolResultContains, toolName, model, responseFormat, endpoint,\n // context, embeddingRequest, sequenceIndex, turnIndex, and hasToolResult. Omitting any of these\n // (the old key only carried turnIndex/hasToolResult/toolResultContains/\n // sequenceIndex/context) flags two legitimately-distinct fixtures — e.g. two\n // that differ ONLY in toolCallId or model — as false duplicates.\n //\n // `predicate` is a function and cannot be compared for equivalence, so it is\n // never folded into a shared key: a fixture carrying a predicate is treated\n // as unconditionally distinct (keyed by its own index) so it neither shadows\n // nor is shadowed by another fixture on the userMessage axis.\n //\n // Values are serialised kind-aware so RegExp / string[] matchers do not\n // collide (a template literal would coerce a RegExp to its source and an\n // array via join, losing the distinction) — mirroring describeMatch in\n // router.ts.\n const um = f.match.userMessage;\n if (typeof um === \"string\" && um) {\n const m = f.match;\n const dedupKey =\n m.predicate !== undefined\n ? `predicate:${i}`\n : [\n serializeMatcher(m.userMessage),\n serializeMatcher(m.systemMessage),\n serializeMatcher(m.inputText),\n m.toolCallId,\n m.toolResultContains,\n m.toolName,\n serializeMatcher(m.model),\n m.responseFormat,\n m.endpoint,\n m.context,\n m.sequenceIndex,\n m.turnIndex,\n m.hasToolResult,\n // Appended only when set, so every key without it is unchanged.\n ...(m.toolNamespace !== undefined ? [`toolNamespace=${m.toolNamespace}`] : []),\n ...(m.embeddingRequest !== undefined ? [JSON.stringify(m.embeddingRequest)] : []),\n ]\n .map((v) => (v === undefined ? \"\" : String(v)))\n .join(\"|\");\n const prev = seenUserMessages.get(dedupKey);\n if (prev !== undefined) {\n const ref: ValidationRef = {\n rule: \"duplicate-user-message\",\n userMessage: um,\n shadows: prev,\n };\n results.push({\n severity: \"warning\",\n fixtureIndex: i,\n message: renderValidationRef(ref, String),\n ref,\n });\n } else {\n seenUserMessages.set(dedupKey, i);\n }\n }\n\n // Catch-all not in last position\n const match = f.match;\n const hasDiscriminator =\n match.endpoint !== undefined ||\n match.context !== undefined ||\n match.userMessage !== undefined ||\n match.systemMessage !== undefined ||\n match.inputText !== undefined ||\n match.embeddingRequest !== undefined ||\n match.responseFormat !== undefined ||\n match.toolCallId !== undefined ||\n match.toolResultContains !== undefined ||\n match.toolName !== undefined ||\n match.toolNamespace !== undefined ||\n match.model !== undefined ||\n match.predicate !== undefined ||\n match.turnIndex !== undefined ||\n match.sequenceIndex !== undefined ||\n match.hasToolResult !== undefined;\n\n if (!hasDiscriminator && i < fixtures.length - 1) {\n const ref: ValidationRef = {\n rule: \"catch-all-not-last\",\n shadowsFrom: i + 1,\n shadowsThrough: fixtures.length - 1,\n };\n results.push({\n severity: \"warning\",\n fixtureIndex: i,\n message: renderValidationRef(ref, String),\n ref,\n });\n }\n }\n\n return results;\n}\n\n// ---------------------------------------------------------------------------\n// FixtureLoadError — the error type for fail-loud fixture-load cases\n// ---------------------------------------------------------------------------\n\n/**\n * The `rule` values owned by MCP fakes (prefix `mcp-fakes/`). The\n * bad-block letters: (a) no scope, (b)\n * malformed scope, (c) shared + deny, (d) bad call entry, (e) duplicate tool,\n * (f) entry-id or block-id collision, (g) bad block/tool shape, (h) unknown\n * key on a block, tool or call entry, (i) mcpFakes, or one of its blocks, has\n * the wrong JSON type, mcpFakes holds no blocks, or an add has an invalid\n * origin or source item, (j) anyArgs not `true`.\n */\nexport const MCP_FAKES_LOAD_RULES = [\n \"mcp-fakes/bad-block:a\",\n \"mcp-fakes/bad-block:b\",\n \"mcp-fakes/bad-block:c\",\n \"mcp-fakes/bad-block:d\",\n \"mcp-fakes/bad-block:e\",\n \"mcp-fakes/bad-block:f\",\n \"mcp-fakes/bad-block:g\",\n \"mcp-fakes/bad-block:h\",\n \"mcp-fakes/bad-block:i\",\n \"mcp-fakes/bad-block:j\",\n \"mcp-fakes/mount-conflict\",\n \"mcp-fakes/loader-cannot-carry-fakes\",\n \"mcp-fakes/watch-reload-changed\",\n] as const;\n\nexport type McpFakesLoadRule = (typeof MCP_FAKES_LOAD_RULES)[number];\n\n/**\n * Every known `FixtureLoadError` rule, by prefix. A feature that throws\n * `FixtureLoadError` adds its own prefix here, with its rule values as a\n * union of string literals; a module can add one by declaration merging.\n */\nexport interface FixtureLoadRuleRegistry {\n \"mcp-fakes\": McpFakesLoadRule;\n}\n\n/**\n * A known `FixtureLoadError` rule, namespaced `<prefix>/<name>`. Each\n * registered value must start with its own prefix and `/` (any other value\n * drops out of the union), so a misspelled or unregistered rule fails to\n * compile.\n */\nexport type FixtureLoadRule = {\n [P in keyof FixtureLoadRuleRegistry]: FixtureLoadRuleRegistry[P] & `${P}/${string}`;\n}[keyof FixtureLoadRuleRegistry];\n\nexport interface FixtureLoadErrorInit {\n /** Namespaced `<prefix>/<name>`, one of {@link FixtureLoadRuleRegistry}. */\n rule: FixtureLoadRule;\n /** Source of the offending input (path, URL, `control-api#<n>`, `code#<n>`), or null. */\n file: string | null;\n /** `<source><load>[<block>]` of the offending block; omit or null when not tied to a block. */\n blockId?: string | null;\n /**\n * Entry id when one entry caused it (bad-block cases d, f, j, and h on a\n * call entry); omit or null otherwise.\n */\n entryId?: string | null;\n /**\n * The offending key or value, in words: text, or a `Message` from the\n * message builder (message-text.ts), whose user parts are quoted and cut\n * once. Cut to `MCP_FAKES_ECHO_LIMIT` chars in the message (see `capPart`).\n */\n detail: string | Message;\n}\n\nexport interface FixtureLoadErrorJSON {\n /** `\"FixtureLoadError\"`, or a subclass's own name (e.g. `\"McpFakesAddError\"`). */\n name: string;\n rule: string;\n file: string | null;\n blockId?: string | null;\n entryId?: string | null;\n message: string;\n}\n\n/**\n * `text` with every run of line breaks, and the spaces around it, folded to\n * one space: CR, LF, VT, FF, NEL, and the Unicode line and paragraph\n * separators.\n */\nfunction oneLine(text: string): string {\n return text.replace(/\\s*[\\r\\n\\v\\f\\u0085\\u2028\\u2029]+\\s*/g, \" \");\n}\n\n/**\n * The `detail` as a `FixtureLoadError` message adds it: built in a budget of\n * its own, cut to at most `MCP_FAKES_ECHO_LIMIT` chars in all, with each of\n * its user parts cut once. A text detail is folded onto one line, gets the\n * escaping of every JSON text part (`jsonText`: each control char left, C0,\n * DEL or C1, and the line and paragraph separators as `\\uXXXX`) and is cut\n * as JSON text (it is\n * made of fixed text, paths and quoted values, where a backslash only starts\n * a JSON escape; a stray one only moves the cut back a few chars).\n */\nfunction capPart(detail: string | Message): Message {\n const message = typeof detail === \"string\" ? msg`${jsonText(oneLine(detail))}` : detail;\n return within(MCP_FAKES_ECHO_LIMIT, message);\n}\n\n/**\n * One `file`, `blockId` or `entryId` as the message names it: JSON-quoted\n * (control chars escaped, so a name can neither break the line nor pose as\n * the message layout), and cut once to `MCP_FAKES_ECHO_LIMIT` chars at most.\n * An empty string is `\"\"`, so the message names every part `toJSON()` keeps.\n */\nfunction namePart(text: string): Message {\n return msg`${quote(text)}`;\n}\n\n/**\n * The message of a `FixtureLoadError` or `MisbehaviorConfigError`: the\n * `file`, every non-null `blockId` and `entryId`, then `[rule] detail`.\n */\nfunction loadErrorMessage(init: {\n rule: string;\n file: string | null;\n blockId?: string | null;\n entryId?: string | null;\n detail: string | Message;\n}): string {\n const file = init.file === null ? msg`<no file>` : namePart(init.file);\n const block = init.blockId == null ? msg`` : msg`, block ${namePart(init.blockId)}`;\n const entry = init.entryId == null ? msg`` : msg`, entry ${namePart(init.entryId)}`;\n return oneLine(\n build(msg`${file}${block}${entry}: [${fixed(init.rule)}] ${capPart(init.detail)}`),\n );\n}\n\n/**\n * Thrown for a fixture-load case aimock cannot honor (fail-loud rule). Only\n * MCP fakes use it (misbehavior throws its own `MisbehaviorConfigError`);\n * other fixture-loader paths log warnings.\n * `blockId` and `entryId` are own properties only when supplied, so\n * `toJSON()` (the control API's HTTP 400 `details`) omits them otherwise.\n * The message names `file` and every non-null `blockId` and `entryId`, an\n * empty string as `\"\"`, so it agrees with `toJSON()`. `options.cause` (the\n * standard `ErrorOptions`) keeps the underlying error, such as a JSON parse\n * failure; `toJSON()` leaves it out.\n */\nexport class FixtureLoadError extends Error {\n /** Typed `string` so a subclass can set its own name. */\n override readonly name: string = \"FixtureLoadError\";\n readonly rule: FixtureLoadRule;\n readonly file: string | null;\n declare readonly blockId?: string | null;\n declare readonly entryId?: string | null;\n\n constructor(init: FixtureLoadErrorInit, options?: ErrorOptions) {\n super(loadErrorMessage(init), options);\n this.rule = init.rule;\n this.file = init.file;\n if (init.blockId !== undefined) {\n Object.defineProperty(this, \"blockId\", {\n value: init.blockId,\n enumerable: true,\n writable: false,\n });\n }\n if (init.entryId !== undefined) {\n Object.defineProperty(this, \"entryId\", {\n value: init.entryId,\n enumerable: true,\n writable: false,\n });\n }\n }\n\n toJSON(): FixtureLoadErrorJSON {\n const json: FixtureLoadErrorJSON = {\n name: this.name,\n rule: this.rule,\n file: this.file,\n message: this.message,\n };\n if (this.blockId !== undefined) json.blockId = this.blockId;\n if (this.entryId !== undefined) json.entryId = this.entryId;\n return json;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Misbehavior in fixtures: recognized only when misbehavior is enabled\n// ---------------------------------------------------------------------------\n\nexport interface MisbehaviorConfigErrorJSON {\n /** `\"MisbehaviorConfigError\"`. */\n name: string;\n rule: string;\n file: string | null;\n message: string;\n}\n\n/**\n * Thrown for an invalid fixture `misbehavior` key, or one that cannot apply\n * to its fixture, on a server with misbehavior enabled (`enableMisbehavior:\n * true`, CLI `--misbehavior`). Without that opt-in the key is unused data\n * and nothing throws. The message has the `FixtureLoadError` layout:\n * `\"<file>\": [<rule>] <detail>`.\n */\nexport class MisbehaviorConfigError extends Error {\n override readonly name: string = \"MisbehaviorConfigError\";\n readonly rule: MisbehaviorRule;\n readonly file: string | null;\n\n constructor(init: { rule: MisbehaviorRule; file: string | null; detail: string }) {\n super(loadErrorMessage(init));\n this.rule = init.rule;\n this.file = init.file;\n }\n\n toJSON(): MisbehaviorConfigErrorJSON {\n return { name: this.name, rule: this.rule, file: this.file, message: this.message };\n }\n}\n\n/** A fixture entry's `misbehavior` value, held until misbehavior is enabled. */\ninterface HeldMisbehavior {\n value: unknown;\n /** Path for messages, e.g. `fixtures[2].misbehavior`. */\n path: string;\n file: string | null;\n}\n\nconst heldFixtureMisbehavior = new WeakMap<Fixture, HeldMisbehavior>();\nconst responsesToolsExtendedFixtures = new WeakSet<Fixture>();\n\n/** The keys a loaded fixture holds until a server with responsesTools \"extended\" reads them. */\ninterface HeldResponsesTools {\n /** `match.toolNamespace` as written, when the entry set it. */\n toolNamespace?: unknown;\n /** Whether the response carries `customToolCalls` / `responsesBlocks` to normalize. */\n normalize: boolean;\n}\n\nconst heldResponsesTools = new WeakMap<Fixture, HeldResponsesTools>();\n\n/**\n * @internal Mark a fixture for a server with `responsesTools: \"extended\"`,\n * where that server made the fixture itself (its own load, `--validate`, the\n * control API): the held `match.toolNamespace` is applied and\n * `customToolCalls` / `responsesBlocks` are normalized in place, and\n * `validateFixtures` checks those keys. Idempotent. A fixture the caller\n * passed in is never marked: an extended server reads it through\n * {@link responsesToolsExtendedView}, so another server sharing it still sees\n * it as 1.44.0 loaded it.\n */\nexport function markFixtureResponsesToolsExtended(fixture: Fixture): void {\n responsesToolsExtendedFixtures.add(fixture);\n const held = heldResponsesTools.get(fixture);\n if (held === undefined) return;\n heldResponsesTools.delete(fixture);\n if (held.toolNamespace !== undefined) {\n fixture.match = withHeldToolNamespace(fixture.match, held.toolNamespace);\n }\n if (held.normalize) {\n normalizeResponsesToolsKeys(fixture.response as unknown as Record<string, unknown>);\n }\n}\n\n/**\n * `match` with `toolNamespace` after `toolName`, in the same key order the\n * listing and journal have always shown in extended mode.\n */\nfunction withHeldToolNamespace(match: FixtureMatch, toolNamespace: unknown): FixtureMatch {\n const rebuilt: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(match)) {\n rebuilt[key] = value;\n if (key === \"toolName\") rebuilt.toolNamespace = toolNamespace;\n }\n if (!(\"toolNamespace\" in rebuilt)) rebuilt.toolNamespace = toolNamespace;\n return rebuilt as FixtureMatch;\n}\n\n/**\n * The extended view of one held fixture. `match` and `response` record the\n * fixture's values the view was built from, so it is rebuilt only when the\n * fixture's own `match` or `response` is replaced.\n */\ninterface ExtendedView {\n view: Fixture;\n match?: FixtureMatch;\n viewMatch?: FixtureMatch;\n response?: Fixture[\"response\"];\n viewResponse?: Fixture[\"response\"];\n}\n\nconst extendedViews = new WeakMap<Fixture, ExtendedView>();\n/** Each extended view's fixture, so identity bookkeeping can name the caller's object. */\nconst extendedViewSources = new WeakMap<Fixture, Fixture>();\n\n/**\n * @internal The fixture an extended view was made from\n * ({@link responsesToolsExtendedView}); any other fixture is itself. An\n * extended server counts and journals the caller's fixture, not its view, so\n * the identity APIs (`getFixtureMatchCount`, `findByFixture`, the count-map\n * keys, `JournalEntry.response.fixture`) take the object the caller passed in.\n */\nexport function responsesToolsViewSource(fixture: Fixture): Fixture {\n return extendedViewSources.get(fixture) ?? fixture;\n}\n\n/**\n * @internal The fixture as a server with `responsesTools: \"extended\"` reads\n * it. A fixture with held keys gets a separate object, one per fixture, with\n * `match.toolNamespace` applied and `customToolCalls` / `responsesBlocks`\n * normalized, as {@link markFixtureResponsesToolsExtended} would leave it.\n * The fixture itself is not changed, so a legacy server that shares it is\n * unaffected. Every other property is read from the fixture on each call.\n * A fixture with nothing held is returned as it is.\n */\nexport function responsesToolsExtendedView(fixture: Fixture): Fixture {\n const held = heldResponsesTools.get(fixture);\n if (held === undefined) return fixture;\n let cached = extendedViews.get(fixture);\n if (cached === undefined) {\n cached = { view: {} as Fixture };\n responsesToolsExtendedFixtures.add(cached.view);\n extendedViewSources.set(cached.view, fixture);\n extendedViews.set(fixture, cached);\n }\n if (cached.viewMatch === undefined || cached.match !== fixture.match) {\n cached.match = fixture.match;\n cached.viewMatch =\n held.toolNamespace !== undefined\n ? withHeldToolNamespace(fixture.match, held.toolNamespace)\n : fixture.match;\n }\n if (!(\"viewResponse\" in cached) || cached.response !== fixture.response) {\n cached.response = fixture.response;\n let viewResponse = fixture.response;\n if (held.normalize && typeof viewResponse === \"object\" && viewResponse !== null) {\n const copy = { ...(viewResponse as unknown as Record<string, unknown>) };\n normalizeResponsesToolsKeys(copy);\n viewResponse = copy as unknown as FixtureResponse;\n }\n cached.viewResponse = viewResponse;\n }\n const view = cached.view as unknown as Record<string, unknown>;\n const source = fixture as unknown as Record<string, unknown>;\n for (const key of Object.keys(view)) if (!(key in source)) delete view[key];\n for (const key of Object.keys(source)) {\n view[key] =\n key === \"match\" ? cached.viewMatch : key === \"response\" ? cached.viewResponse : source[key];\n }\n copyFixtureMisbehaviorPosition(fixture, cached.view);\n return cached.view;\n}\n\n/**\n * The fixture as 1.44.0 saw it: without `match.toolNamespace`,\n * `customToolCalls` and `responsesBlocks`. Returns the same object when none\n * is set; factory responses are left as they are.\n */\nfunction withoutResponsesToolsKeys(fixture: Fixture): Fixture {\n let match = fixture.match;\n if (\"toolNamespace\" in match) {\n const copy = { ...match };\n delete copy.toolNamespace;\n match = copy;\n }\n let response = fixture.response;\n if (\n typeof response !== \"function\" &&\n response !== null &&\n typeof response === \"object\" &&\n (\"customToolCalls\" in response || \"responsesBlocks\" in response)\n ) {\n const copy = { ...(response as unknown as Record<string, unknown>) };\n delete copy.customToolCalls;\n delete copy.responsesBlocks;\n response = copy as unknown as FixtureResponse;\n }\n if (match === fixture.match && response === fixture.response) return fixture;\n return { ...fixture, match, response };\n}\nconst misbehaviorEnabledFixtures = new WeakSet<Fixture>();\n\n/**\n * @internal Recognize the `misbehavior` key a loaded fixture entry carried,\n * for a server with misbehavior enabled: parse it onto the fixture (in place;\n * the loader made the object) and check it applies, or throw\n * `MisbehaviorConfigError`. A fixture with no held key is left unchanged.\n */\nexport function enableHeldFixtureMisbehavior(fixture: Fixture): void {\n const held = heldFixtureMisbehavior.get(fixture);\n if (held === undefined) return;\n const parsed = parseMisbehavior(held.value, held.path);\n if (parsed.ok) fixture.misbehavior = parsed.config;\n const issue = parsed.ok ? validateFixtureMisbehavior(fixture, held.path) : parsed.issue;\n if (issue) {\n throw new MisbehaviorConfigError({ rule: issue.rule, file: held.file, detail: issue.message });\n }\n heldFixtureMisbehavior.delete(fixture);\n misbehaviorEnabledFixtures.add(fixture);\n}\n\n/**\n * @internal Mark a fixture whose `misbehavior` a server with misbehavior\n * enabled has recognized, so `validateFixtures` checks it.\n */\nexport function markFixtureMisbehaviorEnabled(fixture: Fixture): void {\n misbehaviorEnabledFixtures.add(fixture);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAgDA,SAAgB,kBACd,KACA,aACiB;AACjB,KAAI,eAAe,IAAI,CAAE,QAAO,qBAAqB,KAAK,YAAY;CAEtE,MAAM,WAAW,EAAE,GAAG,KAAK;AAG3B,KAAI,OAAO,SAAS,YAAY,YAAY,SAAS,YAAY,KAC/D,UAAS,UAAU,KAAK,UAAU,SAAS,QAAQ;AAIrD,KAAI,MAAM,QAAQ,SAAS,UAAU,CACnC,UAAS,YAAa,SAAS,UAA6C,KAAK,OAAO;AACtF,MAAI,OAAO,GAAG,cAAc,YAAY,GAAG,cAAc,KACvD,QAAO;GAAE,GAAG;GAAI,WAAW,KAAK,UAAU,GAAG,UAAU;GAAE;AAE3D,SAAO;GACP;AAQJ,KAAI,MAAM,QAAQ,SAAS,OAAO,CAChC,UAAS,SAAU,SAAS,OAA0C,KAAK,UAAU;AACnF,MACE,SAAS,QACT,MAAM,SAAS,cACf,OAAO,MAAM,cAAc,YAC3B,MAAM,cAAc,KAEpB,QAAO;GAAE,GAAG;GAAO,WAAW,KAAK,UAAU,MAAM,UAAU;GAAE;AAEjE,SAAO;GACP;AAGJ,QAAO;;;;;;;;;AAUT,SAAgB,4BAA4B,UAAyC;AACnF,KAAI,MAAM,QAAQ,SAAS,gBAAgB,CACzC,UAAS,kBAAmB,SAAS,gBAA8B,KAAK,UAAU;EAChF,MAAM,IAAI;AACV,MACE,MAAM,QACN,OAAO,MAAM,YACb,EAAE,SAAS,cACX,OAAO,EAAE,cAAc,YACvB,EAAE,cAAc,KAEhB,QAAO;GAAE,GAAG;GAAG,WAAW,KAAK,UAAU,EAAE,UAAU;GAAE;AAEzD,SAAO;GACP;AAEJ,KAAI,MAAM,QAAQ,SAAS,gBAAgB,CACzC,UAAS,kBAAmB,SAAS,gBAA8B,KAAK,OAAO;EAC7E,MAAM,QAAQ;AACd,SAAO,UAAU,QAAQ,OAAO,UAAU,YAAY,MAAM,SAAS,SACjE;GAAE,GAAG;GAAO,MAAM;GAAU,GAC5B;GACJ;;AAIN,IAAM,2BAAN,cAAuC,UAAU;AAEjD,SAAgB,eACd,OACA,QACA,aACA,QACS;AACT,KAAI,UAAU,QAAQ,OAAO,UAAU,YAAY,MAAM,QAAQ,MAAM,CACrE,OAAM,IAAI,UAAU,kCAAkC;AAExD,KAAI,MAAM,SAAS,KACjB,OAAM,IAAI,UAAU,kCAAkC;AAExD,KAAI,OAAO,MAAM,UAAU,YAAY,MAAM,QAAQ,MAAM,MAAM,CAC/D,OAAM,IAAI,yBAAyB,kCAAkC;CAEvE,MAAM,UAAmB;EACvB,OAAO;GACL,aAAa,MAAM,MAAM;GACzB,eAAe,MAAM,MAAM;GAC3B,WAAW,MAAM,MAAM;GACvB,GAAI,MAAM,MAAM,qBAAqB,UAAa,EAChD,kBAAkB,MAAM,MAAM,kBAC/B;GACD,YAAY,MAAM,MAAM;GACxB,oBAAoB,MAAM,MAAM;GAChC,UAAU,MAAM,MAAM;GACtB,OAAO,MAAM,MAAM;GACnB,gBAAgB,MAAM,MAAM;GAC5B,UAAU,MAAM,MAAM;GACtB,GAAI,MAAM,MAAM,kBAAkB,UAAa,EAAE,eAAe,MAAM,MAAM,eAAe;GAC3F,GAAI,MAAM,MAAM,cAAc,UAAa,EACzC,WAAW,MAAM,MAAM,WACxB;GACD,GAAI,MAAM,MAAM,kBAAkB,UAAa,EAC7C,eAAe,MAAM,MAAM,eAC5B;GACD,GAAI,MAAM,MAAM,YAAY,UAAa,EAAE,SAAS,MAAM,MAAM,SAAS;GAC1E;EACD,UACE,MAAM,MAAM,aAAa,gBACrB,qBAAqB,MAAM,UAAU,YAAY,GACjD,kBAAkB,MAAM,UAAU,YAAY;EACpD,GAAI,MAAM,YAAY,UAAa,EAAE,SAAS,MAAM,SAAS;EAC7D,GAAI,MAAM,cAAc,UAAa,EAAE,WAAW,MAAM,WAAW;EACnE,GAAI,MAAM,wBAAwB,UAAa,EAC7C,qBAAqB,MAAM,qBAC5B;EACD,GAAI,MAAM,sBAAsB,UAAa,EAAE,mBAAmB,MAAM,mBAAmB;EAC3F,GAAI,MAAM,qBAAqB,UAAa,EAAE,kBAAkB,MAAM,kBAAkB;EACxF,GAAI,MAAM,oBAAoB,UAAa,EAAE,iBAAiB,MAAM,iBAAiB;EACrF,GAAI,MAAM,eAAe,QAAQ,EAAE,aAAa,MAAM,aAAa;EACnE,GAAI,MAAM,UAAU,UAAa,EAAE,OAAO,MAAM,OAAO;EACvD,GAAI,MAAM,yBAAyB,UAAa,EAC9C,sBAAsB,MAAM,sBAC7B;EACD,GAAI,MAAM,uBAAuB,UAAa,EAC5C,oBAAoB,MAAM,oBAC3B;EACD,GAAI,MAAM,aAAa,UAAa,EAAE,UAAU,MAAM,UAAU;EACjE;AAED,KAAI,OAAQ,+BAA8B,SAAS,GAAG,OAAO,KAAK,GAAG,OAAO,QAAQ;CAMpF,MAAM,oBAAoB,MAAM,MAAM,kBAAkB;CACxD,MAAM,WAAoB,QAAQ;CAClC,MAAM,mBACJ,MAAM,MAAM,aAAa,iBACzB,CAAC,eAAe,MAAM,SAAS,IAC/B,OAAO,aAAa,YACpB,aAAa,SACZ,qBAAqB,YAAY,qBAAqB;AACzD,KAAI,qBAAqB,iBACvB,oBAAmB,IAAI,SAAS;EAC9B,GAAI,qBAAqB,EAAE,eAAe,MAAM,MAAM,eAAe;EACrE,WAAW;EACZ,CAAC;AAMJ,KAAI,MAAM,gBAAgB,OACxB,wBAAuB,IAAI,SAAS;EAClC,OAAO,MAAM;EACb,MAAM,SAAS,YAAY,OAAO,MAAM,iBAAiB;EACzD,MAAM,QAAQ,QAAQ;EACvB,CAAC;AAKJ,KAAI,QAAQ,iBAAiB;EAC3B,MAAM,KAAK,QAAQ;AACnB,MAAI,CAAC,OAAO,SAAS,GAAG,OAAO,IAAI,GAAG,SAAS,EAAG,IAAG,SAAS;AAC9D,KAAG,qBAAqB,MAAM,QAAQ,GAAG,mBAAmB,GACxD,GAAG,mBAAmB,QAAQ,MAAM,OAAO,SAAS,EAAE,IAAI,KAAK,EAAE,GACjE,EAAE;AACN,MAAI,CAAC,OAAO,SAAS,GAAG,gBAAgB,IAAI,GAAG,kBAAkB,EAAG,IAAG,kBAAkB;;AAG3F,KAAI,QAAQ,eAAe,QAAQ,QAAQ,eAAe,GAAG;AAC3D,UAAQ,KAAK,6CAA6C,QAAQ,YAAY,aAAa;AAC3F,SAAO,QAAQ;;AAGjB,QAAO;;;;;;;;;;AAWT,SAAS,iBAAiB,OAAuD;AAC/E,KAAI,UAAU,OAAW,QAAO;AAChC,KAAI,iBAAiB,OAAQ,QAAO,MAAM,MAAM,OAAO,QAAQ,MAAM;AACrE,KAAI,MAAM,QAAQ,MAAM,CAAE,QAAO,OAAO,KAAK,UAAU,MAAM;AAC7D,QAAO,OAAO;;AAIhB,SAAS,KAAK,QAA4B,KAAa,GAAG,MAAuB;AAC/E,KAAI,OACF,QAAO,KAAK,KAAK,GAAG,KAAK;KAEzB,SAAQ,KAAK,oBAAoB,OAAO,GAAG,KAAK;;;;;;;AASpD,SAAgB,gBACd,UACA,QAC2B;CAC3B,IAAI;AACJ,KAAI;AACF,QAAM,aAAa,UAAU,QAAQ;UAC9B,KAAK;AACZ,OAAK,QAAQ,uBAAuB,SAAS,IAAI,IAAI;AACrD,SAAO;;AAGT,KAAI;AACF,SAAO,EAAE,OAAO,KAAK,MAAM,IAAI,EAAE;UAC1B,KAAK;AACZ,OAAK,QAAQ,mBAAmB,SAAS,IAAI,IAAI;AACjD,SAAO;;;;;;;;AASX,SAAgB,eAAe,QAAoD;AACjF,QACE,OAAO,WAAW,YAClB,WAAW,QACX,CAAC,MAAM,QAAQ,OAAO,IACtB,OAAO,UAAU,eAAe,KAAK,QAAQ,WAAW;;;;;;;;;;AAY5D,SAAgB,WACd,QACA,UACA,QACA,aACA,kBACA,SAAS,UACE;AACX,KACE,oBACA,OAAO,WAAW,YAClB,WAAW,QACX,CAAC,OAAO,UAAU,eAAe,KAAK,QAAQ,WAAW,CAEzD,QAAO,EAAE;AAEX,KACE,OAAO,WAAW,YAClB,WAAW,QACX,CAAC,MAAM,QAAS,OAAuB,SAAS,EAChD;AACA,OAAK,QAAQ,0CAA0C,WAAW;AAClE,SAAO,EAAE;;CAGX,MAAM,WAAsB,EAAE;AAC9B,MAAK,MAAM,CAAC,OAAO,UAAW,OAAuB,SAAS,SAAS,CACrE,KAAI;AACF,WAAS,KAAK,eAAe,OAAO,QAAQ,aAAa;GAAE,MAAM;GAAQ;GAAO,CAAC,CAAC;UAC3E,OAAO;AACd,MAAI,EAAE,iBAAiB,0BAA2B,OAAM;AACxD,OAAK,QAAQ,6BAA6B,MAAM,MAAM,SAAS,IAAI,MAAM,UAAU;;AAGvF,QAAO;;;;;;AAOT,SAAS,iBAAiB,UAAoC;AAC5D,QAAO,IAAI,iBAAiB;EAC1B,MAAM;EACN,MAAM;EACN,SAAS;EACT,SAAS;EACT,QAAQ,GAAG;EACZ,CAAC;;;;;;;AAQJ,SAAgB,gBACd,UACA,QACA,aACW;AACX,QAAO,mBAAmB,UAAU,UAAU,QAAQ,YAAY;;AAGpE,SAAS,mBACP,UACA,QACA,QACA,aACW;CACX,MAAM,OAAO,gBAAgB,UAAU,OAAO;AAC9C,KAAI,SAAS,KAAM,QAAO,EAAE;AAC5B,KAAI,eAAe,KAAK,MAAM,CAAE,OAAM,iBAAiB,SAAS;AAChE,QAAO,WAAW,KAAK,OAAO,UAAU,QAAQ,aAAa,OAAO,OAAO;;;;;;;;AAS7E,SAAgB,eACd,SACA,QACmD;CACnD,IAAI;AACJ,KAAI;AACF,YAAU,YAAY,QAAQ;UACvB,KAAK;AACZ,OAAK,QAAQ,4BAA4B,QAAQ,IAAI,IAAI;AACzD,SAAO;;CAGT,MAAM,YAAsB,EAAE;CAC9B,MAAM,UAAoB,EAAE;AAC5B,MAAK,MAAM,QAAQ,SAAS;EAC1B,MAAM,WAAW,KAAK,SAAS,KAAK;AACpC,MAAI;AACF,OAAI,SAAS,SAAS,CAAC,aAAa,EAAE;AACpC,YAAQ,KAAK,KAAK;AAClB;;WAEK,KAAK;AAEZ,OADc,IAA8B,SAC/B,SACX,MAAK,QAAQ,kBAAkB,SAAS,IAAI,IAAI;AAElD;;AAEF,MAAI,KAAK,SAAS,QAAQ,CACxB,WAAU,KAAK,KAAK;;AAGxB,WAAU,MAAM;AAChB,SAAQ,MAAM;AACd,QAAO;EAAE;EAAW;EAAS;;;;;;;;;AAU/B,SAAgB,oBACd,SACA,QACA,aACW;AACX,QAAO,kBAAkB,SAAS,IAAI,QAAQ,YAAY;;AAG5D,SAAS,kBACP,SACA,QACA,QACA,aACW;CACX,MAAM,UAAU,eAAe,SAAS,OAAO;AAC/C,KAAI,YAAY,KAAM,QAAO,EAAE;CAE/B,MAAM,WAAsB,EAAE;AAC9B,MAAK,MAAM,QAAQ,QAAQ,WAAW;EACpC,MAAM,WAAW,KAAK,SAAS,KAAK;AACpC,WAAS,KAAK,GAAG,mBAAmB,UAAU,SAAS,MAAM,QAAQ,YAAY,CAAC;;AAKpF,MAAK,MAAM,OAAO,QAAQ,QACxB,UAAS,KACP,GAAG,kBAAkB,KAAK,SAAS,IAAI,EAAE,GAAG,SAAS,IAAI,IAAI,QAAQ,YAAY,CAClF;AAGH,QAAO;;AAwBT,MAAM,yBACJ;;;;;;;AAgBF,SAAgB,oBACd,KACA,aACQ;AACR,QAAO,IAAI,SAAS,2BAChB,0BAA0B,IAAI,YAAY,sBAAsB,YAAY,IAAI,QAAQ,KACxF,gFAAgF,YAC9E,IAAI,YACL,CAAC;;AAGR,SAAS,kBACP,UACA,cACA,SACM;AACN,KAAI,SAAS,cAAc,QACzB;MAAI,OAAO,SAAS,cAAc,SAChC,SAAQ,KAAK;GACX,UAAU;GACV;GACA,SAAS;GACV,CAAC;WACO,SAAS,cAAc,GAChC,SAAQ,KAAK;GACX,UAAU;GACV;GACA,SAAS;GACV,CAAC;;;AAKR,SAAS,oBACP,UACA,cACA,SACM;AACN,KAAI,SAAS,gBAAgB,OAC3B,KAAI,CAAC,MAAM,QAAQ,SAAS,YAAY,CACtC,SAAQ,KAAK;EACX,UAAU;EACV;EACA,SAAS;EACV,CAAC;UACO,SAAS,YAAY,WAAW,EACzC,SAAQ,KAAK;EACX,UAAU;EACV;EACA,SAAS;EACV,CAAC;KAEF,MAAK,IAAI,IAAI,GAAG,IAAI,SAAS,YAAY,QAAQ,KAAK;AACpD,MAAI,OAAO,SAAS,YAAY,OAAO,UAAU;AAC/C,WAAQ,KAAK;IACX,UAAU;IACV;IACA,SAAS,eAAe,EAAE;IAC3B,CAAC;AACF;;AAEF,MAAI,SAAS,YAAY,OAAO,GAC9B,SAAQ,KAAK;GACX,UAAU;GACV;GACA,SAAS,eAAe,EAAE;GAC3B,CAAC;;;;;;;;AAYZ,SAAS,wBAAwB,UAAuC;AACtE,KAAI,aAAa,QAAQ,OAAO,aAAa,SAAU,QAAO;CAC9D,MAAM,IAAI;AACV,KAAI,MAAM,QAAQ,EAAE,gBAAgB,IAAI,EAAE,gBAAgB,SAAS,EACjE,QAAO;AAET,KAAI,MAAM,QAAQ,EAAE,gBAAgB,EAAE;EACpC,MAAM,IAAI,EAAE,gBAAgB,WACzB,MACC,MAAM,QAAQ,OAAO,MAAM,YAAa,EAAyB,SAAS,iBAC7E;AACD,MAAI,KAAK,EAAG,QAAO,mBAAmB,EAAE;;;;;;;;;;;;AAc5C,SAAS,0BACP,UACA,cACA,SACM;AACN,KAAI,aAAa,QAAQ,OAAO,aAAa,SAAU;CACvD,MAAM,IAAI;CACV,MAAM,SAAS,YAAoB,QAAQ,KAAK;EAAE,UAAU;EAAS;EAAc;EAAS,CAAC;CAC7F,MAAM,WAAW,YAAoB,QAAQ,KAAK;EAAE,UAAU;EAAW;EAAc;EAAS,CAAC;CACjG,MAAM,eAAe,IAA6B,SAAiB;AACjE,MAAI,OAAO,GAAG,SAAS,YAAY,GAAG,SAAS,GAC7C,OAAM,GAAG,KAAK,kCAAkC;AAElD,MAAI,OAAO,GAAG,UAAU,SACtB,OAAM,GAAG,KAAK,gDAAgD;WACrD,GAAG,UAAU,GACtB,SAAQ,GAAG,KAAK,iBAAiB;AAEnC,MAAI,GAAG,cAAc,OACnB,OAAM,GAAG,KAAK,0DAA0D;AAE1E,MAAI,GAAG,OAAO,UAAa,OAAO,GAAG,OAAO,SAC1C,OAAM,GAAG,KAAK,4BAA4B,OAAO,GAAG,KAAK;AAE3D,MAAI,GAAG,cAAc,WAAc,OAAO,GAAG,cAAc,YAAY,GAAG,cAAc,IACtF,OAAM,GAAG,KAAK,uCAAuC;;AAGzD,KAAI,EAAE,oBAAoB,OACxB,KAAI,CAAC,MAAM,QAAQ,EAAE,gBAAgB,CACnC,OAAM,yCAAyC,OAAO,EAAE,kBAAkB;KAE1E,GAAE,gBAAgB,SAAS,OAAgB,MAAM;AAC/C,MAAI,UAAU,QAAQ,OAAO,UAAU,YAAY,MAAM,QAAQ,MAAM,EAAE;AACvE,SAAM,mBAAmB,EAAE,qBAAqB;AAChD;;EAEF,MAAM,KAAK;AACX,MAAI,GAAG,SAAS,UAAa,GAAG,SAAS,SACvC,OAAM,mBAAmB,EAAE,sCAAsC;AAEnE,cAAY,IAAI,mBAAmB,EAAE,GAAG;GACxC;AAGN,KAAI,EAAE,oBAAoB,OAAW;AACrC,KAAI,CAAC,MAAM,QAAQ,EAAE,gBAAgB,EAAE;AACrC,QAAM,yCAAyC,OAAO,EAAE,kBAAkB;AAC1E;;AAEF,KAAI,MAAM,QAAQ,EAAE,OAAO,IAAI,EAAE,OAAO,SAAS,KAAK,EAAE,gBAAgB,SAAS,EAC/E,OAAM,2EAA2E;AAEnF,GAAE,gBAAgB,SAAS,OAAgB,MAAM;EAC/C,MAAM,OAAO,mBAAmB,EAAE;AAClC,MAAI,UAAU,QAAQ,OAAO,UAAU,YAAY,MAAM,QAAQ,MAAM,EAAE;AACvE,SAAM,GAAG,KAAK,oBAAoB;AAClC;;EAEF,MAAM,QAAQ;AACd,MAAI,MAAM,SAAS,QAAQ;AACzB,OAAI,OAAO,MAAM,SAAS,SAAU,OAAM,GAAG,KAAK,wBAAwB;AAC1E;;AAEF,MAAI,MAAM,SAAS,kBAAkB;AACnC,eAAY,OAAO,KAAK;AACxB;;AAEF,MAAI,MAAM,SAAS,YAAY;AAC7B,SACE,GAAG,KAAK,4DAA4D,KAAK,UAAU,MAAM,KAAK,GAC/F;AACD;;AAEF,MAAI,OAAO,MAAM,SAAS,YAAY,MAAM,SAAS,GACnD,OAAM,GAAG,KAAK,kCAAkC;AAElD,MAAI,MAAM,OAAO,UAAa,OAAO,MAAM,OAAO,SAChD,OAAM,GAAG,KAAK,4BAA4B,OAAO,MAAM,KAAK;AAE9D,MAAI,MAAM,UAAU,OAClB,OAAM,GAAG,KAAK,kDAAkD;AAElE,MACE,MAAM,cAAc,WACnB,OAAO,MAAM,cAAc,YAAY,MAAM,cAAc,IAE5D,OAAM,GAAG,KAAK,uCAAuC;EAEvD,MAAM,OAAO,MAAM;AACnB,MAAI,OAAO,SAAS,SAClB,KAAI;AACF,QAAK,MAAM,KAAK;UACV;AACN,SAAM,GAAG,KAAK,gCAAgC,KAAK,IAAI,yBAAyB;;WAEzE,SAAS,QAAQ,OAAO,SAAS,SAC1C,OAAM,GAAG,KAAK,0CAA0C;GAE1D;;AAGJ,SAAS,eACP,UACA,cACA,SACM;AACN,KAAI,SAAS,WAAW,OAAW;AAMnC,KAAI,CAAC,MAAM,QAAQ,SAAS,OAAO,EAAE;AACnC,UAAQ,KAAK;GACX,UAAU;GACV;GACA,SAAS,gCAAgC,OAAO,SAAS;GAC1D,CAAC;AACF;;AAGF,MAAK,IAAI,IAAI,GAAG,IAAI,SAAS,OAAO,QAAQ,KAAK;EAC/C,MAAM,QAAQ,SAAS,OAAO;AAC9B,MAAI,OAAO,UAAU,YAAY,UAAU,MAAM;AAC/C,WAAQ,KAAK;IACX,UAAU;IACV;IACA,SAAS,UAAU,EAAE;IACtB,CAAC;AACF;;AAEF,MAAI,MAAM,SAAS,UAAU,MAAM,SAAS,YAAY;AACtD,WAAQ,KAAK;IACX,UAAU;IACV;IACA,SAAS,UAAU,EAAE,2CAA2C,KAAK,UAAU,MAAM,KAAK;IAC3F,CAAC;AACF;;AAEF,MAAI,MAAM,SAAS,QACjB;OAAI,OAAO,MAAM,SAAS,SACxB,SAAQ,KAAK;IACX,UAAU;IACV;IACA,SAAS,UAAU,EAAE,+BAA+B,OAAO,MAAM;IAClE,CAAC;YACO,MAAM,SAAS,GAGxB,SAAQ,KAAK;IACX,UAAU;IACV;IACA,SAAS,UAAU,EAAE;IACtB,CAAC;SAEC;AAEL,OAAI,OAAO,MAAM,SAAS,YAAY,MAAM,SAAS,GACnD,SAAQ,KAAK;IACX,UAAU;IACV;IACA,SAAS,UAAU,EAAE;IACtB,CAAC;AAIJ,OAAI,OAAO,MAAM,cAAc,SAC7B,KAAI;AACF,SAAK,MAAM,MAAM,UAAU;WACrB;AACN,YAAQ,KAAK;KACX,UAAU;KACV;KACA,SAAS,UAAU,EAAE,iCAAiC,MAAM,UAAU,IAAI;KAC3E,CAAC;;YAEK,OAAO,MAAM,cAAc,YAAY,MAAM,cAAc,KACpE,SAAQ,KAAK;IACX,UAAU;IACV;IACA,SAAS,UAAU,EAAE,mDAAmD,OAAO,MAAM;IACtF,CAAC;AAEJ,OAAI,MAAM,OAAO,UAAa,OAAO,MAAM,OAAO,SAChD,SAAQ,KAAK;IACX,UAAU;IACV;IACA,SAAS,UAAU,EAAE,6BAA6B,OAAO,MAAM;IAChE,CAAC;;;CAWR,MAAM,mBAAmB,OAAO,SAAS,YAAY;CACrD,MAAM,qBAAqB,MAAM,QAAQ,SAAS,UAAU;AAC5D,MAAK,oBAAoB,uBAAuB,MAAM,QAAQ,SAAS,OAAO,EAAE;EAC9E,MAAM,aAAa,SAAS,OAAO,QAChC,MACC,KAAK,QACL,OAAO,MAAM,YACZ,EAAyB,SAAS,UACnC,OAAQ,EAAyB,SAAS,SAC7C;EACD,MAAM,qBAAqB,SAAS,OACjC,QACE,MACC,KAAK,QACL,OAAO,MAAM,YACZ,EAAyB,SAAS,cACnC,OAAQ,EAAyB,SAAS,SAC7C,CACA,KAAK,MAAM,EAAE,KAAK;AAGrB,MAAI,kBAEF;OADmB,WAAW,KAAK,MAAM,EAAE,KAAK,CAAC,KAAK,GAAG,KACtC,SAAS,QAC1B,SAAQ,KAAK;IACX,UAAU;IACV;IACA,SACE;IACH,CAAC;;AAKN,MAAI,oBAAoB;GACtB,MAAM,cAAe,SAAS,UAAwC,KAAK,OACzE,OAAO,IAAI,SAAS,WAAW,GAAG,OAAO,OAC1C;AAID,OAAI,EAFF,YAAY,WAAW,mBAAmB,UAC1C,YAAY,OAAO,GAAG,MAAM,MAAM,mBAAmB,GAAG,EAExD,SAAQ,KAAK;IACX,UAAU;IACV;IACA,SACE;IACH,CAAC;;;;;;;;;;;;AAcV,MAAa,wBACX;;;;;;AAOF,SAAgB,mBAAmB,QAAmC;AAIpE,KAAI,OAAO,WAAW,SAAU,QAAO;AACvC,QAAO,OAAO,UAAU,OAAO,IAAI,UAAU,OAAO,UAAU;;;;;;;;;;;AAYhE,SAAS,0BAA0B,KAAqC;CACtE,MAAM,cAAc,IAAI;AACxB,QACE,gBAAgB,UAChB,gBAAgB,UAChB,gBAAgB,eAChB,YAAY,WAAW,WAAW,IAClC,gBAAgB,SAChB,gBAAgB,6BAChB,gBAAgB,sBAChB,gBAAgB,0BAChB,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCpB,MAAM,uCAAuB,IAAI,SAAkB;;AAGnD,SAAgB,eAAe,SAA2B;AACxD,QAAO,qBAAqB,IAAI,QAAQ;;;;;;;AAQ1C,SAAgB,kBAAkB,UAAqB,SAA2B;CAChF,MAAM,MAAM,SAAS,QAAQ,QAAQ;AACrC,KAAI,QAAQ,GAAI,QAAO;AACvB,UAAS,OAAO,KAAK,EAAE;AACvB,wBAAuB,IAAI,SAAS,gBAAgB,SAAS,CAAC;AAC9D,QAAO;;;;;;;;;;;;AAaT,MAAM,yCAAyB,IAAI,SAA4B;AAC/D,MAAM,yCAAyB,IAAI,SAA0B;AAE7D,SAAS,gBAAgB,UAA6B;AACpD,QAAO,uBAAuB,IAAI,kBAAkB,SAAS,CAAC,IAAI;;;;;;;;;;AAWpE,SAAgB,kBAAkB,UAA2B;AAC3D,UAAS,SAAS;AAClB,wBAAuB,IAAI,kBAAkB,SAAS,EAAE,gBAAgB,SAAS,GAAG,EAAE;;;;;;;;;;;;;AAcxF,SAAgB,oBAAoB,UAAqB,SAA+B;AACtF,KAAI,YAAY,QAAQ,CAAC,qBAAqB,IAAI,QAAQ,CAAE;AAC5D,KAAI,SAAS,SAAS,QAAQ,CAAE;AAGhC,KAAI,uBAAuB,IAAI,QAAQ,KAAK,gBAAgB,SAAS,CAAE;AAGvE,UAAS,QAAQ,QAAQ;;AAG3B,SAAgB,kBACd,UACA,QACA,WACS;CACT,MAAM,gBAAiC;EACrC,OAAO;GACL,SAAS,WAAW,WAAW;GAC/B,MAAM,WAAW,QAAQ;GACzB,MAAM,WAAW;GAClB;EACD;EAGA,aAAa;EACd;CACD,MAAM,UAAmB;EACvB,OAAO,EAAE,WAAW,2BAA2B;EAC/C,gBAAgB;AAEd,qBAAkB,UAAU,QAAQ;AACpC,UAAO;;EAEV;AACD,sBAAqB,IAAI,QAAQ;AAEjC,UAAS,QAAQ,QAAQ;AACzB,QAAO;;AAGT,SAAgB,iBACd,UACA,aACoB;CACpB,MAAM,UAA8B,EAAE;CAEtC,MAAM,mCAAmB,IAAI,KAAqB;AAElD,MAAK,IAAI,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;EACxC,MAAM,WAAW,SAAS;EAI1B,MAAM,IAAI,+BAA+B,IAAI,SAAS,GAClD,WACA,0BAA0B,SAAS;EACvC,MAAM,WAAW,EAAE;EAInB,MAAM,mBAAmB,2BAA2B,IAAI,SAAS,GAC7D,2BAA2B,UAAU,YAAY,EAAE,eAAe,GAClE;AACJ,MAAI,iBACF,SAAQ,KAAK;GAAE,UAAU;GAAS,cAAc;GAAG,SAAS,iBAAiB;GAAS,CAAC;WAC9E,2BAA2B,IAAI,SAAS,IAAI,EAAE,gBAAgB,QAAW;GAClF,MAAM,SAAS,iBAAiB,EAAE,YAAY;AAC9C,OAAI,OAAO,MAAM,OAAO,OAAO,SAAS,QACtC;SAAK,MAAM,SAAS,OAAO,OAAO,OAChC,KAAI,MAAM,SAAS,UAAa,MAAM,OAAO,KAAK,MAAM,UAAU,OAChE,SAAQ,KAAK;KACX,UAAU;KACV,cAAc;KACd,SACE;KACH,CAAC;;;AAMV,MACE,OAAO,aAAa,eACnB,eAAe,SAAS,IAAI,EAAE,MAAM,aAAa,eAElD,KAAI;AACF,wBAAqB,UAAU,YAAY;WACpC,OAAO;AACd,WAAQ,KAAK;IACX,UAAU;IACV,cAAc;IACd,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;IAChE,CAAC;;AAMN,MAAI,OAAO,aAAa,YAAY,QAE7B;AAML,OACE,CAAC,0BAA0B,SAAS,IACpC,CAAC,eAAe,SAAS,IACzB,CAAC,mBAAmB,SAAS,IAC7B,CAAC,gBAAgB,SAAS,IAC1B,CAAC,oBAAoB,SAAS,IAC9B,CAAC,gBAAgB,SAAS,IAC1B,CAAC,gBAAgB,SAAS,IAC1B,CAAC,wBAAwB,SAAS,IAClC,CAAC,gBAAgB,SAAS,IAC1B,CAAC,eAAe,SAAS,IACzB,CAAC,eAAe,SAAS,CAEzB,SAAQ,KAAK;IACX,UAAU;IACV,cAAc;IACd,SACE;IACH,CAAC;GASJ,MAAM,oBACH,MAAM,QAAS,SAAkC,OAAO,IACtD,SAAmC,OAAO,SAAS,KACrD,MAAM,QAAS,SAA2C,gBAAgB,IACxE,SAA4C,gBAAgB,SAAS;GAC1E,MAAM,qBACJ,MAAM,QAAS,SAA2C,gBAAgB,IACzE,SAA4C,gBAAgB,SAAS;AAGxE,OAAI,eAAe,SAAS,EAAE;AAC5B,QAAI,SAAS,YAAY,MAAM,CAAC,kBAC9B,SAAQ,KAAK;KACX,UAAU;KACV,cAAc;KACd,SAAS;KACV,CAAC;AAEJ,sBAAkB,UAAU,GAAG,QAAQ;AACvC,wBAAoB,UAAU,GAAG,QAAQ;;AAI3C,OAAI,0BAA0B,SAAS,EAAE;AAOvC,QAAI,OAAO,SAAS,YAAY,UAC9B;SAAI,SAAS,YAAY,MAAM,CAAC,kBAC9B,SAAQ,KAAK;MACX,UAAU;MACV,cAAc;MACd,SAAS;MACV,CAAC;;AAGN,QAAI,MAAM,QAAQ,SAAS,UAAU,EAAE;AACrC,SAAI,SAAS,UAAU,WAAW,KAAK,CAAC,mBACtC,SAAQ,KAAK;MACX,UAAU;MACV,cAAc;MACd,SAAS;MACV,CAAC;AAEJ,UAAK,IAAI,IAAI,GAAG,IAAI,SAAS,UAAU,QAAQ,KAAK;MAClD,MAAM,KAAK,SAAS,UAAU;AAC9B,UAAI,CAAC,GAAG,KACN,SAAQ,KAAK;OACX,UAAU;OACV,cAAc;OACd,SAAS,aAAa,EAAE;OACzB,CAAC;AAEJ,UAAI;AACF,YAAK,MAAM,GAAG,UAAU;cAClB;AACN,eAAQ,KAAK;QACX,UAAU;QACV,cAAc;QACd,SAAS,aAAa,EAAE,iCAAiC,GAAG,UAAU,IAAI;QAC3E,CAAC;;;;AAIR,sBAAkB,UAAU,GAAG,QAAQ;AACvC,wBAAoB,UAAU,GAAG,QAAQ;;AAM3C,kBACE,UACA,GACA,QACD;AAED,6BAA0B,UAAU,GAAG,QAAQ;AAG/C,OAAI,mBAAmB,SAAS,EAAE;AAChC,QAAI,SAAS,UAAU,WAAW,KAAK,CAAC,mBACtC,SAAQ,KAAK;KACX,UAAU;KACV,cAAc;KACd,SAAS;KACV,CAAC;AAEJ,SAAK,IAAI,IAAI,GAAG,IAAI,SAAS,UAAU,QAAQ,KAAK;KAClD,MAAM,KAAK,SAAS,UAAU;AAC9B,SAAI,CAAC,GAAG,KACN,SAAQ,KAAK;MACX,UAAU;MACV,cAAc;MACd,SAAS,aAAa,EAAE;MACzB,CAAC;AAEJ,SAAI;AACF,WAAK,MAAM,GAAG,UAAU;aAClB;AACN,cAAQ,KAAK;OACX,UAAU;OACV,cAAc;OACd,SAAS,aAAa,EAAE,iCAAiC,GAAG,UAAU,IAAI;OAC3E,CAAC;;;AAGN,wBAAoB,UAAU,GAAG,QAAQ;;AAI3C,OAAI,gBAAgB,SAAS,EAAE;AAC7B,QAAI,CAAC,SAAS,MAAM,QAClB,SAAQ,KAAK;KACX,UAAU;KACV,cAAc;KACd,SAAS;KACV,CAAC;AAKJ,QAAI,SAAS,WAAW,UAAa,CAAC,mBAAmB,SAAS,OAAO,CACvE,SAAQ,KAAK;KACX,UAAU;KACV,cAAc;KACd,SAAS,gBAAgB,SAAS,OAAO,6CAA6C;KACvF,CAAC;AAMJ,QAAI,SAAS,gBAAgB,UAAa,OAAO,SAAS,gBAAgB,UACxE,SAAQ,KAAK;KACX,UAAU;KACV,cAAc;KACd,SAAS,4CAA4C,OAAO,SAAS;KACtE,CAAC;;AAKN,OAAI,oBAAoB,SAAS,EAAE;AACjC,QAAI,SAAS,UAAU,WAAW,EAChC,SAAQ,KAAK;KACX,UAAU;KACV,cAAc;KACd,SAAS;KACV,CAAC;AAEJ,SAAK,IAAI,IAAI,GAAG,IAAI,SAAS,UAAU,QAAQ,IAC7C,KAAI,OAAO,SAAS,UAAU,OAAO,UAAU;AAC7C,aAAQ,KAAK;MACX,UAAU;MACV,cAAc;MACd,SAAS,aAAa,EAAE;MACzB,CAAC;AACF;;;AAMN,OAAI,gBAAgB,SAAS,IAAI,OAAO,SAAS,UAAU,UAAU;IACnE,MAAM,WAAW,SAAS;AAC1B,QAAI,OAAO,SAAS,YAAY,YAAY,SAAS,YAAY,GAC/D,SAAQ,KAAK;KACX,UAAU;KACV,cAAc;KACd,SAAS;KACV,CAAC;AAEJ,QAAI,SAAS,gBAAgB,UAAa,OAAO,SAAS,gBAAgB,SACxE,SAAQ,KAAK;KACX,UAAU;KACV,cAAc;KACd,SAAS,2CAA2C,OAAO,SAAS;KACrE,CAAC;;AAKN,OACE,eAAe,SAAS,IACxB,mBAAmB,SAAS,IAC5B,0BAA0B,SAAS,EACnC;IACA,MAAM,IAAI;AACV,QAAI,EAAE,OAAO,UAAa,OAAO,EAAE,OAAO,SACxC,SAAQ,KAAK;KACX,UAAU;KACV,cAAc;KACd,SAAS,uCAAuC,OAAO,EAAE;KAC1D,CAAC;AAEJ,QAAI,EAAE,YAAY,WAAc,OAAO,EAAE,YAAY,YAAY,EAAE,UAAU,GAC3E,SAAQ,KAAK;KACX,UAAU;KACV,cAAc;KACd,SAAS;KACV,CAAC;AAEJ,QAAI,EAAE,UAAU,UAAa,OAAO,EAAE,UAAU,SAC9C,SAAQ,KAAK;KACX,UAAU;KACV,cAAc;KACd,SAAS,0CAA0C,OAAO,EAAE;KAC7D,CAAC;AAEJ,QAAI,EAAE,iBAAiB,UAAa,OAAO,EAAE,iBAAiB,SAC5D,SAAQ,KAAK;KACX,UAAU;KACV,cAAc;KACd,SAAS,iDAAiD,OAAO,EAAE;KACpE,CAAC;AAEJ,QAAI,EAAE,SAAS,UAAa,OAAO,EAAE,SAAS,SAC5C,SAAQ,KAAK;KACX,UAAU;KACV,cAAc;KACd,SAAS,yCAAyC,OAAO,EAAE;KAC5D,CAAC;AAEJ,QAAI,EAAE,UAAU,UAAa,EAAE,UAAU,gBAAgB,EAAE,UAAU,eACnE,SAAQ,KAAK;KACX,UAAU;KACV,cAAc;KACd,SAAS;KACV,CAAC;AAEJ,QAAI,EAAE,sBAAsB,UAAa,OAAO,EAAE,sBAAsB,SACtE,SAAQ,KAAK;KACX,UAAU;KACV,cAAc;KACd,SAAS,sDAAsD,OAAO,EAAE;KACzE,CAAC;AAEJ,QAAI,EAAE,UAAU,OACd,KAAI,OAAO,EAAE,UAAU,YAAY,EAAE,UAAU,QAAQ,MAAM,QAAQ,EAAE,MAAM,CAC3E,SAAQ,KAAK;KACX,UAAU;KACV,cAAc;KACd,SAAS;KACV,CAAC;QAeF,MAAK,MAAM,OAAO,OAAO,KAAK,EAAE,MAAM,EAAE;KACtC,MAAM,MAAO,EAAE,MAAkC;AACjD,SAAI,QAAQ,OAAW;KACvB,MAAM,aAAa,wBAAwB,IAAI;AAC/C,SAAI,WAAW,SAAS,sBAAsB,OAAO,QAAQ,SAAU;AACvE,SAAI,WAAW,SAAS,YAAY,WAAW,SAAS,mBACtD,KAAI,OAAO,QAAQ,YAAY,QAAQ,QAAQ,MAAM,QAAQ,IAAI,CAC/D,SAAQ,KAAK;MACX,UAAU;MACV,cAAc;MACd,SAAS,mBAAmB,IAAI,YAAY,WAAW,SAAS,qBAAqB,uBAAuB,YAAY,QACtH,MAAM,QAAQ,IAAI,GAAG,UAAU,OAAO;MAEzC,CAAC;UACG;MAGL,MAAM,QAAQ;AACd,WAAK,MAAM,YAAY,WAAW,gBAAgB;OAChD,MAAM,WAAW,MAAM;AACvB,WAAI,aAAa,UAAa,OAAO,aAAa,SAChD,SAAQ,KAAK;QACX,UAAU;QACV,cAAc;QACd,SAAS,mBAAmB,IAAI,GAAG,SAAS,0BAA0B,OAAO;QAC9E,CAAC;;;cAIC,WAAW,SAAS,WAC7B;UAAI,OAAO,QAAQ,UACjB,SAAQ,KAAK;OACX,UAAU;OACV,cAAc;OACd,SAAS,mDAAmD,OAAO;OACpE,CAAC;gBAEK,OAAO,QAAQ,SACxB,SAAQ,KAAK;MACX,UAAU;MACV,cAAc;MACd,SAAS,mBAAmB,IAAI,0BAA0B,OAAO;MAClE,CAAC;;;;AASd,MAAI,EAAE,YAAY,UAAa,EAAE,UAAU,EACzC,SAAQ,KAAK;GACX,UAAU;GACV,cAAc;GACd,SAAS;GACV,CAAC;AAEJ,MAAI,EAAE,cAAc,UAAa,EAAE,YAAY,EAC7C,SAAQ,KAAK;GACX,UAAU;GACV,cAAc;GACd,SAAS;GACV,CAAC;AAEJ,MAAI,EAAE,wBAAwB,UAAa,EAAE,sBAAsB,EACjE,SAAQ,KAAK;GACX,UAAU;GACV,cAAc;GACd,SAAS;GACV,CAAC;AAEJ,MAAI,EAAE,sBAAsB,UAAa,EAAE,oBAAoB,EAC7D,SAAQ,KAAK;GACX,UAAU;GACV,cAAc;GACd,SAAS;GACV,CAAC;AAEJ,MAAI,EAAE,qBAAqB,QAAW;GACpC,MAAM,KAAK,EAAE;AACb,OAAI,GAAG,SAAS,UAAa,GAAG,OAAO,EACrC,SAAQ,KAAK;IACX,UAAU;IACV,cAAc;IACd,SAAS;IACV,CAAC;AAEJ,OAAI,GAAG,QAAQ,UAAa,GAAG,OAAO,EACpC,SAAQ,KAAK;IACX,UAAU;IACV,cAAc;IACd,SAAS;IACV,CAAC;AAEJ,OAAI,GAAG,WAAW,WAAc,GAAG,SAAS,KAAK,GAAG,SAAS,GAC3D,SAAQ,KAAK;IACX,UAAU;IACV,cAAc;IACd,SAAS;IACV,CAAC;;AAGN,MAAI,EAAE,UAAU,QAAW;GASzB,MAAM,KAAK,EAAE;AACb,QAAK,MAAM,SAAS,mBAAmB;IACrC,MAAM,QAAQ,GAAG;AACjB,QAAI,UAAU,OAAW;AAEzB,SADiB,OAAO,UAAU,WAAW,gBAAgB,OAAO,MAAM,GAAG,YAC5D,QAAW;KAC1B,MAAM,QAAQ,aAAa,OAAO,UAAU,yBAAyB;AACrE,aAAQ,KAAK;MACX,UAAU;MACV,cAAc;MACd,SAAS,SAAS,MAAM,WAAW,MAAM,iBAAiB,aAAa,OAAO;MAC/E,CAAC;;;;AAMR,MAAI,EAAE,MAAM,cAAc,QACxB;OACE,OAAO,EAAE,MAAM,cAAc,YAC7B,EAAE,MAAM,YAAY,KACpB,CAAC,OAAO,UAAU,EAAE,MAAM,UAAU,CAEpC,SAAQ,KAAK;IACX,UAAU;IACV,cAAc;IACd,SAAS;IACV,CAAC;;AAGN,MAAI,EAAE,MAAM,kBAAkB,QAC5B;OACE,OAAO,EAAE,MAAM,kBAAkB,YACjC,EAAE,MAAM,gBAAgB,KACxB,CAAC,OAAO,UAAU,EAAE,MAAM,cAAc,CAExC,SAAQ,KAAK;IACX,UAAU;IACV,cAAc;IACd,SAAS;IACV,CAAC;;AAGN,MAAI,EAAE,MAAM,kBAAkB,UAAa,OAAO,EAAE,MAAM,kBAAkB,UAC1E,SAAQ,KAAK;GACX,UAAU;GACV,cAAc;GACd,SAAS,8CAA8C,OAAO,EAAE,MAAM;GACvE,CAAC;AAEJ,MAAI,EAAE,MAAM,uBAAuB,QACjC;OAAI,OAAO,EAAE,MAAM,uBAAuB,SACxC,SAAQ,KAAK;IACX,UAAU;IACV,cAAc;IACd,SAAS,kDAAkD,OAAO,EAAE,MAAM;IAC3E,CAAC;YACO,EAAE,MAAM,mBAAmB,WAAW,EAI/C,SAAQ,KAAK;IACX,UAAU;IACV,cAAc;IACd,SAAS;IACV,CAAC;;AAGN,MAAI,EAAE,MAAM,kBAAkB,QAC5B;OAAI,OAAO,EAAE,MAAM,kBAAkB,SACnC,SAAQ,KAAK;IACX,UAAU;IACV,cAAc;IACd,SAAS,6CAA6C,OAAO,EAAE,MAAM;IACtE,CAAC;YACO,EAAE,MAAM,cAAc,WAAW,EAK1C,SAAQ,KAAK;IACX,UAAU;IACV,cAAc;IACd,SAAS;IACV,CAAC;;AAGN,MACE,OAAO,EAAE,MAAM,kBAAkB,YACjC,EAAE,MAAM,kBAAkB,MAC1B,EAAE,MAAM,aAAa,UACrB,EAAE,MAAM,aAAa,OAKrB,SAAQ,KAAK;GACX,UAAU;GACV,cAAc;GACd,SAAS,mDAAmD,KAAK,UAAU,EAAE,MAAM,SAAS,CAAC;GAC9F,CAAC;EAEJ,MAAM,iBACJ,EAAE,MAAM,aAAa,UAAa,EAAE,MAAM,aAAa,SACnD,wBAAwB,EAAE,SAAS,GACnC;AACN,MAAI,mBAAmB,OAGrB,SAAQ,KAAK;GACX,UAAU;GACV,cAAc;GACd,SAAS,GAAG,eAAe,sGAAsG,KAAK,UAAU,EAAE,MAAM,SAAS,CAAC;GACnK,CAAC;AAEJ,MAAI,EAAE,MAAM,kBAAkB,QAAW;GACvC,MAAM,KAAK,EAAE,MAAM;AACnB,OAAI,OAAO,OAAO,UAAU,YAEjB,MAAM,QAAQ,GAAG,EAC1B;QAAI,GAAG,WAAW,EAChB,SAAQ,KAAK;KACX,UAAU;KACV,cAAc;KACd,SAAS;KACV,CAAC;QAEF,MAAK,IAAI,IAAI,GAAG,IAAI,GAAG,QAAQ,IAC7B,KAAI,OAAO,GAAG,OAAO,SACnB,SAAQ,KAAK;KACX,UAAU;KACV,cAAc;KACd,SAAS,uBAAuB,EAAE,0BAA0B,OAAO,GAAG;KACvE,CAAC;SAKR,SAAQ,KAAK;IACX,UAAU;IACV,cAAc;IACd,SAAS,yDAAyD,OAAO;IAC1E,CAAC;;AAGN,MAAI,EAAE,MAAM,YAAY,UAAa,OAAO,EAAE,MAAM,YAAY,SAC9D,SAAQ,KAAK;GACX,UAAU;GACV,cAAc;GACd,SAAS,uCAAuC,OAAO,EAAE,MAAM;GAChE,CAAC;EAwBJ,MAAM,KAAK,EAAE,MAAM;AACnB,MAAI,OAAO,OAAO,YAAY,IAAI;GAChC,MAAM,IAAI,EAAE;GACZ,MAAM,WACJ,EAAE,cAAc,SACZ,aAAa,MACb;IACE,iBAAiB,EAAE,YAAY;IAC/B,iBAAiB,EAAE,cAAc;IACjC,iBAAiB,EAAE,UAAU;IAC7B,EAAE;IACF,EAAE;IACF,EAAE;IACF,iBAAiB,EAAE,MAAM;IACzB,EAAE;IACF,EAAE;IACF,EAAE;IACF,EAAE;IACF,EAAE;IACF,EAAE;IAEF,GAAI,EAAE,kBAAkB,SAAY,CAAC,iBAAiB,EAAE,gBAAgB,GAAG,EAAE;IAC7E,GAAI,EAAE,qBAAqB,SAAY,CAAC,KAAK,UAAU,EAAE,iBAAiB,CAAC,GAAG,EAAE;IACjF,CACE,KAAK,MAAO,MAAM,SAAY,KAAK,OAAO,EAAE,CAAE,CAC9C,KAAK,IAAI;GAClB,MAAM,OAAO,iBAAiB,IAAI,SAAS;AAC3C,OAAI,SAAS,QAAW;IACtB,MAAM,MAAqB;KACzB,MAAM;KACN,aAAa;KACb,SAAS;KACV;AACD,YAAQ,KAAK;KACX,UAAU;KACV,cAAc;KACd,SAAS,oBAAoB,KAAK,OAAO;KACzC;KACD,CAAC;SAEF,kBAAiB,IAAI,UAAU,EAAE;;EAKrC,MAAM,QAAQ,EAAE;AAmBhB,MAAI,EAjBF,MAAM,aAAa,UACnB,MAAM,YAAY,UAClB,MAAM,gBAAgB,UACtB,MAAM,kBAAkB,UACxB,MAAM,cAAc,UACpB,MAAM,qBAAqB,UAC3B,MAAM,mBAAmB,UACzB,MAAM,eAAe,UACrB,MAAM,uBAAuB,UAC7B,MAAM,aAAa,UACnB,MAAM,kBAAkB,UACxB,MAAM,UAAU,UAChB,MAAM,cAAc,UACpB,MAAM,cAAc,UACpB,MAAM,kBAAkB,UACxB,MAAM,kBAAkB,WAED,IAAI,SAAS,SAAS,GAAG;GAChD,MAAM,MAAqB;IACzB,MAAM;IACN,aAAa,IAAI;IACjB,gBAAgB,SAAS,SAAS;IACnC;AACD,WAAQ,KAAK;IACX,UAAU;IACV,cAAc;IACd,SAAS,oBAAoB,KAAK,OAAO;IACzC;IACD,CAAC;;;AAIN,QAAO;;;;;;;AAwFT,SAAS,QAAQ,MAAsB;AACrC,QAAO,KAAK,QAAQ,wCAAwC,IAAI;;;;;;;;;;;;AAalE,SAAS,QAAQ,QAAmC;AAElD,QAAO,OAAO,sBADE,OAAO,WAAW,WAAW,GAAG,GAAG,SAAS,QAAQ,OAAO,CAAC,KAAK,OACrC;;;;;;;;AAS9C,SAAS,SAAS,MAAuB;AACvC,QAAO,GAAG,GAAG,MAAM,KAAK;;;;;;AAO1B,SAAS,iBAAiB,MAMf;AAIT,QAAO,QACL,MAAM,GAAG,GAJE,KAAK,SAAS,OAAO,GAAG,cAAc,SAAS,KAAK,KAAK,GACxD,KAAK,WAAW,OAAO,GAAG,KAAK,GAAG,WAAW,SAAS,KAAK,QAAQ,KACnE,KAAK,WAAW,OAAO,GAAG,KAAK,GAAG,WAAW,SAAS,KAAK,QAAQ,GAE9C,KAAK,MAAM,KAAK,KAAK,CAAC,IAAI,QAAQ,KAAK,OAAO,GAAG,CACnF;;;;;;;;;;;;;AAcH,IAAa,mBAAb,cAAsC,MAAM;;CAE1C,AAAkB,OAAe;CACjC,AAAS;CACT,AAAS;CAIT,YAAY,MAA4B,SAAwB;AAC9D,QAAM,iBAAiB,KAAK,EAAE,QAAQ;AACtC,OAAK,OAAO,KAAK;AACjB,OAAK,OAAO,KAAK;AACjB,MAAI,KAAK,YAAY,OACnB,QAAO,eAAe,MAAM,WAAW;GACrC,OAAO,KAAK;GACZ,YAAY;GACZ,UAAU;GACX,CAAC;AAEJ,MAAI,KAAK,YAAY,OACnB,QAAO,eAAe,MAAM,WAAW;GACrC,OAAO,KAAK;GACZ,YAAY;GACZ,UAAU;GACX,CAAC;;CAIN,SAA+B;EAC7B,MAAM,OAA6B;GACjC,MAAM,KAAK;GACX,MAAM,KAAK;GACX,MAAM,KAAK;GACX,SAAS,KAAK;GACf;AACD,MAAI,KAAK,YAAY,OAAW,MAAK,UAAU,KAAK;AACpD,MAAI,KAAK,YAAY,OAAW,MAAK,UAAU,KAAK;AACpD,SAAO;;;;;;;;;;AAuBX,IAAa,yBAAb,cAA4C,MAAM;CAChD,AAAkB,OAAe;CACjC,AAAS;CACT,AAAS;CAET,YAAY,MAAsE;AAChF,QAAM,iBAAiB,KAAK,CAAC;AAC7B,OAAK,OAAO,KAAK;AACjB,OAAK,OAAO,KAAK;;CAGnB,SAAqC;AACnC,SAAO;GAAE,MAAM,KAAK;GAAM,MAAM,KAAK;GAAM,MAAM,KAAK;GAAM,SAAS,KAAK;GAAS;;;AAYvF,MAAM,yCAAyB,IAAI,SAAmC;AACtE,MAAM,iDAAiC,IAAI,SAAkB;AAU7D,MAAM,qCAAqB,IAAI,SAAsC;;;;;;;;;;;AAYrE,SAAgB,kCAAkC,SAAwB;AACxE,gCAA+B,IAAI,QAAQ;CAC3C,MAAM,OAAO,mBAAmB,IAAI,QAAQ;AAC5C,KAAI,SAAS,OAAW;AACxB,oBAAmB,OAAO,QAAQ;AAClC,KAAI,KAAK,kBAAkB,OACzB,SAAQ,QAAQ,sBAAsB,QAAQ,OAAO,KAAK,cAAc;AAE1E,KAAI,KAAK,UACP,6BAA4B,QAAQ,SAA+C;;;;;;AAQvF,SAAS,sBAAsB,OAAqB,eAAsC;CACxF,MAAM,UAAmC,EAAE;AAC3C,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,EAAE;AAChD,UAAQ,OAAO;AACf,MAAI,QAAQ,WAAY,SAAQ,gBAAgB;;AAElD,KAAI,EAAE,mBAAmB,SAAU,SAAQ,gBAAgB;AAC3D,QAAO;;AAgBT,MAAM,gCAAgB,IAAI,SAAgC;;AAE1D,MAAM,sCAAsB,IAAI,SAA2B;;;;;;;;AAS3D,SAAgB,yBAAyB,SAA2B;AAClE,QAAO,oBAAoB,IAAI,QAAQ,IAAI;;;;;;;;;;;AAY7C,SAAgB,2BAA2B,SAA2B;CACpE,MAAM,OAAO,mBAAmB,IAAI,QAAQ;AAC5C,KAAI,SAAS,OAAW,QAAO;CAC/B,IAAI,SAAS,cAAc,IAAI,QAAQ;AACvC,KAAI,WAAW,QAAW;AACxB,WAAS,EAAE,MAAM,EAAE,EAAa;AAChC,iCAA+B,IAAI,OAAO,KAAK;AAC/C,sBAAoB,IAAI,OAAO,MAAM,QAAQ;AAC7C,gBAAc,IAAI,SAAS,OAAO;;AAEpC,KAAI,OAAO,cAAc,UAAa,OAAO,UAAU,QAAQ,OAAO;AACpE,SAAO,QAAQ,QAAQ;AACvB,SAAO,YACL,KAAK,kBAAkB,SACnB,sBAAsB,QAAQ,OAAO,KAAK,cAAc,GACxD,QAAQ;;AAEhB,KAAI,EAAE,kBAAkB,WAAW,OAAO,aAAa,QAAQ,UAAU;AACvE,SAAO,WAAW,QAAQ;EAC1B,IAAI,eAAe,QAAQ;AAC3B,MAAI,KAAK,aAAa,OAAO,iBAAiB,YAAY,iBAAiB,MAAM;GAC/E,MAAM,OAAO,EAAE,GAAI,cAAqD;AACxE,+BAA4B,KAAK;AACjC,kBAAe;;AAEjB,SAAO,eAAe;;CAExB,MAAM,OAAO,OAAO;CACpB,MAAM,SAAS;AACf,MAAK,MAAM,OAAO,OAAO,KAAK,KAAK,CAAE,KAAI,EAAE,OAAO,QAAS,QAAO,KAAK;AACvE,MAAK,MAAM,OAAO,OAAO,KAAK,OAAO,CACnC,MAAK,OACH,QAAQ,UAAU,OAAO,YAAY,QAAQ,aAAa,OAAO,eAAe,OAAO;AAE3F,gCAA+B,SAAS,OAAO,KAAK;AACpD,QAAO,OAAO;;;;;;;AAQhB,SAAS,0BAA0B,SAA2B;CAC5D,IAAI,QAAQ,QAAQ;AACpB,KAAI,mBAAmB,OAAO;EAC5B,MAAM,OAAO,EAAE,GAAG,OAAO;AACzB,SAAO,KAAK;AACZ,UAAQ;;CAEV,IAAI,WAAW,QAAQ;AACvB,KACE,OAAO,aAAa,cACpB,aAAa,QACb,OAAO,aAAa,aACnB,qBAAqB,YAAY,qBAAqB,WACvD;EACA,MAAM,OAAO,EAAE,GAAI,UAAiD;AACpE,SAAO,KAAK;AACZ,SAAO,KAAK;AACZ,aAAW;;AAEb,KAAI,UAAU,QAAQ,SAAS,aAAa,QAAQ,SAAU,QAAO;AACrE,QAAO;EAAE,GAAG;EAAS;EAAO;EAAU;;AAExC,MAAM,6CAA6B,IAAI,SAAkB;;;;;;;AAQzD,SAAgB,6BAA6B,SAAwB;CACnE,MAAM,OAAO,uBAAuB,IAAI,QAAQ;AAChD,KAAI,SAAS,OAAW;CACxB,MAAM,SAAS,iBAAiB,KAAK,OAAO,KAAK,KAAK;AACtD,KAAI,OAAO,GAAI,SAAQ,cAAc,OAAO;CAC5C,MAAM,QAAQ,OAAO,KAAK,2BAA2B,SAAS,KAAK,KAAK,GAAG,OAAO;AAClF,KAAI,MACF,OAAM,IAAI,uBAAuB;EAAE,MAAM,MAAM;EAAM,MAAM,KAAK;EAAM,QAAQ,MAAM;EAAS,CAAC;AAEhG,wBAAuB,OAAO,QAAQ;AACtC,4BAA2B,IAAI,QAAQ;;;;;;AAOzC,SAAgB,8BAA8B,SAAwB;AACpE,4BAA2B,IAAI,QAAQ"}