@copilotkit/aimock 1.21.0 → 1.22.1

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 (230) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +56 -0
  4. package/README.md +1 -0
  5. package/dist/a2a-mock.cjs +1 -1
  6. package/dist/a2a-mock.cjs.map +1 -1
  7. package/dist/a2a-mock.d.cts.map +1 -1
  8. package/dist/a2a-mock.d.ts.map +1 -1
  9. package/dist/a2a-mock.js +1 -1
  10. package/dist/a2a-mock.js.map +1 -1
  11. package/dist/agui-recorder.cjs +25 -12
  12. package/dist/agui-recorder.cjs.map +1 -1
  13. package/dist/agui-recorder.js +25 -12
  14. package/dist/agui-recorder.js.map +1 -1
  15. package/dist/agui-types.d.cts.map +1 -1
  16. package/dist/aimock-cli.cjs +0 -0
  17. package/dist/aimock-cli.js +0 -0
  18. package/dist/bedrock-converse.cjs +72 -26
  19. package/dist/bedrock-converse.cjs.map +1 -1
  20. package/dist/bedrock-converse.d.cts.map +1 -1
  21. package/dist/bedrock-converse.d.ts.map +1 -1
  22. package/dist/bedrock-converse.js +73 -27
  23. package/dist/bedrock-converse.js.map +1 -1
  24. package/dist/bedrock.cjs +69 -24
  25. package/dist/bedrock.cjs.map +1 -1
  26. package/dist/bedrock.d.cts.map +1 -1
  27. package/dist/bedrock.d.ts.map +1 -1
  28. package/dist/bedrock.js +70 -25
  29. package/dist/bedrock.js.map +1 -1
  30. package/dist/cli.cjs +2 -2
  31. package/dist/cli.cjs.map +1 -1
  32. package/dist/cli.js +2 -2
  33. package/dist/cli.js.map +1 -1
  34. package/dist/cohere.cjs +34 -11
  35. package/dist/cohere.cjs.map +1 -1
  36. package/dist/cohere.d.cts.map +1 -1
  37. package/dist/cohere.d.ts.map +1 -1
  38. package/dist/cohere.js +35 -12
  39. package/dist/cohere.js.map +1 -1
  40. package/dist/config-loader.d.ts.map +1 -1
  41. package/dist/constants.cjs +8 -0
  42. package/dist/constants.cjs.map +1 -0
  43. package/dist/constants.d.cts +8 -0
  44. package/dist/constants.d.cts.map +1 -0
  45. package/dist/constants.d.ts +8 -0
  46. package/dist/constants.d.ts.map +1 -0
  47. package/dist/constants.js +7 -0
  48. package/dist/constants.js.map +1 -0
  49. package/dist/elevenlabs-audio.cjs +46 -20
  50. package/dist/elevenlabs-audio.cjs.map +1 -1
  51. package/dist/elevenlabs-audio.d.cts.map +1 -1
  52. package/dist/elevenlabs-audio.d.ts.map +1 -1
  53. package/dist/elevenlabs-audio.js +47 -21
  54. package/dist/elevenlabs-audio.js.map +1 -1
  55. package/dist/embeddings.cjs +25 -21
  56. package/dist/embeddings.cjs.map +1 -1
  57. package/dist/embeddings.d.cts.map +1 -1
  58. package/dist/embeddings.d.ts.map +1 -1
  59. package/dist/embeddings.js +26 -22
  60. package/dist/embeddings.js.map +1 -1
  61. package/dist/fal-audio.cjs +138 -43
  62. package/dist/fal-audio.cjs.map +1 -1
  63. package/dist/fal-audio.d.cts.map +1 -1
  64. package/dist/fal-audio.d.ts.map +1 -1
  65. package/dist/fal-audio.js +139 -44
  66. package/dist/fal-audio.js.map +1 -1
  67. package/dist/fal.cjs +27 -8
  68. package/dist/fal.cjs.map +1 -1
  69. package/dist/fal.d.cts.map +1 -1
  70. package/dist/fal.d.ts.map +1 -1
  71. package/dist/fal.js +28 -9
  72. package/dist/fal.js.map +1 -1
  73. package/dist/fixture-loader.cjs +9 -1
  74. package/dist/fixture-loader.cjs.map +1 -1
  75. package/dist/fixture-loader.js +9 -1
  76. package/dist/fixture-loader.js.map +1 -1
  77. package/dist/gemini-interactions.cjs +34 -9
  78. package/dist/gemini-interactions.cjs.map +1 -1
  79. package/dist/gemini-interactions.d.cts.map +1 -1
  80. package/dist/gemini-interactions.d.ts.map +1 -1
  81. package/dist/gemini-interactions.js +34 -11
  82. package/dist/gemini-interactions.js.map +1 -1
  83. package/dist/gemini.cjs +50 -21
  84. package/dist/gemini.cjs.map +1 -1
  85. package/dist/gemini.d.cts.map +1 -1
  86. package/dist/gemini.d.ts.map +1 -1
  87. package/dist/gemini.js +51 -22
  88. package/dist/gemini.js.map +1 -1
  89. package/dist/helpers.cjs +82 -8
  90. package/dist/helpers.cjs.map +1 -1
  91. package/dist/helpers.d.cts +7 -0
  92. package/dist/helpers.d.cts.map +1 -1
  93. package/dist/helpers.d.ts +7 -0
  94. package/dist/helpers.d.ts.map +1 -1
  95. package/dist/helpers.js +80 -9
  96. package/dist/helpers.js.map +1 -1
  97. package/dist/images.cjs +31 -10
  98. package/dist/images.cjs.map +1 -1
  99. package/dist/images.d.cts.map +1 -1
  100. package/dist/images.d.ts.map +1 -1
  101. package/dist/images.js +32 -11
  102. package/dist/images.js.map +1 -1
  103. package/dist/index.cjs +2 -1
  104. package/dist/index.d.cts +2 -1
  105. package/dist/index.d.ts +2 -1
  106. package/dist/index.js +2 -1
  107. package/dist/journal.cjs +17 -7
  108. package/dist/journal.cjs.map +1 -1
  109. package/dist/journal.d.cts +2 -3
  110. package/dist/journal.d.cts.map +1 -1
  111. package/dist/journal.d.ts +2 -3
  112. package/dist/journal.d.ts.map +1 -1
  113. package/dist/journal.js +15 -4
  114. package/dist/journal.js.map +1 -1
  115. package/dist/mcp-mock.cjs +1 -1
  116. package/dist/mcp-mock.cjs.map +1 -1
  117. package/dist/mcp-mock.d.cts.map +1 -1
  118. package/dist/mcp-mock.d.ts.map +1 -1
  119. package/dist/mcp-mock.js +1 -1
  120. package/dist/mcp-mock.js.map +1 -1
  121. package/dist/messages.cjs +38 -14
  122. package/dist/messages.cjs.map +1 -1
  123. package/dist/messages.d.cts.map +1 -1
  124. package/dist/messages.d.ts.map +1 -1
  125. package/dist/messages.js +39 -15
  126. package/dist/messages.js.map +1 -1
  127. package/dist/moderation.cjs +3 -2
  128. package/dist/moderation.cjs.map +1 -1
  129. package/dist/moderation.js +3 -2
  130. package/dist/moderation.js.map +1 -1
  131. package/dist/ollama.cjs +69 -22
  132. package/dist/ollama.cjs.map +1 -1
  133. package/dist/ollama.d.cts.map +1 -1
  134. package/dist/ollama.d.ts.map +1 -1
  135. package/dist/ollama.js +70 -23
  136. package/dist/ollama.js.map +1 -1
  137. package/dist/recorder.cjs +89 -41
  138. package/dist/recorder.cjs.map +1 -1
  139. package/dist/recorder.d.cts +3 -2
  140. package/dist/recorder.d.cts.map +1 -1
  141. package/dist/recorder.d.ts +3 -2
  142. package/dist/recorder.d.ts.map +1 -1
  143. package/dist/recorder.js +89 -41
  144. package/dist/recorder.js.map +1 -1
  145. package/dist/rerank.cjs +3 -2
  146. package/dist/rerank.cjs.map +1 -1
  147. package/dist/rerank.js +3 -2
  148. package/dist/rerank.js.map +1 -1
  149. package/dist/responses.cjs +66 -54
  150. package/dist/responses.cjs.map +1 -1
  151. package/dist/responses.d.cts +1 -1
  152. package/dist/responses.d.cts.map +1 -1
  153. package/dist/responses.d.ts +1 -1
  154. package/dist/responses.d.ts.map +1 -1
  155. package/dist/responses.js +67 -55
  156. package/dist/responses.js.map +1 -1
  157. package/dist/search.cjs +3 -2
  158. package/dist/search.cjs.map +1 -1
  159. package/dist/search.js +3 -2
  160. package/dist/search.js.map +1 -1
  161. package/dist/server.cjs +117 -171
  162. package/dist/server.cjs.map +1 -1
  163. package/dist/server.d.cts.map +1 -1
  164. package/dist/server.d.ts.map +1 -1
  165. package/dist/server.js +95 -149
  166. package/dist/server.js.map +1 -1
  167. package/dist/speech.cjs +31 -10
  168. package/dist/speech.cjs.map +1 -1
  169. package/dist/speech.d.cts.map +1 -1
  170. package/dist/speech.d.ts.map +1 -1
  171. package/dist/speech.js +32 -11
  172. package/dist/speech.js.map +1 -1
  173. package/dist/stream-collapse.cjs +51 -21
  174. package/dist/stream-collapse.cjs.map +1 -1
  175. package/dist/stream-collapse.d.cts +1 -0
  176. package/dist/stream-collapse.d.cts.map +1 -1
  177. package/dist/stream-collapse.d.ts +1 -0
  178. package/dist/stream-collapse.d.ts.map +1 -1
  179. package/dist/stream-collapse.js +51 -21
  180. package/dist/stream-collapse.js.map +1 -1
  181. package/dist/transcription.cjs +59 -19
  182. package/dist/transcription.cjs.map +1 -1
  183. package/dist/transcription.d.cts.map +1 -1
  184. package/dist/transcription.d.ts.map +1 -1
  185. package/dist/transcription.js +60 -20
  186. package/dist/transcription.js.map +1 -1
  187. package/dist/types.d.cts +4 -0
  188. package/dist/types.d.cts.map +1 -1
  189. package/dist/types.d.ts +4 -0
  190. package/dist/types.d.ts.map +1 -1
  191. package/dist/vector-mock.cjs +10 -8
  192. package/dist/vector-mock.cjs.map +1 -1
  193. package/dist/vector-mock.d.cts.map +1 -1
  194. package/dist/vector-mock.d.ts.map +1 -1
  195. package/dist/vector-mock.js +10 -8
  196. package/dist/vector-mock.js.map +1 -1
  197. package/dist/vector-types.d.ts.map +1 -1
  198. package/dist/video.cjs +55 -16
  199. package/dist/video.cjs.map +1 -1
  200. package/dist/video.d.cts +8 -1
  201. package/dist/video.d.cts.map +1 -1
  202. package/dist/video.d.ts +8 -1
  203. package/dist/video.d.ts.map +1 -1
  204. package/dist/video.js +56 -17
  205. package/dist/video.js.map +1 -1
  206. package/dist/ws-gemini-live.cjs +40 -31
  207. package/dist/ws-gemini-live.cjs.map +1 -1
  208. package/dist/ws-gemini-live.d.cts +2 -0
  209. package/dist/ws-gemini-live.d.cts.map +1 -1
  210. package/dist/ws-gemini-live.d.ts +2 -0
  211. package/dist/ws-gemini-live.d.ts.map +1 -1
  212. package/dist/ws-gemini-live.js +40 -31
  213. package/dist/ws-gemini-live.js.map +1 -1
  214. package/dist/ws-realtime.cjs +257 -16
  215. package/dist/ws-realtime.cjs.map +1 -1
  216. package/dist/ws-realtime.d.cts +2 -0
  217. package/dist/ws-realtime.d.cts.map +1 -1
  218. package/dist/ws-realtime.d.ts +2 -0
  219. package/dist/ws-realtime.d.ts.map +1 -1
  220. package/dist/ws-realtime.js +257 -16
  221. package/dist/ws-realtime.js.map +1 -1
  222. package/dist/ws-responses.cjs +54 -16
  223. package/dist/ws-responses.cjs.map +1 -1
  224. package/dist/ws-responses.d.cts +2 -0
  225. package/dist/ws-responses.d.cts.map +1 -1
  226. package/dist/ws-responses.d.ts +2 -0
  227. package/dist/ws-responses.d.ts.map +1 -1
  228. package/dist/ws-responses.js +55 -17
  229. package/dist/ws-responses.js.map +1 -1
  230. package/package.json +2 -2
