@copilotkit/aimock 1.42.1 → 1.44.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 (442) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +65 -0
  4. package/README.md +108 -16
  5. package/dist/agui-handler.d.ts.map +1 -1
  6. package/dist/agui-mock.cjs +1 -1
  7. package/dist/agui-mock.cjs.map +1 -1
  8. package/dist/agui-mock.d.cts.map +1 -1
  9. package/dist/agui-mock.d.ts.map +1 -1
  10. package/dist/agui-mock.js +1 -1
  11. package/dist/agui-mock.js.map +1 -1
  12. package/dist/agui-recorder.cjs +33 -11
  13. package/dist/agui-recorder.cjs.map +1 -1
  14. package/dist/agui-recorder.d.cts +10 -1
  15. package/dist/agui-recorder.d.cts.map +1 -1
  16. package/dist/agui-recorder.d.ts +10 -1
  17. package/dist/agui-recorder.d.ts.map +1 -1
  18. package/dist/agui-recorder.js +33 -11
  19. package/dist/agui-recorder.js.map +1 -1
  20. package/dist/agui-types.d.cts +1 -0
  21. package/dist/agui-types.d.cts.map +1 -1
  22. package/dist/agui-types.d.ts +1 -0
  23. package/dist/agui-types.d.ts.map +1 -1
  24. package/dist/aimock-cli.cjs +38 -10
  25. package/dist/aimock-cli.cjs.map +1 -1
  26. package/dist/aimock-cli.d.cts +5 -0
  27. package/dist/aimock-cli.d.cts.map +1 -1
  28. package/dist/aimock-cli.d.ts +5 -0
  29. package/dist/aimock-cli.d.ts.map +1 -1
  30. package/dist/aimock-cli.js +38 -10
  31. package/dist/aimock-cli.js.map +1 -1
  32. package/dist/batches.cjs +458 -0
  33. package/dist/batches.cjs.map +1 -0
  34. package/dist/batches.d.cts +57 -0
  35. package/dist/batches.d.cts.map +1 -0
  36. package/dist/batches.d.ts +57 -0
  37. package/dist/batches.d.ts.map +1 -0
  38. package/dist/batches.js +453 -0
  39. package/dist/batches.js.map +1 -0
  40. package/dist/bedrock-converse.cjs +44 -12
  41. package/dist/bedrock-converse.cjs.map +1 -1
  42. package/dist/bedrock-converse.d.cts.map +1 -1
  43. package/dist/bedrock-converse.d.ts.map +1 -1
  44. package/dist/bedrock-converse.js +46 -14
  45. package/dist/bedrock-converse.js.map +1 -1
  46. package/dist/bedrock.cjs +10 -8
  47. package/dist/bedrock.cjs.map +1 -1
  48. package/dist/bedrock.d.cts.map +1 -1
  49. package/dist/bedrock.d.ts.map +1 -1
  50. package/dist/bedrock.js +12 -10
  51. package/dist/bedrock.js.map +1 -1
  52. package/dist/byteplus-video.cjs +3 -3
  53. package/dist/byteplus-video.cjs.map +1 -1
  54. package/dist/byteplus-video.js +4 -4
  55. package/dist/byteplus-video.js.map +1 -1
  56. package/dist/chaos.cjs +625 -72
  57. package/dist/chaos.cjs.map +1 -1
  58. package/dist/chaos.d.cts +164 -7
  59. package/dist/chaos.d.cts.map +1 -1
  60. package/dist/chaos.d.ts +164 -7
  61. package/dist/chaos.d.ts.map +1 -1
  62. package/dist/chaos.js +616 -74
  63. package/dist/chaos.js.map +1 -1
  64. package/dist/cli.cjs +131 -30
  65. package/dist/cli.cjs.map +1 -1
  66. package/dist/cli.js +132 -31
  67. package/dist/cli.js.map +1 -1
  68. package/dist/cohere.cjs +37 -9
  69. package/dist/cohere.cjs.map +1 -1
  70. package/dist/cohere.d.cts.map +1 -1
  71. package/dist/cohere.d.ts.map +1 -1
  72. package/dist/cohere.js +39 -11
  73. package/dist/cohere.js.map +1 -1
  74. package/dist/config-loader.cjs +1 -1
  75. package/dist/config-loader.js +1 -1
  76. package/dist/constants.cjs +8 -0
  77. package/dist/constants.cjs.map +1 -1
  78. package/dist/constants.d.cts +6 -2
  79. package/dist/constants.d.ts +6 -2
  80. package/dist/constants.js +8 -1
  81. package/dist/constants.js.map +1 -1
  82. package/dist/convert.cjs +1 -1
  83. package/dist/convert.cjs.map +1 -1
  84. package/dist/convert.js +1 -1
  85. package/dist/convert.js.map +1 -1
  86. package/dist/echo-text.cjs +65 -0
  87. package/dist/echo-text.cjs.map +1 -0
  88. package/dist/echo-text.d.cts +22 -0
  89. package/dist/echo-text.d.cts.map +1 -0
  90. package/dist/echo-text.d.ts +22 -0
  91. package/dist/echo-text.d.ts.map +1 -0
  92. package/dist/echo-text.js +65 -0
  93. package/dist/echo-text.js.map +1 -0
  94. package/dist/elevenlabs-audio.cjs +3 -3
  95. package/dist/elevenlabs-audio.cjs.map +1 -1
  96. package/dist/elevenlabs-audio.js +4 -4
  97. package/dist/elevenlabs-audio.js.map +1 -1
  98. package/dist/elevenlabs-voice.cjs +1053 -0
  99. package/dist/elevenlabs-voice.cjs.map +1 -0
  100. package/dist/elevenlabs-voice.d.cts +72 -0
  101. package/dist/elevenlabs-voice.d.cts.map +1 -0
  102. package/dist/elevenlabs-voice.d.ts +72 -0
  103. package/dist/elevenlabs-voice.d.ts.map +1 -0
  104. package/dist/elevenlabs-voice.js +1048 -0
  105. package/dist/elevenlabs-voice.js.map +1 -0
  106. package/dist/embeddings.cjs +26 -5
  107. package/dist/embeddings.cjs.map +1 -1
  108. package/dist/embeddings.d.cts +1 -1
  109. package/dist/embeddings.d.cts.map +1 -1
  110. package/dist/embeddings.d.ts +1 -1
  111. package/dist/embeddings.d.ts.map +1 -1
  112. package/dist/embeddings.js +27 -6
  113. package/dist/embeddings.js.map +1 -1
  114. package/dist/fal-audio.cjs +3 -3
  115. package/dist/fal-audio.cjs.map +1 -1
  116. package/dist/fal-audio.js +4 -4
  117. package/dist/fal-audio.js.map +1 -1
  118. package/dist/fal.cjs +28 -4
  119. package/dist/fal.cjs.map +1 -1
  120. package/dist/fal.d.cts +16 -1
  121. package/dist/fal.d.cts.map +1 -1
  122. package/dist/fal.d.ts +16 -1
  123. package/dist/fal.d.ts.map +1 -1
  124. package/dist/fal.js +28 -5
  125. package/dist/fal.js.map +1 -1
  126. package/dist/files.cjs +1069 -0
  127. package/dist/files.cjs.map +1 -0
  128. package/dist/files.d.cts +68 -0
  129. package/dist/files.d.cts.map +1 -0
  130. package/dist/files.d.ts +68 -0
  131. package/dist/files.d.ts.map +1 -0
  132. package/dist/files.js +1056 -0
  133. package/dist/files.js.map +1 -0
  134. package/dist/fine-tuning.cjs +1033 -0
  135. package/dist/fine-tuning.cjs.map +1 -0
  136. package/dist/fine-tuning.d.cts +96 -0
  137. package/dist/fine-tuning.d.cts.map +1 -0
  138. package/dist/fine-tuning.d.ts +96 -0
  139. package/dist/fine-tuning.d.ts.map +1 -0
  140. package/dist/fine-tuning.js +1026 -0
  141. package/dist/fine-tuning.js.map +1 -0
  142. package/dist/fixture-loader-services.cjs +116 -0
  143. package/dist/fixture-loader-services.cjs.map +1 -0
  144. package/dist/fixture-loader-services.d.cts +48 -0
  145. package/dist/fixture-loader-services.d.cts.map +1 -0
  146. package/dist/fixture-loader-services.d.ts +48 -0
  147. package/dist/fixture-loader-services.d.ts.map +1 -0
  148. package/dist/fixture-loader-services.js +114 -0
  149. package/dist/fixture-loader-services.js.map +1 -0
  150. package/dist/fixture-loader.cjs +0 -0
  151. package/dist/fixture-loader.cjs.map +1 -1
  152. package/dist/fixture-loader.d.cts +131 -5
  153. package/dist/fixture-loader.d.cts.map +1 -1
  154. package/dist/fixture-loader.d.ts +131 -5
  155. package/dist/fixture-loader.d.ts.map +1 -1
  156. package/dist/fixture-loader.js +0 -0
  157. package/dist/fixture-loader.js.map +1 -1
  158. package/dist/gemini-embeddings.cjs +44 -4
  159. package/dist/gemini-embeddings.cjs.map +1 -1
  160. package/dist/gemini-embeddings.js +46 -6
  161. package/dist/gemini-embeddings.js.map +1 -1
  162. package/dist/gemini-interactions.cjs +51 -7
  163. package/dist/gemini-interactions.cjs.map +1 -1
  164. package/dist/gemini-interactions.d.cts.map +1 -1
  165. package/dist/gemini-interactions.d.ts.map +1 -1
  166. package/dist/gemini-interactions.js +53 -9
  167. package/dist/gemini-interactions.js.map +1 -1
  168. package/dist/gemini.cjs +19 -8
  169. package/dist/gemini.cjs.map +1 -1
  170. package/dist/gemini.d.cts.map +1 -1
  171. package/dist/gemini.d.ts.map +1 -1
  172. package/dist/gemini.js +21 -10
  173. package/dist/gemini.js.map +1 -1
  174. package/dist/grok-video.cjs +14 -14
  175. package/dist/grok-video.cjs.map +1 -1
  176. package/dist/grok-video.js +15 -15
  177. package/dist/grok-video.js.map +1 -1
  178. package/dist/helpers.cjs +497 -10
  179. package/dist/helpers.cjs.map +1 -1
  180. package/dist/helpers.d.cts +38 -2
  181. package/dist/helpers.d.cts.map +1 -1
  182. package/dist/helpers.d.ts +38 -2
  183. package/dist/helpers.d.ts.map +1 -1
  184. package/dist/helpers.js +487 -12
  185. package/dist/helpers.js.map +1 -1
  186. package/dist/images.cjs +57 -125
  187. package/dist/images.cjs.map +1 -1
  188. package/dist/images.d.cts.map +1 -1
  189. package/dist/images.d.ts.map +1 -1
  190. package/dist/images.js +58 -126
  191. package/dist/images.js.map +1 -1
  192. package/dist/index.cjs +71 -9
  193. package/dist/index.d.cts +17 -7
  194. package/dist/index.d.ts +17 -7
  195. package/dist/index.js +21 -11
  196. package/dist/jest.cjs +24 -12
  197. package/dist/jest.cjs.map +1 -1
  198. package/dist/jest.d.cts +6 -3
  199. package/dist/jest.d.cts.map +1 -1
  200. package/dist/jest.d.ts +6 -3
  201. package/dist/jest.d.ts.map +1 -1
  202. package/dist/jest.js +25 -13
  203. package/dist/jest.js.map +1 -1
  204. package/dist/journal.cjs +61 -4
  205. package/dist/journal.cjs.map +1 -1
  206. package/dist/journal.d.cts +60 -3
  207. package/dist/journal.d.cts.map +1 -1
  208. package/dist/journal.d.ts +60 -3
  209. package/dist/journal.d.ts.map +1 -1
  210. package/dist/journal.js +61 -5
  211. package/dist/journal.js.map +1 -1
  212. package/dist/live-fixture.cjs +1192 -0
  213. package/dist/live-fixture.cjs.map +1 -0
  214. package/dist/live-fixture.js +1187 -0
  215. package/dist/live-fixture.js.map +1 -0
  216. package/dist/live-recorder.cjs +256 -0
  217. package/dist/live-recorder.cjs.map +1 -0
  218. package/dist/live-recorder.js +254 -0
  219. package/dist/live-recorder.js.map +1 -0
  220. package/dist/live-replay.cjs +286 -0
  221. package/dist/live-replay.cjs.map +1 -0
  222. package/dist/live-replay.js +285 -0
  223. package/dist/live-replay.js.map +1 -0
  224. package/dist/live-sanitize.cjs +196 -0
  225. package/dist/live-sanitize.cjs.map +1 -0
  226. package/dist/live-sanitize.js +196 -0
  227. package/dist/live-sanitize.js.map +1 -0
  228. package/dist/live-types.d.cts +71 -0
  229. package/dist/live-types.d.cts.map +1 -0
  230. package/dist/live-types.d.ts +71 -0
  231. package/dist/live-types.d.ts.map +1 -0
  232. package/dist/llmock.cjs +132 -49
  233. package/dist/llmock.cjs.map +1 -1
  234. package/dist/llmock.d.cts +51 -5
  235. package/dist/llmock.d.cts.map +1 -1
  236. package/dist/llmock.d.ts +51 -5
  237. package/dist/llmock.d.ts.map +1 -1
  238. package/dist/llmock.js +133 -50
  239. package/dist/llmock.js.map +1 -1
  240. package/dist/mcp-fakes-mount.cjs +216 -0
  241. package/dist/mcp-fakes-mount.cjs.map +1 -0
  242. package/dist/mcp-fakes-mount.js +210 -0
  243. package/dist/mcp-fakes-mount.js.map +1 -0
  244. package/dist/mcp-fakes.cjs +1619 -0
  245. package/dist/mcp-fakes.cjs.map +1 -0
  246. package/dist/mcp-fakes.d.cts +101 -0
  247. package/dist/mcp-fakes.d.cts.map +1 -0
  248. package/dist/mcp-fakes.d.ts +101 -0
  249. package/dist/mcp-fakes.d.ts.map +1 -0
  250. package/dist/mcp-fakes.js +1614 -0
  251. package/dist/mcp-fakes.js.map +1 -0
  252. package/dist/mcp-handler.cjs +331 -17
  253. package/dist/mcp-handler.cjs.map +1 -1
  254. package/dist/mcp-handler.js +330 -18
  255. package/dist/mcp-handler.js.map +1 -1
  256. package/dist/mcp-mock.cjs +217 -49
  257. package/dist/mcp-mock.cjs.map +1 -1
  258. package/dist/mcp-mock.d.cts +66 -1
  259. package/dist/mcp-mock.d.cts.map +1 -1
  260. package/dist/mcp-mock.d.ts +66 -1
  261. package/dist/mcp-mock.d.ts.map +1 -1
  262. package/dist/mcp-mock.js +218 -50
  263. package/dist/mcp-mock.js.map +1 -1
  264. package/dist/mcp-stub.cjs +5 -1
  265. package/dist/mcp-stub.d.cts +3 -1
  266. package/dist/mcp-stub.d.ts +3 -1
  267. package/dist/mcp-stub.js +3 -1
  268. package/dist/mcp-types.d.cts +6 -0
  269. package/dist/mcp-types.d.ts +6 -0
  270. package/dist/message-text.cjs +192 -0
  271. package/dist/message-text.cjs.map +1 -0
  272. package/dist/message-text.d.cts +31 -0
  273. package/dist/message-text.d.cts.map +1 -0
  274. package/dist/message-text.d.ts +31 -0
  275. package/dist/message-text.d.ts.map +1 -0
  276. package/dist/message-text.js +184 -0
  277. package/dist/message-text.js.map +1 -0
  278. package/dist/messages.cjs +49 -7
  279. package/dist/messages.cjs.map +1 -1
  280. package/dist/messages.d.cts.map +1 -1
  281. package/dist/messages.d.ts.map +1 -1
  282. package/dist/messages.js +51 -9
  283. package/dist/messages.js.map +1 -1
  284. package/dist/metrics.cjs +278 -45
  285. package/dist/metrics.cjs.map +1 -1
  286. package/dist/metrics.d.cts +17 -7
  287. package/dist/metrics.d.cts.map +1 -1
  288. package/dist/metrics.d.ts +17 -7
  289. package/dist/metrics.d.ts.map +1 -1
  290. package/dist/metrics.js +270 -29
  291. package/dist/metrics.js.map +1 -1
  292. package/dist/moderation.cjs +10 -1
  293. package/dist/moderation.cjs.map +1 -1
  294. package/dist/moderation.d.cts.map +1 -1
  295. package/dist/moderation.d.ts.map +1 -1
  296. package/dist/moderation.js +10 -1
  297. package/dist/moderation.js.map +1 -1
  298. package/dist/ollama.cjs +10 -6
  299. package/dist/ollama.cjs.map +1 -1
  300. package/dist/ollama.d.cts.map +1 -1
  301. package/dist/ollama.d.ts.map +1 -1
  302. package/dist/ollama.js +11 -7
  303. package/dist/ollama.js.map +1 -1
  304. package/dist/openapi.cjs +3405 -0
  305. package/dist/openapi.cjs.map +1 -0
  306. package/dist/openapi.d.cts +30 -0
  307. package/dist/openapi.d.cts.map +1 -0
  308. package/dist/openapi.d.ts +30 -0
  309. package/dist/openapi.d.ts.map +1 -0
  310. package/dist/openapi.js +3404 -0
  311. package/dist/openapi.js.map +1 -0
  312. package/dist/openrouter-video.cjs +9 -9
  313. package/dist/openrouter-video.cjs.map +1 -1
  314. package/dist/openrouter-video.d.cts.map +1 -1
  315. package/dist/openrouter-video.d.ts.map +1 -1
  316. package/dist/openrouter-video.js +10 -10
  317. package/dist/openrouter-video.js.map +1 -1
  318. package/dist/recorder.cjs +54 -10
  319. package/dist/recorder.cjs.map +1 -1
  320. package/dist/recorder.d.cts.map +1 -1
  321. package/dist/recorder.d.ts.map +1 -1
  322. package/dist/recorder.js +52 -12
  323. package/dist/recorder.js.map +1 -1
  324. package/dist/responses.cjs +47 -7
  325. package/dist/responses.cjs.map +1 -1
  326. package/dist/responses.d.cts.map +1 -1
  327. package/dist/responses.d.ts.map +1 -1
  328. package/dist/responses.js +48 -9
  329. package/dist/responses.js.map +1 -1
  330. package/dist/route-registry.cjs +951 -0
  331. package/dist/route-registry.cjs.map +1 -0
  332. package/dist/route-registry.d.cts +33 -0
  333. package/dist/route-registry.d.cts.map +1 -0
  334. package/dist/route-registry.d.ts +33 -0
  335. package/dist/route-registry.d.ts.map +1 -0
  336. package/dist/route-registry.js +866 -0
  337. package/dist/route-registry.js.map +1 -0
  338. package/dist/router.cjs +21 -34
  339. package/dist/router.cjs.map +1 -1
  340. package/dist/router.d.cts +4 -1
  341. package/dist/router.d.cts.map +1 -1
  342. package/dist/router.d.ts +4 -1
  343. package/dist/router.d.ts.map +1 -1
  344. package/dist/router.js +21 -35
  345. package/dist/router.js.map +1 -1
  346. package/dist/server.cjs +1252 -777
  347. package/dist/server.cjs.map +1 -1
  348. package/dist/server.d.cts +5 -1
  349. package/dist/server.d.cts.map +1 -1
  350. package/dist/server.d.ts +5 -1
  351. package/dist/server.d.ts.map +1 -1
  352. package/dist/server.js +1185 -710
  353. package/dist/server.js.map +1 -1
  354. package/dist/speech.cjs +6 -4
  355. package/dist/speech.cjs.map +1 -1
  356. package/dist/speech.js +7 -5
  357. package/dist/speech.js.map +1 -1
  358. package/dist/suite.cjs +1 -1
  359. package/dist/suite.js +1 -1
  360. package/dist/transcription.cjs +6 -6
  361. package/dist/transcription.cjs.map +1 -1
  362. package/dist/transcription.d.cts.map +1 -1
  363. package/dist/transcription.d.ts.map +1 -1
  364. package/dist/transcription.js +7 -7
  365. package/dist/transcription.js.map +1 -1
  366. package/dist/types.cjs +18 -0
  367. package/dist/types.cjs.map +1 -0
  368. package/dist/types.d.cts +320 -17
  369. package/dist/types.d.cts.map +1 -1
  370. package/dist/types.d.ts +320 -17
  371. package/dist/types.d.ts.map +1 -1
  372. package/dist/types.js +17 -0
  373. package/dist/types.js.map +1 -0
  374. package/dist/validate-cli.cjs +983 -0
  375. package/dist/validate-cli.cjs.map +1 -0
  376. package/dist/validate-cli.js +982 -0
  377. package/dist/validate-cli.js.map +1 -0
  378. package/dist/vector-stores.cjs +1180 -0
  379. package/dist/vector-stores.cjs.map +1 -0
  380. package/dist/vector-stores.d.cts +106 -0
  381. package/dist/vector-stores.d.cts.map +1 -0
  382. package/dist/vector-stores.d.ts +106 -0
  383. package/dist/vector-stores.d.ts.map +1 -0
  384. package/dist/vector-stores.js +1164 -0
  385. package/dist/vector-stores.js.map +1 -0
  386. package/dist/vector-types.d.cts.map +1 -1
  387. package/dist/vector-types.d.ts.map +1 -1
  388. package/dist/veo-video.cjs +13 -13
  389. package/dist/veo-video.cjs.map +1 -1
  390. package/dist/veo-video.js +14 -14
  391. package/dist/veo-video.js.map +1 -1
  392. package/dist/video.cjs +23 -4
  393. package/dist/video.cjs.map +1 -1
  394. package/dist/video.d.cts +1 -1
  395. package/dist/video.d.cts.map +1 -1
  396. package/dist/video.d.ts +1 -1
  397. package/dist/video.d.ts.map +1 -1
  398. package/dist/video.js +24 -5
  399. package/dist/video.js.map +1 -1
  400. package/dist/vitest.cjs +23 -11
  401. package/dist/vitest.cjs.map +1 -1
  402. package/dist/vitest.d.cts +5 -2
  403. package/dist/vitest.d.cts.map +1 -1
  404. package/dist/vitest.d.ts +5 -2
  405. package/dist/vitest.d.ts.map +1 -1
  406. package/dist/vitest.js +25 -13
  407. package/dist/vitest.js.map +1 -1
  408. package/dist/watcher.cjs +2 -1
  409. package/dist/watcher.cjs.map +1 -1
  410. package/dist/watcher.js +2 -1
  411. package/dist/watcher.js.map +1 -1
  412. package/dist/ws-framing.cjs +150 -35
  413. package/dist/ws-framing.cjs.map +1 -1
  414. package/dist/ws-framing.d.cts +18 -3
  415. package/dist/ws-framing.d.cts.map +1 -1
  416. package/dist/ws-framing.d.ts +18 -3
  417. package/dist/ws-framing.d.ts.map +1 -1
  418. package/dist/ws-framing.js +151 -36
  419. package/dist/ws-framing.js.map +1 -1
  420. package/dist/ws-gemini-live.cjs +2 -2
  421. package/dist/ws-gemini-live.js +2 -2
  422. package/dist/ws-live.cjs +401 -0
  423. package/dist/ws-live.cjs.map +1 -0
  424. package/dist/ws-live.js +400 -0
  425. package/dist/ws-live.js.map +1 -0
  426. package/dist/ws-realtime.cjs +46 -2
  427. package/dist/ws-realtime.cjs.map +1 -1
  428. package/dist/ws-realtime.d.cts.map +1 -1
  429. package/dist/ws-realtime.d.ts.map +1 -1
  430. package/dist/ws-realtime.js +46 -2
  431. package/dist/ws-realtime.js.map +1 -1
  432. package/dist/ws-responses.cjs +16 -4
  433. package/dist/ws-responses.cjs.map +1 -1
  434. package/dist/ws-responses.js +16 -4
  435. package/dist/ws-responses.js.map +1 -1
  436. package/dist/ws-upstream.cjs +118 -0
  437. package/dist/ws-upstream.cjs.map +1 -0
  438. package/dist/ws-upstream.js +115 -0
  439. package/dist/ws-upstream.js.map +1 -0
  440. package/fixtures/openai-live-client.json +390 -0
  441. package/fixtures/openai-live-managed.json +709 -0
  442. package/package.json +4 -1
