@copilotkit/aimock 1.16.4 → 1.18.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 (326) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +35 -0
  4. package/README.md +10 -10
  5. package/dist/a2a-mock.d.cts +2 -2
  6. package/dist/a2a-mock.d.cts.map +1 -1
  7. package/dist/a2a-mock.d.ts +2 -2
  8. package/dist/a2a-mock.d.ts.map +1 -1
  9. package/dist/a2a-mock.js +2 -2
  10. package/dist/a2a-mock.js.map +1 -1
  11. package/dist/agui-handler.cjs +120 -5
  12. package/dist/agui-handler.cjs.map +1 -1
  13. package/dist/agui-handler.d.cts +41 -5
  14. package/dist/agui-handler.d.cts.map +1 -1
  15. package/dist/agui-handler.d.ts +41 -5
  16. package/dist/agui-handler.d.ts.map +1 -1
  17. package/dist/agui-handler.js +114 -6
  18. package/dist/agui-handler.js.map +1 -1
  19. package/dist/agui-mock.cjs +18 -7
  20. package/dist/agui-mock.cjs.map +1 -1
  21. package/dist/agui-mock.d.cts +2 -2
  22. package/dist/agui-mock.d.cts.map +1 -1
  23. package/dist/agui-mock.d.ts +2 -2
  24. package/dist/agui-mock.d.ts.map +1 -1
  25. package/dist/agui-mock.js +20 -9
  26. package/dist/agui-mock.js.map +1 -1
  27. package/dist/agui-recorder.cjs +43 -22
  28. package/dist/agui-recorder.cjs.map +1 -1
  29. package/dist/agui-recorder.d.cts +4 -3
  30. package/dist/agui-recorder.d.cts.map +1 -1
  31. package/dist/agui-recorder.d.ts +4 -3
  32. package/dist/agui-recorder.d.ts.map +1 -1
  33. package/dist/agui-recorder.js +45 -24
  34. package/dist/agui-recorder.js.map +1 -1
  35. package/dist/agui-stub.cjs +28 -0
  36. package/dist/agui-stub.d.cts +5 -0
  37. package/dist/agui-stub.d.ts +5 -0
  38. package/dist/agui-stub.js +5 -0
  39. package/dist/agui-types.d.cts +33 -6
  40. package/dist/agui-types.d.cts.map +1 -1
  41. package/dist/agui-types.d.ts +33 -6
  42. package/dist/agui-types.d.ts.map +1 -1
  43. package/dist/aimock-cli.cjs +1 -1
  44. package/dist/aimock-cli.js +1 -1
  45. package/dist/aws-event-stream.d.cts +2 -2
  46. package/dist/aws-event-stream.d.cts.map +1 -1
  47. package/dist/aws-event-stream.d.ts +2 -2
  48. package/dist/aws-event-stream.d.ts.map +1 -1
  49. package/dist/bedrock-converse.cjs +4 -4
  50. package/dist/bedrock-converse.cjs.map +1 -1
  51. package/dist/bedrock-converse.d.cts +3 -3
  52. package/dist/bedrock-converse.d.cts.map +1 -1
  53. package/dist/bedrock-converse.d.ts +3 -3
  54. package/dist/bedrock-converse.d.ts.map +1 -1
  55. package/dist/bedrock-converse.js +4 -4
  56. package/dist/bedrock-converse.js.map +1 -1
  57. package/dist/bedrock.cjs +4 -4
  58. package/dist/bedrock.cjs.map +1 -1
  59. package/dist/bedrock.d.cts +3 -3
  60. package/dist/bedrock.d.cts.map +1 -1
  61. package/dist/bedrock.d.ts +3 -3
  62. package/dist/bedrock.d.ts.map +1 -1
  63. package/dist/bedrock.js +4 -4
  64. package/dist/bedrock.js.map +1 -1
  65. package/dist/chaos.cjs +35 -9
  66. package/dist/chaos.cjs.map +1 -1
  67. package/dist/chaos.d.cts +19 -4
  68. package/dist/chaos.d.cts.map +1 -1
  69. package/dist/chaos.d.ts +19 -4
  70. package/dist/chaos.d.ts.map +1 -1
  71. package/dist/chaos.js +35 -10
  72. package/dist/chaos.js.map +1 -1
  73. package/dist/cli.cjs +6 -5
  74. package/dist/cli.cjs.map +1 -1
  75. package/dist/cli.js +6 -5
  76. package/dist/cli.js.map +1 -1
  77. package/dist/cohere.cjs +2 -2
  78. package/dist/cohere.cjs.map +1 -1
  79. package/dist/cohere.d.cts +2 -2
  80. package/dist/cohere.d.cts.map +1 -1
  81. package/dist/cohere.d.ts +2 -2
  82. package/dist/cohere.d.ts.map +1 -1
  83. package/dist/cohere.js +2 -2
  84. package/dist/cohere.js.map +1 -1
  85. package/dist/config-loader.cjs +3 -3
  86. package/dist/config-loader.d.cts +1 -1
  87. package/dist/config-loader.d.cts.map +1 -1
  88. package/dist/config-loader.d.ts +1 -1
  89. package/dist/config-loader.d.ts.map +1 -1
  90. package/dist/config-loader.js +2 -2
  91. package/dist/convert-vidaimock.cjs +1 -1
  92. package/dist/convert-vidaimock.js +1 -1
  93. package/dist/convert.cjs +1 -1
  94. package/dist/convert.js +1 -1
  95. package/dist/elevenlabs-audio.cjs +212 -0
  96. package/dist/elevenlabs-audio.cjs.map +1 -0
  97. package/dist/elevenlabs-audio.d.cts +11 -0
  98. package/dist/elevenlabs-audio.d.cts.map +1 -0
  99. package/dist/elevenlabs-audio.d.ts +11 -0
  100. package/dist/elevenlabs-audio.d.ts.map +1 -0
  101. package/dist/elevenlabs-audio.js +212 -0
  102. package/dist/elevenlabs-audio.js.map +1 -0
  103. package/dist/embeddings.cjs +2 -2
  104. package/dist/embeddings.cjs.map +1 -1
  105. package/dist/embeddings.d.cts +2 -2
  106. package/dist/embeddings.d.cts.map +1 -1
  107. package/dist/embeddings.d.ts +2 -2
  108. package/dist/embeddings.d.ts.map +1 -1
  109. package/dist/embeddings.js +2 -2
  110. package/dist/embeddings.js.map +1 -1
  111. package/dist/fal-audio.cjs +484 -0
  112. package/dist/fal-audio.cjs.map +1 -0
  113. package/dist/fal-audio.d.cts +10 -0
  114. package/dist/fal-audio.d.cts.map +1 -0
  115. package/dist/fal-audio.d.ts +10 -0
  116. package/dist/fal-audio.d.ts.map +1 -0
  117. package/dist/fal-audio.js +480 -0
  118. package/dist/fal-audio.js.map +1 -0
  119. package/dist/fal.cjs +424 -0
  120. package/dist/fal.cjs.map +1 -0
  121. package/dist/fal.d.cts +39 -0
  122. package/dist/fal.d.cts.map +1 -0
  123. package/dist/fal.d.ts +39 -0
  124. package/dist/fal.d.ts.map +1 -0
  125. package/dist/fal.js +420 -0
  126. package/dist/fal.js.map +1 -0
  127. package/dist/fixture-loader.cjs +16 -3
  128. package/dist/fixture-loader.cjs.map +1 -1
  129. package/dist/fixture-loader.d.cts.map +1 -1
  130. package/dist/fixture-loader.d.ts.map +1 -1
  131. package/dist/fixture-loader.js +17 -4
  132. package/dist/fixture-loader.js.map +1 -1
  133. package/dist/fixtures-remote.cjs +1 -1
  134. package/dist/fixtures-remote.js +1 -1
  135. package/dist/gemini-interactions.cjs +619 -0
  136. package/dist/gemini-interactions.cjs.map +1 -0
  137. package/dist/gemini-interactions.d.cts +46 -0
  138. package/dist/gemini-interactions.d.cts.map +1 -0
  139. package/dist/gemini-interactions.d.ts +46 -0
  140. package/dist/gemini-interactions.d.ts.map +1 -0
  141. package/dist/gemini-interactions.js +618 -0
  142. package/dist/gemini-interactions.js.map +1 -0
  143. package/dist/gemini.cjs +78 -2
  144. package/dist/gemini.cjs.map +1 -1
  145. package/dist/gemini.d.cts +2 -2
  146. package/dist/gemini.d.cts.map +1 -1
  147. package/dist/gemini.d.ts +2 -2
  148. package/dist/gemini.d.ts.map +1 -1
  149. package/dist/gemini.js +79 -3
  150. package/dist/gemini.js.map +1 -1
  151. package/dist/helpers.cjs +28 -1
  152. package/dist/helpers.cjs.map +1 -1
  153. package/dist/helpers.d.cts +13 -3
  154. package/dist/helpers.d.cts.map +1 -1
  155. package/dist/helpers.d.ts +13 -3
  156. package/dist/helpers.d.ts.map +1 -1
  157. package/dist/helpers.js +26 -2
  158. package/dist/helpers.js.map +1 -1
  159. package/dist/images.cjs +2 -2
  160. package/dist/images.cjs.map +1 -1
  161. package/dist/images.d.cts +2 -2
  162. package/dist/images.d.cts.map +1 -1
  163. package/dist/images.d.ts +2 -2
  164. package/dist/images.d.ts.map +1 -1
  165. package/dist/images.js +2 -2
  166. package/dist/images.js.map +1 -1
  167. package/dist/index.cjs +24 -4
  168. package/dist/index.d.cts +12 -8
  169. package/dist/index.d.ts +12 -8
  170. package/dist/index.js +11 -7
  171. package/dist/jest.cjs +1 -1
  172. package/dist/jest.js +1 -1
  173. package/dist/jsonrpc.d.cts +3 -3
  174. package/dist/jsonrpc.d.cts.map +1 -1
  175. package/dist/jsonrpc.d.ts +3 -3
  176. package/dist/jsonrpc.d.ts.map +1 -1
  177. package/dist/llmock.cjs +53 -2
  178. package/dist/llmock.cjs.map +1 -1
  179. package/dist/llmock.d.cts +6 -0
  180. package/dist/llmock.d.cts.map +1 -1
  181. package/dist/llmock.d.ts +6 -0
  182. package/dist/llmock.d.ts.map +1 -1
  183. package/dist/llmock.js +53 -2
  184. package/dist/llmock.js.map +1 -1
  185. package/dist/logger.cjs +5 -4
  186. package/dist/logger.cjs.map +1 -1
  187. package/dist/logger.d.cts +1 -1
  188. package/dist/logger.d.cts.map +1 -1
  189. package/dist/logger.d.ts +1 -1
  190. package/dist/logger.d.ts.map +1 -1
  191. package/dist/logger.js +5 -4
  192. package/dist/logger.js.map +1 -1
  193. package/dist/mcp-mock.d.cts +2 -2
  194. package/dist/mcp-mock.d.cts.map +1 -1
  195. package/dist/mcp-mock.d.ts +2 -2
  196. package/dist/mcp-mock.d.ts.map +1 -1
  197. package/dist/mcp-mock.js +2 -2
  198. package/dist/mcp-mock.js.map +1 -1
  199. package/dist/messages.cjs +2 -2
  200. package/dist/messages.cjs.map +1 -1
  201. package/dist/messages.d.cts +2 -2
  202. package/dist/messages.d.cts.map +1 -1
  203. package/dist/messages.d.ts +2 -2
  204. package/dist/messages.d.ts.map +1 -1
  205. package/dist/messages.js +2 -2
  206. package/dist/messages.js.map +1 -1
  207. package/dist/moderation.d.cts +3 -3
  208. package/dist/moderation.d.cts.map +1 -1
  209. package/dist/moderation.d.ts +3 -3
  210. package/dist/moderation.d.ts.map +1 -1
  211. package/dist/ndjson-writer.d.cts +2 -2
  212. package/dist/ndjson-writer.d.cts.map +1 -1
  213. package/dist/ndjson-writer.d.ts +2 -2
  214. package/dist/ndjson-writer.d.ts.map +1 -1
  215. package/dist/ollama.cjs +4 -4
  216. package/dist/ollama.cjs.map +1 -1
  217. package/dist/ollama.d.cts +3 -3
  218. package/dist/ollama.d.cts.map +1 -1
  219. package/dist/ollama.d.ts +3 -3
  220. package/dist/ollama.d.ts.map +1 -1
  221. package/dist/ollama.js +4 -4
  222. package/dist/ollama.js.map +1 -1
  223. package/dist/recorder.cjs +106 -38
  224. package/dist/recorder.cjs.map +1 -1
  225. package/dist/recorder.d.cts +52 -7
  226. package/dist/recorder.d.cts.map +1 -1
  227. package/dist/recorder.d.ts +52 -7
  228. package/dist/recorder.d.ts.map +1 -1
  229. package/dist/recorder.js +108 -40
  230. package/dist/recorder.js.map +1 -1
  231. package/dist/rerank.d.cts +3 -3
  232. package/dist/rerank.d.cts.map +1 -1
  233. package/dist/rerank.d.ts +3 -3
  234. package/dist/rerank.d.ts.map +1 -1
  235. package/dist/responses.cjs +2 -2
  236. package/dist/responses.cjs.map +1 -1
  237. package/dist/responses.d.cts +2 -2
  238. package/dist/responses.d.cts.map +1 -1
  239. package/dist/responses.d.ts +2 -2
  240. package/dist/responses.d.ts.map +1 -1
  241. package/dist/responses.js +2 -2
  242. package/dist/responses.js.map +1 -1
  243. package/dist/router.cjs +1 -1
  244. package/dist/router.cjs.map +1 -1
  245. package/dist/router.d.cts.map +1 -1
  246. package/dist/router.d.ts.map +1 -1
  247. package/dist/router.js +2 -2
  248. package/dist/router.js.map +1 -1
  249. package/dist/search.d.cts +3 -3
  250. package/dist/search.d.cts.map +1 -1
  251. package/dist/search.d.ts +3 -3
  252. package/dist/search.d.ts.map +1 -1
  253. package/dist/server.cjs +253 -8
  254. package/dist/server.cjs.map +1 -1
  255. package/dist/server.d.cts +2 -2
  256. package/dist/server.d.cts.map +1 -1
  257. package/dist/server.d.ts +2 -2
  258. package/dist/server.d.ts.map +1 -1
  259. package/dist/server.js +257 -12
  260. package/dist/server.js.map +1 -1
  261. package/dist/speech.cjs +20 -11
  262. package/dist/speech.cjs.map +1 -1
  263. package/dist/speech.d.cts +2 -2
  264. package/dist/speech.d.cts.map +1 -1
  265. package/dist/speech.d.ts +2 -2
  266. package/dist/speech.d.ts.map +1 -1
  267. package/dist/speech.js +20 -11
  268. package/dist/speech.js.map +1 -1
  269. package/dist/sse-writer.d.cts +3 -3
  270. package/dist/sse-writer.d.cts.map +1 -1
  271. package/dist/sse-writer.d.ts +3 -3
  272. package/dist/sse-writer.d.ts.map +1 -1
  273. package/dist/stream-collapse.cjs +80 -9
  274. package/dist/stream-collapse.cjs.map +1 -1
  275. package/dist/stream-collapse.d.cts +11 -1
  276. package/dist/stream-collapse.d.cts.map +1 -1
  277. package/dist/stream-collapse.d.ts +11 -1
  278. package/dist/stream-collapse.d.ts.map +1 -1
  279. package/dist/stream-collapse.js +80 -10
  280. package/dist/stream-collapse.js.map +1 -1
  281. package/dist/suite.cjs +1 -1
  282. package/dist/suite.d.cts +2 -2
  283. package/dist/suite.d.ts +2 -2
  284. package/dist/suite.js +1 -1
  285. package/dist/transcription.cjs +2 -2
  286. package/dist/transcription.cjs.map +1 -1
  287. package/dist/transcription.d.cts +2 -2
  288. package/dist/transcription.d.cts.map +1 -1
  289. package/dist/transcription.d.ts +2 -2
  290. package/dist/transcription.d.ts.map +1 -1
  291. package/dist/transcription.js +2 -2
  292. package/dist/transcription.js.map +1 -1
  293. package/dist/types.d.cts +38 -11
  294. package/dist/types.d.cts.map +1 -1
  295. package/dist/types.d.ts +38 -11
  296. package/dist/types.d.ts.map +1 -1
  297. package/dist/vector-mock.d.cts +2 -2
  298. package/dist/vector-mock.d.cts.map +1 -1
  299. package/dist/vector-mock.d.ts +2 -2
  300. package/dist/vector-mock.d.ts.map +1 -1
  301. package/dist/vector-mock.js +2 -2
  302. package/dist/vector-mock.js.map +1 -1
  303. package/dist/vector-types.d.cts.map +1 -1
  304. package/dist/vector-types.d.ts.map +1 -1
  305. package/dist/video.cjs +9 -3
  306. package/dist/video.cjs.map +1 -1
  307. package/dist/video.d.cts +3 -3
  308. package/dist/video.d.cts.map +1 -1
  309. package/dist/video.d.ts +3 -3
  310. package/dist/video.d.ts.map +1 -1
  311. package/dist/video.js +9 -3
  312. package/dist/video.js.map +1 -1
  313. package/dist/vitest.cjs +1 -1
  314. package/dist/vitest.js +1 -1
  315. package/dist/ws-framing.d.cts +2 -2
  316. package/dist/ws-framing.d.cts.map +1 -1
  317. package/dist/ws-framing.d.ts +2 -2
  318. package/dist/ws-framing.d.ts.map +1 -1
  319. package/dist/ws-gemini-live.cjs +145 -2
  320. package/dist/ws-gemini-live.cjs.map +1 -1
  321. package/dist/ws-gemini-live.d.cts.map +1 -1
  322. package/dist/ws-gemini-live.d.ts.map +1 -1
  323. package/dist/ws-gemini-live.js +146 -3
  324. package/dist/ws-gemini-live.js.map +1 -1
  325. package/package.json +16 -2
  326. package/skills/write-fixtures/SKILL.md +10 -10