@@ -1,5 +1,6 @@
1
+ const require_constants = require('./constants.cjs');
1
2
  const require_helpers = require('./helpers.cjs');
2
- const require_journal = require('./journal.cjs');
3
+ require('./journal.cjs');
3
4
  const require_router = require('./router.cjs');
4
5
  const require_sse_writer = require('./sse-writer.cjs');
5
6
  const require_interruption = require('./interruption.cjs');
@@ -28,7 +29,9 @@ function handleWebSocketResponses(ws, fixtures, journal, defaults) {
28
29
  logger.error(`WebSocket responses error: ${msg}`);
29
30
  try {
30
31
  ws.send(JSON.stringify(buildErrorEvent(msg, "server_error")));
31
- } catch {}
32
+ } catch (sendErr) {
33
+ defaults.logger.debug(`Failed to send error to client: ${sendErr instanceof Error ? sendErr.message : "unknown"}`);
34
+ }
32
35
  }));
33
36
  });
34
37
  }
@@ -36,8 +39,9 @@ async function processMessage(raw, ws, fixtures, journal, defaults) {
36
39
  let parsed;
37
40
  try {
38
41
  parsed = JSON.parse(raw);
39
- } catch {
40
- ws.send(JSON.stringify(buildErrorEvent("Malformed JSON", "invalid_request_error", "invalid_json")));
42
+ } catch (parseErr) {
43
+ const detail = parseErr instanceof Error ? parseErr.message : "unknown";
44
+ ws.send(JSON.stringify(buildErrorEvent(`Malformed JSON: ${detail}`, "invalid_request_error", "invalid_json")));
41
45
  return;
42
46
  }
43
47
  if (!isResponseCreateMessage(parsed)) {
@@ -54,23 +58,36 @@ async function processMessage(raw, ws, fixtures, journal, defaults) {
54
58
  temperature: parsed.temperature,
55
59
  max_output_tokens: parsed.max_output_tokens
56
60
  });
57
- const testId = defaults.testId ?? require_journal.DEFAULT_TEST_ID;
61
+ completionReq._endpointType = "chat";
62
+ const testId = defaults.testId ?? require_constants.DEFAULT_TEST_ID;
58
63
  const fixture = require_router.matchFixture(fixtures, completionReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform);
59
64
  if (fixture) journal.incrementFixtureMatchCount(fixture, fixtures, testId);
60
65
  if (!fixture) {
61
- if (defaults.strict) {
66
+ if (require_helpers.resolveStrictMode(defaults.strict, defaults.upgradeHeaders)) {
62
67
  defaults.logger.warn(`STRICT: No fixture matched for WebSocket message`);
68
+ journal.add({
69
+ method: "WS",
70
+ path: "/v1/responses",
71
+ headers: require_helpers.flattenHeaders(defaults.upgradeHeaders ?? {}),
72
+ body: completionReq,
73
+ response: {
74
+ status: 503,
75
+ fixture: null,
76
+ ...require_helpers.strictOverrideField(defaults.strict, defaults.upgradeHeaders)
77
+ }
78
+ });
63
79
  ws.close(1008, "Strict mode: no fixture matched");
64
80
  return;
65
81
  }
66
82
  journal.add({
67
83
  method: "WS",
68
84
  path: "/v1/responses",
69
- headers: {},
85
+ headers: require_helpers.flattenHeaders(defaults.upgradeHeaders ?? {}),
70
86
  body: completionReq,
71
87
  response: {
72
88
  status: 404,
73
- fixture: null
89
+ fixture: null,
90
+ ...require_helpers.strictOverrideField(defaults.strict, defaults.upgradeHeaders)
74
91
  }
75
92
  });
76
93
  ws.send(JSON.stringify(buildErrorEvent("No fixture matched", "invalid_request_error", "no_fixture_match")));
@@ -84,7 +101,7 @@ async function processMessage(raw, ws, fixtures, journal, defaults) {
84
101
  journal.add({
85
102
  method: "WS",
86
103
  path: "/v1/responses",
87
- headers: {},
104
+ headers: require_helpers.flattenHeaders(defaults.upgradeHeaders ?? {}),
88
105
  body: completionReq,
89
106
  response: {
90
107
  status,
@@ -94,18 +111,39 @@ async function processMessage(raw, ws, fixtures, journal, defaults) {
94
111
  ws.send(JSON.stringify(buildErrorEvent(response.error.message, response.error.type, response.error.code)));
95
112
  return;
96
113
  }
114
+ if (require_helpers.isContentWithToolCallsResponse(response)) {
115
+ const journalEntry = journal.add({
116
+ method: "WS",
117
+ path: "/v1/responses",
118
+ headers: require_helpers.flattenHeaders(defaults.upgradeHeaders ?? {}),
119
+ body: completionReq,
120
+ response: {
121
+ status: 200,
122
+ fixture
123
+ }
124
+ });
125
+ const events = require_responses.buildContentWithToolCallsStreamEvents(response.content, response.toolCalls, completionReq.model, chunkSize, response.reasoning, response.webSearches, require_helpers.extractOverrides(response));
126
+ const interruption = require_interruption.createInterruptionSignal(fixture);
127
+ if (!await sendEvents(ws, events, latency, interruption?.signal, interruption?.tick)) {
128
+ ws.destroy();
129
+ journalEntry.response.interrupted = true;
130
+ journalEntry.response.interruptReason = interruption?.reason();
131
+ }
132
+ interruption?.cleanup();
133
+ return;
134
+ }
97
135
  if (require_helpers.isTextResponse(response)) {
98
136
  const journalEntry = journal.add({
99
137
  method: "WS",
100
138
  path: "/v1/responses",
101
- headers: {},
139
+ headers: require_helpers.flattenHeaders(defaults.upgradeHeaders ?? {}),
102
140
  body: completionReq,
103
141
  response: {
104
142
  status: 200,
105
143
  fixture
106
144
  }
107
145
  });
108
- const events = require_responses.buildTextStreamEvents(response.content, completionReq.model, chunkSize, response.reasoning, response.webSearches);
146
+ const events = require_responses.buildTextStreamEvents(response.content, completionReq.model, chunkSize, response.reasoning, response.webSearches, require_helpers.extractOverrides(response));
109
147
  const interruption = require_interruption.createInterruptionSignal(fixture);
110
148
  if (!await sendEvents(ws, events, latency, interruption?.signal, interruption?.tick)) {
111
149
  ws.destroy();
@@ -119,14 +157,14 @@ async function processMessage(raw, ws, fixtures, journal, defaults) {
119
157
  const journalEntry = journal.add({
120
158
  method: "WS",
121
159
  path: "/v1/responses",
122
- headers: {},
160
+ headers: require_helpers.flattenHeaders(defaults.upgradeHeaders ?? {}),
123
161
  body: completionReq,
124
162
  response: {
125
163
  status: 200,
126
164
  fixture
127
165
  }
128
166
  });
129
- const events = require_responses.buildToolCallStreamEvents(response.toolCalls, completionReq.model, chunkSize);
167
+ const events = require_responses.buildToolCallStreamEvents(response.toolCalls, completionReq.model, chunkSize, response.webSearches, require_helpers.extractOverrides(response));
130
168
  const interruption = require_interruption.createInterruptionSignal(fixture);
131
169
  if (!await sendEvents(ws, events, latency, interruption?.signal, interruption?.tick)) {
132
170
  ws.destroy();
@@ -139,7 +177,7 @@ async function processMessage(raw, ws, fixtures, journal, defaults) {
139
177
  journal.add({
140
178
  method: "WS",
141
179
  path: "/v1/responses",
142
- headers: {},
180
+ headers: require_helpers.flattenHeaders(defaults.upgradeHeaders ?? {}),
143
181
  body: completionReq,
144
182
  response: {
145
183
  status: 500,
@@ -150,10 +188,10 @@ async function processMessage(raw, ws, fixtures, journal, defaults) {
150
188
  }
151
189
  async function sendEvents(ws, events, latency, signal, onChunkSent) {
152
190
  for (const event of events) {
153
- if (ws.isClosed) return true;
191
+ if (ws.isClosed) return false;
154
192
  if (latency > 0) await require_sse_writer.delay(latency, signal);
155
193
  if (signal?.aborted) return false;
156
- if (ws.isClosed) return true;
194
+ if (ws.isClosed) return false;
157
195
  ws.send(JSON.stringify(event));
158
196
  onChunkSent?.();
159
197
  if (signal?.aborted) return false;
@@ -1 +1 @@
1
- {"version":3,"file":"ws-responses.cjs","names":["responsesToCompletionRequest","DEFAULT_TEST_ID","matchFixture","resolveResponse","isErrorResponse","isTextResponse","buildTextStreamEvents","createInterruptionSignal","isToolCallResponse","buildToolCallStreamEvents","delay"],"sources":["../src/ws-responses.ts"],"sourcesContent":["/**\n * WebSocket handler for OpenAI Responses API.\n *\n * Accepts `{ type: \"response.create\", model: \"...\", input: [...] }` messages over\n * WebSocket and sends back the same Responses API SSE events as the HTTP\n * handler, but as individual WebSocket text frames.\n */\n\nimport type { ChatCompletionRequest, Fixture } from \"./types.js\";\nimport { matchFixture } from \"./router.js\";\nimport {\n responsesToCompletionRequest,\n buildTextStreamEvents,\n buildToolCallStreamEvents,\n type ResponsesSSEEvent,\n} from \"./responses.js\";\nimport { isTextResponse, isToolCallResponse, isErrorResponse, resolveResponse } from \"./helpers.js\";\nimport { createInterruptionSignal } from \"./interruption.js\";\nimport { delay } from \"./sse-writer.js\";\nimport { DEFAULT_TEST_ID, type Journal } from \"./journal.js\";\nimport type { Logger } from \"./logger.js\";\nimport type { WebSocketConnection } from \"./ws-framing.js\";\n\ninterface ResponseCreateMessage {\n type: \"response.create\";\n model?: string;\n input?: unknown[];\n instructions?: string;\n tools?: unknown[];\n tool_choice?: string | object;\n stream?: boolean;\n temperature?: number;\n max_output_tokens?: number;\n [key: string]: unknown;\n}\n\nfunction isResponseCreateMessage(msg: unknown): msg is ResponseCreateMessage {\n return (\n typeof msg === \"object\" &&\n msg !== null &&\n (msg as ResponseCreateMessage).type === \"response.create\"\n );\n}\n\nfunction buildErrorEvent(\n message: string,\n type = \"invalid_request_error\",\n code?: string,\n): ResponsesSSEEvent {\n return {\n type: \"error\",\n error: { message, type, code },\n };\n}\n\nexport function handleWebSocketResponses(\n ws: WebSocketConnection,\n fixtures: Fixture[],\n journal: Journal,\n defaults: {\n latency: number;\n chunkSize: number;\n model: string;\n logger: Logger;\n strict?: boolean;\n requestTransform?: (req: ChatCompletionRequest) => ChatCompletionRequest;\n testId?: string;\n },\n): void {\n const { logger } = defaults;\n // Serialize message processing to prevent event interleaving\n let pending = Promise.resolve();\n ws.on(\"message\", (raw: string) => {\n pending = pending.then(() =>\n processMessage(raw, ws, fixtures, journal, defaults).catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n logger.error(`WebSocket responses error: ${msg}`);\n try {\n ws.send(JSON.stringify(buildErrorEvent(msg, \"server_error\")));\n } catch {\n // Connection already gone — original error already logged above\n }\n }),\n );\n });\n}\n\nasync function processMessage(\n raw: string,\n ws: WebSocketConnection,\n fixtures: Fixture[],\n journal: Journal,\n defaults: {\n latency: number;\n chunkSize: number;\n model: string;\n logger: Logger;\n strict?: boolean;\n requestTransform?: (req: ChatCompletionRequest) => ChatCompletionRequest;\n testId?: string;\n },\n): Promise<void> {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n ws.send(\n JSON.stringify(buildErrorEvent(\"Malformed JSON\", \"invalid_request_error\", \"invalid_json\")),\n );\n return;\n }\n\n if (!isResponseCreateMessage(parsed)) {\n ws.send(\n JSON.stringify(\n buildErrorEvent(\n 'Expected message type \"response.create\"',\n \"invalid_request_error\",\n \"invalid_message_type\",\n ),\n ),\n );\n return;\n }\n\n const responsesReq = {\n model: parsed.model ?? defaults.model,\n input: (parsed.input ?? []) as {\n role?: string;\n type?: string;\n content?: string | { type: string; text?: string }[];\n call_id?: string;\n name?: string;\n arguments?: string;\n output?: string;\n id?: string;\n }[],\n instructions: parsed.instructions,\n tools: parsed.tools as\n | {\n type: \"function\";\n name: string;\n description?: string;\n parameters?: object;\n strict?: boolean;\n }[]\n | undefined,\n tool_choice: parsed.tool_choice,\n stream: parsed.stream,\n temperature: parsed.temperature,\n max_output_tokens: parsed.max_output_tokens,\n };\n\n const completionReq = responsesToCompletionRequest(responsesReq);\n const testId = defaults.testId ?? DEFAULT_TEST_ID;\n const fixture = matchFixture(\n fixtures,\n completionReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n );\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n }\n\n if (!fixture) {\n if (defaults.strict) {\n defaults.logger.warn(`STRICT: No fixture matched for WebSocket message`);\n ws.close(1008, \"Strict mode: no fixture matched\");\n return;\n }\n journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: {},\n body: completionReq,\n response: { status: 404, fixture: null },\n });\n ws.send(\n JSON.stringify(\n buildErrorEvent(\"No fixture matched\", \"invalid_request_error\", \"no_fixture_match\"),\n ),\n );\n return;\n }\n\n const response = await resolveResponse(fixture, completionReq);\n const latency = fixture.latency ?? defaults.latency;\n const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);\n\n // Error response\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: {},\n body: completionReq,\n response: { status, fixture },\n });\n ws.send(\n JSON.stringify(\n buildErrorEvent(response.error.message, response.error.type, response.error.code),\n ),\n );\n return;\n }\n\n // Text response\n if (isTextResponse(response)) {\n const journalEntry = journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: {},\n body: completionReq,\n response: { status: 200, fixture },\n });\n\n const events = buildTextStreamEvents(\n response.content,\n completionReq.model,\n chunkSize,\n response.reasoning,\n response.webSearches,\n );\n const interruption = createInterruptionSignal(fixture);\n const completed = await sendEvents(\n ws,\n events,\n latency,\n interruption?.signal,\n interruption?.tick,\n );\n if (!completed) {\n ws.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n return;\n }\n\n // Tool call response\n if (isToolCallResponse(response)) {\n const journalEntry = journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: {},\n body: completionReq,\n response: { status: 200, fixture },\n });\n const events = buildToolCallStreamEvents(response.toolCalls, completionReq.model, chunkSize);\n const interruption = createInterruptionSignal(fixture);\n const completed = await sendEvents(\n ws,\n events,\n latency,\n interruption?.signal,\n interruption?.tick,\n );\n if (!completed) {\n ws.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n return;\n }\n\n // Unknown response type\n journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: {},\n body: completionReq,\n response: { status: 500, fixture },\n });\n ws.send(\n JSON.stringify(\n buildErrorEvent(\"Fixture response did not match any known type\", \"server_error\"),\n ),\n );\n}\n\nasync function sendEvents(\n ws: WebSocketConnection,\n events: ResponsesSSEEvent[],\n latency: number,\n signal?: AbortSignal,\n onChunkSent?: () => void,\n): Promise<boolean> {\n for (const event of events) {\n if (ws.isClosed) return true;\n if (latency > 0) await delay(latency, signal);\n if (signal?.aborted) return false;\n if (ws.isClosed) return true;\n ws.send(JSON.stringify(event));\n onChunkSent?.();\n if (signal?.aborted) return false;\n }\n return true;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ws-responses.cjs","names":["responsesToCompletionRequest","DEFAULT_TEST_ID","matchFixture","resolveStrictMode","flattenHeaders","strictOverrideField","resolveResponse","isErrorResponse","isContentWithToolCallsResponse","buildContentWithToolCallsStreamEvents","extractOverrides","createInterruptionSignal","isTextResponse","buildTextStreamEvents","isToolCallResponse","buildToolCallStreamEvents","delay"],"sources":["../src/ws-responses.ts"],"sourcesContent":["/**\n * WebSocket handler for OpenAI Responses API.\n *\n * Accepts `{ type: \"response.create\", model: \"...\", input: [...] }` messages over\n * WebSocket and sends back the same Responses API SSE events as the HTTP\n * handler, but as individual WebSocket text frames.\n */\n\nimport type { ChatCompletionRequest, Fixture } from \"./types.js\";\nimport { matchFixture } from \"./router.js\";\nimport {\n responsesToCompletionRequest,\n buildTextStreamEvents,\n buildToolCallStreamEvents,\n buildContentWithToolCallsStreamEvents,\n type ResponsesSSEEvent,\n} from \"./responses.js\";\nimport {\n isTextResponse,\n isToolCallResponse,\n isContentWithToolCallsResponse,\n isErrorResponse,\n extractOverrides,\n resolveResponse,\n resolveStrictMode,\n strictOverrideField,\n flattenHeaders,\n} from \"./helpers.js\";\nimport { createInterruptionSignal } from \"./interruption.js\";\nimport { delay } from \"./sse-writer.js\";\nimport { DEFAULT_TEST_ID, type Journal } from \"./journal.js\";\nimport type { Logger } from \"./logger.js\";\nimport type { WebSocketConnection } from \"./ws-framing.js\";\n\ninterface ResponseCreateMessage {\n type: \"response.create\";\n model?: string;\n input?: unknown[];\n instructions?: string;\n tools?: unknown[];\n tool_choice?: string | object;\n stream?: boolean;\n temperature?: number;\n max_output_tokens?: number;\n [key: string]: unknown;\n}\n\nfunction isResponseCreateMessage(msg: unknown): msg is ResponseCreateMessage {\n return (\n typeof msg === \"object\" &&\n msg !== null &&\n (msg as ResponseCreateMessage).type === \"response.create\"\n );\n}\n\nfunction buildErrorEvent(\n message: string,\n type = \"invalid_request_error\",\n code?: string,\n): ResponsesSSEEvent {\n return {\n type: \"error\",\n error: { message, type, code },\n };\n}\n\nexport function handleWebSocketResponses(\n ws: WebSocketConnection,\n fixtures: Fixture[],\n journal: Journal,\n defaults: {\n latency: number;\n chunkSize: number;\n model: string;\n logger: Logger;\n strict?: boolean;\n requestTransform?: (req: ChatCompletionRequest) => ChatCompletionRequest;\n testId?: string;\n upgradeHeaders?: import(\"node:http\").IncomingHttpHeaders;\n },\n): void {\n const { logger } = defaults;\n // Serialize message processing to prevent event interleaving\n let pending = Promise.resolve();\n ws.on(\"message\", (raw: string) => {\n pending = pending.then(() =>\n processMessage(raw, ws, fixtures, journal, defaults).catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n logger.error(`WebSocket responses error: ${msg}`);\n try {\n ws.send(JSON.stringify(buildErrorEvent(msg, \"server_error\")));\n } catch (sendErr) {\n defaults.logger.debug(\n `Failed to send error to client: ${sendErr instanceof Error ? sendErr.message : \"unknown\"}`,\n );\n }\n }),\n );\n });\n}\n\nasync function processMessage(\n raw: string,\n ws: WebSocketConnection,\n fixtures: Fixture[],\n journal: Journal,\n defaults: {\n latency: number;\n chunkSize: number;\n model: string;\n logger: Logger;\n strict?: boolean;\n requestTransform?: (req: ChatCompletionRequest) => ChatCompletionRequest;\n testId?: string;\n upgradeHeaders?: import(\"node:http\").IncomingHttpHeaders;\n },\n): Promise<void> {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (parseErr) {\n const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n ws.send(\n JSON.stringify(\n buildErrorEvent(`Malformed JSON: ${detail}`, \"invalid_request_error\", \"invalid_json\"),\n ),\n );\n return;\n }\n\n if (!isResponseCreateMessage(parsed)) {\n ws.send(\n JSON.stringify(\n buildErrorEvent(\n 'Expected message type \"response.create\"',\n \"invalid_request_error\",\n \"invalid_message_type\",\n ),\n ),\n );\n return;\n }\n\n const responsesReq = {\n model: parsed.model ?? defaults.model,\n input: (parsed.input ?? []) as {\n role?: string;\n type?: string;\n content?: string | { type: string; text?: string }[];\n call_id?: string;\n name?: string;\n arguments?: string;\n output?: string;\n id?: string;\n }[],\n instructions: parsed.instructions,\n tools: parsed.tools as\n | {\n type: \"function\";\n name: string;\n description?: string;\n parameters?: object;\n strict?: boolean;\n }[]\n | undefined,\n tool_choice: parsed.tool_choice,\n stream: parsed.stream,\n temperature: parsed.temperature,\n max_output_tokens: parsed.max_output_tokens,\n };\n\n const completionReq = responsesToCompletionRequest(responsesReq);\n completionReq._endpointType = \"chat\";\n const testId = defaults.testId ?? DEFAULT_TEST_ID;\n const fixture = matchFixture(\n fixtures,\n completionReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n );\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n }\n\n if (!fixture) {\n if (resolveStrictMode(defaults.strict, defaults.upgradeHeaders)) {\n defaults.logger.warn(`STRICT: No fixture matched for WebSocket message`);\n journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: {\n status: 503,\n fixture: null,\n ...strictOverrideField(defaults.strict, defaults.upgradeHeaders),\n },\n });\n ws.close(1008, \"Strict mode: no fixture matched\");\n return;\n }\n journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: {\n status: 404,\n fixture: null,\n ...strictOverrideField(defaults.strict, defaults.upgradeHeaders),\n },\n });\n ws.send(\n JSON.stringify(\n buildErrorEvent(\"No fixture matched\", \"invalid_request_error\", \"no_fixture_match\"),\n ),\n );\n return;\n }\n\n const response = await resolveResponse(fixture, completionReq);\n const latency = fixture.latency ?? defaults.latency;\n const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);\n\n // Error response\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status, fixture },\n });\n ws.send(\n JSON.stringify(\n buildErrorEvent(response.error.message, response.error.type, response.error.code),\n ),\n );\n return;\n }\n\n // Content + tool calls response (must be checked before isTextResponse / isToolCallResponse)\n if (isContentWithToolCallsResponse(response)) {\n const journalEntry = journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status: 200, fixture },\n });\n\n const events = buildContentWithToolCallsStreamEvents(\n response.content,\n response.toolCalls,\n completionReq.model,\n chunkSize,\n response.reasoning,\n response.webSearches,\n extractOverrides(response),\n );\n\n const interruption = createInterruptionSignal(fixture);\n const completed = await sendEvents(\n ws,\n events,\n latency,\n interruption?.signal,\n interruption?.tick,\n );\n if (!completed) {\n ws.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n return;\n }\n\n // Text response\n if (isTextResponse(response)) {\n const journalEntry = journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status: 200, fixture },\n });\n\n const events = buildTextStreamEvents(\n response.content,\n completionReq.model,\n chunkSize,\n response.reasoning,\n response.webSearches,\n extractOverrides(response),\n );\n const interruption = createInterruptionSignal(fixture);\n const completed = await sendEvents(\n ws,\n events,\n latency,\n interruption?.signal,\n interruption?.tick,\n );\n if (!completed) {\n ws.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n return;\n }\n\n // Tool call response\n if (isToolCallResponse(response)) {\n const journalEntry = journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status: 200, fixture },\n });\n const events = buildToolCallStreamEvents(\n response.toolCalls,\n completionReq.model,\n chunkSize,\n response.webSearches,\n extractOverrides(response),\n );\n const interruption = createInterruptionSignal(fixture);\n const completed = await sendEvents(\n ws,\n events,\n latency,\n interruption?.signal,\n interruption?.tick,\n );\n if (!completed) {\n ws.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n return;\n }\n\n // Unknown response type\n journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status: 500, fixture },\n });\n ws.send(\n JSON.stringify(\n buildErrorEvent(\"Fixture response did not match any known type\", \"server_error\"),\n ),\n );\n}\n\nasync function sendEvents(\n ws: WebSocketConnection,\n events: ResponsesSSEEvent[],\n latency: number,\n signal?: AbortSignal,\n onChunkSent?: () => void,\n): Promise<boolean> {\n for (const event of events) {\n if (ws.isClosed) return false;\n if (latency > 0) await delay(latency, signal);\n if (signal?.aborted) return false;\n if (ws.isClosed) return false;\n ws.send(JSON.stringify(event));\n onChunkSent?.();\n if (signal?.aborted) return false;\n }\n return true;\n}\n"],"mappings":"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"}
@@ -2,6 +2,7 @@ import { Journal } from "./journal.cjs";
2
2
  import { Logger } from "./logger.cjs";
3
3
  import { ChatCompletionRequest, Fixture } from "./types.cjs";
4
4
  import { WebSocketConnection } from "./ws-framing.cjs";
5
+ import * as node_http0 from "node:http";
5
6
 
6
7
  //#region src/ws-responses.d.ts
7
8
 
@@ -13,6 +14,7 @@ declare function handleWebSocketResponses(ws: WebSocketConnection, fixtures: Fix
13
14
  strict?: boolean;
14
15
  requestTransform?: (req: ChatCompletionRequest) => ChatCompletionRequest;
15
16
  testId?: string;
17
+ upgradeHeaders?: node_http0.IncomingHttpHeaders;
16
18
  }): void;
17
19
  //# sourceMappingURL=ws-responses.d.ts.map
18
20
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"ws-responses.d.cts","names":[],"sources":["../src/ws-responses.ts"],"sourcesContent":[],"mappings":";;;;;;;AA+DY,iBARI,wBAAA,CAQJ,EAAA,EAPN,mBAOM,EAAA,QAAA,EANA,OAMA,EAAA,EAAA,OAAA,EALD,OAKC,EAAA,QAAA,EAAA;SAEiB,EAAA,MAAA;WAA0B,EAAA,MAAA;EAAqB,KAAA,EAAA,MAAA;UAFhE;;2BAEiB,0BAA0B"}
1
+ {"version":3,"file":"ws-responses.d.cts","names":[],"sources":["../src/ws-responses.ts"],"sourcesContent":[],"mappings":";;;;;;;;AAqEW,iBAHK,wBAAA,CAGL,EAAA,EAFL,mBAEK,EAAA,QAAA,EADC,OACD,EAAA,EAAA,OAAA,EAAA,OAAA,EAAA,QAAA,EAAA;SAKC,EAAA,MAAA;WAEiB,EAAA,MAAA;OAA0B,EAAA,MAAA;QAAqB,EAFhE,MAI6B;EAAmB,MAAA,CAAA,EAAA,OAAA;2BAF/B,0BAA0B;;mBAAqB,UAAA,CAEnC"}
@@ -2,6 +2,7 @@ import { Journal } from "./journal.js";
2
2
  import { Logger } from "./logger.js";
3
3
  import { ChatCompletionRequest, Fixture } from "./types.js";
4
4
  import { WebSocketConnection } from "./ws-framing.js";
5
+ import * as node_http0 from "node:http";
5
6
 
6
7
  //#region src/ws-responses.d.ts
7
8
 
@@ -13,6 +14,7 @@ declare function handleWebSocketResponses(ws: WebSocketConnection, fixtures: Fix
13
14
  strict?: boolean;
14
15
  requestTransform?: (req: ChatCompletionRequest) => ChatCompletionRequest;
15
16
  testId?: string;
17
+ upgradeHeaders?: node_http0.IncomingHttpHeaders;
16
18
  }): void;
17
19
  //# sourceMappingURL=ws-responses.d.ts.map
18
20
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"ws-responses.d.ts","names":[],"sources":["../src/ws-responses.ts"],"sourcesContent":[],"mappings":";;;;;;;AA+DY,iBARI,wBAAA,CAQJ,EAAA,EAPN,mBAOM,EAAA,QAAA,EANA,OAMA,EAAA,EAAA,OAAA,EALD,OAKC,EAAA,QAAA,EAAA;SAEiB,EAAA,MAAA;WAA0B,EAAA,MAAA;EAAqB,KAAA,EAAA,MAAA;UAFhE;;2BAEiB,0BAA0B"}
1
+ {"version":3,"file":"ws-responses.d.ts","names":[],"sources":["../src/ws-responses.ts"],"sourcesContent":[],"mappings":";;;;;;;;AAqEW,iBAHK,wBAAA,CAGL,EAAA,EAFL,mBAEK,EAAA,QAAA,EADC,OACD,EAAA,EAAA,OAAA,EAAA,OAAA,EAAA,QAAA,EAAA;SAKC,EAAA,MAAA;WAEiB,EAAA,MAAA;OAA0B,EAAA,MAAA;QAAqB,EAFhE,MAI6B;EAAmB,MAAA,CAAA,EAAA,OAAA;2BAF/B,0BAA0B;;mBAAqB,UAAA,CAEnC"}
@@ -1,9 +1,10 @@
1
- import { isErrorResponse, isTextResponse, isToolCallResponse, resolveResponse } from "./helpers.js";
2
- import { DEFAULT_TEST_ID } from "./journal.js";
1
+ import { DEFAULT_TEST_ID } from "./constants.js";
2
+ import { extractOverrides, flattenHeaders, isContentWithToolCallsResponse, isErrorResponse, isTextResponse, isToolCallResponse, resolveResponse, resolveStrictMode, strictOverrideField } from "./helpers.js";
3
+ import "./journal.js";
3
4
  import { matchFixture } from "./router.js";
4
5
  import { delay } from "./sse-writer.js";
5
6
  import { createInterruptionSignal } from "./interruption.js";
6
- import { buildTextStreamEvents, buildToolCallStreamEvents, responsesToCompletionRequest } from "./responses.js";
7
+ import { buildContentWithToolCallsStreamEvents, buildTextStreamEvents, buildToolCallStreamEvents, responsesToCompletionRequest } from "./responses.js";
7
8
 
8
9
  //#region src/ws-responses.ts
9
10
  function isResponseCreateMessage(msg) {
@@ -28,7 +29,9 @@ function handleWebSocketResponses(ws, fixtures, journal, defaults) {
28
29
  logger.error(`WebSocket responses error: ${msg}`);
29
30
  try {
30
31
  ws.send(JSON.stringify(buildErrorEvent(msg, "server_error")));
31
- } catch {}
32
+ } catch (sendErr) {
33
+ defaults.logger.debug(`Failed to send error to client: ${sendErr instanceof Error ? sendErr.message : "unknown"}`);
34
+ }
32
35
  }));
33
36
  });
34
37
  }
@@ -36,8 +39,9 @@ async function processMessage(raw, ws, fixtures, journal, defaults) {
36
39
  let parsed;
37
40
  try {
38
41
  parsed = JSON.parse(raw);
39
- } catch {
40
- ws.send(JSON.stringify(buildErrorEvent("Malformed JSON", "invalid_request_error", "invalid_json")));
42
+ } catch (parseErr) {
43
+ const detail = parseErr instanceof Error ? parseErr.message : "unknown";
44
+ ws.send(JSON.stringify(buildErrorEvent(`Malformed JSON: ${detail}`, "invalid_request_error", "invalid_json")));
41
45
  return;
42
46
  }
43
47
  if (!isResponseCreateMessage(parsed)) {
@@ -54,23 +58,36 @@ async function processMessage(raw, ws, fixtures, journal, defaults) {
54
58
  temperature: parsed.temperature,
55
59
  max_output_tokens: parsed.max_output_tokens
56
60
  });
61
+ completionReq._endpointType = "chat";
57
62
  const testId = defaults.testId ?? DEFAULT_TEST_ID;
58
63
  const fixture = matchFixture(fixtures, completionReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform);
59
64
  if (fixture) journal.incrementFixtureMatchCount(fixture, fixtures, testId);
60
65
  if (!fixture) {
61
- if (defaults.strict) {
66
+ if (resolveStrictMode(defaults.strict, defaults.upgradeHeaders)) {
62
67
  defaults.logger.warn(`STRICT: No fixture matched for WebSocket message`);
68
+ journal.add({
69
+ method: "WS",
70
+ path: "/v1/responses",
71
+ headers: flattenHeaders(defaults.upgradeHeaders ?? {}),
72
+ body: completionReq,
73
+ response: {
74
+ status: 503,
75
+ fixture: null,
76
+ ...strictOverrideField(defaults.strict, defaults.upgradeHeaders)
77
+ }
78
+ });
63
79
  ws.close(1008, "Strict mode: no fixture matched");
64
80
  return;
65
81
  }
66
82
  journal.add({
67
83
  method: "WS",
68
84
  path: "/v1/responses",
69
- headers: {},
85
+ headers: flattenHeaders(defaults.upgradeHeaders ?? {}),
70
86
  body: completionReq,
71
87
  response: {
72
88
  status: 404,
73
- fixture: null
89
+ fixture: null,
90
+ ...strictOverrideField(defaults.strict, defaults.upgradeHeaders)
74
91
  }
75
92
  });
76
93
  ws.send(JSON.stringify(buildErrorEvent("No fixture matched", "invalid_request_error", "no_fixture_match")));
@@ -84,7 +101,7 @@ async function processMessage(raw, ws, fixtures, journal, defaults) {
84
101
  journal.add({
85
102
  method: "WS",
86
103
  path: "/v1/responses",
87
- headers: {},
104
+ headers: flattenHeaders(defaults.upgradeHeaders ?? {}),
88
105
  body: completionReq,
89
106
  response: {
90
107
  status,
@@ -94,18 +111,39 @@ async function processMessage(raw, ws, fixtures, journal, defaults) {
94
111
  ws.send(JSON.stringify(buildErrorEvent(response.error.message, response.error.type, response.error.code)));
95
112
  return;
96
113
  }
114
+ if (isContentWithToolCallsResponse(response)) {
115
+ const journalEntry = journal.add({
116
+ method: "WS",
117
+ path: "/v1/responses",
118
+ headers: flattenHeaders(defaults.upgradeHeaders ?? {}),
119
+ body: completionReq,
120
+ response: {
121
+ status: 200,
122
+ fixture
123
+ }
124
+ });
125
+ const events = buildContentWithToolCallsStreamEvents(response.content, response.toolCalls, completionReq.model, chunkSize, response.reasoning, response.webSearches, extractOverrides(response));
126
+ const interruption = createInterruptionSignal(fixture);
127
+ if (!await sendEvents(ws, events, latency, interruption?.signal, interruption?.tick)) {
128
+ ws.destroy();
129
+ journalEntry.response.interrupted = true;
130
+ journalEntry.response.interruptReason = interruption?.reason();
131
+ }
132
+ interruption?.cleanup();
133
+ return;
134
+ }
97
135
  if (isTextResponse(response)) {
98
136
  const journalEntry = journal.add({
99
137
  method: "WS",
100
138
  path: "/v1/responses",
101
- headers: {},
139
+ headers: flattenHeaders(defaults.upgradeHeaders ?? {}),
102
140
  body: completionReq,
103
141
  response: {
104
142
  status: 200,
105
143
  fixture
106
144
  }
107
145
  });
108
- const events = buildTextStreamEvents(response.content, completionReq.model, chunkSize, response.reasoning, response.webSearches);
146
+ const events = buildTextStreamEvents(response.content, completionReq.model, chunkSize, response.reasoning, response.webSearches, extractOverrides(response));
109
147
  const interruption = createInterruptionSignal(fixture);
110
148
  if (!await sendEvents(ws, events, latency, interruption?.signal, interruption?.tick)) {
111
149
  ws.destroy();
@@ -119,14 +157,14 @@ async function processMessage(raw, ws, fixtures, journal, defaults) {
119
157
  const journalEntry = journal.add({
120
158
  method: "WS",
121
159
  path: "/v1/responses",
122
- headers: {},
160
+ headers: flattenHeaders(defaults.upgradeHeaders ?? {}),
123
161
  body: completionReq,
124
162
  response: {
125
163
  status: 200,
126
164
  fixture
127
165
  }
128
166
  });
129
- const events = buildToolCallStreamEvents(response.toolCalls, completionReq.model, chunkSize);
167
+ const events = buildToolCallStreamEvents(response.toolCalls, completionReq.model, chunkSize, response.webSearches, extractOverrides(response));
130
168
  const interruption = createInterruptionSignal(fixture);
131
169
  if (!await sendEvents(ws, events, latency, interruption?.signal, interruption?.tick)) {
132
170
  ws.destroy();
@@ -139,7 +177,7 @@ async function processMessage(raw, ws, fixtures, journal, defaults) {
139
177
  journal.add({
140
178
  method: "WS",
141
179
  path: "/v1/responses",
142
- headers: {},
180
+ headers: flattenHeaders(defaults.upgradeHeaders ?? {}),
143
181
  body: completionReq,
144
182
  response: {
145
183
  status: 500,
@@ -150,10 +188,10 @@ async function processMessage(raw, ws, fixtures, journal, defaults) {
150
188
  }
151
189
  async function sendEvents(ws, events, latency, signal, onChunkSent) {
152
190
  for (const event of events) {
153
- if (ws.isClosed) return true;
191
+ if (ws.isClosed) return false;
154
192
  if (latency > 0) await delay(latency, signal);
155
193
  if (signal?.aborted) return false;
156
- if (ws.isClosed) return true;
194
+ if (ws.isClosed) return false;
157
195
  ws.send(JSON.stringify(event));
158
196
  onChunkSent?.();
159
197
  if (signal?.aborted) return false;
@@ -1 +1 @@
1
- {"version":3,"file":"ws-responses.js","names":[],"sources":["../src/ws-responses.ts"],"sourcesContent":["/**\n * WebSocket handler for OpenAI Responses API.\n *\n * Accepts `{ type: \"response.create\", model: \"...\", input: [...] }` messages over\n * WebSocket and sends back the same Responses API SSE events as the HTTP\n * handler, but as individual WebSocket text frames.\n */\n\nimport type { ChatCompletionRequest, Fixture } from \"./types.js\";\nimport { matchFixture } from \"./router.js\";\nimport {\n responsesToCompletionRequest,\n buildTextStreamEvents,\n buildToolCallStreamEvents,\n type ResponsesSSEEvent,\n} from \"./responses.js\";\nimport { isTextResponse, isToolCallResponse, isErrorResponse, resolveResponse } from \"./helpers.js\";\nimport { createInterruptionSignal } from \"./interruption.js\";\nimport { delay } from \"./sse-writer.js\";\nimport { DEFAULT_TEST_ID, type Journal } from \"./journal.js\";\nimport type { Logger } from \"./logger.js\";\nimport type { WebSocketConnection } from \"./ws-framing.js\";\n\ninterface ResponseCreateMessage {\n type: \"response.create\";\n model?: string;\n input?: unknown[];\n instructions?: string;\n tools?: unknown[];\n tool_choice?: string | object;\n stream?: boolean;\n temperature?: number;\n max_output_tokens?: number;\n [key: string]: unknown;\n}\n\nfunction isResponseCreateMessage(msg: unknown): msg is ResponseCreateMessage {\n return (\n typeof msg === \"object\" &&\n msg !== null &&\n (msg as ResponseCreateMessage).type === \"response.create\"\n );\n}\n\nfunction buildErrorEvent(\n message: string,\n type = \"invalid_request_error\",\n code?: string,\n): ResponsesSSEEvent {\n return {\n type: \"error\",\n error: { message, type, code },\n };\n}\n\nexport function handleWebSocketResponses(\n ws: WebSocketConnection,\n fixtures: Fixture[],\n journal: Journal,\n defaults: {\n latency: number;\n chunkSize: number;\n model: string;\n logger: Logger;\n strict?: boolean;\n requestTransform?: (req: ChatCompletionRequest) => ChatCompletionRequest;\n testId?: string;\n },\n): void {\n const { logger } = defaults;\n // Serialize message processing to prevent event interleaving\n let pending = Promise.resolve();\n ws.on(\"message\", (raw: string) => {\n pending = pending.then(() =>\n processMessage(raw, ws, fixtures, journal, defaults).catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n logger.error(`WebSocket responses error: ${msg}`);\n try {\n ws.send(JSON.stringify(buildErrorEvent(msg, \"server_error\")));\n } catch {\n // Connection already gone — original error already logged above\n }\n }),\n );\n });\n}\n\nasync function processMessage(\n raw: string,\n ws: WebSocketConnection,\n fixtures: Fixture[],\n journal: Journal,\n defaults: {\n latency: number;\n chunkSize: number;\n model: string;\n logger: Logger;\n strict?: boolean;\n requestTransform?: (req: ChatCompletionRequest) => ChatCompletionRequest;\n testId?: string;\n },\n): Promise<void> {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n ws.send(\n JSON.stringify(buildErrorEvent(\"Malformed JSON\", \"invalid_request_error\", \"invalid_json\")),\n );\n return;\n }\n\n if (!isResponseCreateMessage(parsed)) {\n ws.send(\n JSON.stringify(\n buildErrorEvent(\n 'Expected message type \"response.create\"',\n \"invalid_request_error\",\n \"invalid_message_type\",\n ),\n ),\n );\n return;\n }\n\n const responsesReq = {\n model: parsed.model ?? defaults.model,\n input: (parsed.input ?? []) as {\n role?: string;\n type?: string;\n content?: string | { type: string; text?: string }[];\n call_id?: string;\n name?: string;\n arguments?: string;\n output?: string;\n id?: string;\n }[],\n instructions: parsed.instructions,\n tools: parsed.tools as\n | {\n type: \"function\";\n name: string;\n description?: string;\n parameters?: object;\n strict?: boolean;\n }[]\n | undefined,\n tool_choice: parsed.tool_choice,\n stream: parsed.stream,\n temperature: parsed.temperature,\n max_output_tokens: parsed.max_output_tokens,\n };\n\n const completionReq = responsesToCompletionRequest(responsesReq);\n const testId = defaults.testId ?? DEFAULT_TEST_ID;\n const fixture = matchFixture(\n fixtures,\n completionReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n );\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n }\n\n if (!fixture) {\n if (defaults.strict) {\n defaults.logger.warn(`STRICT: No fixture matched for WebSocket message`);\n ws.close(1008, \"Strict mode: no fixture matched\");\n return;\n }\n journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: {},\n body: completionReq,\n response: { status: 404, fixture: null },\n });\n ws.send(\n JSON.stringify(\n buildErrorEvent(\"No fixture matched\", \"invalid_request_error\", \"no_fixture_match\"),\n ),\n );\n return;\n }\n\n const response = await resolveResponse(fixture, completionReq);\n const latency = fixture.latency ?? defaults.latency;\n const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);\n\n // Error response\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: {},\n body: completionReq,\n response: { status, fixture },\n });\n ws.send(\n JSON.stringify(\n buildErrorEvent(response.error.message, response.error.type, response.error.code),\n ),\n );\n return;\n }\n\n // Text response\n if (isTextResponse(response)) {\n const journalEntry = journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: {},\n body: completionReq,\n response: { status: 200, fixture },\n });\n\n const events = buildTextStreamEvents(\n response.content,\n completionReq.model,\n chunkSize,\n response.reasoning,\n response.webSearches,\n );\n const interruption = createInterruptionSignal(fixture);\n const completed = await sendEvents(\n ws,\n events,\n latency,\n interruption?.signal,\n interruption?.tick,\n );\n if (!completed) {\n ws.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n return;\n }\n\n // Tool call response\n if (isToolCallResponse(response)) {\n const journalEntry = journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: {},\n body: completionReq,\n response: { status: 200, fixture },\n });\n const events = buildToolCallStreamEvents(response.toolCalls, completionReq.model, chunkSize);\n const interruption = createInterruptionSignal(fixture);\n const completed = await sendEvents(\n ws,\n events,\n latency,\n interruption?.signal,\n interruption?.tick,\n );\n if (!completed) {\n ws.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n return;\n }\n\n // Unknown response type\n journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: {},\n body: completionReq,\n response: { status: 500, fixture },\n });\n ws.send(\n JSON.stringify(\n buildErrorEvent(\"Fixture response did not match any known type\", \"server_error\"),\n ),\n );\n}\n\nasync function sendEvents(\n ws: WebSocketConnection,\n events: ResponsesSSEEvent[],\n latency: number,\n signal?: AbortSignal,\n onChunkSent?: () => void,\n): Promise<boolean> {\n for (const event of events) {\n if (ws.isClosed) return true;\n if (latency > 0) await delay(latency, signal);\n if (signal?.aborted) return false;\n if (ws.isClosed) return true;\n ws.send(JSON.stringify(event));\n onChunkSent?.();\n if (signal?.aborted) return false;\n }\n return true;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ws-responses.js","names":[],"sources":["../src/ws-responses.ts"],"sourcesContent":["/**\n * WebSocket handler for OpenAI Responses API.\n *\n * Accepts `{ type: \"response.create\", model: \"...\", input: [...] }` messages over\n * WebSocket and sends back the same Responses API SSE events as the HTTP\n * handler, but as individual WebSocket text frames.\n */\n\nimport type { ChatCompletionRequest, Fixture } from \"./types.js\";\nimport { matchFixture } from \"./router.js\";\nimport {\n responsesToCompletionRequest,\n buildTextStreamEvents,\n buildToolCallStreamEvents,\n buildContentWithToolCallsStreamEvents,\n type ResponsesSSEEvent,\n} from \"./responses.js\";\nimport {\n isTextResponse,\n isToolCallResponse,\n isContentWithToolCallsResponse,\n isErrorResponse,\n extractOverrides,\n resolveResponse,\n resolveStrictMode,\n strictOverrideField,\n flattenHeaders,\n} from \"./helpers.js\";\nimport { createInterruptionSignal } from \"./interruption.js\";\nimport { delay } from \"./sse-writer.js\";\nimport { DEFAULT_TEST_ID, type Journal } from \"./journal.js\";\nimport type { Logger } from \"./logger.js\";\nimport type { WebSocketConnection } from \"./ws-framing.js\";\n\ninterface ResponseCreateMessage {\n type: \"response.create\";\n model?: string;\n input?: unknown[];\n instructions?: string;\n tools?: unknown[];\n tool_choice?: string | object;\n stream?: boolean;\n temperature?: number;\n max_output_tokens?: number;\n [key: string]: unknown;\n}\n\nfunction isResponseCreateMessage(msg: unknown): msg is ResponseCreateMessage {\n return (\n typeof msg === \"object\" &&\n msg !== null &&\n (msg as ResponseCreateMessage).type === \"response.create\"\n );\n}\n\nfunction buildErrorEvent(\n message: string,\n type = \"invalid_request_error\",\n code?: string,\n): ResponsesSSEEvent {\n return {\n type: \"error\",\n error: { message, type, code },\n };\n}\n\nexport function handleWebSocketResponses(\n ws: WebSocketConnection,\n fixtures: Fixture[],\n journal: Journal,\n defaults: {\n latency: number;\n chunkSize: number;\n model: string;\n logger: Logger;\n strict?: boolean;\n requestTransform?: (req: ChatCompletionRequest) => ChatCompletionRequest;\n testId?: string;\n upgradeHeaders?: import(\"node:http\").IncomingHttpHeaders;\n },\n): void {\n const { logger } = defaults;\n // Serialize message processing to prevent event interleaving\n let pending = Promise.resolve();\n ws.on(\"message\", (raw: string) => {\n pending = pending.then(() =>\n processMessage(raw, ws, fixtures, journal, defaults).catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n logger.error(`WebSocket responses error: ${msg}`);\n try {\n ws.send(JSON.stringify(buildErrorEvent(msg, \"server_error\")));\n } catch (sendErr) {\n defaults.logger.debug(\n `Failed to send error to client: ${sendErr instanceof Error ? sendErr.message : \"unknown\"}`,\n );\n }\n }),\n );\n });\n}\n\nasync function processMessage(\n raw: string,\n ws: WebSocketConnection,\n fixtures: Fixture[],\n journal: Journal,\n defaults: {\n latency: number;\n chunkSize: number;\n model: string;\n logger: Logger;\n strict?: boolean;\n requestTransform?: (req: ChatCompletionRequest) => ChatCompletionRequest;\n testId?: string;\n upgradeHeaders?: import(\"node:http\").IncomingHttpHeaders;\n },\n): Promise<void> {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (parseErr) {\n const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n ws.send(\n JSON.stringify(\n buildErrorEvent(`Malformed JSON: ${detail}`, \"invalid_request_error\", \"invalid_json\"),\n ),\n );\n return;\n }\n\n if (!isResponseCreateMessage(parsed)) {\n ws.send(\n JSON.stringify(\n buildErrorEvent(\n 'Expected message type \"response.create\"',\n \"invalid_request_error\",\n \"invalid_message_type\",\n ),\n ),\n );\n return;\n }\n\n const responsesReq = {\n model: parsed.model ?? defaults.model,\n input: (parsed.input ?? []) as {\n role?: string;\n type?: string;\n content?: string | { type: string; text?: string }[];\n call_id?: string;\n name?: string;\n arguments?: string;\n output?: string;\n id?: string;\n }[],\n instructions: parsed.instructions,\n tools: parsed.tools as\n | {\n type: \"function\";\n name: string;\n description?: string;\n parameters?: object;\n strict?: boolean;\n }[]\n | undefined,\n tool_choice: parsed.tool_choice,\n stream: parsed.stream,\n temperature: parsed.temperature,\n max_output_tokens: parsed.max_output_tokens,\n };\n\n const completionReq = responsesToCompletionRequest(responsesReq);\n completionReq._endpointType = \"chat\";\n const testId = defaults.testId ?? DEFAULT_TEST_ID;\n const fixture = matchFixture(\n fixtures,\n completionReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n );\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n }\n\n if (!fixture) {\n if (resolveStrictMode(defaults.strict, defaults.upgradeHeaders)) {\n defaults.logger.warn(`STRICT: No fixture matched for WebSocket message`);\n journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: {\n status: 503,\n fixture: null,\n ...strictOverrideField(defaults.strict, defaults.upgradeHeaders),\n },\n });\n ws.close(1008, \"Strict mode: no fixture matched\");\n return;\n }\n journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: {\n status: 404,\n fixture: null,\n ...strictOverrideField(defaults.strict, defaults.upgradeHeaders),\n },\n });\n ws.send(\n JSON.stringify(\n buildErrorEvent(\"No fixture matched\", \"invalid_request_error\", \"no_fixture_match\"),\n ),\n );\n return;\n }\n\n const response = await resolveResponse(fixture, completionReq);\n const latency = fixture.latency ?? defaults.latency;\n const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);\n\n // Error response\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status, fixture },\n });\n ws.send(\n JSON.stringify(\n buildErrorEvent(response.error.message, response.error.type, response.error.code),\n ),\n );\n return;\n }\n\n // Content + tool calls response (must be checked before isTextResponse / isToolCallResponse)\n if (isContentWithToolCallsResponse(response)) {\n const journalEntry = journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status: 200, fixture },\n });\n\n const events = buildContentWithToolCallsStreamEvents(\n response.content,\n response.toolCalls,\n completionReq.model,\n chunkSize,\n response.reasoning,\n response.webSearches,\n extractOverrides(response),\n );\n\n const interruption = createInterruptionSignal(fixture);\n const completed = await sendEvents(\n ws,\n events,\n latency,\n interruption?.signal,\n interruption?.tick,\n );\n if (!completed) {\n ws.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n return;\n }\n\n // Text response\n if (isTextResponse(response)) {\n const journalEntry = journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status: 200, fixture },\n });\n\n const events = buildTextStreamEvents(\n response.content,\n completionReq.model,\n chunkSize,\n response.reasoning,\n response.webSearches,\n extractOverrides(response),\n );\n const interruption = createInterruptionSignal(fixture);\n const completed = await sendEvents(\n ws,\n events,\n latency,\n interruption?.signal,\n interruption?.tick,\n );\n if (!completed) {\n ws.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n return;\n }\n\n // Tool call response\n if (isToolCallResponse(response)) {\n const journalEntry = journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status: 200, fixture },\n });\n const events = buildToolCallStreamEvents(\n response.toolCalls,\n completionReq.model,\n chunkSize,\n response.webSearches,\n extractOverrides(response),\n );\n const interruption = createInterruptionSignal(fixture);\n const completed = await sendEvents(\n ws,\n events,\n latency,\n interruption?.signal,\n interruption?.tick,\n );\n if (!completed) {\n ws.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n return;\n }\n\n // Unknown response type\n journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status: 500, fixture },\n });\n ws.send(\n JSON.stringify(\n buildErrorEvent(\"Fixture response did not match any known type\", \"server_error\"),\n ),\n );\n}\n\nasync function sendEvents(\n ws: WebSocketConnection,\n events: ResponsesSSEEvent[],\n latency: number,\n signal?: AbortSignal,\n onChunkSent?: () => void,\n): Promise<boolean> {\n for (const event of events) {\n if (ws.isClosed) return false;\n if (latency > 0) await delay(latency, signal);\n if (signal?.aborted) return false;\n if (ws.isClosed) return false;\n ws.send(JSON.stringify(event));\n onChunkSent?.();\n if (signal?.aborted) return false;\n }\n return true;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@copilotkit/aimock",
3
- "version": "1.21.0",
3
+ "version": "1.22.1",
4
4
  "description": "Mock infrastructure for AI application testing — LLM APIs, image generation, text-to-speech, transcription, audio generation, video generation, MCP tools, A2A agents, AG-UI event streams, vector databases, search, rerank, and moderation. One package, one port, zero dependencies.",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -170,7 +170,7 @@
170
170
  "lint": "eslint .",
171
171
  "format:check": "prettier --check .",
172
172
  "release": "pnpm build && pnpm test && pnpm lint && npm publish",
173
- "prepare": "husky"
173
+ "prepare": "husky || true"
174
174
  },
175
175
  "lint-staged": {
176
176
  "*.{ts,mts,js,mjs,cjs,json,html,css,md}": "prettier --write",