@@ -0,0 +1,1053 @@
1
+ const require_helpers = require('./helpers.cjs');
2
+ const require_sse_writer = require('./sse-writer.cjs');
3
+ const require_chaos = require('./chaos.cjs');
4
+ const require_fixture_loader = require('./fixture-loader.cjs');
5
+ const require_router = require('./router.cjs');
6
+ const require_recorder = require('./recorder.cjs');
7
+
8
+ //#region src/elevenlabs-voice.ts
9
+ /**
10
+ * ElevenLabs Voice Design record + replay: `POST /v1/text-to-voice/design`
11
+ * (previews), `POST /v1/text-to-voice` (create a voice from a preview),
12
+ * `GET /v1/voices/{voice_id}`, `DELETE /v1/voices/{voice_id}`.
13
+ *
14
+ * PROVENANCE OF EVERY WIRE FACT IN THIS FILE. Read this before changing a
15
+ * field name, a status code, or a literal below.
16
+ *
17
+ * FIRSTHAND — observed by this repo against live `api.elevenlabs.io` on
18
+ * 2026-09-15, with NO api key and with a deliberately invalid one. No
19
+ * ElevenLabs api key is reachable from this machine (1Password holds an
20
+ * `elevenlabs.io` web login, not a key), so only the pre-auth and auth layers
21
+ * were ever exercised:
22
+ * - `POST /v1/text-to-voice/design` with a >=20-char `voice_description` and
23
+ * no credential -> 401
24
+ * `{"detail":{"type":"authentication_error","code":"unauthorized",
25
+ * "message":"Neither authorization header nor xi-api-key received, please
26
+ * provide one.","status":"needs_authorization","request_id":"<hex>"}}`.
27
+ * Same envelope from `GET /v1/voices/{id}`; with a bogus `xi-api-key` the
28
+ * message/status become `"Invalid API key"` / `"invalid_api_key"`.
29
+ * - `voice_description` shorter than 20 chars -> 422 with a pydantic-style
30
+ * `{"detail":[{"type":"string_too_short","loc":["body",
31
+ * "voice_description"],...}]}`; `POST /v1/text-to-voice` reports missing
32
+ * `voice_name` / `generated_voice_id` the same way. So the real service
33
+ * validates the body BEFORE it authenticates. THIS HANDLER NOW ENFORCES
34
+ * THAT 20-CODE-POINT MINIMUM on `POST /v1/text-to-voice/design`
35
+ * (`VOICE_DESCRIPTION_MIN_LENGTH`) and answers with that same `422
36
+ * {"detail":[...]}` envelope rather than aimock's house error shape — the
37
+ * one response on this surface whose real shape was observed. The `msg`
38
+ * STRING it fills in ("String should have at least 20 characters") is
39
+ * AUTHORED: the observed body was recorded with `msg` elided, so only the
40
+ * status, the `detail` array and the `loc`/`type` values are firsthand.
41
+ * ORDER ON `POST /v1/text-to-voice/design`: missing-parameter 400 ->
42
+ * fixture match (a registered short-description fixture still serves) ->
43
+ * chaos -> strict refusal / record-mode proxy (so the REAL 422 is recorded
44
+ * verbatim instead of pre-empted) -> this local 20-code-point 422 ->
45
+ * no-match 404. `POST /v1/text-to-voice` follows the same order, with its
46
+ * empty-string 422 in the same slot ahead of the synthesized voice.
47
+ * The minimum is NOT enforced on `POST /v1/text-to-voice`, whose
48
+ * `voice_description` bound has never been probed; that route rejects only
49
+ * an EMPTY description, which is short under any `min_length >= 1`.
50
+ * - `GET /v1/voices/aimock-nonexistent-voice` -> 400
51
+ * `{"detail":{"type":"invalid_request","code":"bad_request","message":"An
52
+ * invalid ID has been received: ...","status":"invalid_uid",...}}`.
53
+ * KNOWN DIVERGENCE, recorded rather than papered over: every real envelope
54
+ * above is keyed `detail`, while this handler still emits aimock's house
55
+ * `{ error: { message, type, code } }` shape — and aimock's own 400/404/503
56
+ * codes — for EVERY failure except the `voice_description` minimum. That is
57
+ * deliberate consistency with every other surface here, but it means those
58
+ * ERROR envelopes are NOT wire-faithful to ElevenLabs. Making the whole
59
+ * module speak `detail` is a FOLLOW-UP: it is a breaking change for every
60
+ * existing consumer of this surface, and the shapes of the OTHER failures
61
+ * (missing parameter, unknown voice, strict-mode refusal) have not been
62
+ * observed, so they cannot be authored from the one case that has. A
63
+ * RECORDED fixture is a second exception: it replays the upstream `detail`
64
+ * body verbatim under the upstream status, so it is faithful by
65
+ * construction.
66
+ *
67
+ * SECONDARY — the official client `@elevenlabs/elevenlabs-js`, pinned to the
68
+ * exact published version `2.68.0` (`node_modules/@elevenlabs/elevenlabs-js/
69
+ * package.json` -> `"version": "2.68.0"`). Every claim below was read out of
70
+ * these files, at that version, and nowhere else:
71
+ * - `api/resources/textToVoice/client/Client.d.ts` /`.js` — the routes, and
72
+ * the `@throws` list for each (`UnprocessableEntityError` only);
73
+ * - `api/resources/textToVoice/client/requests/VoiceDesignRequestModel.d.ts`
74
+ * and `.../BodyCreateANewVoiceFromVoicePreviewV1TextToVoicePost.d.ts` —
75
+ * the request bodies;
76
+ * - `api/types/VoiceDesignPreviewResponse.d.ts`,
77
+ * `api/types/VoicePreviewResponseModel.d.ts`, `api/types/Voice.d.ts`,
78
+ * `api/types/HttpValidationError.d.ts`, `api/types/ValidationError.d.ts`,
79
+ * `api/types/ValidationErrorLocItem.d.ts` — the response models;
80
+ * - the matching `serialization/types/*.d.ts` `Raw` interfaces and
81
+ * `serialization/types/*.js` schemas — the on-wire snake_case names and
82
+ * each field's optional/nullable treatment;
83
+ * - `api/errors/UnprocessableEntityError.js` (422) and
84
+ * `api/errors/ConflictError.js` (409, never declared for these routes).
85
+ * It pins:
86
+ * - the four routes above, and that create-from-preview is `POST
87
+ * /v1/text-to-voice` taking `voice_name`, `voice_description`,
88
+ * `generated_voice_id`, optional `labels`;
89
+ * - the design SUCCESS body `{ previews: [...], text }` where each preview is
90
+ * `{ audio_base_64, generated_voice_id, media_type, duration_secs,
91
+ * language? }` — `language` optional, the rest required;
92
+ * - `model_id` values `eleven_multilingual_ttv_v2` / `eleven_ttv_v3`;
93
+ * - the `Voice` object's wire fields. Every key `buildSyntheticVoice()`
94
+ * emits (`voice_id`, `name`, `category`, `description`, `labels`,
95
+ * `preview_url`, `available_for_tiers`, `settings`, `sharing`,
96
+ * `high_quality_base_model_ids`, `samples`, `safety_control`,
97
+ * `voice_verification` with its four inner keys, `permission_on_resource`,
98
+ * `is_owner`, `is_legacy`, `is_mixed`) appears in `Voice.Raw` /
99
+ * `VoiceVerificationResponse.Raw`, and `"generated"` is one of that SDK's
100
+ * six `VoiceCategory` tokens;
101
+ * - `DELETE /v1/voices/{id}` answering `{ status: "ok" }`.
102
+ * ElevenLabs' own documentation site is a client-rendered SPA and was NOT
103
+ * read; the SDK is the only document behind these shapes.
104
+ *
105
+ * UNVERIFIED / HAND-AUTHORED — name it, do not soften it:
106
+ * - NO successful response from ElevenLabs has ever been observed by this
107
+ * repo, for any of these four routes. There is no recorded fixture or tape
108
+ * for this surface anywhere in the tree; the drift test in
109
+ * `src/__tests__/drift/elevenlabs-voice.drift.ts` restates the same
110
+ * hand-authored shape it checks, so it detects mock-vs-fixture divergence
111
+ * only and can
112
+ * never detect divergence from the real API.
113
+ * - The VALUES `buildSyntheticVoice()` fills those fields with (`null`s, `[]`,
114
+ * `is_owner: true`, `is_legacy`/`is_mixed` false, the zeroed
115
+ * `voice_verification`) are aimock's choices for a just-created voice, not
116
+ * anything observed. The SDK marks all of them optional and nullable, so
117
+ * they are plausible, not confirmed.
118
+ * - `voice_id === generated_voice_id` for a created voice. AUTHORED — see
119
+ * `buildSyntheticVoice()`. The SDK relates the two nowhere; aimock picks
120
+ * the alias so a caller can GET back what it just created. No code should
121
+ * depend on it as a wire fact.
122
+ * - Re-creating an already-created `generated_voice_id` overwrites and
123
+ * answers 200. AUTHORED — see `rememberElevenLabsVoice()`. The real
124
+ * behaviour is unknown and the SDK declares no `ConflictError` for the
125
+ * route, so aimock warns through the module logger instead of inventing a
126
+ * rejection.
127
+ * - `Voice.Raw` carries further fields this handler never emits
128
+ * (`fine_tuning`, `verified_languages`, `collection_ids`,
129
+ * `created_at_unix`, `favorited_at_unix`, `is_bookmarked`,
130
+ * `recording_quality`, `labelling_status`, `recording_quality_reason`). A
131
+ * consumer reading them off a replayed voice gets `undefined`.
132
+ * The standing rule still holds: THE MOCK NEVER AUTHORS A WIRE VALUE IT DID
133
+ * NOT OBSERVE. This surface currently does, and this block is where that debt
134
+ * is declared. Recording one real design + create round-trip against a funded
135
+ * key would retire most of it.
136
+ */
137
+ const VOICE_STORE_MAX = 1e4;
138
+ /**
139
+ * `POST /v1/text-to-voice/design` rejects a `voice_description` shorter than
140
+ * this. FIRSTHAND: observed against live `api.elevenlabs.io` on 2026-09-15 (see
141
+ * the PROVENANCE block above) — a sub-20-character description comes back 422
142
+ * with a pydantic `string_too_short` entry, BEFORE the request is even
143
+ * authenticated, so the bound is observable without a key.
144
+ *
145
+ * THE UNIT IS CODE POINTS, NOT UTF-16 CODE UNITS. The bound is pydantic's
146
+ * `min_length`, which is `len(str)` in Python — a count of code points. A
147
+ * JavaScript `String.length` counts UTF-16 code units instead, so it
148
+ * DOUBLE-COUNTS every non-BMP character: a 19-emoji description is 19 code
149
+ * points to the real service (422) and 38 `.length` to JavaScript (would have
150
+ * passed here and then failed on the wire, which is the exact class of
151
+ * divergence this mock exists to prevent). Measure with `[...s].length`, whose
152
+ * string iterator yields code points. Grapheme clusters are deliberately NOT
153
+ * the unit: Python does not count those either, so `Intl.Segmenter` would
154
+ * re-introduce the same disagreement from the other side.
155
+ */
156
+ const VOICE_DESCRIPTION_MIN_LENGTH = 20;
157
+ /**
158
+ * The real service answers body-validation failures with FastAPI/pydantic's
159
+ * envelope: HTTP 422 and `{"detail":[{"loc":[...],"msg":...,"type":...}]}`.
160
+ * The key set is pinned by `@elevenlabs/elevenlabs-js@2.68.0`
161
+ * (`api/types/HttpValidationError.d.ts` -> `detail?: ValidationError[]`,
162
+ * `api/types/ValidationError.d.ts` -> `{ loc, msg, type }`,
163
+ * `serialization/types/ValidationError.d.ts` for the identical on-wire Raw),
164
+ * and the status by `api/errors/UnprocessableEntityError.js`
165
+ * (`statusCode: 422`), which is what that SDK throws for these routes.
166
+ *
167
+ * DELIBERATE DIVERGENCE FROM ITS SIBLINGS: every other validation failure in
168
+ * this file answers aimock's house `400 {"error":{...}}`. This one does not,
169
+ * because this is the only body-validation response on this surface whose real
170
+ * shape this repo has actually observed. Aligning the rest is a follow-up, not
171
+ * a licence to author the others' shapes from this one.
172
+ */
173
+ function writeValidationError(res, loc, msg, type) {
174
+ require_sse_writer.writeErrorResponse(res, 422, JSON.stringify({ detail: [{
175
+ loc,
176
+ msg,
177
+ type
178
+ }] }));
179
+ }
180
+ /**
181
+ * Voices created via POST /v1/text-to-voice, keyed by voice_id.
182
+ *
183
+ * MODULE-GLOBAL, deliberately: it is the same shape every other bounded store
184
+ * here uses (`FalJobMap` in `./fal-audio.ts`, `FalQueueStateMap` in `./fal.ts`,
185
+ * and the four video job maps), so one process shares one voice store across
186
+ * every `LLMock` instance. `performFullReset` (`./server.ts`) clears it
187
+ * alongside those siblings, which is what `POST /__aimock/reset` and
188
+ * `LLMock.reset()` both route through. `stop()` does NOT clear it — no sibling
189
+ * store is cleared on `stop()` either; reset is the isolation barrier, not
190
+ * server teardown. A test file that creates voices must therefore reset (or
191
+ * call `clearElevenLabsVoices()`) between tests, or a voice saved by one test
192
+ * is still visible to the next.
193
+ */
194
+ const elevenLabsVoices = /* @__PURE__ */ new Map();
195
+ function clearElevenLabsVoices() {
196
+ elevenLabsVoices.clear();
197
+ }
198
+ /**
199
+ * Store a created voice under its own `voice_id` — the id the client sees in
200
+ * the body we are about to write, so a follow-up `GET /v1/voices/{id}` by that
201
+ * id hits.
202
+ *
203
+ * Returns `false` when the body carries no string `voice_id` and nothing was
204
+ * stored, so the caller can say so instead of dropping the voice silently.
205
+ *
206
+ * AUTHORED: a second create for a `voice_id` already in the store OVERWRITES
207
+ * the first, and still answers 200. Whether the real
208
+ * `POST /v1/text-to-voice` accepts an already-consumed `generated_voice_id` is
209
+ * UNKNOWN to this repo — no successful create has ever been observed, and
210
+ * `@elevenlabs/elevenlabs-js@2.68.0` gives no signal either way: its
211
+ * `api/resources/textToVoice/client/Client.d.ts` declares only
212
+ * `@throws UnprocessableEntityError` for `create`, and never `ConflictError`
213
+ * (`api/errors/ConflictError.js`), so authoring a 409 here would be inventing a
214
+ * status nothing supports. Overwrite-and-200 is aimock's choice; it is made
215
+ * AUDIBLE via `logger.warn` rather than silent, so a test that overwrites a
216
+ * voice by accident can see it.
217
+ *
218
+ * EVICTION IS FIFO AND SILENT, by insertion order — the same rule every
219
+ * sibling store applies (see `FalJobMap.set`, `src/fal-audio.ts:107-116`, and
220
+ * the identical block in `fal.ts` / `openrouter-video.ts` / `grok-video.ts` /
221
+ * `byteplus-video.ts`). Once the map exceeds `VOICE_STORE_MAX`, the
222
+ * oldest-inserted entries are deleted with no log line and no error: a later
223
+ * `GET /v1/voices/{id}` for an evicted voice is indistinguishable from one for
224
+ * a voice that never existed (404, or 503 under strict). It is NOT LRU —
225
+ * reading a voice does not refresh it, and re-saving an existing `voice_id`
226
+ * keeps its ORIGINAL insertion slot, because `Map.set` on a present key does
227
+ * not reorder. The cap exists to bound a long-lived mock process, not to model
228
+ * anything ElevenLabs does; at 10k voices per process no test approaches it.
229
+ */
230
+ function rememberElevenLabsVoice(voice, logger) {
231
+ const voiceId = typeof voice.voice_id === "string" ? voice.voice_id : void 0;
232
+ if (!voiceId) return false;
233
+ if (elevenLabsVoices.has(voiceId)) logger?.warn(`ElevenLabs voice '${voiceId}' already existed and was overwritten; aimock does not know whether the real API rejects a reused generated_voice_id`);
234
+ elevenLabsVoices.set(voiceId, voice);
235
+ if (elevenLabsVoices.size > VOICE_STORE_MAX) {
236
+ const excess = elevenLabsVoices.size - VOICE_STORE_MAX;
237
+ const iter = elevenLabsVoices.keys();
238
+ for (let i = 0; i < excess; i++) {
239
+ const next = iter.next();
240
+ if (!next.done) elevenLabsVoices.delete(next.value);
241
+ }
242
+ }
243
+ return true;
244
+ }
245
+ /**
246
+ * The status a recorded `{ json, status }` replay must answer with.
247
+ *
248
+ * `proxyAndRecord` persists the upstream status alongside the verbatim body for
249
+ * this surface (`src/recorder.ts:901` — `{ json: parsedResponse, status:
250
+ * upstreamStatus }`), and `RawJSONResponse.status` (`src/types.ts:476`) is the
251
+ * field it lands in. Replaying every such fixture as 200 turns every recorded
252
+ * ElevenLabs error — all of which carry a `detail` body, not aimock's house
253
+ * error envelope, so `isErrorResponse()` never claims them — into a fabricated
254
+ * success. `src/fal-audio.ts:1061` reads the same field the same way.
255
+ *
256
+ * Only `status` round-trips: that recorder branch stores no headers for a
257
+ * verbatim-JSON recording, so there is no recorded header to honour here.
258
+ */
259
+ function replayStatus(response) {
260
+ return response.status ?? 200;
261
+ }
262
+ function voiceDesignToJson(response) {
263
+ return { json: {
264
+ previews: response.previews.map((preview, index) => {
265
+ const entry = {
266
+ generated_voice_id: preview.generated_voice_id || `aimock-preview-${index}`,
267
+ audio_base_64: preview.audio_base_64 ?? "",
268
+ media_type: preview.media_type ?? "audio/mpeg",
269
+ duration_secs: preview.duration_secs ?? 0
270
+ };
271
+ if (typeof preview.language === "string") entry.language = preview.language;
272
+ return entry;
273
+ }),
274
+ text: response.text ?? ""
275
+ } };
276
+ }
277
+ function writeJson(res, status, payload) {
278
+ res.writeHead(status, { "Content-Type": "application/json" });
279
+ res.end(JSON.stringify(payload));
280
+ }
281
+ /**
282
+ * The journal `source` for a chaos fault on a request no fixture answers — the
283
+ * same rule `src/server.ts` applies, via the shared {@link wouldProxyMiss}:
284
+ * the miss is aimock's own answer, "internal", unless record mode has an
285
+ * ElevenLabs upstream to forward it to AND strict mode (server default or the
286
+ * per-request `X-AIMock-Strict` header) is not refusing it first. Both halves
287
+ * used to be hardcoded — design/create said "proxy" with no record config at
288
+ * all, and the slot routes said "internal" while record mode was about to
289
+ * proxy the very same miss.
290
+ */
291
+ function noFixtureSource(defaults, headers) {
292
+ return require_helpers.wouldProxyMiss(require_helpers.resolveStrictMode(defaults.strict, headers), defaults.record, "elevenlabs") ? "proxy" : "internal";
293
+ }
294
+ function parseJsonObject(body, req, res, journal, path, method) {
295
+ let parsed;
296
+ try {
297
+ parsed = JSON.parse(body);
298
+ } catch (parseErr) {
299
+ const detail = parseErr instanceof Error ? parseErr.message : "unknown";
300
+ require_sse_writer.writeErrorResponse(res, 400, JSON.stringify({ error: {
301
+ message: `Malformed JSON: ${detail}`,
302
+ type: "invalid_request_error",
303
+ code: "invalid_json"
304
+ } }));
305
+ journal.add({
306
+ method,
307
+ path,
308
+ headers: require_helpers.flattenHeaders(req.headers),
309
+ service: "elevenlabs-voice",
310
+ body: null,
311
+ response: {
312
+ status: 400,
313
+ fixture: null
314
+ }
315
+ });
316
+ return null;
317
+ }
318
+ if (!require_helpers.isJsonObject(parsed)) {
319
+ require_sse_writer.writeErrorResponse(res, 400, JSON.stringify({ error: {
320
+ message: "Request body must be a JSON object",
321
+ type: "invalid_request_error"
322
+ } }));
323
+ journal.add({
324
+ method,
325
+ path,
326
+ headers: require_helpers.flattenHeaders(req.headers),
327
+ service: "elevenlabs-voice",
328
+ body: null,
329
+ response: {
330
+ status: 400,
331
+ fixture: null
332
+ }
333
+ });
334
+ return null;
335
+ }
336
+ return parsed;
337
+ }
338
+ function buildSyntheticReq(parsed, matchText, endpoint, modelFallback, req) {
339
+ return {
340
+ model: typeof parsed.model_id === "string" ? parsed.model_id : modelFallback,
341
+ messages: [{
342
+ role: "user",
343
+ content: matchText
344
+ }],
345
+ _endpointType: endpoint,
346
+ _context: require_helpers.getContext(req)
347
+ };
348
+ }
349
+ async function missPath(req, res, syntheticReq, fixtures, defaults, journal, path, method, body, skippedBySequenceOrTurn, proxyOptions) {
350
+ if (require_helpers.resolveStrictMode(defaults.strict, req.headers)) {
351
+ const strictMessage = require_helpers.strictNoMatchMessage(skippedBySequenceOrTurn);
352
+ defaults.logger.error(require_helpers.strictNoMatchLogLine(method, path, skippedBySequenceOrTurn));
353
+ require_sse_writer.writeErrorResponse(res, 503, JSON.stringify({ error: {
354
+ message: strictMessage,
355
+ type: "invalid_request_error",
356
+ code: "no_fixture_match"
357
+ } }));
358
+ journal.add({
359
+ method,
360
+ path,
361
+ headers: require_helpers.flattenHeaders(req.headers),
362
+ service: "elevenlabs-voice",
363
+ body: syntheticReq,
364
+ response: {
365
+ status: 503,
366
+ fixture: null,
367
+ ...require_helpers.strictOverrideField(defaults.strict, req.headers)
368
+ }
369
+ });
370
+ return "handled";
371
+ }
372
+ if (defaults.record) {
373
+ const outcome = await require_recorder.proxyAndRecord(req, res, syntheticReq, "elevenlabs", req.url ?? path, fixtures, defaults, body, proxyOptions);
374
+ if (outcome === "handled_by_hook") return "handled";
375
+ if (outcome !== "not_configured") {
376
+ journal.add({
377
+ method,
378
+ path,
379
+ headers: require_helpers.flattenHeaders(req.headers),
380
+ service: "elevenlabs-voice",
381
+ body: syntheticReq,
382
+ response: {
383
+ status: res.statusCode ?? 200,
384
+ fixture: null,
385
+ source: "proxy"
386
+ }
387
+ });
388
+ return "handled";
389
+ }
390
+ }
391
+ return "miss";
392
+ }
393
+ function writeNoMatch(req, res, syntheticReq, defaults, journal, path, method) {
394
+ require_sse_writer.writeErrorResponse(res, 404, JSON.stringify({ error: {
395
+ message: "No fixture matched",
396
+ type: "invalid_request_error",
397
+ code: "no_fixture_match"
398
+ } }));
399
+ journal.add({
400
+ method,
401
+ path,
402
+ headers: require_helpers.flattenHeaders(req.headers),
403
+ service: "elevenlabs-voice",
404
+ body: syntheticReq,
405
+ response: {
406
+ status: 404,
407
+ fixture: null,
408
+ ...require_helpers.strictOverrideField(defaults.strict, req.headers)
409
+ }
410
+ });
411
+ }
412
+ /**
413
+ * Builds the `beforeWriteResponse` observer for the voice routes. It NEVER
414
+ * handles the response — it returns `false` so `proxyAndRecord` relays the
415
+ * upstream bytes and the caller still journals the `"relayed"` outcome — it
416
+ * only copies a successfully proxied voice object into the in-process store, so
417
+ * a follow-up `GET /v1/voices/{id}` answers from memory exactly as it does
418
+ * after an in-process create instead of hitting upstream a second time.
419
+ *
420
+ * It is a FACTORY OVER THE LOGGER, not a bare function, because the store has
421
+ * exactly two things worth saying and neither can be said without one: the
422
+ * AUTHORED overwrite of an already-stored `voice_id`, which
423
+ * `rememberElevenLabsVoice` documents as made AUDIBLE rather than silent, and a
424
+ * 2xx voice body carrying no string `voice_id`, which it reports by returning
425
+ * `false`. Both are already reported on the in-process and fixture-replay paths
426
+ * (see the `create` handler's `defaults.logger` calls); passing the observer a
427
+ * bare function dropped both on EVERY proxy path, which is the one path where
428
+ * the body came from somewhere aimock does not control.
429
+ */
430
+ function rememberProxiedVoice(logger) {
431
+ return (captured) => {
432
+ if (captured.status < 200 || captured.status >= 300) return false;
433
+ let parsed;
434
+ try {
435
+ parsed = JSON.parse(captured.body.toString("utf-8"));
436
+ } catch (err) {
437
+ if (err instanceof SyntaxError) return false;
438
+ throw err;
439
+ }
440
+ if (!require_helpers.isJsonObject(parsed)) return false;
441
+ if (!rememberElevenLabsVoice(parsed, logger)) logger.warn(`ElevenLabs relayed a ${captured.status} voice body from upstream that carries no string \`voice_id\` — the voice was NOT stored, so a follow-up GET /v1/voices/{id} will miss and re-hit upstream instead of replaying from memory. Upstream body: ${captured.body.toString("utf-8").slice(0, 200)}`);
442
+ return false;
443
+ };
444
+ }
445
+ function buildSyntheticVoice(parsed) {
446
+ const generatedVoiceId = String(parsed.generated_voice_id);
447
+ const labels = parsed.labels && typeof parsed.labels === "object" && !Array.isArray(parsed.labels) ? parsed.labels : {};
448
+ return {
449
+ voice_id: generatedVoiceId,
450
+ name: String(parsed.voice_name),
451
+ category: "generated",
452
+ description: String(parsed.voice_description),
453
+ labels,
454
+ preview_url: null,
455
+ available_for_tiers: [],
456
+ settings: null,
457
+ sharing: null,
458
+ high_quality_base_model_ids: [],
459
+ samples: null,
460
+ safety_control: null,
461
+ voice_verification: {
462
+ requires_verification: false,
463
+ is_verified: false,
464
+ verification_failures: [],
465
+ verification_attempts_count: 0
466
+ },
467
+ permission_on_resource: null,
468
+ is_owner: true,
469
+ is_legacy: false,
470
+ is_mixed: false
471
+ };
472
+ }
473
+ async function handleElevenLabsVoiceDesign(req, res, body, fixtures, defaults, journal) {
474
+ const path = req.url ?? "/v1/text-to-voice/design";
475
+ const method = req.method ?? "POST";
476
+ const parsed = parseJsonObject(body, req, res, journal, path, method);
477
+ if (!parsed) return;
478
+ const description = typeof parsed.voice_description === "string" ? parsed.voice_description : void 0;
479
+ const syntheticReq = buildSyntheticReq(parsed, description ?? "", "elevenlabs-voice-design", "eleven_multilingual_ttv_v2", req);
480
+ if (description === void 0) {
481
+ require_sse_writer.writeErrorResponse(res, 400, JSON.stringify({ error: {
482
+ message: "Missing required parameter: 'voice_description'",
483
+ type: "invalid_request_error"
484
+ } }));
485
+ journal.add({
486
+ method,
487
+ path,
488
+ headers: require_helpers.flattenHeaders(req.headers),
489
+ service: "elevenlabs-voice",
490
+ body: syntheticReq,
491
+ response: {
492
+ status: 400,
493
+ fixture: null
494
+ }
495
+ });
496
+ return;
497
+ }
498
+ const testId = require_helpers.getTestId(req);
499
+ const { fixture, skippedBySequenceOrTurn } = require_router.selectFixtureForServing(fixtures, syntheticReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform);
500
+ if (fixture) journal.incrementFixtureMatchCount(fixture, fixtures, testId);
501
+ if (await require_chaos.applyChaosAsync(res, fixture, defaults.chaos, req.headers, req.url, journal, {
502
+ method,
503
+ path,
504
+ headers: require_helpers.flattenHeaders(req.headers),
505
+ body: syntheticReq,
506
+ service: "elevenlabs-voice"
507
+ }, fixture ? "fixture" : noFixtureSource(defaults, req.headers), defaults.registry, defaults.logger)) {
508
+ require_fixture_loader.releaseOneShotError(fixtures, fixture);
509
+ return;
510
+ }
511
+ if (!fixture) {
512
+ if (await missPath(req, res, syntheticReq, fixtures, defaults, journal, path, method, body, skippedBySequenceOrTurn) === "handled") return;
513
+ if ([...description].length < VOICE_DESCRIPTION_MIN_LENGTH) {
514
+ writeValidationError(res, ["body", "voice_description"], `String should have at least ${VOICE_DESCRIPTION_MIN_LENGTH} characters`, "string_too_short");
515
+ journal.add({
516
+ method,
517
+ path,
518
+ headers: require_helpers.flattenHeaders(req.headers),
519
+ service: "elevenlabs-voice",
520
+ body: syntheticReq,
521
+ response: {
522
+ status: 422,
523
+ fixture: null
524
+ }
525
+ });
526
+ return;
527
+ }
528
+ writeNoMatch(req, res, syntheticReq, defaults, journal, path, method);
529
+ return;
530
+ }
531
+ const response = await require_helpers.resolveResponse(fixture, syntheticReq);
532
+ if (require_helpers.isErrorResponse(response)) {
533
+ const status = response.status ?? 500;
534
+ require_sse_writer.writeErrorResponse(res, status, require_helpers.serializeErrorResponse(response), { retryAfter: response.retryAfter });
535
+ journal.add({
536
+ method,
537
+ path,
538
+ headers: require_helpers.flattenHeaders(req.headers),
539
+ service: "elevenlabs-voice",
540
+ body: syntheticReq,
541
+ response: {
542
+ status,
543
+ fixture
544
+ }
545
+ });
546
+ return;
547
+ }
548
+ if (!require_helpers.isJSONResponse(response)) {
549
+ require_sse_writer.writeErrorResponse(res, 500, JSON.stringify({ error: {
550
+ message: "Fixture response is not a JSON type for voice design",
551
+ type: "server_error"
552
+ } }));
553
+ journal.add({
554
+ method,
555
+ path,
556
+ headers: require_helpers.flattenHeaders(req.headers),
557
+ service: "elevenlabs-voice",
558
+ body: syntheticReq,
559
+ response: {
560
+ status: 500,
561
+ fixture
562
+ }
563
+ });
564
+ return;
565
+ }
566
+ const status = replayStatus(response);
567
+ writeJson(res, status, response.json);
568
+ journal.add({
569
+ method,
570
+ path,
571
+ headers: require_helpers.flattenHeaders(req.headers),
572
+ service: "elevenlabs-voice",
573
+ body: syntheticReq,
574
+ response: {
575
+ status,
576
+ fixture
577
+ }
578
+ });
579
+ }
580
+ async function handleElevenLabsVoiceCreate(req, res, body, fixtures, defaults, journal) {
581
+ const path = req.url ?? "/v1/text-to-voice";
582
+ const method = req.method ?? "POST";
583
+ const parsed = parseJsonObject(body, req, res, journal, path, method);
584
+ if (!parsed) return;
585
+ const missing = typeof parsed.voice_name !== "string" ? "voice_name" : typeof parsed.voice_description !== "string" ? "voice_description" : typeof parsed.generated_voice_id !== "string" ? "generated_voice_id" : null;
586
+ const tooShort = parsed.voice_name === "" ? "voice_name" : parsed.voice_description === "" ? "voice_description" : parsed.generated_voice_id === "" ? "generated_voice_id" : null;
587
+ const syntheticReq = buildSyntheticReq(parsed, typeof parsed.generated_voice_id === "string" ? parsed.generated_voice_id : "", "elevenlabs-voice", "eleven_multilingual_ttv_v2", req);
588
+ if (missing) {
589
+ require_sse_writer.writeErrorResponse(res, 400, JSON.stringify({ error: {
590
+ message: `Missing required parameter: '${missing}'`,
591
+ type: "invalid_request_error"
592
+ } }));
593
+ journal.add({
594
+ method,
595
+ path,
596
+ headers: require_helpers.flattenHeaders(req.headers),
597
+ service: "elevenlabs-voice",
598
+ body: syntheticReq,
599
+ response: {
600
+ status: 400,
601
+ fixture: null
602
+ }
603
+ });
604
+ return;
605
+ }
606
+ const testId = require_helpers.getTestId(req);
607
+ const { fixture, skippedBySequenceOrTurn } = require_router.selectFixtureForServing(fixtures, syntheticReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform);
608
+ if (fixture) journal.incrementFixtureMatchCount(fixture, fixtures, testId);
609
+ if (await require_chaos.applyChaosAsync(res, fixture, defaults.chaos, req.headers, req.url, journal, {
610
+ method,
611
+ path,
612
+ headers: require_helpers.flattenHeaders(req.headers),
613
+ body: syntheticReq,
614
+ service: "elevenlabs-voice"
615
+ }, fixture ? "fixture" : noFixtureSource(defaults, req.headers), defaults.registry, defaults.logger)) {
616
+ require_fixture_loader.releaseOneShotError(fixtures, fixture);
617
+ return;
618
+ }
619
+ if (!fixture) {
620
+ if (await missPath(req, res, syntheticReq, fixtures, defaults, journal, path, method, body, skippedBySequenceOrTurn, { beforeWriteResponse: rememberProxiedVoice(defaults.logger) }) === "handled") return;
621
+ if (tooShort) {
622
+ writeValidationError(res, ["body", tooShort], "String should have at least 1 character", "string_too_short");
623
+ journal.add({
624
+ method,
625
+ path,
626
+ headers: require_helpers.flattenHeaders(req.headers),
627
+ service: "elevenlabs-voice",
628
+ body: syntheticReq,
629
+ response: {
630
+ status: 422,
631
+ fixture: null
632
+ }
633
+ });
634
+ return;
635
+ }
636
+ const voice = buildSyntheticVoice(parsed);
637
+ rememberElevenLabsVoice(voice, defaults.logger);
638
+ writeJson(res, 200, voice);
639
+ journal.add({
640
+ method,
641
+ path,
642
+ headers: require_helpers.flattenHeaders(req.headers),
643
+ service: "elevenlabs-voice",
644
+ body: syntheticReq,
645
+ response: {
646
+ status: 200,
647
+ fixture: null
648
+ }
649
+ });
650
+ return;
651
+ }
652
+ const response = await require_helpers.resolveResponse(fixture, syntheticReq);
653
+ if (require_helpers.isErrorResponse(response)) {
654
+ const status = response.status ?? 500;
655
+ require_sse_writer.writeErrorResponse(res, status, require_helpers.serializeErrorResponse(response), { retryAfter: response.retryAfter });
656
+ journal.add({
657
+ method,
658
+ path,
659
+ headers: require_helpers.flattenHeaders(req.headers),
660
+ service: "elevenlabs-voice",
661
+ body: syntheticReq,
662
+ response: {
663
+ status,
664
+ fixture
665
+ }
666
+ });
667
+ return;
668
+ }
669
+ if (!require_helpers.isJSONResponse(response)) {
670
+ require_sse_writer.writeErrorResponse(res, 500, JSON.stringify({ error: {
671
+ message: "Fixture response is not a JSON type for voice create",
672
+ type: "server_error"
673
+ } }));
674
+ journal.add({
675
+ method,
676
+ path,
677
+ headers: require_helpers.flattenHeaders(req.headers),
678
+ service: "elevenlabs-voice",
679
+ body: syntheticReq,
680
+ response: {
681
+ status: 500,
682
+ fixture
683
+ }
684
+ });
685
+ return;
686
+ }
687
+ const status = replayStatus(response);
688
+ if (status >= 200 && status < 300) {
689
+ if (!(require_helpers.isJsonObject(response.json) && rememberElevenLabsVoice(response.json, defaults.logger))) defaults.logger.warn(`ElevenLabs voice create fixture matched generated_voice_id "${String(parsed.generated_voice_id)}" but its json carries no string \`voice_id\` — the voice was NOT stored, so a follow-up GET /v1/voices/{id} will 404 with a "voice not found" message that points at the id instead of at the fixture. Add a string voice_id to the fixture's json`);
690
+ }
691
+ writeJson(res, status, response.json);
692
+ journal.add({
693
+ method,
694
+ path,
695
+ headers: require_helpers.flattenHeaders(req.headers),
696
+ service: "elevenlabs-voice",
697
+ body: syntheticReq,
698
+ response: {
699
+ status,
700
+ fixture
701
+ }
702
+ });
703
+ }
704
+ /**
705
+ * The synthetic request the slot routes (GET/DELETE /v1/voices/{id}) match and
706
+ * journal on. The voice id stands in for the user message so the entry carries
707
+ * the one thing that identifies the request.
708
+ *
709
+ * The endpoint type is the CALLER's, not a constant: ONE ROUTE, ONE ENDPOINT
710
+ * TYPE. GET declares "elevenlabs-voice-get" and DELETE declares
711
+ * "elevenlabs-voice-delete", so neither route's fixtures can ever be confused
712
+ * with the create route's ("elevenlabs-voice") — all three would otherwise
713
+ * carry the same voice id as their match text and answer for one another.
714
+ */
715
+ function buildVoiceSlotReq(req, voiceId, endpointType) {
716
+ return {
717
+ model: "eleven_multilingual_ttv_v2",
718
+ messages: [{
719
+ role: "user",
720
+ content: voiceId
721
+ }],
722
+ _endpointType: endpointType,
723
+ _context: require_helpers.getContext(req)
724
+ };
725
+ }
726
+ /**
727
+ * Gate a slot route on chaos exactly as {@link handleElevenLabsVoiceDesign} and
728
+ * {@link handleElevenLabsVoiceCreate} gate theirs: the same `applyChaosAsync`
729
+ * call, awaited BEFORE anything is served, so a configured latency/drop/429
730
+ * applies to a store hit as well as to a miss. It is called AFTER the fixture
731
+ * match and is handed the fixture that matched, because chaos can be authored
732
+ * ON a fixture (`Fixture.chaos`, folded in by `chaos.ts`): passing null here
733
+ * dropped a fixture-level `chaos` block on the floor for these two routes
734
+ * while design/create honoured the identical block. The journal source follows
735
+ * the same fixture/no-fixture split design/create use, via the same
736
+ * {@link noFixtureSource}: "internal" when this process answers the miss,
737
+ * "proxy" when record mode has an ElevenLabs upstream and would forward it.
738
+ *
739
+ * `applyChaosAsync` reads the per-testId chaos scope off `req.url`, which is
740
+ * why the raw url — not the fallback `path` — is what it is handed, matching
741
+ * design/create. The context carries `service: "elevenlabs-voice"` like every
742
+ * other journal write in this module, so a faulted request stays selectable by
743
+ * `GET /__aimock/journal?service=elevenlabs-voice`.
744
+ */
745
+ async function gateVoiceSlotChaos(req, res, syntheticReq, fixture, defaults, journal, path, method) {
746
+ return await require_chaos.applyChaosAsync(res, fixture, defaults.chaos, req.headers, req.url, journal, {
747
+ method,
748
+ path,
749
+ headers: require_helpers.flattenHeaders(req.headers),
750
+ body: syntheticReq,
751
+ service: "elevenlabs-voice"
752
+ }, fixture ? "fixture" : noFixtureSource(defaults, req.headers), defaults.registry, defaults.logger);
753
+ }
754
+ /**
755
+ * Refuse a slot-route miss under strict mode, mirroring {@link missPath}: the
756
+ * refusal is LOGGED through the shared `strictNoMatchLogLine` and the client
757
+ * message names strict mode, so a strict 503 is never mistaken for the route's
758
+ * own 404.
759
+ *
760
+ * `skippedBySequenceOrTurn` is the count `selectFixtureForServing` handed the
761
+ * caller. Both slot routes are fixture routes now, so a candidate fixture CAN
762
+ * be skipped by sequence/turn state here exactly as on create — hardcoding 0
763
+ * told every such refusal "no fixture matched" when the truth was "a fixture
764
+ * matched the content and was held back by its sequenceIndex". When the count
765
+ * is non-zero the client message switches to the shared
766
+ * {@link strictNoMatchMessage} wording that names it; otherwise the
767
+ * voice-specific message is kept, since naming the id is the more useful thing
768
+ * to say when nothing matched at all.
769
+ */
770
+ function writeVoiceSlotStrictRefusal(req, res, syntheticReq, defaults, journal, path, method, voiceId, skippedBySequenceOrTurn) {
771
+ defaults.logger.error(require_helpers.strictNoMatchLogLine(method, path, skippedBySequenceOrTurn));
772
+ require_sse_writer.writeErrorResponse(res, 503, JSON.stringify({ error: {
773
+ message: skippedBySequenceOrTurn > 0 ? require_helpers.strictNoMatchMessage(skippedBySequenceOrTurn) : `Strict mode: voice '${voiceId}' not found`,
774
+ type: "invalid_request_error",
775
+ code: "no_fixture_match"
776
+ } }));
777
+ journal.add({
778
+ method,
779
+ path,
780
+ headers: require_helpers.flattenHeaders(req.headers),
781
+ service: "elevenlabs-voice",
782
+ body: syntheticReq,
783
+ response: {
784
+ status: 503,
785
+ fixture: null,
786
+ ...require_helpers.strictOverrideField(defaults.strict, req.headers)
787
+ }
788
+ });
789
+ }
790
+ /**
791
+ * Proxy a slot-route miss upstream in record mode. Returns "handled" when the
792
+ * upstream answer was relayed (or a hook took over), "miss" when no upstream is
793
+ * configured for the provider — `proxyAndRecord` already warns on that path, so
794
+ * the caller falls through to its own miss answer.
795
+ */
796
+ async function proxyVoiceSlotMiss(req, res, syntheticReq, defaults, journal, path, method, fixtures = [], proxyOptions) {
797
+ const outcome = await require_recorder.proxyAndRecord(req, res, syntheticReq, "elevenlabs", req.url ?? path, fixtures, defaults, "", proxyOptions);
798
+ if (outcome === "handled_by_hook") return "handled";
799
+ if (outcome === "not_configured") return "miss";
800
+ journal.add({
801
+ method,
802
+ path,
803
+ headers: require_helpers.flattenHeaders(req.headers),
804
+ service: "elevenlabs-voice",
805
+ body: syntheticReq,
806
+ response: {
807
+ status: res.statusCode ?? 200,
808
+ fixture: null,
809
+ source: "proxy"
810
+ }
811
+ });
812
+ return "handled";
813
+ }
814
+ /**
815
+ * Replay a fixture that matched on a slot route (`GET`/`DELETE /v1/voices/{id}`).
816
+ *
817
+ * Both slot routes replay identically, so the body lives here once: resolve,
818
+ * relay an error envelope under its own status, refuse a non-JSON response,
819
+ * and otherwise write the recorded JSON under {@link replayStatus} — the
820
+ * UPSTREAM status the recorder persisted, not a hardcoded 200. GET used to
821
+ * hardcode 200, which turned every recorded ElevenLabs failure (a `detail`
822
+ * body that `isErrorResponse()` never claims) into a fabricated success.
823
+ *
824
+ * Returns the status actually written, or `null` when the fixture could not be
825
+ * served as a success (an error envelope was relayed, or the response was not
826
+ * JSON at all). DELETE uses that to decide whether the local store entry is
827
+ * gone.
828
+ */
829
+ async function replayVoiceSlotFixture(req, res, syntheticReq, fixture, journal, path, method, routeLabel) {
830
+ const response = await require_helpers.resolveResponse(fixture, syntheticReq);
831
+ if (require_helpers.isErrorResponse(response)) {
832
+ const status = response.status ?? 500;
833
+ require_sse_writer.writeErrorResponse(res, status, require_helpers.serializeErrorResponse(response), { retryAfter: response.retryAfter });
834
+ journal.add({
835
+ method,
836
+ path,
837
+ headers: require_helpers.flattenHeaders(req.headers),
838
+ service: "elevenlabs-voice",
839
+ body: syntheticReq,
840
+ response: {
841
+ status,
842
+ fixture
843
+ }
844
+ });
845
+ return null;
846
+ }
847
+ if (!require_helpers.isJSONResponse(response)) {
848
+ require_sse_writer.writeErrorResponse(res, 500, JSON.stringify({ error: {
849
+ message: `Fixture response is not a JSON type for ${routeLabel}`,
850
+ type: "server_error"
851
+ } }));
852
+ journal.add({
853
+ method,
854
+ path,
855
+ headers: require_helpers.flattenHeaders(req.headers),
856
+ service: "elevenlabs-voice",
857
+ body: syntheticReq,
858
+ response: {
859
+ status: 500,
860
+ fixture
861
+ }
862
+ });
863
+ return null;
864
+ }
865
+ const status = replayStatus(response);
866
+ writeJson(res, status, response.json);
867
+ journal.add({
868
+ method,
869
+ path,
870
+ headers: require_helpers.flattenHeaders(req.headers),
871
+ service: "elevenlabs-voice",
872
+ body: syntheticReq,
873
+ response: {
874
+ status,
875
+ fixture
876
+ }
877
+ });
878
+ return status;
879
+ }
880
+ /**
881
+ * FIXTURE BEFORE STORE, on both slot routes.
882
+ *
883
+ * A registered or recorded fixture is explicit authoring intent and wins over
884
+ * the in-process voice store, which is a replay CONVENIENCE populated as a
885
+ * side effect of create. That is the order every other replay route in this
886
+ * repo uses — fixture match first, internal state only on a miss — and without
887
+ * it a fixture authored specifically for `GET /v1/voices/{id}` (a recorded 401,
888
+ * say) was silently unreachable for any id that create had already stored.
889
+ *
890
+ * The shadowing is AUDIBLE rather than silent: when a fixture wins over a
891
+ * present store entry this logs at debug, so a test whose store entry stopped
892
+ * being served can see why.
893
+ */
894
+ function logVoiceSlotShadow(defaults, method, path, voiceId) {
895
+ defaults.logger.debug(`ElevenLabs ${method} ${path}: a matching fixture shadows the in-process voice store entry for '${voiceId}' — the fixture is being served`);
896
+ }
897
+ async function handleElevenLabsVoiceGet(req, res, voiceId, fixtures, defaults, journal) {
898
+ const path = req.url ?? `/v1/voices/${voiceId}`;
899
+ const method = req.method ?? "GET";
900
+ const syntheticReq = buildVoiceSlotReq(req, voiceId, "elevenlabs-voice-get");
901
+ const testId = require_helpers.getTestId(req);
902
+ const { fixture, skippedBySequenceOrTurn } = require_router.selectFixtureForServing(fixtures, syntheticReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform);
903
+ if (fixture) journal.incrementFixtureMatchCount(fixture, fixtures, testId);
904
+ if (await gateVoiceSlotChaos(req, res, syntheticReq, fixture, defaults, journal, path, method)) {
905
+ require_fixture_loader.releaseOneShotError(fixtures, fixture);
906
+ return;
907
+ }
908
+ const stored = elevenLabsVoices.get(voiceId);
909
+ if (fixture) {
910
+ if (stored) logVoiceSlotShadow(defaults, method, path, voiceId);
911
+ await replayVoiceSlotFixture(req, res, syntheticReq, fixture, journal, path, method, "voice get");
912
+ return;
913
+ }
914
+ const serveStored = (voice) => {
915
+ writeJson(res, 200, voice);
916
+ journal.add({
917
+ method,
918
+ path,
919
+ headers: require_helpers.flattenHeaders(req.headers),
920
+ service: "elevenlabs-voice",
921
+ body: syntheticReq,
922
+ response: {
923
+ status: 200,
924
+ fixture: null
925
+ }
926
+ });
927
+ };
928
+ if (stored && !defaults.record) {
929
+ serveStored(stored);
930
+ return;
931
+ }
932
+ if (require_helpers.resolveStrictMode(defaults.strict, req.headers)) {
933
+ writeVoiceSlotStrictRefusal(req, res, syntheticReq, defaults, journal, path, method, voiceId, skippedBySequenceOrTurn);
934
+ return;
935
+ }
936
+ if (defaults.record) {
937
+ if (await proxyVoiceSlotMiss(req, res, syntheticReq, defaults, journal, path, method, fixtures, { beforeWriteResponse: rememberProxiedVoice(defaults.logger) }) === "handled") return;
938
+ if (stored) {
939
+ serveStored(stored);
940
+ return;
941
+ }
942
+ }
943
+ require_sse_writer.writeErrorResponse(res, 404, JSON.stringify({ error: {
944
+ message: `Voice '${voiceId}' not found`,
945
+ type: "invalid_request_error",
946
+ code: "voice_not_found"
947
+ } }));
948
+ journal.add({
949
+ method,
950
+ path,
951
+ headers: require_helpers.flattenHeaders(req.headers),
952
+ service: "elevenlabs-voice",
953
+ body: syntheticReq,
954
+ response: {
955
+ status: 404,
956
+ fixture: null,
957
+ ...require_helpers.strictOverrideField(defaults.strict, req.headers)
958
+ }
959
+ });
960
+ }
961
+ /**
962
+ * DELETE /v1/voices/{voice_id}. A first-class record/replay route, exactly
963
+ * like GET: chaos, then a fixture match, then the in-process store, then
964
+ * strict, then record, then the lenient answer.
965
+ *
966
+ * The success body is `{ status: "ok" }` — SECONDARY, from
967
+ * `@elevenlabs/elevenlabs-js@2.68.0`: `voices.delete()` parses the response as
968
+ * `DeleteVoiceResponseModel`, whose only wire field is `status: string`, and
969
+ * whose doc comment reads "The status of the voice deletion request. If the
970
+ * request was successful, the status will be 'ok'." In record mode nothing is
971
+ * authored at all — the upstream's own response is relayed AND recorded.
972
+ *
973
+ * IN RECORD MODE THE LOCAL STORE IS NOT AUTHORITATIVE. A locally-known voice
974
+ * used to be deleted from the map and answered 200 without the request ever
975
+ * leaving the process, so the real voice leaked upstream and the tape got a
976
+ * hole exactly where the delete belonged. Record mode forwards regardless of
977
+ * what the store knows; the store is a replay convenience, not a claim about
978
+ * what exists upstream. It still evicts ONLY on a 2xx from upstream, the same
979
+ * rule the fixture-replay branch applies: a refused delete must not lose the
980
+ * local voice.
981
+ *
982
+ * A miss on a LENIENT server stays an idempotent 200: deleting an id that was
983
+ * never saved is not an error on this surface.
984
+ */
985
+ async function handleElevenLabsVoiceDelete(req, res, voiceId, fixtures, defaults, journal) {
986
+ const path = req.url ?? `/v1/voices/${voiceId}`;
987
+ const method = req.method ?? "DELETE";
988
+ const syntheticReq = buildVoiceSlotReq(req, voiceId, "elevenlabs-voice-delete");
989
+ const testId = require_helpers.getTestId(req);
990
+ const { fixture, skippedBySequenceOrTurn } = require_router.selectFixtureForServing(fixtures, syntheticReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform);
991
+ if (fixture) journal.incrementFixtureMatchCount(fixture, fixtures, testId);
992
+ if (await gateVoiceSlotChaos(req, res, syntheticReq, fixture, defaults, journal, path, method)) {
993
+ require_fixture_loader.releaseOneShotError(fixtures, fixture);
994
+ return;
995
+ }
996
+ const stored = elevenLabsVoices.has(voiceId);
997
+ if (fixture) {
998
+ if (stored) logVoiceSlotShadow(defaults, method, path, voiceId);
999
+ const status = await replayVoiceSlotFixture(req, res, syntheticReq, fixture, journal, path, method, "voice delete");
1000
+ if (status !== null && status >= 200 && status < 300) elevenLabsVoices.delete(voiceId);
1001
+ return;
1002
+ }
1003
+ if (stored && !defaults.record) {
1004
+ elevenLabsVoices.delete(voiceId);
1005
+ writeJson(res, 200, { status: "ok" });
1006
+ journal.add({
1007
+ method,
1008
+ path,
1009
+ headers: require_helpers.flattenHeaders(req.headers),
1010
+ service: "elevenlabs-voice",
1011
+ body: syntheticReq,
1012
+ response: {
1013
+ status: 200,
1014
+ fixture: null,
1015
+ ...require_helpers.strictOverrideField(defaults.strict, req.headers)
1016
+ }
1017
+ });
1018
+ return;
1019
+ }
1020
+ if (require_helpers.resolveStrictMode(defaults.strict, req.headers)) {
1021
+ writeVoiceSlotStrictRefusal(req, res, syntheticReq, defaults, journal, path, method, voiceId, skippedBySequenceOrTurn);
1022
+ return;
1023
+ }
1024
+ if (defaults.record) {
1025
+ if (await proxyVoiceSlotMiss(req, res, syntheticReq, defaults, journal, path, method, fixtures, { beforeWriteResponse: (captured) => {
1026
+ if (captured.status >= 200 && captured.status < 300) elevenLabsVoices.delete(voiceId);
1027
+ return false;
1028
+ } }) === "handled") return;
1029
+ }
1030
+ elevenLabsVoices.delete(voiceId);
1031
+ writeJson(res, 200, { status: "ok" });
1032
+ journal.add({
1033
+ method,
1034
+ path,
1035
+ headers: require_helpers.flattenHeaders(req.headers),
1036
+ service: "elevenlabs-voice",
1037
+ body: syntheticReq,
1038
+ response: {
1039
+ status: 200,
1040
+ fixture: null,
1041
+ ...require_helpers.strictOverrideField(defaults.strict, req.headers)
1042
+ }
1043
+ });
1044
+ }
1045
+
1046
+ //#endregion
1047
+ exports.clearElevenLabsVoices = clearElevenLabsVoices;
1048
+ exports.handleElevenLabsVoiceCreate = handleElevenLabsVoiceCreate;
1049
+ exports.handleElevenLabsVoiceDelete = handleElevenLabsVoiceDelete;
1050
+ exports.handleElevenLabsVoiceDesign = handleElevenLabsVoiceDesign;
1051
+ exports.handleElevenLabsVoiceGet = handleElevenLabsVoiceGet;
1052
+ exports.voiceDesignToJson = voiceDesignToJson;
1053
+ //# sourceMappingURL=elevenlabs-voice.cjs.map