@@ -0,0 +1,618 @@
1
+ import { extractOverrides, flattenHeaders, generateToolCallId, getTestId, isContentWithToolCallsResponse, isErrorResponse, isTextResponse, isToolCallResponse } from "./helpers.js";
2
+ import { matchFixture } from "./router.js";
3
+ import { calculateDelay, delay, writeErrorResponse } from "./sse-writer.js";
4
+ import { createInterruptionSignal } from "./interruption.js";
5
+ import { applyChaos } from "./chaos.js";
6
+ import { proxyAndRecord } from "./recorder.js";
7
+
8
+ //#region src/gemini-interactions.ts
9
+ function geminiInteractionsToCompletionRequest(req) {
10
+ const messages = [];
11
+ const model = req.model ?? "gemini-2.5-flash";
12
+ if (req.system_instruction) messages.push({
13
+ role: "system",
14
+ content: req.system_instruction
15
+ });
16
+ if (req.input !== void 0) {
17
+ if (typeof req.input === "string") messages.push({
18
+ role: "user",
19
+ content: req.input
20
+ });
21
+ else if (Array.isArray(req.input)) {
22
+ const firstItem = req.input[0];
23
+ if (firstItem && "role" in firstItem) for (const turn of req.input) {
24
+ const role = turn.role === "model" ? "assistant" : turn.role;
25
+ const blocks = turn.content ?? turn.parts;
26
+ if (!blocks || blocks.length === 0) {
27
+ if (role === "user" || role === "assistant") messages.push({
28
+ role,
29
+ content: ""
30
+ });
31
+ continue;
32
+ }
33
+ const funcCallParts = blocks.filter((p) => p.type === "function_call");
34
+ const funcResultParts = blocks.filter((p) => p.type === "function_result");
35
+ const textParts = blocks.filter((p) => p.type === "text");
36
+ if (funcCallParts.length > 0) {
37
+ const textContent = textParts.map((p) => p.text ?? "").join("");
38
+ messages.push({
39
+ role: "assistant",
40
+ content: textContent || null,
41
+ tool_calls: funcCallParts.map((p) => ({
42
+ id: p.id ?? p.call_id ?? generateToolCallId(),
43
+ type: "function",
44
+ function: {
45
+ name: p.name ?? "",
46
+ arguments: JSON.stringify(p.arguments ?? {})
47
+ }
48
+ }))
49
+ });
50
+ } else if (funcResultParts.length > 0) {
51
+ for (const part of funcResultParts) {
52
+ const resultValue = part.result ?? part.output;
53
+ messages.push({
54
+ role: "tool",
55
+ content: typeof resultValue === "string" ? resultValue : JSON.stringify(resultValue ?? ""),
56
+ tool_call_id: part.call_id ?? part.id ?? ""
57
+ });
58
+ }
59
+ if (textParts.length > 0) {
60
+ const text = textParts.map((p) => p.text ?? "").join("");
61
+ if (text) messages.push({
62
+ role: "user",
63
+ content: text
64
+ });
65
+ }
66
+ } else {
67
+ const text = textParts.map((p) => p.text ?? "").join("");
68
+ if (role === "user" || role === "assistant" || role === "system") messages.push({
69
+ role,
70
+ content: text
71
+ });
72
+ }
73
+ }
74
+ else {
75
+ const text = req.input.filter((p) => p.type === "text").map((p) => p.text ?? "").join("");
76
+ messages.push({
77
+ role: "user",
78
+ content: text || ""
79
+ });
80
+ }
81
+ }
82
+ }
83
+ let tools;
84
+ if (req.tools && req.tools.length > 0) {
85
+ const funcTools = req.tools.filter((t) => t.type === "function");
86
+ if (funcTools.length > 0) tools = funcTools.map((t) => ({
87
+ type: "function",
88
+ function: {
89
+ name: t.name,
90
+ description: t.description,
91
+ parameters: t.parameters
92
+ }
93
+ }));
94
+ }
95
+ return {
96
+ model,
97
+ messages,
98
+ stream: req.stream !== false,
99
+ temperature: req.generation_config?.temperature,
100
+ max_tokens: req.generation_config?.max_output_tokens,
101
+ tools
102
+ };
103
+ }
104
+ let interactionCounter = 0;
105
+ function nextInteractionId() {
106
+ return `aimock-int-${interactionCounter++}`;
107
+ }
108
+ function interactionsUsage(overrides) {
109
+ if (!overrides?.usage) return {
110
+ total_input_tokens: 0,
111
+ total_output_tokens: 0,
112
+ total_tokens: 0
113
+ };
114
+ const input = overrides.usage.input_tokens ?? overrides.usage.prompt_tokens ?? 0;
115
+ const output = overrides.usage.output_tokens ?? overrides.usage.completion_tokens ?? 0;
116
+ return {
117
+ total_input_tokens: input,
118
+ total_output_tokens: output,
119
+ total_tokens: input + output
120
+ };
121
+ }
122
+ function buildInteractionsTextResponse(content, model, interactionId, overrides) {
123
+ return {
124
+ id: interactionId,
125
+ status: "completed",
126
+ model: overrides?.model ?? model,
127
+ role: "model",
128
+ outputs: [{
129
+ type: "text",
130
+ text: content
131
+ }],
132
+ usage: interactionsUsage(overrides)
133
+ };
134
+ }
135
+ function buildInteractionsToolCallResponse(toolCalls, model, interactionId, logger, overrides) {
136
+ return {
137
+ id: interactionId,
138
+ status: "requires_action",
139
+ model: overrides?.model ?? model,
140
+ role: "model",
141
+ outputs: toolCalls.map((tc) => {
142
+ let argsObj;
143
+ try {
144
+ argsObj = JSON.parse(tc.arguments || "{}");
145
+ } catch {
146
+ logger.warn(`Malformed JSON in fixture tool call arguments for "${tc.name}": ${tc.arguments}`);
147
+ argsObj = {};
148
+ }
149
+ return {
150
+ type: "function_call",
151
+ id: tc.id || generateToolCallId(),
152
+ name: tc.name,
153
+ arguments: argsObj
154
+ };
155
+ }),
156
+ usage: interactionsUsage(overrides)
157
+ };
158
+ }
159
+ function buildInteractionsContentWithToolCallsResponse(content, toolCalls, model, interactionId, logger, overrides) {
160
+ const outputs = [{
161
+ type: "text",
162
+ text: content
163
+ }];
164
+ for (const tc of toolCalls) {
165
+ let argsObj;
166
+ try {
167
+ argsObj = JSON.parse(tc.arguments || "{}");
168
+ } catch {
169
+ logger.warn(`Malformed JSON in fixture tool call arguments for "${tc.name}": ${tc.arguments}`);
170
+ argsObj = {};
171
+ }
172
+ outputs.push({
173
+ type: "function_call",
174
+ id: tc.id || generateToolCallId(),
175
+ name: tc.name,
176
+ arguments: argsObj
177
+ });
178
+ }
179
+ return {
180
+ id: interactionId,
181
+ status: "requires_action",
182
+ model: overrides?.model ?? model,
183
+ role: "model",
184
+ outputs,
185
+ usage: interactionsUsage(overrides)
186
+ };
187
+ }
188
+ function buildInteractionsErrorResponse(message, code) {
189
+ return { error: {
190
+ code: code ?? "INVALID_ARGUMENT",
191
+ message
192
+ } };
193
+ }
194
+ let eventIdCounter = 0;
195
+ function nextEventId() {
196
+ return `evt_${++eventIdCounter}`;
197
+ }
198
+ function buildInteractionsTextSSEEvents(content, interactionId, chunkSize, overrides) {
199
+ const events = [];
200
+ events.push({
201
+ event_type: "interaction.start",
202
+ interaction: {
203
+ id: interactionId,
204
+ status: "in_progress"
205
+ },
206
+ event_id: nextEventId()
207
+ });
208
+ events.push({
209
+ event_type: "content.start",
210
+ index: 0,
211
+ content: { type: "text" },
212
+ event_id: nextEventId()
213
+ });
214
+ if (content.length === 0) events.push({
215
+ event_type: "content.delta",
216
+ index: 0,
217
+ delta: {
218
+ type: "text",
219
+ text: ""
220
+ },
221
+ event_id: nextEventId()
222
+ });
223
+ else for (let i = 0; i < content.length; i += chunkSize) {
224
+ const slice = content.slice(i, i + chunkSize);
225
+ events.push({
226
+ event_type: "content.delta",
227
+ index: 0,
228
+ delta: {
229
+ type: "text",
230
+ text: slice
231
+ },
232
+ event_id: nextEventId()
233
+ });
234
+ }
235
+ events.push({
236
+ event_type: "content.stop",
237
+ index: 0,
238
+ event_id: nextEventId()
239
+ });
240
+ events.push({
241
+ event_type: "interaction.complete",
242
+ interaction: {
243
+ id: interactionId,
244
+ status: "completed",
245
+ usage: interactionsUsage(overrides)
246
+ },
247
+ event_id: nextEventId()
248
+ });
249
+ return events;
250
+ }
251
+ function buildInteractionsToolCallSSEEvents(toolCalls, interactionId, logger, overrides) {
252
+ const events = [];
253
+ events.push({
254
+ event_type: "interaction.start",
255
+ interaction: {
256
+ id: interactionId,
257
+ status: "in_progress"
258
+ },
259
+ event_id: nextEventId()
260
+ });
261
+ for (let idx = 0; idx < toolCalls.length; idx++) {
262
+ const tc = toolCalls[idx];
263
+ let argsObj;
264
+ try {
265
+ argsObj = JSON.parse(tc.arguments || "{}");
266
+ } catch {
267
+ logger.warn(`Malformed JSON in fixture tool call arguments for "${tc.name}": ${tc.arguments}`);
268
+ argsObj = {};
269
+ }
270
+ events.push({
271
+ event_type: "content.start",
272
+ index: idx,
273
+ content: { type: "function_call" },
274
+ event_id: nextEventId()
275
+ });
276
+ events.push({
277
+ event_type: "content.delta",
278
+ index: idx,
279
+ delta: {
280
+ type: "function_call",
281
+ id: tc.id || generateToolCallId(),
282
+ name: tc.name,
283
+ arguments: argsObj
284
+ },
285
+ event_id: nextEventId()
286
+ });
287
+ events.push({
288
+ event_type: "content.stop",
289
+ index: idx,
290
+ event_id: nextEventId()
291
+ });
292
+ }
293
+ events.push({
294
+ event_type: "interaction.complete",
295
+ interaction: {
296
+ id: interactionId,
297
+ status: "requires_action",
298
+ usage: interactionsUsage(overrides)
299
+ },
300
+ event_id: nextEventId()
301
+ });
302
+ return events;
303
+ }
304
+ function buildInteractionsContentWithToolCallsSSEEvents(content, toolCalls, interactionId, chunkSize, logger, overrides) {
305
+ const events = [];
306
+ events.push({
307
+ event_type: "interaction.start",
308
+ interaction: {
309
+ id: interactionId,
310
+ status: "in_progress"
311
+ },
312
+ event_id: nextEventId()
313
+ });
314
+ events.push({
315
+ event_type: "content.start",
316
+ index: 0,
317
+ content: { type: "text" },
318
+ event_id: nextEventId()
319
+ });
320
+ if (content.length === 0) events.push({
321
+ event_type: "content.delta",
322
+ index: 0,
323
+ delta: {
324
+ type: "text",
325
+ text: ""
326
+ },
327
+ event_id: nextEventId()
328
+ });
329
+ else for (let i = 0; i < content.length; i += chunkSize) {
330
+ const slice = content.slice(i, i + chunkSize);
331
+ events.push({
332
+ event_type: "content.delta",
333
+ index: 0,
334
+ delta: {
335
+ type: "text",
336
+ text: slice
337
+ },
338
+ event_id: nextEventId()
339
+ });
340
+ }
341
+ events.push({
342
+ event_type: "content.stop",
343
+ index: 0,
344
+ event_id: nextEventId()
345
+ });
346
+ for (let i = 0; i < toolCalls.length; i++) {
347
+ const tc = toolCalls[i];
348
+ const idx = i + 1;
349
+ let argsObj;
350
+ try {
351
+ argsObj = JSON.parse(tc.arguments || "{}");
352
+ } catch {
353
+ logger.warn(`Malformed JSON in fixture tool call arguments for "${tc.name}": ${tc.arguments}`);
354
+ argsObj = {};
355
+ }
356
+ events.push({
357
+ event_type: "content.start",
358
+ index: idx,
359
+ content: { type: "function_call" },
360
+ event_id: nextEventId()
361
+ });
362
+ events.push({
363
+ event_type: "content.delta",
364
+ index: idx,
365
+ delta: {
366
+ type: "function_call",
367
+ id: tc.id || generateToolCallId(),
368
+ name: tc.name,
369
+ arguments: argsObj
370
+ },
371
+ event_id: nextEventId()
372
+ });
373
+ events.push({
374
+ event_type: "content.stop",
375
+ index: idx,
376
+ event_id: nextEventId()
377
+ });
378
+ }
379
+ events.push({
380
+ event_type: "interaction.complete",
381
+ interaction: {
382
+ id: interactionId,
383
+ status: "requires_action",
384
+ usage: interactionsUsage(overrides)
385
+ },
386
+ event_id: nextEventId()
387
+ });
388
+ return events;
389
+ }
390
+ async function writeGeminiInteractionsSSEStream(res, events, optionsOrLatency) {
391
+ const opts = typeof optionsOrLatency === "number" ? { latency: optionsOrLatency } : optionsOrLatency ?? {};
392
+ const latency = opts.latency ?? 0;
393
+ const profile = opts.streamingProfile;
394
+ const signal = opts.signal;
395
+ const onChunkSent = opts.onChunkSent;
396
+ if (res.writableEnded) return true;
397
+ res.setHeader("Content-Type", "text/event-stream");
398
+ res.setHeader("Cache-Control", "no-cache");
399
+ res.setHeader("Connection", "keep-alive");
400
+ let chunkIndex = 0;
401
+ for (const event of events) {
402
+ const chunkDelay = calculateDelay(chunkIndex, profile, latency);
403
+ if (chunkDelay > 0) await delay(chunkDelay, signal);
404
+ if (signal?.aborted) return false;
405
+ if (res.writableEnded) return true;
406
+ res.write(`data: ${JSON.stringify(event)}\n\n`);
407
+ onChunkSent?.();
408
+ if (signal?.aborted) return false;
409
+ chunkIndex++;
410
+ }
411
+ if (!res.writableEnded) res.end();
412
+ return true;
413
+ }
414
+ async function handleGeminiInteractions(req, res, raw, fixtures, journal, defaults, setCorsHeaders) {
415
+ const { logger } = defaults;
416
+ setCorsHeaders(res);
417
+ const urlPath = req.url ?? "/v1beta/interactions";
418
+ let interactionsReq;
419
+ try {
420
+ interactionsReq = JSON.parse(raw);
421
+ } catch {
422
+ journal.add({
423
+ method: req.method ?? "POST",
424
+ path: urlPath,
425
+ headers: flattenHeaders(req.headers),
426
+ body: null,
427
+ response: {
428
+ status: 400,
429
+ fixture: null
430
+ }
431
+ });
432
+ writeErrorResponse(res, 400, JSON.stringify(buildInteractionsErrorResponse("Malformed JSON", "INVALID_ARGUMENT")));
433
+ return;
434
+ }
435
+ const completionReq = geminiInteractionsToCompletionRequest(interactionsReq);
436
+ completionReq._endpointType = "chat";
437
+ const streaming = interactionsReq.stream !== false;
438
+ const model = completionReq.model;
439
+ const testId = getTestId(req);
440
+ const fixture = matchFixture(fixtures, completionReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform);
441
+ if (fixture) journal.incrementFixtureMatchCount(fixture, fixtures, testId);
442
+ if (applyChaos(res, fixture, defaults.chaos, req.headers, journal, {
443
+ method: req.method ?? "POST",
444
+ path: urlPath,
445
+ headers: flattenHeaders(req.headers),
446
+ body: completionReq
447
+ }, fixture ? "fixture" : "proxy", defaults.registry, defaults.logger)) return;
448
+ if (!fixture) {
449
+ if (defaults.record) {
450
+ const outcome = await proxyAndRecord(req, res, completionReq, "gemini-interactions", urlPath, fixtures, defaults, raw);
451
+ if (outcome === "handled_by_hook") return;
452
+ if (outcome === "relayed") {
453
+ journal.add({
454
+ method: req.method ?? "POST",
455
+ path: urlPath,
456
+ headers: flattenHeaders(req.headers),
457
+ body: completionReq,
458
+ response: {
459
+ status: res.statusCode ?? 200,
460
+ fixture: null,
461
+ source: "proxy"
462
+ }
463
+ });
464
+ return;
465
+ }
466
+ }
467
+ const strictStatus = defaults.strict ? 503 : 404;
468
+ const strictMessage = defaults.strict ? "Strict mode: no fixture matched" : "No fixture matched";
469
+ if (defaults.strict) logger.error(`STRICT: No fixture matched for ${req.method ?? "POST"} ${urlPath}`);
470
+ journal.add({
471
+ method: req.method ?? "POST",
472
+ path: urlPath,
473
+ headers: flattenHeaders(req.headers),
474
+ body: completionReq,
475
+ response: {
476
+ status: strictStatus,
477
+ fixture: null
478
+ }
479
+ });
480
+ writeErrorResponse(res, strictStatus, JSON.stringify(buildInteractionsErrorResponse(strictMessage, defaults.strict ? "UNAVAILABLE" : "NOT_FOUND")));
481
+ return;
482
+ }
483
+ const response = fixture.response;
484
+ const latency = fixture.latency ?? defaults.latency;
485
+ const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);
486
+ if (isErrorResponse(response)) {
487
+ const status = response.status ?? 500;
488
+ journal.add({
489
+ method: req.method ?? "POST",
490
+ path: urlPath,
491
+ headers: flattenHeaders(req.headers),
492
+ body: completionReq,
493
+ response: {
494
+ status,
495
+ fixture
496
+ }
497
+ });
498
+ writeErrorResponse(res, status, JSON.stringify(buildInteractionsErrorResponse(response.error.message, response.error.type ?? "ERROR")));
499
+ return;
500
+ }
501
+ const interactionId = nextInteractionId();
502
+ if (isContentWithToolCallsResponse(response)) {
503
+ if (response.webSearches?.length) logger.warn("webSearches in fixture response are not supported for Gemini Interactions API — ignoring");
504
+ const overrides = extractOverrides(response);
505
+ const journalEntry = journal.add({
506
+ method: req.method ?? "POST",
507
+ path: urlPath,
508
+ headers: flattenHeaders(req.headers),
509
+ body: completionReq,
510
+ response: {
511
+ status: 200,
512
+ fixture
513
+ }
514
+ });
515
+ if (!streaming) {
516
+ const body = buildInteractionsContentWithToolCallsResponse(response.content, response.toolCalls, model, interactionId, logger, overrides);
517
+ res.writeHead(200, { "Content-Type": "application/json" });
518
+ res.end(JSON.stringify(body));
519
+ } else {
520
+ const events = buildInteractionsContentWithToolCallsSSEEvents(response.content, response.toolCalls, interactionId, chunkSize, logger, overrides);
521
+ const interruption = createInterruptionSignal(fixture);
522
+ if (!await writeGeminiInteractionsSSEStream(res, events, {
523
+ latency,
524
+ streamingProfile: fixture.streamingProfile,
525
+ signal: interruption?.signal,
526
+ onChunkSent: interruption?.tick
527
+ })) {
528
+ if (!res.writableEnded) res.destroy();
529
+ journalEntry.response.interrupted = true;
530
+ journalEntry.response.interruptReason = interruption?.reason();
531
+ }
532
+ interruption?.cleanup();
533
+ }
534
+ return;
535
+ }
536
+ if (isTextResponse(response)) {
537
+ if (response.webSearches?.length) logger.warn("webSearches in fixture response are not supported for Gemini Interactions API — ignoring");
538
+ const overrides = extractOverrides(response);
539
+ const journalEntry = journal.add({
540
+ method: req.method ?? "POST",
541
+ path: urlPath,
542
+ headers: flattenHeaders(req.headers),
543
+ body: completionReq,
544
+ response: {
545
+ status: 200,
546
+ fixture
547
+ }
548
+ });
549
+ if (!streaming) {
550
+ const body = buildInteractionsTextResponse(response.content, model, interactionId, overrides);
551
+ res.writeHead(200, { "Content-Type": "application/json" });
552
+ res.end(JSON.stringify(body));
553
+ } else {
554
+ const events = buildInteractionsTextSSEEvents(response.content, interactionId, chunkSize, overrides);
555
+ const interruption = createInterruptionSignal(fixture);
556
+ if (!await writeGeminiInteractionsSSEStream(res, events, {
557
+ latency,
558
+ streamingProfile: fixture.streamingProfile,
559
+ signal: interruption?.signal,
560
+ onChunkSent: interruption?.tick
561
+ })) {
562
+ if (!res.writableEnded) res.destroy();
563
+ journalEntry.response.interrupted = true;
564
+ journalEntry.response.interruptReason = interruption?.reason();
565
+ }
566
+ interruption?.cleanup();
567
+ }
568
+ return;
569
+ }
570
+ if (isToolCallResponse(response)) {
571
+ const overrides = extractOverrides(response);
572
+ const journalEntry = journal.add({
573
+ method: req.method ?? "POST",
574
+ path: urlPath,
575
+ headers: flattenHeaders(req.headers),
576
+ body: completionReq,
577
+ response: {
578
+ status: 200,
579
+ fixture
580
+ }
581
+ });
582
+ if (!streaming) {
583
+ const body = buildInteractionsToolCallResponse(response.toolCalls, model, interactionId, logger, overrides);
584
+ res.writeHead(200, { "Content-Type": "application/json" });
585
+ res.end(JSON.stringify(body));
586
+ } else {
587
+ const events = buildInteractionsToolCallSSEEvents(response.toolCalls, interactionId, logger, overrides);
588
+ const interruption = createInterruptionSignal(fixture);
589
+ if (!await writeGeminiInteractionsSSEStream(res, events, {
590
+ latency,
591
+ streamingProfile: fixture.streamingProfile,
592
+ signal: interruption?.signal,
593
+ onChunkSent: interruption?.tick
594
+ })) {
595
+ if (!res.writableEnded) res.destroy();
596
+ journalEntry.response.interrupted = true;
597
+ journalEntry.response.interruptReason = interruption?.reason();
598
+ }
599
+ interruption?.cleanup();
600
+ }
601
+ return;
602
+ }
603
+ journal.add({
604
+ method: req.method ?? "POST",
605
+ path: urlPath,
606
+ headers: flattenHeaders(req.headers),
607
+ body: completionReq,
608
+ response: {
609
+ status: 500,
610
+ fixture
611
+ }
612
+ });
613
+ writeErrorResponse(res, 500, JSON.stringify(buildInteractionsErrorResponse("Fixture response did not match any known type", "INTERNAL")));
614
+ }
615
+
616
+ //#endregion
617
+ export { geminiInteractionsToCompletionRequest, handleGeminiInteractions };
618
+ //# sourceMappingURL=gemini-interactions.js.map