@copilotkit/aimock 1.45.1 → 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 (425) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +13 -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.cts.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-handler.cjs +7 -3
  177. package/dist/mcp-handler.cjs.map +1 -1
  178. package/dist/mcp-handler.js +7 -3
  179. package/dist/mcp-handler.js.map +1 -1
  180. package/dist/mcp-recorder.cjs +2 -1
  181. package/dist/mcp-recorder.cjs.map +1 -1
  182. package/dist/mcp-recorder.js +3 -2
  183. package/dist/mcp-recorder.js.map +1 -1
  184. package/dist/messages.cjs +111 -96
  185. package/dist/messages.cjs.map +1 -1
  186. package/dist/messages.d.cts.map +1 -1
  187. package/dist/messages.d.ts.map +1 -1
  188. package/dist/messages.js +109 -95
  189. package/dist/messages.js.map +1 -1
  190. package/dist/metrics.cjs +5 -2
  191. package/dist/metrics.cjs.map +1 -1
  192. package/dist/metrics.d.cts.map +1 -1
  193. package/dist/metrics.d.ts.map +1 -1
  194. package/dist/metrics.js +6 -3
  195. package/dist/metrics.js.map +1 -1
  196. package/dist/model-utils.cjs +1 -0
  197. package/dist/model-utils.cjs.map +1 -1
  198. package/dist/model-utils.js +1 -1
  199. package/dist/model-utils.js.map +1 -1
  200. package/dist/moderation.cjs +15 -12
  201. package/dist/moderation.cjs.map +1 -1
  202. package/dist/moderation.js +14 -11
  203. package/dist/moderation.js.map +1 -1
  204. package/dist/ollama.cjs +128 -68
  205. package/dist/ollama.cjs.map +1 -1
  206. package/dist/ollama.d.cts.map +1 -1
  207. package/dist/ollama.d.ts.map +1 -1
  208. package/dist/ollama.js +124 -64
  209. package/dist/ollama.js.map +1 -1
  210. package/dist/openapi.cjs +323 -3
  211. package/dist/openapi.cjs.map +1 -1
  212. package/dist/openapi.d.cts.map +1 -1
  213. package/dist/openapi.d.ts.map +1 -1
  214. package/dist/openapi.js +324 -4
  215. package/dist/openapi.js.map +1 -1
  216. package/dist/openrouter-chat.cjs +11 -155
  217. package/dist/openrouter-chat.cjs.map +1 -1
  218. package/dist/openrouter-chat.js +11 -155
  219. package/dist/openrouter-chat.js.map +1 -1
  220. package/dist/openrouter-video.cjs +51 -74
  221. package/dist/openrouter-video.cjs.map +1 -1
  222. package/dist/openrouter-video.d.cts.map +1 -1
  223. package/dist/openrouter-video.d.ts.map +1 -1
  224. package/dist/openrouter-video.js +37 -60
  225. package/dist/openrouter-video.js.map +1 -1
  226. package/dist/recorder.cjs +176 -95
  227. package/dist/recorder.cjs.map +1 -1
  228. package/dist/recorder.d.cts.map +1 -1
  229. package/dist/recorder.d.ts.map +1 -1
  230. package/dist/recorder.js +176 -95
  231. package/dist/recorder.js.map +1 -1
  232. package/dist/rerank.cjs +13 -11
  233. package/dist/rerank.cjs.map +1 -1
  234. package/dist/rerank.js +12 -10
  235. package/dist/rerank.js.map +1 -1
  236. package/dist/response/catalog/anthropic-page.cjs +39 -0
  237. package/dist/response/catalog/anthropic-page.cjs.map +1 -0
  238. package/dist/response/catalog/anthropic-page.js +38 -0
  239. package/dist/response/catalog/anthropic-page.js.map +1 -0
  240. package/dist/response/catalog/collect.cjs +61 -0
  241. package/dist/response/catalog/collect.cjs.map +1 -0
  242. package/dist/response/catalog/collect.js +59 -0
  243. package/dist/response/catalog/collect.js.map +1 -0
  244. package/dist/response/catalog/gemini-page.cjs +25 -0
  245. package/dist/response/catalog/gemini-page.cjs.map +1 -0
  246. package/dist/response/catalog/gemini-page.js +24 -0
  247. package/dist/response/catalog/gemini-page.js.map +1 -0
  248. package/dist/response/catalog/render.cjs +182 -0
  249. package/dist/response/catalog/render.cjs.map +1 -0
  250. package/dist/response/catalog/render.js +171 -0
  251. package/dist/response/catalog/render.js.map +1 -0
  252. package/dist/response/errors.cjs +225 -0
  253. package/dist/response/errors.cjs.map +1 -0
  254. package/dist/response/errors.js +224 -0
  255. package/dist/response/errors.js.map +1 -0
  256. package/dist/response/finish.cjs +39 -0
  257. package/dist/response/finish.cjs.map +1 -0
  258. package/dist/response/finish.js +35 -0
  259. package/dist/response/finish.js.map +1 -0
  260. package/dist/response/policy.cjs +126 -0
  261. package/dist/response/policy.cjs.map +1 -0
  262. package/dist/response/policy.js +120 -0
  263. package/dist/response/policy.js.map +1 -0
  264. package/dist/response/profiles.cjs +69 -0
  265. package/dist/response/profiles.cjs.map +1 -0
  266. package/dist/response/profiles.js +65 -0
  267. package/dist/response/profiles.js.map +1 -0
  268. package/dist/response/usage/codecs/anthropic.cjs +52 -0
  269. package/dist/response/usage/codecs/anthropic.cjs.map +1 -0
  270. package/dist/response/usage/codecs/anthropic.js +50 -0
  271. package/dist/response/usage/codecs/anthropic.js.map +1 -0
  272. package/dist/response/usage/codecs/cohere.cjs +36 -0
  273. package/dist/response/usage/codecs/cohere.cjs.map +1 -0
  274. package/dist/response/usage/codecs/cohere.js +35 -0
  275. package/dist/response/usage/codecs/cohere.js.map +1 -0
  276. package/dist/response/usage/codecs/converse.cjs +16 -0
  277. package/dist/response/usage/codecs/converse.cjs.map +1 -0
  278. package/dist/response/usage/codecs/converse.js +15 -0
  279. package/dist/response/usage/codecs/converse.js.map +1 -0
  280. package/dist/response/usage/codecs/gemini.cjs +31 -0
  281. package/dist/response/usage/codecs/gemini.cjs.map +1 -0
  282. package/dist/response/usage/codecs/gemini.js +31 -0
  283. package/dist/response/usage/codecs/gemini.js.map +1 -0
  284. package/dist/response/usage/codecs/interactions.cjs +22 -0
  285. package/dist/response/usage/codecs/interactions.cjs.map +1 -0
  286. package/dist/response/usage/codecs/interactions.js +21 -0
  287. package/dist/response/usage/codecs/interactions.js.map +1 -0
  288. package/dist/response/usage/codecs/ollama.cjs +17 -0
  289. package/dist/response/usage/codecs/ollama.cjs.map +1 -0
  290. package/dist/response/usage/codecs/ollama.js +17 -0
  291. package/dist/response/usage/codecs/ollama.js.map +1 -0
  292. package/dist/response/usage/codecs/openai.cjs +52 -0
  293. package/dist/response/usage/codecs/openai.cjs.map +1 -0
  294. package/dist/response/usage/codecs/openai.js +50 -0
  295. package/dist/response/usage/codecs/openai.js.map +1 -0
  296. package/dist/response/usage/codecs/openrouter.cjs +68 -0
  297. package/dist/response/usage/codecs/openrouter.cjs.map +1 -0
  298. package/dist/response/usage/codecs/openrouter.js +67 -0
  299. package/dist/response/usage/codecs/openrouter.js.map +1 -0
  300. package/dist/response/usage/codecs/realtime.cjs +19 -0
  301. package/dist/response/usage/codecs/realtime.cjs.map +1 -0
  302. package/dist/response/usage/codecs/realtime.js +18 -0
  303. package/dist/response/usage/codecs/realtime.js.map +1 -0
  304. package/dist/response/usage/codecs/responses.cjs +21 -0
  305. package/dist/response/usage/codecs/responses.cjs.map +1 -0
  306. package/dist/response/usage/codecs/responses.js +20 -0
  307. package/dist/response/usage/codecs/responses.js.map +1 -0
  308. package/dist/response/usage/estimate.cjs +23 -0
  309. package/dist/response/usage/estimate.cjs.map +1 -0
  310. package/dist/response/usage/estimate.js +21 -0
  311. package/dist/response/usage/estimate.js.map +1 -0
  312. package/dist/response/usage/fields.cjs +72 -0
  313. package/dist/response/usage/fields.cjs.map +1 -0
  314. package/dist/response/usage/fields.js +70 -0
  315. package/dist/response/usage/fields.js.map +1 -0
  316. package/dist/response/usage/recording.cjs +52 -0
  317. package/dist/response/usage/recording.cjs.map +1 -0
  318. package/dist/response/usage/recording.js +52 -0
  319. package/dist/response/usage/recording.js.map +1 -0
  320. package/dist/responses.cjs +45 -58
  321. package/dist/responses.cjs.map +1 -1
  322. package/dist/responses.d.cts.map +1 -1
  323. package/dist/responses.d.ts.map +1 -1
  324. package/dist/responses.js +44 -57
  325. package/dist/responses.js.map +1 -1
  326. package/dist/route-registry.cjs +45 -0
  327. package/dist/route-registry.cjs.map +1 -1
  328. package/dist/route-registry.d.cts.map +1 -1
  329. package/dist/route-registry.d.ts.map +1 -1
  330. package/dist/route-registry.js +41 -1
  331. package/dist/route-registry.js.map +1 -1
  332. package/dist/router.cjs +4 -0
  333. package/dist/router.cjs.map +1 -1
  334. package/dist/router.d.cts.map +1 -1
  335. package/dist/router.d.ts.map +1 -1
  336. package/dist/router.js +3 -0
  337. package/dist/router.js.map +1 -1
  338. package/dist/search.cjs +13 -11
  339. package/dist/search.cjs.map +1 -1
  340. package/dist/search.js +12 -10
  341. package/dist/search.js.map +1 -1
  342. package/dist/server.cjs +222 -146
  343. package/dist/server.cjs.map +1 -1
  344. package/dist/server.d.cts.map +1 -1
  345. package/dist/server.d.ts.map +1 -1
  346. package/dist/server.js +223 -147
  347. package/dist/server.js.map +1 -1
  348. package/dist/speech.cjs +20 -18
  349. package/dist/speech.cjs.map +1 -1
  350. package/dist/speech.d.cts.map +1 -1
  351. package/dist/speech.d.ts.map +1 -1
  352. package/dist/speech.js +19 -17
  353. package/dist/speech.js.map +1 -1
  354. package/dist/sse-writer.cjs +7 -0
  355. package/dist/sse-writer.cjs.map +1 -1
  356. package/dist/sse-writer.d.cts.map +1 -1
  357. package/dist/sse-writer.d.ts.map +1 -1
  358. package/dist/sse-writer.js +7 -1
  359. package/dist/sse-writer.js.map +1 -1
  360. package/dist/stream-collapse.cjs +118 -11
  361. package/dist/stream-collapse.cjs.map +1 -1
  362. package/dist/stream-collapse.d.cts +10 -9
  363. package/dist/stream-collapse.d.cts.map +1 -1
  364. package/dist/stream-collapse.d.ts +10 -9
  365. package/dist/stream-collapse.d.ts.map +1 -1
  366. package/dist/stream-collapse.js +118 -11
  367. package/dist/stream-collapse.js.map +1 -1
  368. package/dist/transcription.cjs +12 -10
  369. package/dist/transcription.cjs.map +1 -1
  370. package/dist/transcription.d.cts.map +1 -1
  371. package/dist/transcription.d.ts.map +1 -1
  372. package/dist/transcription.js +11 -9
  373. package/dist/transcription.js.map +1 -1
  374. package/dist/types.cjs.map +1 -1
  375. package/dist/types.d.cts +62 -3
  376. package/dist/types.d.cts.map +1 -1
  377. package/dist/types.d.ts +62 -3
  378. package/dist/types.d.ts.map +1 -1
  379. package/dist/types.js.map +1 -1
  380. package/dist/vector-mock.cjs +3 -8
  381. package/dist/vector-mock.cjs.map +1 -1
  382. package/dist/vector-mock.d.cts.map +1 -1
  383. package/dist/vector-mock.d.ts.map +1 -1
  384. package/dist/vector-mock.js +3 -8
  385. package/dist/vector-mock.js.map +1 -1
  386. package/dist/vector-stores.cjs +2 -4
  387. package/dist/vector-stores.cjs.map +1 -1
  388. package/dist/vector-stores.d.cts.map +1 -1
  389. package/dist/vector-stores.d.ts.map +1 -1
  390. package/dist/vector-stores.js +2 -4
  391. package/dist/vector-stores.js.map +1 -1
  392. package/dist/veo-video.cjs +16 -14
  393. package/dist/veo-video.cjs.map +1 -1
  394. package/dist/veo-video.js +10 -8
  395. package/dist/veo-video.js.map +1 -1
  396. package/dist/video.cjs +21 -19
  397. package/dist/video.cjs.map +1 -1
  398. package/dist/video.d.cts.map +1 -1
  399. package/dist/video.d.ts.map +1 -1
  400. package/dist/video.js +20 -18
  401. package/dist/video.js.map +1 -1
  402. package/dist/ws-gemini-live.cjs +11 -3
  403. package/dist/ws-gemini-live.cjs.map +1 -1
  404. package/dist/ws-gemini-live.d.cts +0 -1
  405. package/dist/ws-gemini-live.d.cts.map +1 -1
  406. package/dist/ws-gemini-live.d.ts +0 -1
  407. package/dist/ws-gemini-live.d.ts.map +1 -1
  408. package/dist/ws-gemini-live.js +10 -2
  409. package/dist/ws-gemini-live.js.map +1 -1
  410. package/dist/ws-live.cjs +6 -5
  411. package/dist/ws-live.cjs.map +1 -1
  412. package/dist/ws-live.js +5 -4
  413. package/dist/ws-live.js.map +1 -1
  414. package/dist/ws-realtime.cjs +28 -54
  415. package/dist/ws-realtime.cjs.map +1 -1
  416. package/dist/ws-realtime.d.cts.map +1 -1
  417. package/dist/ws-realtime.d.ts.map +1 -1
  418. package/dist/ws-realtime.js +25 -51
  419. package/dist/ws-realtime.js.map +1 -1
  420. package/dist/ws-responses.cjs +8 -6
  421. package/dist/ws-responses.cjs.map +1 -1
  422. package/dist/ws-responses.js +7 -5
  423. package/dist/ws-responses.js.map +1 -1
  424. package/package.json +4 -1
  425. package/skills/write-fixtures/SKILL.md +25 -16
@@ -1,4 +1,4 @@
1
- import { ApiKeyAuthConfig, ChaosConfig, McpRecordConfig, MisbehaviorConfig, MisbehaviorFaultId, RecordConfig, ResponsesToolsMode } from "./types.js";
1
+ import { AimockPolicy, ApiKeyAuthConfig, ChaosConfig, McpRecordConfig, MisbehaviorConfig, MisbehaviorFaultId, RecordConfig, ResponsesToolsMode } from "./types.js";
2
2
  import { A2AAgentDefinition, A2AArtifact, A2APart, A2AStreamEvent } from "./a2a-types.js";
3
3
  import { AGUIEvent } from "./agui-types.js";
4
4
  import { LLMock } from "./llmock.js";
@@ -115,6 +115,8 @@ interface AimockConfig {
115
115
  strictToolArguments?: boolean;
116
116
  /** See `MockServerOptions.responsesTools`. */
117
117
  responsesTools?: ResponsesToolsMode;
118
+ /** See `MockServerOptions.policy`. Independent of `strict`. */
119
+ policy?: AimockPolicy;
118
120
  };
119
121
  mcp?: MCPConfig;
120
122
  a2a?: A2AConfig;
@@ -144,7 +146,11 @@ interface StartFromConfigOverrides {
144
146
  strictToolArguments?: boolean;
145
147
  /** `aimock --responses-tools`: overrides `llm.responsesTools`. */
146
148
  responsesTools?: ResponsesToolsMode;
149
+ /** `aimock --policy`: overrides individual keys from `llm.policy`. */
150
+ policy?: AimockPolicy;
147
151
  }
152
+ /** Both CLI entry points use the shared policy parser and treat failures as usage errors. */
153
+
148
154
  declare function startFromConfig(config: AimockConfig, overrides?: StartFromConfigOverrides): Promise<{
149
155
  llmock: LLMock;
150
156
  url: string;
@@ -1 +1 @@
1
- {"version":3,"file":"config-loader.d.ts","names":[],"sources":["../src/config-loader.ts"],"sourcesContent":[],"mappings":";;;;;;;;UA4BiB,aAAA,SAAsB;;AAAvC;AAIiB,UAAA,iBAAA,CAAiB;EASjB,GAAA,EAAA,MAAA;EAAgB,IAAA,EAAA,MAAA;UAEnB,CAAA,EAAA,MAAA;aAF2B,CAAA,EAAA,MAAA;EAAmB,IAAA,CAAA,EAAA,MAAA;EAM3C,IAAA,CAAA,EAAA,MAAS;;AAGhB,UATO,eAAA,SAAwB,mBAS/B,CAAA;QACI,CAAA,EAAA;IACF,QAAA,EATE,KASF,CAAA;MAAe,IAAA,EAAA,MAAA;MAGV,OAAA,EAAA;QAAgB,IAAA,EAAA,MAAA;QAEvB,IAAA,EAAA,MAAA;MACI,CAAA;IACH,CAAA,CAAA;EAAc,CAAA;AAIzB;AAAgC,UAhBf,SAAA,CAgBe;MACnB,CAAA,EAAA,MAAA;YACH,CAAA,EAAA;IACS,IAAA,EAAA,MAAA;IAHqB,OAAA,EAAA,MAAA;EAAkB,CAAA;EAMzC,KAAA,CAAA,EAnBP,aAmBgB,EAAA;EAKT,SAAA,CAAA,EAvBH,iBAuBoB,EAAA;EAOjB,OAAA,CAAA,EA7BL,eA+BC,EAAA;AAGb;AAAuC,UA/BtB,gBAAA,CA+BsB;SAMxB,EAAA,MAAA;OAHH,CAAA,EAhCF,OAgCE,EAAA;WAKK,CAAA,EApCH,WAoCG,EAAA;EAAW,MAAA,CAAA,EAnCjB,cAmCiB,EAAA;EAGX,OAAA,CAAA,EAAA,MAAY;AAM7B;AAAoC,UAxCnB,cAAA,SAAuB,kBAwCJ,CAAA;UAEJ,CAAA,EAzCnB,gBAyCmB,EAAA;OAAxB,CAAA,EAxCE,gBAwCF,EAAA;gBAFoC,CAAA,EArCzB,gBAqCyB,EAAA;;AAK3B,UAvCA,SAAA,CAuCY;EAAA,IAAA,CAAA,EAAA,MAAA;QACpB,CAAA,EAtCE,cAsCF,EAAA;;AAaS,UAhDD,iBAAA,CAgDC;OAAoB,EAAA;IAGzB,OAAA,CAAA,EAAA,MAAA;IASQ,UAAA,CAAA,EAAA,MAAA;IAEb,QAAA,CAAA,EAAA,MAAA;IACA,QAAA,CAAA,EAAA,MAAA;;MAEG,CAAA,EAAA,MAAA;EAAY,MAAA,CAAA,EA9DZ,SA8DY,EAAA;EAgBP,OAAA,CAAA,EAAA,MAAU;AAgB1B;AAWsB,UArGL,UAAA,CAqGoB;EAAA,IAAA,CAAA,EAAA,MAAA;UAC3B,CAAA,EApGG,iBAoGH,EAAA;;AAEW,UAnGJ,sBAAA,CAmGI;MAAlB,EAAA,MAAA;EAAO,SAAA,EAAA,MAAA;YAhGE;;;eAGG;;iBAEE;;UAGA,YAAA;;gBAED;;;UAIC,kBAAA,SAA2B;;QAEpC,wBAAwB;;UAGf,YAAA;SACR;;;;;;;YAOG;;;;;;kBAMM,oBAAoB;;;aAGzB;;;;;;;;;qBASQ;;QAEb;QACA;SACC;WACE;;;;;;;;;;;iBAgBK,UAAA,sBAAgC;;;;;UAgB/B,wBAAA;;;;;;;;mBAQE;;iBAGG,eAAA,SACZ,0BACI,2BACX;UAAkB"}
1
+ {"version":3,"file":"config-loader.d.ts","names":[],"sources":["../src/config-loader.ts"],"sourcesContent":[],"mappings":";;;;;;;;UA8BiB,aAAA,SAAsB;;AAAvC;AAIiB,UAAA,iBAAA,CAAiB;EASjB,GAAA,EAAA,MAAA;EAAgB,IAAA,EAAA,MAAA;UAEnB,CAAA,EAAA,MAAA;aAF2B,CAAA,EAAA,MAAA;EAAmB,IAAA,CAAA,EAAA,MAAA;EAM3C,IAAA,CAAA,EAAA,MAAS;;AAGhB,UATO,eAAA,SAAwB,mBAS/B,CAAA;QACI,CAAA,EAAA;IACF,QAAA,EATE,KASF,CAAA;MAAe,IAAA,EAAA,MAAA;MAGV,OAAA,EAAA;QAAgB,IAAA,EAAA,MAAA;QAEvB,IAAA,EAAA,MAAA;MACI,CAAA;IACH,CAAA,CAAA;EAAc,CAAA;AAIzB;AAAgC,UAhBf,SAAA,CAgBe;MACnB,CAAA,EAAA,MAAA;YACH,CAAA,EAAA;IACS,IAAA,EAAA,MAAA;IAHqB,OAAA,EAAA,MAAA;EAAkB,CAAA;EAMzC,KAAA,CAAA,EAnBP,aAmBgB,EAEf;EAGM,SAAA,CAAA,EAvBH,iBAuBoB,EAGvB;EAIM,OAAA,CAAA,EA7BL,eA+BC,EAAA;AAGb;AAAuC,UA/BtB,gBAAA,CA+BsB;SAMxB,EAAA,MAAA;OAHH,CAAA,EAhCF,OAgCE,EAAA;WAKK,CAAA,EApCH,WAoCG,EAAA;EAAW,MAAA,CAAA,EAnCjB,cAmCiB,EAAA;EAGX,OAAA,CAAA,EAAA,MAAY;AAM7B;AAAoC,UAxCnB,cAAA,SAAuB,kBAwCJ,CAAA;UAEJ,CAAA,EAzCnB,gBAyCmB,EAAA;OAAxB,CAAA,EAxCE,gBAwCF,EAAA;gBAFoC,CAAA,EArCzB,gBAqCyB,EAAA;;AAK3B,UAvCA,SAAA,CAuCY;EAAA,IAAA,CAAA,EAAA,MAAA;QACpB,CAAA,EAtCE,cAsCF,EAAA;;AAaS,UAhDD,iBAAA,CAgDC;OAAoB,EAAA;IAGzB,OAAA,CAAA,EAAA,MAAA;IASQ,UAAA,CAAA,EAAA,MAAA;IAER,QAAA,CAAA,EAAA,MAAA;IAEL,QAAA,CAAA,EAAA,MAAA;;MAEC,CAAA,EAAA,MAAA;QACE,CAAA,EAhEA,SAgEA,EAAA;EAAY,OAAA,CAAA,EAAA,MAAA;AAgBvB;AAgBiB,UA5FA,UAAA,CA4FA;EAAwB,IAAA,CAAA,EAAA,MAAA;UAQtB,CAAA,EAlGN,iBAkGM,EAAA;;AAEI,UAjGN,sBAAA,CAiGM;EA0BD,IAAA,EAAA,MAAA;EAAe,SAAA,EAAA,MAAA;SAC3B,CAAA,EAzHE,KAyHF,CAAA;IACI,EAAA,EAAA,MAAA;IACO,MAAA,EAAA,MAAA,EAAA;IAAlB,QAAA,CAAA,EAxHY,MAwHZ,CAAA,MAAA,EAAA,OAAA,CAAA;EAAO,CAAA,CAAA;iBAtHO;;UAGA,YAAA;;gBAED;;;UAIC,kBAAA,SAA2B;;QAEpC,wBAAwB;;UAGf,YAAA;SACR;;;;;;;YAOG;;;;;;kBAMM,oBAAoB;;;aAGzB;;;;;;;;;qBASQ;;aAER;;QAEL;QACA;SACC;WACE;;;;;;;;;;;iBAgBK,UAAA,sBAAgC;;;;;UAgB/B,wBAAA;;;;;;;;mBAQE;;WAER;;;;iBA0BW,eAAA,SACZ,0BACI,2BACX;UAAkB"}
@@ -1,4 +1,5 @@
1
1
  import { resolveInboundAuth, selectInboundAuthSource } from "./api-key-auth.js";
2
+ import { assignPolicyEntry, parsePolicy, parsePolicyEntry } from "./response/policy.js";
2
3
  import { A2AMock } from "./a2a-mock.js";
3
4
  import { buildTextResponse } from "./agui-handler.js";
4
5
  import { parseMisbehavior } from "./misbehavior.js";
@@ -27,8 +28,33 @@ function loadConfig(configPath) {
27
28
  function hasMcpRecordMount(mcp) {
28
29
  return typeof mcp === "object" && mcp !== null && !Array.isArray(mcp) && Object.keys(mcp).length > 0;
29
30
  }
31
+ /** Both CLI entry points use the shared policy parser and treat failures as usage errors. */
32
+ function parsePolicyFlags(entries) {
33
+ const policy = {};
34
+ for (const entry of entries) {
35
+ const separator = entry.indexOf("=");
36
+ if (separator < 0) return {
37
+ ok: false,
38
+ error: `Invalid --policy ${JSON.stringify(entry)}: expected key=value.`
39
+ };
40
+ const parsed = parsePolicyEntry(entry.slice(0, separator), entry.slice(separator + 1));
41
+ if (!parsed.ok) return {
42
+ ok: false,
43
+ error: `Invalid --policy ${JSON.stringify(entry)}: ${parsed.reason} Use key=value.`
44
+ };
45
+ assignPolicyEntry(policy, parsed);
46
+ }
47
+ return {
48
+ ok: true,
49
+ policy
50
+ };
51
+ }
30
52
  async function startFromConfig(config, overrides) {
31
53
  const logger = new Logger("info");
54
+ const policy = {
55
+ ...parsePolicy(config.llm?.policy, (message) => logger.warn(message)),
56
+ ...parsePolicy(overrides?.policy, (message) => logger.warn(message))
57
+ };
32
58
  const booleanOptIn = (key) => {
33
59
  const value = config.llm?.[key];
34
60
  if (value === void 0 || typeof value === "boolean") return value;
@@ -73,7 +99,8 @@ async function startFromConfig(config, overrides) {
73
99
  metrics: config.metrics,
74
100
  strict: config.strict,
75
101
  strictToolArguments: overrides?.strictToolArguments ?? configStrictToolArguments,
76
- responsesTools: overrides?.responsesTools ?? responsesTools
102
+ responsesTools: overrides?.responsesTools ?? responsesTools,
103
+ policy
77
104
  }, resolvedAuth);
78
105
  const configMounts = /* @__PURE__ */ new Map();
79
106
  if (config.llm?.fixtures) {
@@ -205,5 +232,5 @@ async function startFromConfig(config, overrides) {
205
232
  }
206
233
 
207
234
  //#endregion
208
- export { loadConfig, startFromConfig };
235
+ export { loadConfig, parsePolicyFlags, startFromConfig };
209
236
  //# sourceMappingURL=config-loader.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"config-loader.js","names":[],"sources":["../src/config-loader.ts"],"sourcesContent":["import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { LLMock, createLLMockWithResolvedAuth } from \"./llmock.js\";\nimport { resolveInboundAuth, selectInboundAuthSource } from \"./api-key-auth.js\";\nimport { MCPMock } from \"./mcp-mock.js\";\nimport { configToolHandlers } from \"./mcp-handler.js\";\nimport { A2AMock } from \"./a2a-mock.js\";\nimport { AGUIMock } from \"./agui-mock.js\";\nimport type {\n ApiKeyAuthConfig,\n ChaosConfig,\n McpRecordConfig,\n MisbehaviorConfig,\n MisbehaviorFaultId,\n Mountable,\n RecordConfig,\n ResponsesToolsMode,\n} from \"./types.js\";\nimport { parseMisbehavior } from \"./misbehavior.js\";\nimport type { MCPToolDefinition, MCPPromptDefinition } from \"./mcp-types.js\";\nimport type { A2AAgentDefinition, A2APart, A2AArtifact, A2AStreamEvent } from \"./a2a-types.js\";\nimport type { AGUIEvent } from \"./agui-types.js\";\nimport { buildTextResponse } from \"./agui-handler.js\";\nimport { VectorMock } from \"./vector-mock.js\";\nimport type { QueryResult } from \"./vector-types.js\";\nimport { Logger } from \"./logger.js\";\nimport { wireMcpRecording } from \"./mcp-recorder.js\";\n\nexport interface MCPConfigTool extends MCPToolDefinition {\n result?: string;\n}\n\nexport interface MCPConfigResource {\n uri: string;\n name: string;\n mimeType?: string;\n description?: string;\n text?: string;\n blob?: string;\n}\n\nexport interface MCPConfigPrompt extends MCPPromptDefinition {\n result?: {\n messages: Array<{ role: string; content: { type: string; text: string } }>;\n };\n}\n\nexport interface MCPConfig {\n path?: string;\n serverInfo?: { name: string; version: string };\n tools?: MCPConfigTool[];\n resources?: MCPConfigResource[];\n prompts?: MCPConfigPrompt[];\n}\n\nexport interface A2AConfigPattern {\n pattern: string;\n parts?: A2APart[];\n artifacts?: A2AArtifact[];\n events?: A2AStreamEvent[];\n delayMs?: number;\n}\n\nexport interface A2AConfigAgent extends A2AAgentDefinition {\n messages?: A2AConfigPattern[];\n tasks?: A2AConfigPattern[];\n streamingTasks?: A2AConfigPattern[];\n}\n\nexport interface A2AConfig {\n path?: string;\n agents?: A2AConfigAgent[];\n}\n\nexport interface AGUIConfigFixture {\n match: { message?: string; toolCallId?: string; toolName?: string; stateKey?: string };\n text?: string; // shorthand: uses buildTextResponse\n events?: AGUIEvent[]; // raw events\n delayMs?: number;\n}\n\nexport interface AGUIConfig {\n path?: string; // mount path, default \"/agui\"\n fixtures?: AGUIConfigFixture[];\n}\n\nexport interface VectorConfigCollection {\n name: string;\n dimension: number;\n vectors?: Array<{\n id: string;\n values: number[];\n metadata?: Record<string, unknown>;\n }>;\n queryResults?: QueryResult[];\n}\n\nexport interface VectorConfig {\n path?: string;\n collections?: VectorConfigCollection[];\n}\n\n/** `llm.record` in a config file: the LLM `RecordConfig`, plus MCP record mounts (C10). */\nexport interface AimockRecordConfig extends RecordConfig {\n /** MR1: mount path → upstream URL, or a full MCP record config. */\n mcp?: Record<string, string | McpRecordConfig>;\n}\n\nexport interface AimockConfig {\n auth?: ApiKeyAuthConfig;\n llm?: {\n fixtures?: string;\n latency?: number;\n chunkSize?: number;\n replaySpeed?: number;\n logLevel?: \"silent\" | \"warn\" | \"info\" | \"debug\";\n chaos?: ChaosConfig;\n /**\n * Server-wide misbehavior defaults. Read only when misbehavior is enabled\n * (`llm.enableMisbehavior: true` or `aimock --misbehavior`); otherwise the\n * key is ignored with a warning, as 1.44.0 ignored it.\n */\n misbehavior?: MisbehaviorConfig | MisbehaviorFaultId;\n /** See `MockServerOptions.enableMisbehavior`. */\n enableMisbehavior?: boolean;\n record?: AimockRecordConfig;\n /**\n * Turns on `llm.record.mcp`. Without it the key is ignored with a warning,\n * as 1.44.0 ignored it.\n */\n enableMcpRecording?: boolean;\n /** See `MockServerOptions.strictToolArguments`. */\n strictToolArguments?: boolean;\n /** See `MockServerOptions.responsesTools`. */\n responsesTools?: ResponsesToolsMode;\n };\n mcp?: MCPConfig;\n a2a?: A2AConfig;\n agui?: AGUIConfig;\n vector?: VectorConfig;\n services?: { search?: boolean; rerank?: boolean; moderate?: boolean };\n metrics?: boolean;\n strict?: boolean;\n port?: number;\n host?: string;\n}\n\n/** C10: the LLM part of `llm.record`. `mcp` is MCP-only; no `providers` means no LLM recording. */\nfunction llmRecordOf(record: AimockRecordConfig | undefined): RecordConfig | undefined {\n if (!record) return undefined;\n const { mcp: _mcp, ...llm } = record;\n void _mcp; // repo pattern for an intentionally unused binding (byteplus-video.ts:1379-1380)\n return llm.providers === undefined ? undefined : llm;\n}\n\nexport function loadConfig(configPath: string): AimockConfig {\n const raw = fs.readFileSync(configPath, \"utf-8\");\n return JSON.parse(raw) as AimockConfig;\n}\n\n/** C3: `llm.record.mcp` is an object with at least one mount. */\nfunction hasMcpRecordMount(mcp: unknown): boolean {\n return (\n typeof mcp === \"object\" && mcp !== null && !Array.isArray(mcp) && Object.keys(mcp).length > 0\n );\n}\n\n/**\n * Command-line overrides for `startFromConfig`. The opt-in flags win over the\n * matching `llm.*` config keys.\n */\nexport interface StartFromConfigOverrides {\n port?: number;\n host?: string;\n /** `aimock --misbehavior`: overrides `llm.enableMisbehavior`. */\n enableMisbehavior?: boolean;\n /** `aimock --strict-tool-arguments`: overrides `llm.strictToolArguments`. */\n strictToolArguments?: boolean;\n /** `aimock --responses-tools`: overrides `llm.responsesTools`. */\n responsesTools?: ResponsesToolsMode;\n}\n\nexport async function startFromConfig(\n config: AimockConfig,\n overrides?: StartFromConfigOverrides,\n): Promise<{ llmock: LLMock; url: string }> {\n const logger = new Logger(\"info\");\n // 1.44.0 ignored these keys, so a non-boolean value (such as the string\n // \"true\") is ignored with a warning, as `llm.misbehavior` is, and the\n // option stays off unless a CLI flag turns it on (`llm.enableMcpRecording`\n // has no CLI flag).\n const booleanOptIn = (\n key: \"enableMisbehavior\" | \"enableMcpRecording\" | \"strictToolArguments\",\n ): boolean | undefined => {\n const value: unknown = config.llm?.[key];\n if (value === undefined || typeof value === \"boolean\") return value;\n logger.warn(\n `Ignoring llm.${key} because it must be true or false, got ${JSON.stringify(value)}.`,\n );\n return undefined;\n };\n const configEnableMisbehavior = booleanOptIn(\"enableMisbehavior\");\n const configStrictToolArguments = booleanOptIn(\"strictToolArguments\");\n // MCP recording also needs an explicit opt-in. 1.44.0 ignored\n // `llm.record.mcp`, so without `llm.enableMcpRecording: true` the key is\n // ignored (with a warning): nothing is mounted or validated and the log level\n // is unchanged. With the opt-in, an invalid value fails startup.\n const enableMcpRecording = booleanOptIn(\"enableMcpRecording\") === true;\n const mcpRecord = enableMcpRecording ? config.llm?.record?.mcp : undefined;\n // Misbehavior needs an explicit opt-in that a 1.44.0 config cannot contain.\n // 1.44.0 ignored `llm.misbehavior`, so without the opt-in the key is ignored\n // (with a warning) and the server serves exactly what 1.44.0 served. With the\n // opt-in, an invalid value fails startup like any other enabled-mode error.\n const enableMisbehavior = (overrides?.enableMisbehavior ?? configEnableMisbehavior) === true;\n let misbehavior: MisbehaviorConfig | undefined;\n if (config.llm?.misbehavior !== undefined) {\n if (!enableMisbehavior) {\n logger.warn(\n \"Ignoring llm.misbehavior because misbehavior is not enabled. Set llm.enableMisbehavior: true or pass --misbehavior to use it.\",\n );\n } else {\n const parsed = parseMisbehavior(config.llm.misbehavior, \"llm.misbehavior\");\n if (!parsed.ok) {\n throw new TypeError(`${parsed.issue.rule}: ${parsed.issue.path}: ${parsed.issue.message}`);\n }\n misbehavior = parsed.config;\n }\n }\n if (config.llm?.record?.mcp !== undefined && !enableMcpRecording) {\n logger.warn(\n \"Ignoring llm.record.mcp because MCP recording is not enabled. Set llm.enableMcpRecording: true to use it.\",\n );\n }\n\n // 1.44.0 ignored `llm.responsesTools`, so an invalid value is ignored with a\n // warning (as `llm.misbehavior` is) and the server runs in \"legacy\" mode,\n // unless `--responses-tools` sets the mode. The warning names the mode that\n // actually runs.\n let responsesTools = config.llm?.responsesTools;\n if (\n responsesTools !== undefined &&\n responsesTools !== \"legacy\" &&\n responsesTools !== \"extended\"\n ) {\n const effective =\n overrides?.responsesTools !== undefined\n ? `\"${overrides.responsesTools}\" from --responses-tools`\n : '\"legacy\"';\n logger.warn(\n `Ignoring llm.responsesTools because it must be \"legacy\" or \"extended\", got ${JSON.stringify(responsesTools)}. Using ${effective}.`,\n );\n responsesTools = undefined;\n }\n\n // A non-positive replaySpeed fails calculateDelay's `speed > 0` check and applies the\n // full delay rather than none. Mirrors the fixture-level guard in fixture-loader.ts.\n let replaySpeed = config.llm?.replaySpeed;\n if (replaySpeed != null && (!Number.isFinite(replaySpeed) || replaySpeed <= 0)) {\n logger.warn(`llm.replaySpeed must be a positive number, got ${replaySpeed}. Ignoring.`);\n replaySpeed = undefined;\n }\n\n // Load fixtures if specified\n const resolvedAuth = resolveInboundAuth(selectInboundAuthSource(config.auth));\n const llmock = createLLMockWithResolvedAuth(\n {\n port: overrides?.port ?? config.port ?? 0,\n host: overrides?.host ?? config.host ?? \"127.0.0.1\",\n latency: config.llm?.latency,\n chunkSize: config.llm?.chunkSize,\n replaySpeed,\n // llm.record.mcp reports skipped or failed recordings only as MCP-RECORD: warnings,\n // so an enabled config with at least one mount defaults to \"warn\" rather than the silent\n // default. An empty llm.record.mcp records nothing. An explicit logLevel wins.\n logLevel: config.llm?.logLevel ?? (hasMcpRecordMount(mcpRecord) ? \"warn\" : undefined),\n chaos: config.llm?.chaos,\n misbehavior,\n ...(enableMisbehavior ? { enableMisbehavior: true } : {}),\n record: llmRecordOf(config.llm?.record),\n metrics: config.metrics,\n strict: config.strict,\n strictToolArguments: overrides?.strictToolArguments ?? configStrictToolArguments,\n responsesTools: overrides?.responsesTools ?? responsesTools,\n },\n resolvedAuth,\n );\n\n // What this function mounted, by path (MR1: llm.record.mcp records through an MCP mount).\n const configMounts = new Map<string, Mountable>();\n\n if (config.llm?.fixtures) {\n const fixturePath = path.resolve(config.llm.fixtures);\n const stat = fs.statSync(fixturePath);\n if (stat.isDirectory()) {\n llmock.loadFixtureDir(fixturePath);\n } else {\n llmock.loadFixtureFile(fixturePath);\n }\n }\n\n // MCP\n if (config.mcp) {\n const mcpConfig = config.mcp;\n const mcp = new MCPMock({\n serverInfo: mcpConfig.serverInfo,\n });\n\n if (mcpConfig.tools) {\n for (const tool of mcpConfig.tools) {\n const { result, ...def } = tool;\n mcp.addTool(def);\n if (result !== undefined) {\n const answer = () => result;\n configToolHandlers.add(answer);\n mcp.onToolCall(def.name, answer);\n }\n }\n }\n\n if (mcpConfig.resources) {\n for (const res of mcpConfig.resources) {\n mcp.addResource(\n { uri: res.uri, name: res.name, mimeType: res.mimeType, description: res.description },\n res.text !== undefined || res.blob !== undefined\n ? { text: res.text, blob: res.blob, mimeType: res.mimeType }\n : undefined,\n );\n }\n }\n\n if (mcpConfig.prompts) {\n for (const prompt of mcpConfig.prompts) {\n const { result, ...def } = prompt;\n if (result) {\n mcp.addPrompt(def, () => result as import(\"./mcp-types.js\").MCPPromptResult);\n } else {\n mcp.addPrompt(def);\n }\n }\n }\n\n const mcpPath = mcpConfig.path ?? \"/mcp\";\n llmock.mount(mcpPath, mcp);\n configMounts.set(mcpPath, mcp);\n logger.info(`MCPMock mounted at ${mcpPath}`);\n }\n\n // A2A\n if (config.a2a) {\n const a2aConfig = config.a2a;\n const a2a = new A2AMock();\n\n if (a2aConfig.agents) {\n for (const agentConfig of a2aConfig.agents) {\n const { messages, tasks, streamingTasks, ...def } = agentConfig;\n a2a.registerAgent(def);\n\n if (messages) {\n for (const m of messages) {\n a2a.onMessage(def.name, m.pattern, m.parts ?? [{ text: \"\" }]);\n }\n }\n\n if (tasks) {\n for (const t of tasks) {\n a2a.onTask(def.name, t.pattern, t.artifacts ?? []);\n }\n }\n\n if (streamingTasks) {\n for (const s of streamingTasks) {\n a2a.onStreamingTask(def.name, s.pattern, s.events ?? [], s.delayMs);\n }\n }\n }\n }\n\n const a2aPath = a2aConfig.path ?? \"/a2a\";\n llmock.mount(a2aPath, a2a);\n configMounts.set(a2aPath, a2a);\n logger.info(`A2AMock mounted at ${a2aPath}`);\n }\n\n // AG-UI\n if (config.agui) {\n const aguiConfig = config.agui;\n const agui = new AGUIMock();\n\n if (aguiConfig.fixtures) {\n for (const f of aguiConfig.fixtures) {\n if (f.match.toolCallId && f.text && !f.events) {\n logger.warn(\n `AG-UI fixture uses text shorthand with toolCallId — text shorthand ignores toolCallId matching; use events[] instead (match: ${JSON.stringify(f.match)})`,\n );\n }\n if (f.text) {\n if (f.match.message !== undefined) {\n agui.onMessage(f.match.message, f.text, { delayMs: f.delayMs });\n } else {\n // No message pattern — register via addFixture so it only matches\n // on other criteria (toolCallId, toolName, stateKey) instead of\n // becoming a catch-all that matches every request.\n agui.addFixture({\n match: {\n toolCallId: f.match.toolCallId,\n toolName: f.match.toolName,\n stateKey: f.match.stateKey,\n },\n events: buildTextResponse(f.text),\n delayMs: f.delayMs,\n });\n }\n } else if (f.events) {\n agui.addFixture({\n match: {\n message: f.match.message,\n toolCallId: f.match.toolCallId,\n toolName: f.match.toolName,\n stateKey: f.match.stateKey,\n },\n events: f.events,\n delayMs: f.delayMs,\n });\n } else {\n logger.warn(\n `AG-UI fixture has neither text nor events — it will be skipped (match: ${JSON.stringify(f.match)})`,\n );\n }\n }\n }\n\n const aguiPath = aguiConfig.path ?? \"/agui\";\n llmock.mount(aguiPath, agui);\n configMounts.set(aguiPath, agui);\n logger.info(`AGUIMock mounted at ${aguiPath}`);\n }\n\n // Vector\n if (config.vector) {\n const vectorConfig = config.vector;\n const vector = new VectorMock();\n\n if (vectorConfig.collections) {\n for (const col of vectorConfig.collections) {\n vector.addCollection(col.name, { dimension: col.dimension });\n\n if (col.vectors && col.vectors.length > 0) {\n vector.upsert(col.name, col.vectors);\n }\n\n if (col.queryResults) {\n vector.onQuery(col.name, col.queryResults);\n }\n }\n }\n\n const vectorPath = vectorConfig.path ?? \"/vector\";\n llmock.mount(vectorPath, vector);\n configMounts.set(vectorPath, vector);\n logger.info(`VectorMock mounted at ${vectorPath}`);\n }\n\n // MR1: llm.record.mcp — record mounts (auto-mount an MCPMock where none is).\n // Read only with llm.enableMcpRecording: true. Any value that is present is then\n // validated, so null, false, 0 and \"\" fail at start too.\n if (mcpRecord !== undefined) {\n wireMcpRecording(llmock, mcpRecord, {\n mounted: configMounts,\n mcpMock: MCPMock,\n fixtures: config.llm?.fixtures,\n fixturePath: config.llm?.record?.fixturePath,\n strict: config.strict,\n env: process.env,\n });\n }\n\n // Services — configure default catch-all responses\n if (config.services) {\n if (config.services.search) {\n llmock.onSearch(/.*/, []);\n logger.info(\"Search service enabled with default empty results\");\n }\n if (config.services.rerank) {\n llmock.onRerank(/.*/, []);\n logger.info(\"Rerank service enabled with default empty results\");\n }\n if (config.services.moderate) {\n llmock.onModerate(/.*/, { flagged: false, categories: {} });\n logger.info(\"Moderation service enabled with default unflagged results\");\n }\n }\n\n const url = await llmock.start();\n return { llmock, url };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"config-loader.js","names":[],"sources":["../src/config-loader.ts"],"sourcesContent":["import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { LLMock, createLLMockWithResolvedAuth } from \"./llmock.js\";\nimport { resolveInboundAuth, selectInboundAuthSource } from \"./api-key-auth.js\";\nimport { MCPMock } from \"./mcp-mock.js\";\nimport { configToolHandlers } from \"./mcp-handler.js\";\nimport { A2AMock } from \"./a2a-mock.js\";\nimport { AGUIMock } from \"./agui-mock.js\";\nimport type {\n AimockPolicy,\n ApiKeyAuthConfig,\n ChaosConfig,\n McpRecordConfig,\n MisbehaviorConfig,\n MisbehaviorFaultId,\n Mountable,\n RecordConfig,\n ResponsesToolsMode,\n} from \"./types.js\";\nimport { parseMisbehavior } from \"./misbehavior.js\";\nimport type { MCPToolDefinition, MCPPromptDefinition } from \"./mcp-types.js\";\nimport type { A2AAgentDefinition, A2APart, A2AArtifact, A2AStreamEvent } from \"./a2a-types.js\";\nimport type { AGUIEvent } from \"./agui-types.js\";\nimport { buildTextResponse } from \"./agui-handler.js\";\nimport { VectorMock } from \"./vector-mock.js\";\nimport type { QueryResult } from \"./vector-types.js\";\nimport { Logger } from \"./logger.js\";\nimport { wireMcpRecording } from \"./mcp-recorder.js\";\nimport { assignPolicyEntry, parsePolicy, parsePolicyEntry } from \"./response/policy.js\";\n\nexport interface MCPConfigTool extends MCPToolDefinition {\n result?: string;\n}\n\nexport interface MCPConfigResource {\n uri: string;\n name: string;\n mimeType?: string;\n description?: string;\n text?: string;\n blob?: string;\n}\n\nexport interface MCPConfigPrompt extends MCPPromptDefinition {\n result?: {\n messages: Array<{ role: string; content: { type: string; text: string } }>;\n };\n}\n\nexport interface MCPConfig {\n path?: string;\n serverInfo?: { name: string; version: string };\n tools?: MCPConfigTool[];\n resources?: MCPConfigResource[];\n prompts?: MCPConfigPrompt[];\n}\n\nexport interface A2AConfigPattern {\n pattern: string;\n parts?: A2APart[];\n artifacts?: A2AArtifact[];\n events?: A2AStreamEvent[];\n delayMs?: number;\n}\n\nexport interface A2AConfigAgent extends A2AAgentDefinition {\n messages?: A2AConfigPattern[];\n tasks?: A2AConfigPattern[];\n streamingTasks?: A2AConfigPattern[];\n}\n\nexport interface A2AConfig {\n path?: string;\n agents?: A2AConfigAgent[];\n}\n\nexport interface AGUIConfigFixture {\n match: { message?: string; toolCallId?: string; toolName?: string; stateKey?: string };\n text?: string; // shorthand: uses buildTextResponse\n events?: AGUIEvent[]; // raw events\n delayMs?: number;\n}\n\nexport interface AGUIConfig {\n path?: string; // mount path, default \"/agui\"\n fixtures?: AGUIConfigFixture[];\n}\n\nexport interface VectorConfigCollection {\n name: string;\n dimension: number;\n vectors?: Array<{\n id: string;\n values: number[];\n metadata?: Record<string, unknown>;\n }>;\n queryResults?: QueryResult[];\n}\n\nexport interface VectorConfig {\n path?: string;\n collections?: VectorConfigCollection[];\n}\n\n/** `llm.record` in a config file: the LLM `RecordConfig`, plus MCP record mounts (C10). */\nexport interface AimockRecordConfig extends RecordConfig {\n /** MR1: mount path → upstream URL, or a full MCP record config. */\n mcp?: Record<string, string | McpRecordConfig>;\n}\n\nexport interface AimockConfig {\n auth?: ApiKeyAuthConfig;\n llm?: {\n fixtures?: string;\n latency?: number;\n chunkSize?: number;\n replaySpeed?: number;\n logLevel?: \"silent\" | \"warn\" | \"info\" | \"debug\";\n chaos?: ChaosConfig;\n /**\n * Server-wide misbehavior defaults. Read only when misbehavior is enabled\n * (`llm.enableMisbehavior: true` or `aimock --misbehavior`); otherwise the\n * key is ignored with a warning, as 1.44.0 ignored it.\n */\n misbehavior?: MisbehaviorConfig | MisbehaviorFaultId;\n /** See `MockServerOptions.enableMisbehavior`. */\n enableMisbehavior?: boolean;\n record?: AimockRecordConfig;\n /**\n * Turns on `llm.record.mcp`. Without it the key is ignored with a warning,\n * as 1.44.0 ignored it.\n */\n enableMcpRecording?: boolean;\n /** See `MockServerOptions.strictToolArguments`. */\n strictToolArguments?: boolean;\n /** See `MockServerOptions.responsesTools`. */\n responsesTools?: ResponsesToolsMode;\n /** See `MockServerOptions.policy`. Independent of `strict`. */\n policy?: AimockPolicy;\n };\n mcp?: MCPConfig;\n a2a?: A2AConfig;\n agui?: AGUIConfig;\n vector?: VectorConfig;\n services?: { search?: boolean; rerank?: boolean; moderate?: boolean };\n metrics?: boolean;\n strict?: boolean;\n port?: number;\n host?: string;\n}\n\n/** C10: the LLM part of `llm.record`. `mcp` is MCP-only; no `providers` means no LLM recording. */\nfunction llmRecordOf(record: AimockRecordConfig | undefined): RecordConfig | undefined {\n if (!record) return undefined;\n const { mcp: _mcp, ...llm } = record;\n void _mcp; // repo pattern for an intentionally unused binding (byteplus-video.ts:1379-1380)\n return llm.providers === undefined ? undefined : llm;\n}\n\nexport function loadConfig(configPath: string): AimockConfig {\n const raw = fs.readFileSync(configPath, \"utf-8\");\n return JSON.parse(raw) as AimockConfig;\n}\n\n/** C3: `llm.record.mcp` is an object with at least one mount. */\nfunction hasMcpRecordMount(mcp: unknown): boolean {\n return (\n typeof mcp === \"object\" && mcp !== null && !Array.isArray(mcp) && Object.keys(mcp).length > 0\n );\n}\n\n/**\n * Command-line overrides for `startFromConfig`. The opt-in flags win over the\n * matching `llm.*` config keys.\n */\nexport interface StartFromConfigOverrides {\n port?: number;\n host?: string;\n /** `aimock --misbehavior`: overrides `llm.enableMisbehavior`. */\n enableMisbehavior?: boolean;\n /** `aimock --strict-tool-arguments`: overrides `llm.strictToolArguments`. */\n strictToolArguments?: boolean;\n /** `aimock --responses-tools`: overrides `llm.responsesTools`. */\n responsesTools?: ResponsesToolsMode;\n /** `aimock --policy`: overrides individual keys from `llm.policy`. */\n policy?: AimockPolicy;\n}\n\n/** Both CLI entry points use the shared policy parser and treat failures as usage errors. */\nexport function parsePolicyFlags(entries: string[]) {\n const policy: AimockPolicy = {};\n for (const entry of entries) {\n const separator = entry.indexOf(\"=\");\n if (separator < 0) {\n return {\n ok: false,\n error: `Invalid --policy ${JSON.stringify(entry)}: expected key=value.`,\n } as const;\n }\n const parsed = parsePolicyEntry(entry.slice(0, separator), entry.slice(separator + 1));\n if (!parsed.ok) {\n return {\n ok: false,\n error: `Invalid --policy ${JSON.stringify(entry)}: ${parsed.reason} Use key=value.`,\n } as const;\n }\n assignPolicyEntry(policy, parsed);\n }\n return { ok: true, policy } as const;\n}\n\nexport async function startFromConfig(\n config: AimockConfig,\n overrides?: StartFromConfigOverrides,\n): Promise<{ llmock: LLMock; url: string }> {\n const logger = new Logger(\"info\");\n const policy = {\n ...parsePolicy(config.llm?.policy, (message) => logger.warn(message)),\n ...parsePolicy(overrides?.policy, (message) => logger.warn(message)),\n };\n // 1.44.0 ignored these keys, so a non-boolean value (such as the string\n // \"true\") is ignored with a warning, as `llm.misbehavior` is, and the\n // option stays off unless a CLI flag turns it on (`llm.enableMcpRecording`\n // has no CLI flag).\n const booleanOptIn = (\n key: \"enableMisbehavior\" | \"enableMcpRecording\" | \"strictToolArguments\",\n ): boolean | undefined => {\n const value: unknown = config.llm?.[key];\n if (value === undefined || typeof value === \"boolean\") return value;\n logger.warn(\n `Ignoring llm.${key} because it must be true or false, got ${JSON.stringify(value)}.`,\n );\n return undefined;\n };\n const configEnableMisbehavior = booleanOptIn(\"enableMisbehavior\");\n const configStrictToolArguments = booleanOptIn(\"strictToolArguments\");\n // MCP recording also needs an explicit opt-in. 1.44.0 ignored\n // `llm.record.mcp`, so without `llm.enableMcpRecording: true` the key is\n // ignored (with a warning): nothing is mounted or validated and the log level\n // is unchanged. With the opt-in, an invalid value fails startup.\n const enableMcpRecording = booleanOptIn(\"enableMcpRecording\") === true;\n const mcpRecord = enableMcpRecording ? config.llm?.record?.mcp : undefined;\n // Misbehavior needs an explicit opt-in that a 1.44.0 config cannot contain.\n // 1.44.0 ignored `llm.misbehavior`, so without the opt-in the key is ignored\n // (with a warning) and the server serves exactly what 1.44.0 served. With the\n // opt-in, an invalid value fails startup like any other enabled-mode error.\n const enableMisbehavior = (overrides?.enableMisbehavior ?? configEnableMisbehavior) === true;\n let misbehavior: MisbehaviorConfig | undefined;\n if (config.llm?.misbehavior !== undefined) {\n if (!enableMisbehavior) {\n logger.warn(\n \"Ignoring llm.misbehavior because misbehavior is not enabled. Set llm.enableMisbehavior: true or pass --misbehavior to use it.\",\n );\n } else {\n const parsed = parseMisbehavior(config.llm.misbehavior, \"llm.misbehavior\");\n if (!parsed.ok) {\n throw new TypeError(`${parsed.issue.rule}: ${parsed.issue.path}: ${parsed.issue.message}`);\n }\n misbehavior = parsed.config;\n }\n }\n if (config.llm?.record?.mcp !== undefined && !enableMcpRecording) {\n logger.warn(\n \"Ignoring llm.record.mcp because MCP recording is not enabled. Set llm.enableMcpRecording: true to use it.\",\n );\n }\n\n // 1.44.0 ignored `llm.responsesTools`, so an invalid value is ignored with a\n // warning (as `llm.misbehavior` is) and the server runs in \"legacy\" mode,\n // unless `--responses-tools` sets the mode. The warning names the mode that\n // actually runs.\n let responsesTools = config.llm?.responsesTools;\n if (\n responsesTools !== undefined &&\n responsesTools !== \"legacy\" &&\n responsesTools !== \"extended\"\n ) {\n const effective =\n overrides?.responsesTools !== undefined\n ? `\"${overrides.responsesTools}\" from --responses-tools`\n : '\"legacy\"';\n logger.warn(\n `Ignoring llm.responsesTools because it must be \"legacy\" or \"extended\", got ${JSON.stringify(responsesTools)}. Using ${effective}.`,\n );\n responsesTools = undefined;\n }\n\n // A non-positive replaySpeed fails calculateDelay's `speed > 0` check and applies the\n // full delay rather than none. Mirrors the fixture-level guard in fixture-loader.ts.\n let replaySpeed = config.llm?.replaySpeed;\n if (replaySpeed != null && (!Number.isFinite(replaySpeed) || replaySpeed <= 0)) {\n logger.warn(`llm.replaySpeed must be a positive number, got ${replaySpeed}. Ignoring.`);\n replaySpeed = undefined;\n }\n\n // Load fixtures if specified\n const resolvedAuth = resolveInboundAuth(selectInboundAuthSource(config.auth));\n const llmock = createLLMockWithResolvedAuth(\n {\n port: overrides?.port ?? config.port ?? 0,\n host: overrides?.host ?? config.host ?? \"127.0.0.1\",\n latency: config.llm?.latency,\n chunkSize: config.llm?.chunkSize,\n replaySpeed,\n // llm.record.mcp reports skipped or failed recordings only as MCP-RECORD: warnings,\n // so an enabled config with at least one mount defaults to \"warn\" rather than the silent\n // default. An empty llm.record.mcp records nothing. An explicit logLevel wins.\n logLevel: config.llm?.logLevel ?? (hasMcpRecordMount(mcpRecord) ? \"warn\" : undefined),\n chaos: config.llm?.chaos,\n misbehavior,\n ...(enableMisbehavior ? { enableMisbehavior: true } : {}),\n record: llmRecordOf(config.llm?.record),\n metrics: config.metrics,\n strict: config.strict,\n strictToolArguments: overrides?.strictToolArguments ?? configStrictToolArguments,\n responsesTools: overrides?.responsesTools ?? responsesTools,\n policy,\n },\n resolvedAuth,\n );\n\n // What this function mounted, by path (MR1: llm.record.mcp records through an MCP mount).\n const configMounts = new Map<string, Mountable>();\n\n if (config.llm?.fixtures) {\n const fixturePath = path.resolve(config.llm.fixtures);\n const stat = fs.statSync(fixturePath);\n if (stat.isDirectory()) {\n llmock.loadFixtureDir(fixturePath);\n } else {\n llmock.loadFixtureFile(fixturePath);\n }\n }\n\n // MCP\n if (config.mcp) {\n const mcpConfig = config.mcp;\n const mcp = new MCPMock({\n serverInfo: mcpConfig.serverInfo,\n });\n\n if (mcpConfig.tools) {\n for (const tool of mcpConfig.tools) {\n const { result, ...def } = tool;\n mcp.addTool(def);\n if (result !== undefined) {\n const answer = () => result;\n configToolHandlers.add(answer);\n mcp.onToolCall(def.name, answer);\n }\n }\n }\n\n if (mcpConfig.resources) {\n for (const res of mcpConfig.resources) {\n mcp.addResource(\n { uri: res.uri, name: res.name, mimeType: res.mimeType, description: res.description },\n res.text !== undefined || res.blob !== undefined\n ? { text: res.text, blob: res.blob, mimeType: res.mimeType }\n : undefined,\n );\n }\n }\n\n if (mcpConfig.prompts) {\n for (const prompt of mcpConfig.prompts) {\n const { result, ...def } = prompt;\n if (result) {\n mcp.addPrompt(def, () => result as import(\"./mcp-types.js\").MCPPromptResult);\n } else {\n mcp.addPrompt(def);\n }\n }\n }\n\n const mcpPath = mcpConfig.path ?? \"/mcp\";\n llmock.mount(mcpPath, mcp);\n configMounts.set(mcpPath, mcp);\n logger.info(`MCPMock mounted at ${mcpPath}`);\n }\n\n // A2A\n if (config.a2a) {\n const a2aConfig = config.a2a;\n const a2a = new A2AMock();\n\n if (a2aConfig.agents) {\n for (const agentConfig of a2aConfig.agents) {\n const { messages, tasks, streamingTasks, ...def } = agentConfig;\n a2a.registerAgent(def);\n\n if (messages) {\n for (const m of messages) {\n a2a.onMessage(def.name, m.pattern, m.parts ?? [{ text: \"\" }]);\n }\n }\n\n if (tasks) {\n for (const t of tasks) {\n a2a.onTask(def.name, t.pattern, t.artifacts ?? []);\n }\n }\n\n if (streamingTasks) {\n for (const s of streamingTasks) {\n a2a.onStreamingTask(def.name, s.pattern, s.events ?? [], s.delayMs);\n }\n }\n }\n }\n\n const a2aPath = a2aConfig.path ?? \"/a2a\";\n llmock.mount(a2aPath, a2a);\n configMounts.set(a2aPath, a2a);\n logger.info(`A2AMock mounted at ${a2aPath}`);\n }\n\n // AG-UI\n if (config.agui) {\n const aguiConfig = config.agui;\n const agui = new AGUIMock();\n\n if (aguiConfig.fixtures) {\n for (const f of aguiConfig.fixtures) {\n if (f.match.toolCallId && f.text && !f.events) {\n logger.warn(\n `AG-UI fixture uses text shorthand with toolCallId — text shorthand ignores toolCallId matching; use events[] instead (match: ${JSON.stringify(f.match)})`,\n );\n }\n if (f.text) {\n if (f.match.message !== undefined) {\n agui.onMessage(f.match.message, f.text, { delayMs: f.delayMs });\n } else {\n // No message pattern — register via addFixture so it only matches\n // on other criteria (toolCallId, toolName, stateKey) instead of\n // becoming a catch-all that matches every request.\n agui.addFixture({\n match: {\n toolCallId: f.match.toolCallId,\n toolName: f.match.toolName,\n stateKey: f.match.stateKey,\n },\n events: buildTextResponse(f.text),\n delayMs: f.delayMs,\n });\n }\n } else if (f.events) {\n agui.addFixture({\n match: {\n message: f.match.message,\n toolCallId: f.match.toolCallId,\n toolName: f.match.toolName,\n stateKey: f.match.stateKey,\n },\n events: f.events,\n delayMs: f.delayMs,\n });\n } else {\n logger.warn(\n `AG-UI fixture has neither text nor events — it will be skipped (match: ${JSON.stringify(f.match)})`,\n );\n }\n }\n }\n\n const aguiPath = aguiConfig.path ?? \"/agui\";\n llmock.mount(aguiPath, agui);\n configMounts.set(aguiPath, agui);\n logger.info(`AGUIMock mounted at ${aguiPath}`);\n }\n\n // Vector\n if (config.vector) {\n const vectorConfig = config.vector;\n const vector = new VectorMock();\n\n if (vectorConfig.collections) {\n for (const col of vectorConfig.collections) {\n vector.addCollection(col.name, { dimension: col.dimension });\n\n if (col.vectors && col.vectors.length > 0) {\n vector.upsert(col.name, col.vectors);\n }\n\n if (col.queryResults) {\n vector.onQuery(col.name, col.queryResults);\n }\n }\n }\n\n const vectorPath = vectorConfig.path ?? \"/vector\";\n llmock.mount(vectorPath, vector);\n configMounts.set(vectorPath, vector);\n logger.info(`VectorMock mounted at ${vectorPath}`);\n }\n\n // MR1: llm.record.mcp — record mounts (auto-mount an MCPMock where none is).\n // Read only with llm.enableMcpRecording: true. Any value that is present is then\n // validated, so null, false, 0 and \"\" fail at start too.\n if (mcpRecord !== undefined) {\n wireMcpRecording(llmock, mcpRecord, {\n mounted: configMounts,\n mcpMock: MCPMock,\n fixtures: config.llm?.fixtures,\n fixturePath: config.llm?.record?.fixturePath,\n strict: config.strict,\n env: process.env,\n });\n }\n\n // Services — configure default catch-all responses\n if (config.services) {\n if (config.services.search) {\n llmock.onSearch(/.*/, []);\n logger.info(\"Search service enabled with default empty results\");\n }\n if (config.services.rerank) {\n llmock.onRerank(/.*/, []);\n logger.info(\"Rerank service enabled with default empty results\");\n }\n if (config.services.moderate) {\n llmock.onModerate(/.*/, { flagged: false, categories: {} });\n logger.info(\"Moderation service enabled with default unflagged results\");\n }\n }\n\n const url = await llmock.start();\n return { llmock, url };\n}\n"],"mappings":"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"}
@@ -0,0 +1,146 @@
1
+ const require_estimate = require('./response/usage/estimate.cjs');
2
+ const require_profiles = require('./response/profiles.cjs');
3
+ const require_errors = require('./response/errors.cjs');
4
+ const require_policy = require('./response/policy.cjs');
5
+ const require_helpers = require('./helpers.cjs');
6
+ const require_messages = require('./messages.cjs');
7
+ const require_gemini = require('./gemini.cjs');
8
+
9
+ //#region src/count-tokens.ts
10
+ function toolsError(tools, google) {
11
+ if (tools === void 0) return;
12
+ if (!Array.isArray(tools)) return "tools must be an array";
13
+ for (const tool of tools) {
14
+ if (!require_helpers.isJsonObject(tool)) return "tools entries must be objects";
15
+ if (google && tool.functionDeclarations !== void 0) {
16
+ if (!Array.isArray(tool.functionDeclarations)) return "functionDeclarations must be an array";
17
+ if (tool.functionDeclarations.some((declaration) => !require_helpers.isJsonObject(declaration))) return "functionDeclarations entries must be objects";
18
+ }
19
+ }
20
+ }
21
+ function hasObjectText(blocks, google, includeThought = false) {
22
+ return blocks.some((block) => require_helpers.isJsonObject(block) && (google ? includeThought || !block.thought : block.type === "text") && block.text !== null && typeof block.text === "object");
23
+ }
24
+ function contentError(contents, google) {
25
+ if (!Array.isArray(contents)) return google ? "contents must be an array" : "messages must be an array";
26
+ for (const content of contents) {
27
+ if (!require_helpers.isJsonObject(content)) return google ? "contents entries must be objects" : "messages entries must be objects";
28
+ const parts = google ? content.parts : content.content;
29
+ if (google) {
30
+ if (parts !== void 0 && !Array.isArray(parts)) return "parts must be an array";
31
+ } else if (typeof parts !== "string" && !Array.isArray(parts)) return "message content must be a string or an array";
32
+ if (Array.isArray(parts) && parts.some((part) => !require_helpers.isJsonObject(part))) return "content blocks must be objects";
33
+ const role = google ? content.role ?? "user" : content.role;
34
+ const consumed = google ? role === "user" || role === "model" : role === "user" || role === "assistant";
35
+ if (Array.isArray(parts) && consumed) {
36
+ if (hasObjectText(parts, google)) return "content text must not be an object or array";
37
+ if (!google && role === "user" && parts.some((block) => require_helpers.isJsonObject(block) && block.type === "tool_result" && Array.isArray(block.content) && hasObjectText(block.content, false))) return "tool_result content text must not be an object or array";
38
+ }
39
+ }
40
+ }
41
+ /** Counts always use the existing conversion and estimator; no fixture or egress path. */
42
+ function handleCountTokens(req, res, raw, google, model, journal, defaults, setCorsHeaders) {
43
+ setCorsHeaders(res);
44
+ const reject = (message, requestId) => {
45
+ journal.add({
46
+ method: req.method ?? "POST",
47
+ path: req.url ?? "?",
48
+ headers: require_helpers.flattenHeaders(req.headers),
49
+ body: null,
50
+ response: {
51
+ status: 400,
52
+ fixture: null,
53
+ source: "internal"
54
+ }
55
+ });
56
+ const rendered = google ? require_errors.renderError("gemini", "fixture", {
57
+ message,
58
+ bodyCode: 400,
59
+ status: "INVALID_ARGUMENT"
60
+ }) : require_errors.renderError("messages", "thinking", {
61
+ message,
62
+ requestId
63
+ });
64
+ res.writeHead(400, {
65
+ "Content-Type": "application/json",
66
+ ...rendered.headers
67
+ });
68
+ res.end(rendered.body);
69
+ };
70
+ const policy = require_policy.resolvePolicy(defaults, req.headers);
71
+ const missingHeader = !google && policy.requiredHeaders === "enforce" ? require_profiles.missingRequiredHeader("messages", req.headers) : void 0;
72
+ if (missingHeader) {
73
+ reject(`${missingHeader}: header is required`, require_helpers.resolveRequestId(req.headers).id);
74
+ return;
75
+ }
76
+ let body;
77
+ try {
78
+ body = JSON.parse(raw);
79
+ } catch (error) {
80
+ reject(`Malformed JSON body: ${error instanceof Error ? error.message : "unknown"}`);
81
+ return;
82
+ }
83
+ if (google && require_helpers.isJsonObject(body) && body.generateContentRequest !== void 0) body = body.generateContentRequest;
84
+ if (!require_helpers.isJsonObject(body)) {
85
+ reject("Request body must be an object");
86
+ return;
87
+ }
88
+ const invalidContent = contentError(google ? body.contents : body.messages, google);
89
+ const invalidTools = toolsError(body.tools, google);
90
+ const invalidBody = invalidContent ?? invalidTools;
91
+ if (invalidBody) {
92
+ reject(invalidBody);
93
+ return;
94
+ }
95
+ if (!google && body.system !== void 0) {
96
+ if (typeof body.system !== "string" && !Array.isArray(body.system)) {
97
+ reject("system must be a string or an array");
98
+ return;
99
+ }
100
+ if (Array.isArray(body.system) && body.system.some((block) => !require_helpers.isJsonObject(block))) {
101
+ reject("system blocks must be objects");
102
+ return;
103
+ }
104
+ if (Array.isArray(body.system) && hasObjectText(body.system, false)) {
105
+ reject("system text must not be an object or array");
106
+ return;
107
+ }
108
+ }
109
+ if (google && body.systemInstruction !== void 0 && body.systemInstruction !== null) {
110
+ if (!require_helpers.isJsonObject(body.systemInstruction) || body.systemInstruction.parts !== void 0 && body.systemInstruction.parts !== null && !Array.isArray(body.systemInstruction.parts)) {
111
+ reject("systemInstruction must be an object with an array of parts");
112
+ return;
113
+ }
114
+ if (Array.isArray(body.systemInstruction.parts) && body.systemInstruction.parts.some((part) => !require_helpers.isJsonObject(part))) {
115
+ reject("systemInstruction parts must be objects");
116
+ return;
117
+ }
118
+ if (Array.isArray(body.systemInstruction.parts) && hasObjectText(body.systemInstruction.parts, true, true)) {
119
+ reject("systemInstruction text must not be an object or array");
120
+ return;
121
+ }
122
+ }
123
+ const converted = google ? require_gemini.geminiToCompletionRequest(body, model, false) : require_messages.claudeToCompletionRequest({
124
+ ...body,
125
+ model: typeof body.model === "string" ? body.model : "",
126
+ max_tokens: 0
127
+ });
128
+ const count = require_estimate.estimatePromptTokens(converted.messages) + (converted.tools === void 0 ? 0 : require_estimate.estimateTokens(JSON.stringify(converted.tools)));
129
+ journal.add({
130
+ method: req.method ?? "POST",
131
+ path: req.url ?? "?",
132
+ headers: require_helpers.flattenHeaders(req.headers),
133
+ body: converted,
134
+ response: {
135
+ status: 200,
136
+ fixture: null,
137
+ source: "internal"
138
+ }
139
+ });
140
+ res.writeHead(200, { "Content-Type": "application/json" });
141
+ res.end(JSON.stringify(google ? { totalTokens: count } : { input_tokens: count }));
142
+ }
143
+
144
+ //#endregion
145
+ exports.handleCountTokens = handleCountTokens;
146
+ //# sourceMappingURL=count-tokens.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"count-tokens.cjs","names":["isJsonObject","flattenHeaders","renderError","resolvePolicy","missingRequiredHeader","resolveRequestId","geminiToCompletionRequest","claudeToCompletionRequest","estimatePromptTokens","estimateTokens"],"sources":["../src/count-tokens.ts"],"sourcesContent":["import type * as http from \"node:http\";\nimport { claudeToCompletionRequest } from \"./messages.js\";\nimport { geminiToCompletionRequest } from \"./gemini.js\";\nimport { flattenHeaders, isJsonObject, resolveRequestId } from \"./helpers.js\";\nimport type { Journal } from \"./journal.js\";\nimport type { ChatCompletionRequest, HandlerDefaults } from \"./types.js\";\nimport { renderError } from \"./response/errors.js\";\nimport { resolvePolicy } from \"./response/policy.js\";\nimport { missingRequiredHeader } from \"./response/profiles.js\";\nimport { estimatePromptTokens, estimateTokens } from \"./response/usage/estimate.js\";\n\ntype ClaudeRequest = Parameters<typeof claudeToCompletionRequest>[0];\ntype GeminiRequest = Parameters<typeof geminiToCompletionRequest>[0];\n\nfunction toolsError(tools: unknown, google: boolean): string | undefined {\n if (tools === undefined) return;\n if (!Array.isArray(tools)) return \"tools must be an array\";\n for (const tool of tools) {\n if (!isJsonObject(tool)) return \"tools entries must be objects\";\n if (google && tool.functionDeclarations !== undefined) {\n if (!Array.isArray(tool.functionDeclarations)) return \"functionDeclarations must be an array\";\n if (tool.functionDeclarations.some((declaration: unknown) => !isJsonObject(declaration)))\n return \"functionDeclarations entries must be objects\";\n }\n }\n}\n\nfunction hasObjectText(blocks: unknown[], google: boolean, includeThought = false): boolean {\n return blocks.some(\n (block) =>\n isJsonObject(block) &&\n (google ? includeThought || !block.thought : block.type === \"text\") &&\n block.text !== null &&\n typeof block.text === \"object\",\n );\n}\n\nfunction contentError(contents: unknown, google: boolean): string | undefined {\n if (!Array.isArray(contents))\n return google ? \"contents must be an array\" : \"messages must be an array\";\n for (const content of contents) {\n if (!isJsonObject(content))\n return google ? \"contents entries must be objects\" : \"messages entries must be objects\";\n const parts = google ? content.parts : content.content;\n if (google) {\n if (parts !== undefined && !Array.isArray(parts)) return \"parts must be an array\";\n } else if (typeof parts !== \"string\" && !Array.isArray(parts)) {\n return \"message content must be a string or an array\";\n }\n if (Array.isArray(parts) && parts.some((part: unknown) => !isJsonObject(part)))\n return \"content blocks must be objects\";\n const role = google ? (content.role ?? \"user\") : content.role;\n const consumed = google\n ? role === \"user\" || role === \"model\"\n : role === \"user\" || role === \"assistant\";\n if (Array.isArray(parts) && consumed) {\n if (hasObjectText(parts, google)) return \"content text must not be an object or array\";\n if (\n !google &&\n role === \"user\" &&\n parts.some(\n (block) =>\n isJsonObject(block) &&\n block.type === \"tool_result\" &&\n Array.isArray(block.content) &&\n hasObjectText(block.content, false),\n )\n )\n return \"tool_result content text must not be an object or array\";\n }\n }\n}\n\n/** Counts always use the existing conversion and estimator; no fixture or egress path. */\nexport function handleCountTokens(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n google: boolean,\n model: string,\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n): void {\n setCorsHeaders(res);\n const reject = (message: string, requestId?: string): void => {\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"?\",\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null, source: \"internal\" },\n });\n const rendered = google\n ? renderError(\"gemini\", \"fixture\", { message, bodyCode: 400, status: \"INVALID_ARGUMENT\" })\n : renderError(\"messages\", \"thinking\", { message, requestId });\n res.writeHead(400, { \"Content-Type\": \"application/json\", ...rendered.headers });\n res.end(rendered.body);\n };\n const policy = resolvePolicy(defaults, req.headers);\n const missingHeader =\n !google && policy.requiredHeaders === \"enforce\"\n ? missingRequiredHeader(\"messages\", req.headers)\n : undefined;\n if (missingHeader) {\n reject(`${missingHeader}: header is required`, resolveRequestId(req.headers).id);\n return;\n }\n let body: unknown;\n try {\n body = JSON.parse(raw);\n } catch (error) {\n reject(`Malformed JSON body: ${error instanceof Error ? error.message : \"unknown\"}`);\n return;\n }\n if (google && isJsonObject(body) && body.generateContentRequest !== undefined)\n body = body.generateContentRequest;\n if (!isJsonObject(body)) {\n reject(\"Request body must be an object\");\n return;\n }\n const invalidContent = contentError(google ? body.contents : body.messages, google);\n const invalidTools = toolsError(body.tools, google);\n const invalidBody = invalidContent ?? invalidTools;\n if (invalidBody) {\n reject(invalidBody);\n return;\n }\n if (!google && body.system !== undefined) {\n if (typeof body.system !== \"string\" && !Array.isArray(body.system)) {\n reject(\"system must be a string or an array\");\n return;\n }\n if (Array.isArray(body.system) && body.system.some((block: unknown) => !isJsonObject(block))) {\n reject(\"system blocks must be objects\");\n return;\n }\n if (Array.isArray(body.system) && hasObjectText(body.system, false)) {\n reject(\"system text must not be an object or array\");\n return;\n }\n }\n if (google && body.systemInstruction !== undefined && body.systemInstruction !== null) {\n if (\n !isJsonObject(body.systemInstruction) ||\n (body.systemInstruction.parts !== undefined &&\n body.systemInstruction.parts !== null &&\n !Array.isArray(body.systemInstruction.parts))\n ) {\n reject(\"systemInstruction must be an object with an array of parts\");\n return;\n }\n if (\n Array.isArray(body.systemInstruction.parts) &&\n body.systemInstruction.parts.some((part: unknown) => !isJsonObject(part))\n ) {\n reject(\"systemInstruction parts must be objects\");\n return;\n }\n if (\n Array.isArray(body.systemInstruction.parts) &&\n hasObjectText(body.systemInstruction.parts, true, true)\n ) {\n reject(\"systemInstruction text must not be an object or array\");\n return;\n }\n }\n // The guards above validate the fields consumed by the existing converters.\n const converted: ChatCompletionRequest = google\n ? geminiToCompletionRequest(body as GeminiRequest, model, false)\n : claudeToCompletionRequest({\n ...body,\n model: typeof body.model === \"string\" ? body.model : \"\",\n max_tokens: 0,\n } as ClaudeRequest);\n const count =\n estimatePromptTokens(converted.messages) +\n (converted.tools === undefined ? 0 : estimateTokens(JSON.stringify(converted.tools)));\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"?\",\n headers: flattenHeaders(req.headers),\n body: converted,\n response: { status: 200, fixture: null, source: \"internal\" },\n });\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(google ? { totalTokens: count } : { input_tokens: count }));\n}\n"],"mappings":"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"}
@@ -0,0 +1,146 @@
1
+ import { estimatePromptTokens, estimateTokens } from "./response/usage/estimate.js";
2
+ import { missingRequiredHeader } from "./response/profiles.js";
3
+ import { renderError } from "./response/errors.js";
4
+ import { resolvePolicy } from "./response/policy.js";
5
+ import { flattenHeaders, isJsonObject, resolveRequestId } from "./helpers.js";
6
+ import { claudeToCompletionRequest } from "./messages.js";
7
+ import { geminiToCompletionRequest } from "./gemini.js";
8
+
9
+ //#region src/count-tokens.ts
10
+ function toolsError(tools, google) {
11
+ if (tools === void 0) return;
12
+ if (!Array.isArray(tools)) return "tools must be an array";
13
+ for (const tool of tools) {
14
+ if (!isJsonObject(tool)) return "tools entries must be objects";
15
+ if (google && tool.functionDeclarations !== void 0) {
16
+ if (!Array.isArray(tool.functionDeclarations)) return "functionDeclarations must be an array";
17
+ if (tool.functionDeclarations.some((declaration) => !isJsonObject(declaration))) return "functionDeclarations entries must be objects";
18
+ }
19
+ }
20
+ }
21
+ function hasObjectText(blocks, google, includeThought = false) {
22
+ return blocks.some((block) => isJsonObject(block) && (google ? includeThought || !block.thought : block.type === "text") && block.text !== null && typeof block.text === "object");
23
+ }
24
+ function contentError(contents, google) {
25
+ if (!Array.isArray(contents)) return google ? "contents must be an array" : "messages must be an array";
26
+ for (const content of contents) {
27
+ if (!isJsonObject(content)) return google ? "contents entries must be objects" : "messages entries must be objects";
28
+ const parts = google ? content.parts : content.content;
29
+ if (google) {
30
+ if (parts !== void 0 && !Array.isArray(parts)) return "parts must be an array";
31
+ } else if (typeof parts !== "string" && !Array.isArray(parts)) return "message content must be a string or an array";
32
+ if (Array.isArray(parts) && parts.some((part) => !isJsonObject(part))) return "content blocks must be objects";
33
+ const role = google ? content.role ?? "user" : content.role;
34
+ const consumed = google ? role === "user" || role === "model" : role === "user" || role === "assistant";
35
+ if (Array.isArray(parts) && consumed) {
36
+ if (hasObjectText(parts, google)) return "content text must not be an object or array";
37
+ if (!google && role === "user" && parts.some((block) => isJsonObject(block) && block.type === "tool_result" && Array.isArray(block.content) && hasObjectText(block.content, false))) return "tool_result content text must not be an object or array";
38
+ }
39
+ }
40
+ }
41
+ /** Counts always use the existing conversion and estimator; no fixture or egress path. */
42
+ function handleCountTokens(req, res, raw, google, model, journal, defaults, setCorsHeaders) {
43
+ setCorsHeaders(res);
44
+ const reject = (message, requestId) => {
45
+ journal.add({
46
+ method: req.method ?? "POST",
47
+ path: req.url ?? "?",
48
+ headers: flattenHeaders(req.headers),
49
+ body: null,
50
+ response: {
51
+ status: 400,
52
+ fixture: null,
53
+ source: "internal"
54
+ }
55
+ });
56
+ const rendered = google ? renderError("gemini", "fixture", {
57
+ message,
58
+ bodyCode: 400,
59
+ status: "INVALID_ARGUMENT"
60
+ }) : renderError("messages", "thinking", {
61
+ message,
62
+ requestId
63
+ });
64
+ res.writeHead(400, {
65
+ "Content-Type": "application/json",
66
+ ...rendered.headers
67
+ });
68
+ res.end(rendered.body);
69
+ };
70
+ const policy = resolvePolicy(defaults, req.headers);
71
+ const missingHeader = !google && policy.requiredHeaders === "enforce" ? missingRequiredHeader("messages", req.headers) : void 0;
72
+ if (missingHeader) {
73
+ reject(`${missingHeader}: header is required`, resolveRequestId(req.headers).id);
74
+ return;
75
+ }
76
+ let body;
77
+ try {
78
+ body = JSON.parse(raw);
79
+ } catch (error) {
80
+ reject(`Malformed JSON body: ${error instanceof Error ? error.message : "unknown"}`);
81
+ return;
82
+ }
83
+ if (google && isJsonObject(body) && body.generateContentRequest !== void 0) body = body.generateContentRequest;
84
+ if (!isJsonObject(body)) {
85
+ reject("Request body must be an object");
86
+ return;
87
+ }
88
+ const invalidContent = contentError(google ? body.contents : body.messages, google);
89
+ const invalidTools = toolsError(body.tools, google);
90
+ const invalidBody = invalidContent ?? invalidTools;
91
+ if (invalidBody) {
92
+ reject(invalidBody);
93
+ return;
94
+ }
95
+ if (!google && body.system !== void 0) {
96
+ if (typeof body.system !== "string" && !Array.isArray(body.system)) {
97
+ reject("system must be a string or an array");
98
+ return;
99
+ }
100
+ if (Array.isArray(body.system) && body.system.some((block) => !isJsonObject(block))) {
101
+ reject("system blocks must be objects");
102
+ return;
103
+ }
104
+ if (Array.isArray(body.system) && hasObjectText(body.system, false)) {
105
+ reject("system text must not be an object or array");
106
+ return;
107
+ }
108
+ }
109
+ if (google && body.systemInstruction !== void 0 && body.systemInstruction !== null) {
110
+ if (!isJsonObject(body.systemInstruction) || body.systemInstruction.parts !== void 0 && body.systemInstruction.parts !== null && !Array.isArray(body.systemInstruction.parts)) {
111
+ reject("systemInstruction must be an object with an array of parts");
112
+ return;
113
+ }
114
+ if (Array.isArray(body.systemInstruction.parts) && body.systemInstruction.parts.some((part) => !isJsonObject(part))) {
115
+ reject("systemInstruction parts must be objects");
116
+ return;
117
+ }
118
+ if (Array.isArray(body.systemInstruction.parts) && hasObjectText(body.systemInstruction.parts, true, true)) {
119
+ reject("systemInstruction text must not be an object or array");
120
+ return;
121
+ }
122
+ }
123
+ const converted = google ? geminiToCompletionRequest(body, model, false) : claudeToCompletionRequest({
124
+ ...body,
125
+ model: typeof body.model === "string" ? body.model : "",
126
+ max_tokens: 0
127
+ });
128
+ const count = estimatePromptTokens(converted.messages) + (converted.tools === void 0 ? 0 : estimateTokens(JSON.stringify(converted.tools)));
129
+ journal.add({
130
+ method: req.method ?? "POST",
131
+ path: req.url ?? "?",
132
+ headers: flattenHeaders(req.headers),
133
+ body: converted,
134
+ response: {
135
+ status: 200,
136
+ fixture: null,
137
+ source: "internal"
138
+ }
139
+ });
140
+ res.writeHead(200, { "Content-Type": "application/json" });
141
+ res.end(JSON.stringify(google ? { totalTokens: count } : { input_tokens: count }));
142
+ }
143
+
144
+ //#endregion
145
+ export { handleCountTokens };
146
+ //# sourceMappingURL=count-tokens.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"count-tokens.js","names":[],"sources":["../src/count-tokens.ts"],"sourcesContent":["import type * as http from \"node:http\";\nimport { claudeToCompletionRequest } from \"./messages.js\";\nimport { geminiToCompletionRequest } from \"./gemini.js\";\nimport { flattenHeaders, isJsonObject, resolveRequestId } from \"./helpers.js\";\nimport type { Journal } from \"./journal.js\";\nimport type { ChatCompletionRequest, HandlerDefaults } from \"./types.js\";\nimport { renderError } from \"./response/errors.js\";\nimport { resolvePolicy } from \"./response/policy.js\";\nimport { missingRequiredHeader } from \"./response/profiles.js\";\nimport { estimatePromptTokens, estimateTokens } from \"./response/usage/estimate.js\";\n\ntype ClaudeRequest = Parameters<typeof claudeToCompletionRequest>[0];\ntype GeminiRequest = Parameters<typeof geminiToCompletionRequest>[0];\n\nfunction toolsError(tools: unknown, google: boolean): string | undefined {\n if (tools === undefined) return;\n if (!Array.isArray(tools)) return \"tools must be an array\";\n for (const tool of tools) {\n if (!isJsonObject(tool)) return \"tools entries must be objects\";\n if (google && tool.functionDeclarations !== undefined) {\n if (!Array.isArray(tool.functionDeclarations)) return \"functionDeclarations must be an array\";\n if (tool.functionDeclarations.some((declaration: unknown) => !isJsonObject(declaration)))\n return \"functionDeclarations entries must be objects\";\n }\n }\n}\n\nfunction hasObjectText(blocks: unknown[], google: boolean, includeThought = false): boolean {\n return blocks.some(\n (block) =>\n isJsonObject(block) &&\n (google ? includeThought || !block.thought : block.type === \"text\") &&\n block.text !== null &&\n typeof block.text === \"object\",\n );\n}\n\nfunction contentError(contents: unknown, google: boolean): string | undefined {\n if (!Array.isArray(contents))\n return google ? \"contents must be an array\" : \"messages must be an array\";\n for (const content of contents) {\n if (!isJsonObject(content))\n return google ? \"contents entries must be objects\" : \"messages entries must be objects\";\n const parts = google ? content.parts : content.content;\n if (google) {\n if (parts !== undefined && !Array.isArray(parts)) return \"parts must be an array\";\n } else if (typeof parts !== \"string\" && !Array.isArray(parts)) {\n return \"message content must be a string or an array\";\n }\n if (Array.isArray(parts) && parts.some((part: unknown) => !isJsonObject(part)))\n return \"content blocks must be objects\";\n const role = google ? (content.role ?? \"user\") : content.role;\n const consumed = google\n ? role === \"user\" || role === \"model\"\n : role === \"user\" || role === \"assistant\";\n if (Array.isArray(parts) && consumed) {\n if (hasObjectText(parts, google)) return \"content text must not be an object or array\";\n if (\n !google &&\n role === \"user\" &&\n parts.some(\n (block) =>\n isJsonObject(block) &&\n block.type === \"tool_result\" &&\n Array.isArray(block.content) &&\n hasObjectText(block.content, false),\n )\n )\n return \"tool_result content text must not be an object or array\";\n }\n }\n}\n\n/** Counts always use the existing conversion and estimator; no fixture or egress path. */\nexport function handleCountTokens(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n google: boolean,\n model: string,\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n): void {\n setCorsHeaders(res);\n const reject = (message: string, requestId?: string): void => {\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"?\",\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null, source: \"internal\" },\n });\n const rendered = google\n ? renderError(\"gemini\", \"fixture\", { message, bodyCode: 400, status: \"INVALID_ARGUMENT\" })\n : renderError(\"messages\", \"thinking\", { message, requestId });\n res.writeHead(400, { \"Content-Type\": \"application/json\", ...rendered.headers });\n res.end(rendered.body);\n };\n const policy = resolvePolicy(defaults, req.headers);\n const missingHeader =\n !google && policy.requiredHeaders === \"enforce\"\n ? missingRequiredHeader(\"messages\", req.headers)\n : undefined;\n if (missingHeader) {\n reject(`${missingHeader}: header is required`, resolveRequestId(req.headers).id);\n return;\n }\n let body: unknown;\n try {\n body = JSON.parse(raw);\n } catch (error) {\n reject(`Malformed JSON body: ${error instanceof Error ? error.message : \"unknown\"}`);\n return;\n }\n if (google && isJsonObject(body) && body.generateContentRequest !== undefined)\n body = body.generateContentRequest;\n if (!isJsonObject(body)) {\n reject(\"Request body must be an object\");\n return;\n }\n const invalidContent = contentError(google ? body.contents : body.messages, google);\n const invalidTools = toolsError(body.tools, google);\n const invalidBody = invalidContent ?? invalidTools;\n if (invalidBody) {\n reject(invalidBody);\n return;\n }\n if (!google && body.system !== undefined) {\n if (typeof body.system !== \"string\" && !Array.isArray(body.system)) {\n reject(\"system must be a string or an array\");\n return;\n }\n if (Array.isArray(body.system) && body.system.some((block: unknown) => !isJsonObject(block))) {\n reject(\"system blocks must be objects\");\n return;\n }\n if (Array.isArray(body.system) && hasObjectText(body.system, false)) {\n reject(\"system text must not be an object or array\");\n return;\n }\n }\n if (google && body.systemInstruction !== undefined && body.systemInstruction !== null) {\n if (\n !isJsonObject(body.systemInstruction) ||\n (body.systemInstruction.parts !== undefined &&\n body.systemInstruction.parts !== null &&\n !Array.isArray(body.systemInstruction.parts))\n ) {\n reject(\"systemInstruction must be an object with an array of parts\");\n return;\n }\n if (\n Array.isArray(body.systemInstruction.parts) &&\n body.systemInstruction.parts.some((part: unknown) => !isJsonObject(part))\n ) {\n reject(\"systemInstruction parts must be objects\");\n return;\n }\n if (\n Array.isArray(body.systemInstruction.parts) &&\n hasObjectText(body.systemInstruction.parts, true, true)\n ) {\n reject(\"systemInstruction text must not be an object or array\");\n return;\n }\n }\n // The guards above validate the fields consumed by the existing converters.\n const converted: ChatCompletionRequest = google\n ? geminiToCompletionRequest(body as GeminiRequest, model, false)\n : claudeToCompletionRequest({\n ...body,\n model: typeof body.model === \"string\" ? body.model : \"\",\n max_tokens: 0,\n } as ClaudeRequest);\n const count =\n estimatePromptTokens(converted.messages) +\n (converted.tools === undefined ? 0 : estimateTokens(JSON.stringify(converted.tools)));\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"?\",\n headers: flattenHeaders(req.headers),\n body: converted,\n response: { status: 200, fixture: null, source: \"internal\" },\n });\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(google ? { totalTokens: count } : { input_tokens: count }));\n}\n"],"mappings":";;;;;;;;;AAcA,SAAS,WAAW,OAAgB,QAAqC;AACvE,KAAI,UAAU,OAAW;AACzB,KAAI,CAAC,MAAM,QAAQ,MAAM,CAAE,QAAO;AAClC,MAAK,MAAM,QAAQ,OAAO;AACxB,MAAI,CAAC,aAAa,KAAK,CAAE,QAAO;AAChC,MAAI,UAAU,KAAK,yBAAyB,QAAW;AACrD,OAAI,CAAC,MAAM,QAAQ,KAAK,qBAAqB,CAAE,QAAO;AACtD,OAAI,KAAK,qBAAqB,MAAM,gBAAyB,CAAC,aAAa,YAAY,CAAC,CACtF,QAAO;;;;AAKf,SAAS,cAAc,QAAmB,QAAiB,iBAAiB,OAAgB;AAC1F,QAAO,OAAO,MACX,UACC,aAAa,MAAM,KAClB,SAAS,kBAAkB,CAAC,MAAM,UAAU,MAAM,SAAS,WAC5D,MAAM,SAAS,QACf,OAAO,MAAM,SAAS,SACzB;;AAGH,SAAS,aAAa,UAAmB,QAAqC;AAC5E,KAAI,CAAC,MAAM,QAAQ,SAAS,CAC1B,QAAO,SAAS,8BAA8B;AAChD,MAAK,MAAM,WAAW,UAAU;AAC9B,MAAI,CAAC,aAAa,QAAQ,CACxB,QAAO,SAAS,qCAAqC;EACvD,MAAM,QAAQ,SAAS,QAAQ,QAAQ,QAAQ;AAC/C,MAAI,QACF;OAAI,UAAU,UAAa,CAAC,MAAM,QAAQ,MAAM,CAAE,QAAO;aAChD,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,MAAM,CAC3D,QAAO;AAET,MAAI,MAAM,QAAQ,MAAM,IAAI,MAAM,MAAM,SAAkB,CAAC,aAAa,KAAK,CAAC,CAC5E,QAAO;EACT,MAAM,OAAO,SAAU,QAAQ,QAAQ,SAAU,QAAQ;EACzD,MAAM,WAAW,SACb,SAAS,UAAU,SAAS,UAC5B,SAAS,UAAU,SAAS;AAChC,MAAI,MAAM,QAAQ,MAAM,IAAI,UAAU;AACpC,OAAI,cAAc,OAAO,OAAO,CAAE,QAAO;AACzC,OACE,CAAC,UACD,SAAS,UACT,MAAM,MACH,UACC,aAAa,MAAM,IACnB,MAAM,SAAS,iBACf,MAAM,QAAQ,MAAM,QAAQ,IAC5B,cAAc,MAAM,SAAS,MAAM,CACtC,CAED,QAAO;;;;;AAMf,SAAgB,kBACd,KACA,KACA,KACA,QACA,OACA,SACA,UACA,gBACM;AACN,gBAAe,IAAI;CACnB,MAAM,UAAU,SAAiB,cAA6B;AAC5D,UAAQ,IAAI;GACV,QAAQ,IAAI,UAAU;GACtB,MAAM,IAAI,OAAO;GACjB,SAAS,eAAe,IAAI,QAAQ;GACpC,MAAM;GACN,UAAU;IAAE,QAAQ;IAAK,SAAS;IAAM,QAAQ;IAAY;GAC7D,CAAC;EACF,MAAM,WAAW,SACb,YAAY,UAAU,WAAW;GAAE;GAAS,UAAU;GAAK,QAAQ;GAAoB,CAAC,GACxF,YAAY,YAAY,YAAY;GAAE;GAAS;GAAW,CAAC;AAC/D,MAAI,UAAU,KAAK;GAAE,gBAAgB;GAAoB,GAAG,SAAS;GAAS,CAAC;AAC/E,MAAI,IAAI,SAAS,KAAK;;CAExB,MAAM,SAAS,cAAc,UAAU,IAAI,QAAQ;CACnD,MAAM,gBACJ,CAAC,UAAU,OAAO,oBAAoB,YAClC,sBAAsB,YAAY,IAAI,QAAQ,GAC9C;AACN,KAAI,eAAe;AACjB,SAAO,GAAG,cAAc,uBAAuB,iBAAiB,IAAI,QAAQ,CAAC,GAAG;AAChF;;CAEF,IAAI;AACJ,KAAI;AACF,SAAO,KAAK,MAAM,IAAI;UACf,OAAO;AACd,SAAO,wBAAwB,iBAAiB,QAAQ,MAAM,UAAU,YAAY;AACpF;;AAEF,KAAI,UAAU,aAAa,KAAK,IAAI,KAAK,2BAA2B,OAClE,QAAO,KAAK;AACd,KAAI,CAAC,aAAa,KAAK,EAAE;AACvB,SAAO,iCAAiC;AACxC;;CAEF,MAAM,iBAAiB,aAAa,SAAS,KAAK,WAAW,KAAK,UAAU,OAAO;CACnF,MAAM,eAAe,WAAW,KAAK,OAAO,OAAO;CACnD,MAAM,cAAc,kBAAkB;AACtC,KAAI,aAAa;AACf,SAAO,YAAY;AACnB;;AAEF,KAAI,CAAC,UAAU,KAAK,WAAW,QAAW;AACxC,MAAI,OAAO,KAAK,WAAW,YAAY,CAAC,MAAM,QAAQ,KAAK,OAAO,EAAE;AAClE,UAAO,sCAAsC;AAC7C;;AAEF,MAAI,MAAM,QAAQ,KAAK,OAAO,IAAI,KAAK,OAAO,MAAM,UAAmB,CAAC,aAAa,MAAM,CAAC,EAAE;AAC5F,UAAO,gCAAgC;AACvC;;AAEF,MAAI,MAAM,QAAQ,KAAK,OAAO,IAAI,cAAc,KAAK,QAAQ,MAAM,EAAE;AACnE,UAAO,6CAA6C;AACpD;;;AAGJ,KAAI,UAAU,KAAK,sBAAsB,UAAa,KAAK,sBAAsB,MAAM;AACrF,MACE,CAAC,aAAa,KAAK,kBAAkB,IACpC,KAAK,kBAAkB,UAAU,UAChC,KAAK,kBAAkB,UAAU,QACjC,CAAC,MAAM,QAAQ,KAAK,kBAAkB,MAAM,EAC9C;AACA,UAAO,6DAA6D;AACpE;;AAEF,MACE,MAAM,QAAQ,KAAK,kBAAkB,MAAM,IAC3C,KAAK,kBAAkB,MAAM,MAAM,SAAkB,CAAC,aAAa,KAAK,CAAC,EACzE;AACA,UAAO,0CAA0C;AACjD;;AAEF,MACE,MAAM,QAAQ,KAAK,kBAAkB,MAAM,IAC3C,cAAc,KAAK,kBAAkB,OAAO,MAAM,KAAK,EACvD;AACA,UAAO,wDAAwD;AAC/D;;;CAIJ,MAAM,YAAmC,SACrC,0BAA0B,MAAuB,OAAO,MAAM,GAC9D,0BAA0B;EACxB,GAAG;EACH,OAAO,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ;EACrD,YAAY;EACb,CAAkB;CACvB,MAAM,QACJ,qBAAqB,UAAU,SAAS,IACvC,UAAU,UAAU,SAAY,IAAI,eAAe,KAAK,UAAU,UAAU,MAAM,CAAC;AACtF,SAAQ,IAAI;EACV,QAAQ,IAAI,UAAU;EACtB,MAAM,IAAI,OAAO;EACjB,SAAS,eAAe,IAAI,QAAQ;EACpC,MAAM;EACN,UAAU;GAAE,QAAQ;GAAK,SAAS;GAAM,QAAQ;GAAY;EAC7D,CAAC;AACF,KAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC1D,KAAI,IAAI,KAAK,UAAU,SAAS,EAAE,aAAa,OAAO,GAAG,EAAE,cAAc,OAAO,CAAC,CAAC"}