@copilotkit/aimock 1.37.4 → 1.39.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 (175) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +48 -0
  4. package/README.md +50 -11
  5. package/dist/agui-handler.cjs +20 -18
  6. package/dist/agui-handler.cjs.map +1 -1
  7. package/dist/agui-handler.d.cts +8 -1
  8. package/dist/agui-handler.d.cts.map +1 -1
  9. package/dist/agui-handler.d.ts +8 -1
  10. package/dist/agui-handler.d.ts.map +1 -1
  11. package/dist/agui-handler.js +20 -18
  12. package/dist/agui-handler.js.map +1 -1
  13. package/dist/agui-recorder.cjs +6 -0
  14. package/dist/agui-recorder.cjs.map +1 -1
  15. package/dist/agui-recorder.d.cts.map +1 -1
  16. package/dist/agui-recorder.d.ts.map +1 -1
  17. package/dist/agui-recorder.js +6 -0
  18. package/dist/agui-recorder.js.map +1 -1
  19. package/dist/agui-stub.d.cts +2 -2
  20. package/dist/agui-stub.d.ts +2 -2
  21. package/dist/agui-types.d.cts +19 -1
  22. package/dist/agui-types.d.cts.map +1 -1
  23. package/dist/agui-types.d.ts +19 -1
  24. package/dist/agui-types.d.ts.map +1 -1
  25. package/dist/aimock-cli.cjs +1 -1
  26. package/dist/aimock-cli.cjs.map +1 -1
  27. package/dist/aimock-cli.js +1 -1
  28. package/dist/aimock-cli.js.map +1 -1
  29. package/dist/api-key-auth.cjs +166 -0
  30. package/dist/api-key-auth.cjs.map +1 -0
  31. package/dist/api-key-auth.d.cts +17 -0
  32. package/dist/api-key-auth.d.cts.map +1 -0
  33. package/dist/api-key-auth.d.ts +17 -0
  34. package/dist/api-key-auth.d.ts.map +1 -0
  35. package/dist/api-key-auth.js +158 -0
  36. package/dist/api-key-auth.js.map +1 -0
  37. package/dist/cli.cjs +14 -11
  38. package/dist/cli.cjs.map +1 -1
  39. package/dist/cli.js +14 -11
  40. package/dist/cli.js.map +1 -1
  41. package/dist/config-loader.cjs +4 -2
  42. package/dist/config-loader.cjs.map +1 -1
  43. package/dist/config-loader.d.cts +2 -1
  44. package/dist/config-loader.d.cts.map +1 -1
  45. package/dist/config-loader.d.ts +2 -1
  46. package/dist/config-loader.d.ts.map +1 -1
  47. package/dist/config-loader.js +5 -3
  48. package/dist/config-loader.js.map +1 -1
  49. package/dist/fal-audio.cjs +2 -0
  50. package/dist/fal-audio.cjs.map +1 -1
  51. package/dist/fal-audio.d.cts.map +1 -1
  52. package/dist/fal-audio.d.ts.map +1 -1
  53. package/dist/fal-audio.js +2 -0
  54. package/dist/fal-audio.js.map +1 -1
  55. package/dist/fal.cjs +4 -1
  56. package/dist/fal.cjs.map +1 -1
  57. package/dist/fal.d.cts.map +1 -1
  58. package/dist/fal.d.ts.map +1 -1
  59. package/dist/fal.js +4 -1
  60. package/dist/fal.js.map +1 -1
  61. package/dist/fixture-loader.cjs +0 -0
  62. package/dist/fixture-loader.cjs.map +1 -1
  63. package/dist/fixture-loader.d.cts +3 -1
  64. package/dist/fixture-loader.d.cts.map +1 -1
  65. package/dist/fixture-loader.d.ts +3 -1
  66. package/dist/fixture-loader.d.ts.map +1 -1
  67. package/dist/fixture-loader.js +0 -0
  68. package/dist/fixture-loader.js.map +1 -1
  69. package/dist/grok-video.cjs +9 -2
  70. package/dist/grok-video.cjs.map +1 -1
  71. package/dist/grok-video.js +10 -3
  72. package/dist/grok-video.js.map +1 -1
  73. package/dist/helpers.cjs +16 -22
  74. package/dist/helpers.cjs.map +1 -1
  75. package/dist/helpers.d.cts.map +1 -1
  76. package/dist/helpers.d.ts.map +1 -1
  77. package/dist/helpers.js +16 -21
  78. package/dist/helpers.js.map +1 -1
  79. package/dist/index.d.cts +3 -3
  80. package/dist/index.d.ts +3 -3
  81. package/dist/llmock.cjs +37 -12
  82. package/dist/llmock.cjs.map +1 -1
  83. package/dist/llmock.d.cts +26 -2
  84. package/dist/llmock.d.cts.map +1 -1
  85. package/dist/llmock.d.ts +26 -2
  86. package/dist/llmock.d.ts.map +1 -1
  87. package/dist/llmock.js +39 -15
  88. package/dist/llmock.js.map +1 -1
  89. package/dist/openrouter-chat.cjs +333 -0
  90. package/dist/openrouter-chat.cjs.map +1 -0
  91. package/dist/openrouter-chat.js +325 -0
  92. package/dist/openrouter-chat.js.map +1 -0
  93. package/dist/openrouter-video.cjs +24 -7
  94. package/dist/openrouter-video.cjs.map +1 -1
  95. package/dist/openrouter-video.d.cts.map +1 -1
  96. package/dist/openrouter-video.d.ts.map +1 -1
  97. package/dist/openrouter-video.js +25 -8
  98. package/dist/openrouter-video.js.map +1 -1
  99. package/dist/provider-auth.cjs +20 -0
  100. package/dist/provider-auth.cjs.map +1 -1
  101. package/dist/provider-auth.js +20 -1
  102. package/dist/provider-auth.js.map +1 -1
  103. package/dist/recorder.cjs +50 -8
  104. package/dist/recorder.cjs.map +1 -1
  105. package/dist/recorder.d.cts.map +1 -1
  106. package/dist/recorder.d.ts.map +1 -1
  107. package/dist/recorder.js +50 -10
  108. package/dist/recorder.js.map +1 -1
  109. package/dist/responses.cjs +200 -89
  110. package/dist/responses.cjs.map +1 -1
  111. package/dist/responses.d.cts +18 -3
  112. package/dist/responses.d.cts.map +1 -1
  113. package/dist/responses.d.ts +18 -3
  114. package/dist/responses.d.ts.map +1 -1
  115. package/dist/responses.js +199 -90
  116. package/dist/responses.js.map +1 -1
  117. package/dist/server.cjs +190 -83
  118. package/dist/server.cjs.map +1 -1
  119. package/dist/server.d.cts +7 -2
  120. package/dist/server.d.cts.map +1 -1
  121. package/dist/server.d.ts +7 -2
  122. package/dist/server.d.ts.map +1 -1
  123. package/dist/server.js +190 -85
  124. package/dist/server.js.map +1 -1
  125. package/dist/sse-writer.cjs +3 -0
  126. package/dist/sse-writer.cjs.map +1 -1
  127. package/dist/sse-writer.d.cts +8 -0
  128. package/dist/sse-writer.d.cts.map +1 -1
  129. package/dist/sse-writer.d.ts +8 -0
  130. package/dist/sse-writer.d.ts.map +1 -1
  131. package/dist/sse-writer.js +3 -0
  132. package/dist/sse-writer.js.map +1 -1
  133. package/dist/stream-collapse.cjs +103 -0
  134. package/dist/stream-collapse.cjs.map +1 -1
  135. package/dist/stream-collapse.d.cts +7 -0
  136. package/dist/stream-collapse.d.cts.map +1 -1
  137. package/dist/stream-collapse.d.ts +7 -0
  138. package/dist/stream-collapse.d.ts.map +1 -1
  139. package/dist/stream-collapse.js +103 -0
  140. package/dist/stream-collapse.js.map +1 -1
  141. package/dist/transcription.cjs +49 -2
  142. package/dist/transcription.cjs.map +1 -1
  143. package/dist/transcription.d.cts.map +1 -1
  144. package/dist/transcription.d.ts.map +1 -1
  145. package/dist/transcription.js +50 -3
  146. package/dist/transcription.js.map +1 -1
  147. package/dist/types.d.cts +187 -5
  148. package/dist/types.d.cts.map +1 -1
  149. package/dist/types.d.ts +187 -5
  150. package/dist/types.d.ts.map +1 -1
  151. package/dist/vector-types.d.ts.map +1 -1
  152. package/dist/veo-video.cjs +9 -2
  153. package/dist/veo-video.cjs.map +1 -1
  154. package/dist/veo-video.js +10 -3
  155. package/dist/veo-video.js.map +1 -1
  156. package/dist/ws-gemini-live.cjs +41 -6
  157. package/dist/ws-gemini-live.cjs.map +1 -1
  158. package/dist/ws-gemini-live.js +41 -6
  159. package/dist/ws-gemini-live.js.map +1 -1
  160. package/dist/ws-realtime.cjs +306 -57
  161. package/dist/ws-realtime.cjs.map +1 -1
  162. package/dist/ws-realtime.d.cts +1 -0
  163. package/dist/ws-realtime.d.cts.map +1 -1
  164. package/dist/ws-realtime.d.ts +1 -0
  165. package/dist/ws-realtime.d.ts.map +1 -1
  166. package/dist/ws-realtime.js +307 -58
  167. package/dist/ws-realtime.js.map +1 -1
  168. package/dist/ws-responses.cjs +11 -6
  169. package/dist/ws-responses.cjs.map +1 -1
  170. package/dist/ws-responses.d.cts.map +1 -1
  171. package/dist/ws-responses.d.ts.map +1 -1
  172. package/dist/ws-responses.js +12 -7
  173. package/dist/ws-responses.js.map +1 -1
  174. package/package.json +3 -2
  175. package/skills/write-fixtures/SKILL.md +53 -19
package/dist/server.cjs CHANGED
@@ -1,4 +1,5 @@
1
1
  const require_runtime = require('./_virtual/_rolldown/runtime.cjs');
2
+ const require_api_key_auth = require('./api-key-auth.cjs');
2
3
  const require_helpers = require('./helpers.cjs');
3
4
  const require_logger = require('./logger.cjs');
4
5
  const require_journal = require('./journal.cjs');
@@ -6,6 +7,7 @@ const require_router = require('./router.cjs');
6
7
  const require_fixture_loader = require('./fixture-loader.cjs');
7
8
  const require_sse_writer = require('./sse-writer.cjs');
8
9
  const require_interruption = require('./interruption.cjs');
10
+ const require_openrouter_chat = require('./openrouter-chat.cjs');
9
11
  const require_chaos = require('./chaos.cjs');
10
12
  const require_recorder = require('./recorder.cjs');
11
13
  const require_responses = require('./responses.cjs');
@@ -143,23 +145,31 @@ function handleNotFound(res, message) {
143
145
  }
144
146
  const CONTROL_PREFIX = "/__aimock";
145
147
  /**
146
- * Perform a full fixtures reset: clear the fixtures array, journal, video/fal
147
- * generation state, and the interaction/event-id counters, then zero the
148
- * `aimock_fixtures_loaded` gauge. Shared by `/reset/fixtures` and the
149
- * deprecated `/reset` alias.
148
+ * Perform a full reset: clear the fixtures array, the journal (entries *and*
149
+ * per-test fixture match-counts, i.e. sequence position), the video and fal.ai
150
+ * job/queue state, and the Gemini interaction/event-id counters, then re-zero
151
+ * the `aimock_fixtures_loaded` gauge.
152
+ *
153
+ * `targets` is `null` when no server is running (an in-process `reset()` before
154
+ * `start()`). The process-global generation state is reset either way, since it
155
+ * is not owned by any one server instance.
156
+ *
157
+ * Shared by `POST /__aimock/reset` (canonical), its deprecated
158
+ * `POST /__aimock/reset/fixtures` alias, and `LLMock.reset()`.
150
159
  */
151
- function performFixturesReset(fixtures, journal, videoStates, openRouterVideoJobs, veoVideoJobs, grokVideoJobs, defaults) {
160
+ function performFullReset(fixtures, targets) {
152
161
  fixtures.length = 0;
153
- journal.clear();
154
- videoStates.clear();
155
- openRouterVideoJobs.clear();
156
- veoVideoJobs.clear();
157
- grokVideoJobs.clear();
158
162
  require_fal_audio.falJobs.clear();
159
163
  require_fal.falQueueStates.clear();
160
164
  require_gemini_interactions.resetInteractionCounter();
161
165
  require_gemini_interactions.resetEventIdCounter();
162
- if (defaults.registry) defaults.registry.setGauge("aimock_fixtures_loaded", {}, fixtures.length);
166
+ if (!targets) return;
167
+ targets.journal.clear();
168
+ targets.videoStates.clear();
169
+ targets.openRouterVideoJobs.clear();
170
+ targets.veoVideoJobs.clear();
171
+ targets.grokVideoJobs.clear();
172
+ if (targets.defaults.registry) targets.defaults.registry.setGauge("aimock_fixtures_loaded", {}, fixtures.length);
163
173
  }
164
174
  /**
165
175
  * Handle requests under `/__aimock/`. Returns `true` if the request was
@@ -228,8 +238,16 @@ async function handleControlAPI(req, res, pathname, fixtures, journal, videoStat
228
238
  res.end(JSON.stringify({ cleared: true }));
229
239
  return true;
230
240
  }
231
- if (subPath === "/reset/fixtures" && req.method === "POST") {
232
- performFixturesReset(fixtures, journal, videoStates, openRouterVideoJobs, veoVideoJobs, grokVideoJobs, defaults);
241
+ const resetTargets = () => ({
242
+ journal,
243
+ videoStates,
244
+ openRouterVideoJobs,
245
+ veoVideoJobs,
246
+ grokVideoJobs,
247
+ defaults
248
+ });
249
+ if (subPath === "/reset" && req.method === "POST") {
250
+ performFullReset(fixtures, resetTargets());
233
251
  res.writeHead(200, { "Content-Type": "application/json" });
234
252
  res.end(JSON.stringify({ reset: true }));
235
253
  return true;
@@ -240,10 +258,10 @@ async function handleControlAPI(req, res, pathname, fixtures, journal, videoStat
240
258
  res.end(JSON.stringify({ reset: true }));
241
259
  return true;
242
260
  }
243
- if (subPath === "/reset" && req.method === "POST") {
244
- performFixturesReset(fixtures, journal, videoStates, openRouterVideoJobs, veoVideoJobs, grokVideoJobs, defaults);
245
- const deprecation = "POST /__aimock/reset is deprecated; use POST /__aimock/reset/fixtures (full reset) or POST /__aimock/reset/journal (journal only)";
246
- defaults.logger.warn("POST /__aimock/reset is deprecated; use /__aimock/reset/fixtures or /__aimock/reset/journal");
261
+ if (subPath === "/reset/fixtures" && req.method === "POST") {
262
+ performFullReset(fixtures, resetTargets());
263
+ const deprecation = "POST /__aimock/reset/fixtures is deprecated; use POST /__aimock/reset (full reset) or POST /__aimock/reset/journal (journal only)";
264
+ defaults.logger.warn("POST /__aimock/reset/fixtures is deprecated; use /__aimock/reset or /__aimock/reset/journal");
247
265
  res.writeHead(200, {
248
266
  "Content-Type": "application/json",
249
267
  Deprecation: "true"
@@ -311,7 +329,7 @@ async function handleControlAPI(req, res, pathname, fixtures, journal, videoStat
311
329
  handleNotFound(res, `Unknown control endpoint: ${pathname}`);
312
330
  return true;
313
331
  }
314
- async function handleCompletions(req, res, fixtures, journal, defaults, modelFallback, providerKey) {
332
+ async function handleCompletions(req, res, fixtures, journal, defaults, modelFallback, providerKey, openRouter = false) {
315
333
  setCorsHeaders(res);
316
334
  let raw;
317
335
  try {
@@ -328,7 +346,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
328
346
  fixture: null
329
347
  }
330
348
  });
331
- require_sse_writer.writeErrorResponse(res, 500, JSON.stringify({ error: {
349
+ require_sse_writer.writeErrorResponse(res, 500, openRouter ? require_openrouter_chat.serializeOpenRouterError(500, `Request body read failed: ${msg}`) : JSON.stringify({ error: {
332
350
  message: `Request body read failed: ${msg}`,
333
351
  type: "server_error"
334
352
  } }));
@@ -350,7 +368,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
350
368
  fixture: null
351
369
  }
352
370
  });
353
- require_sse_writer.writeErrorResponse(res, 400, JSON.stringify({ error: {
371
+ require_sse_writer.writeErrorResponse(res, 400, openRouter ? require_openrouter_chat.serializeOpenRouterError(400, `Malformed JSON: ${detail}`) : JSON.stringify({ error: {
354
372
  message: `Malformed JSON: ${detail}`,
355
373
  type: "invalid_request_error",
356
374
  param: null,
@@ -369,7 +387,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
369
387
  fixture: null
370
388
  }
371
389
  });
372
- require_sse_writer.writeErrorResponse(res, 400, JSON.stringify({ error: {
390
+ require_sse_writer.writeErrorResponse(res, 400, openRouter ? require_openrouter_chat.serializeOpenRouterError(400, "Missing required parameter: 'messages'") : JSON.stringify({ error: {
373
391
  message: "Missing required parameter: 'messages'",
374
392
  type: "invalid_request_error",
375
393
  param: null,
@@ -383,9 +401,66 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
383
401
  body._endpointType = "chat";
384
402
  body._context = require_helpers.getContext(req);
385
403
  const testId = require_helpers.getTestId(req);
386
- const { fixture, skippedBySequenceOrTurn } = require_router.matchFixtureDiagnostic(fixtures, body, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform, require_router.recordMatchOptions(!!(defaults.record && providerKey), defaults.logger));
404
+ const matchOptions = require_router.recordMatchOptions(!!(defaults.record && providerKey), defaults.logger);
405
+ let fixture = null;
406
+ let skippedBySequenceOrTurn = 0;
407
+ let preResolvedResponse = null;
408
+ let responseModel = body.model;
409
+ let fixtureCountIncremented = false;
410
+ if (openRouter && Array.isArray(body.models) && body.models.length > 0) {
411
+ const candidates = require_openrouter_chat.buildOpenRouterCandidates(body);
412
+ let matchCounts = journal.getFixtureMatchCountsForTest(testId);
413
+ const allowFallbacks = body.provider?.allow_fallbacks !== false;
414
+ let lastErrorFixture = null;
415
+ let lastErrorResponse = null;
416
+ const resolveCandidate = async (candidate) => {
417
+ const probe = {
418
+ ...body,
419
+ model: candidate
420
+ };
421
+ const attempt = require_router.matchFixtureDiagnostic(fixtures, probe, matchCounts, defaults.requestTransform, matchOptions);
422
+ skippedBySequenceOrTurn = Math.max(skippedBySequenceOrTurn, attempt.skippedBySequenceOrTurn);
423
+ if (!attempt.fixture) return null;
424
+ const response = await require_helpers.resolveResponse(attempt.fixture, probe);
425
+ return {
426
+ fixture: attempt.fixture,
427
+ response,
428
+ isError: require_helpers.isErrorResponse(response)
429
+ };
430
+ };
431
+ for (const candidate of candidates) {
432
+ const outcome = await resolveCandidate(candidate);
433
+ if (!outcome) {
434
+ if (!allowFallbacks) break;
435
+ continue;
436
+ }
437
+ journal.incrementFixtureMatchCount(outcome.fixture, fixtures, testId);
438
+ fixtureCountIncremented = true;
439
+ matchCounts = journal.getFixtureMatchCountsForTest(testId);
440
+ responseModel = candidate;
441
+ if (outcome.isError) {
442
+ const errorFallsThrough = outcome.response.fallthrough !== false;
443
+ if (allowFallbacks && errorFallsThrough) {
444
+ lastErrorFixture = outcome.fixture;
445
+ lastErrorResponse = outcome.response;
446
+ continue;
447
+ }
448
+ }
449
+ fixture = outcome.fixture;
450
+ preResolvedResponse = outcome.response;
451
+ break;
452
+ }
453
+ if (!fixture && lastErrorFixture) {
454
+ fixture = lastErrorFixture;
455
+ preResolvedResponse = lastErrorResponse;
456
+ }
457
+ } else {
458
+ const single = require_router.matchFixtureDiagnostic(fixtures, body, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform, matchOptions);
459
+ fixture = single.fixture;
460
+ skippedBySequenceOrTurn = single.skippedBySequenceOrTurn;
461
+ }
387
462
  if (fixture) {
388
- journal.incrementFixtureMatchCount(fixture, fixtures, testId);
463
+ if (!fixtureCountIncremented) journal.incrementFixtureMatchCount(fixture, fixtures, testId);
389
464
  defaults.logger.debug(`Fixture matched: ${JSON.stringify(fixture.match).slice(0, 120)}`);
390
465
  } else {
391
466
  const lastUserMsg = body.messages.filter((m) => m.role === "user").pop();
@@ -423,7 +498,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
423
498
  ...require_helpers.strictOverrideField(defaults.strict, req.headers)
424
499
  }
425
500
  });
426
- require_sse_writer.writeErrorResponse(res, strictStatus, JSON.stringify({ error: {
501
+ require_sse_writer.writeErrorResponse(res, strictStatus, openRouter ? require_openrouter_chat.serializeOpenRouterError(strictStatus, strictMessage) : JSON.stringify({ error: {
427
502
  message: strictMessage,
428
503
  type: "invalid_request_error",
429
504
  param: null,
@@ -474,7 +549,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
474
549
  ...require_helpers.strictOverrideField(defaults.strict, req.headers)
475
550
  }
476
551
  });
477
- require_sse_writer.writeErrorResponse(res, 404, JSON.stringify({ error: {
552
+ require_sse_writer.writeErrorResponse(res, 404, openRouter ? require_openrouter_chat.serializeOpenRouterError(404, "No fixture matched") : JSON.stringify({ error: {
478
553
  message: "No fixture matched",
479
554
  type: "invalid_request_error",
480
555
  param: null,
@@ -482,10 +557,23 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
482
557
  } }));
483
558
  return;
484
559
  }
485
- const response = await require_helpers.resolveResponse(fixture, body);
560
+ const response = preResolvedResponse ?? await require_helpers.resolveResponse(fixture, body);
486
561
  const latency = fixture.latency ?? defaults.latency;
487
562
  const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);
488
- const includeUsage = body.stream === true && body.stream_options?.include_usage === true;
563
+ const emitStreamingUsage = body.stream === true && body.stream_options?.include_usage === true || openRouter;
564
+ const streamingPromptText = body.messages.map((m) => typeof m.content === "string" ? m.content : Array.isArray(m.content) ? m.content.map((p) => p.text ?? "").join("") : "").join("");
565
+ const resolveStreamingUsageTokens = (overrides, completionText) => require_helpers.resolveUsage(overrides, streamingPromptText, completionText);
566
+ const shapeORCompletion = (completion, overrides) => {
567
+ if (!openRouter) return completion;
568
+ return require_openrouter_chat.shapeOpenRouterCompletion(completion, require_openrouter_chat.resolveOpenRouterShaping(overrides, overrides?.model ?? responseModel), overrides?.id !== void 0);
569
+ };
570
+ const shapeORChunks = (chunks, usageChunk, overrides) => {
571
+ if (!openRouter) return;
572
+ const shaping = require_openrouter_chat.resolveOpenRouterShaping(overrides, overrides?.model ?? responseModel);
573
+ require_openrouter_chat.shapeOpenRouterChunks(chunks, shaping, overrides?.id !== void 0);
574
+ if (usageChunk) require_openrouter_chat.shapeOpenRouterChunks([usageChunk], shaping, overrides?.id !== void 0);
575
+ };
576
+ const openRouterProcessing = !!(openRouter && fixture.openRouterProcessing);
489
577
  if (require_helpers.isErrorResponse(response)) {
490
578
  const status = response.status ?? 500;
491
579
  journal.add({
@@ -498,7 +586,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
498
586
  fixture
499
587
  }
500
588
  });
501
- require_sse_writer.writeErrorResponse(res, status, require_helpers.serializeErrorResponse(response), { retryAfter: response.retryAfter });
589
+ require_sse_writer.writeErrorResponse(res, status, openRouter ? require_openrouter_chat.serializeOpenRouterError(status, response.error.message, response.error.metadata) : require_helpers.serializeErrorResponse(response), { retryAfter: response.retryAfter });
502
590
  return;
503
591
  }
504
592
  if (require_helpers.isAudioResponse(response)) {
@@ -512,7 +600,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
512
600
  fixture
513
601
  }
514
602
  });
515
- require_sse_writer.writeErrorResponse(res, 422, JSON.stringify({ error: {
603
+ require_sse_writer.writeErrorResponse(res, 422, openRouter ? require_openrouter_chat.serializeOpenRouterError(422, "Audio responses are not supported on the chat completions endpoint. Use Gemini generateContent or a dedicated audio endpoint.") : JSON.stringify({ error: {
516
604
  message: "Audio responses are not supported on the chat completions endpoint. Use Gemini generateContent or a dedicated audio endpoint.",
517
605
  type: "invalid_request_error"
518
606
  } }));
@@ -522,7 +610,9 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
522
610
  if (response.webSearches?.length) defaults.logger.warn("webSearches in fixture response are not supported for Chat Completions API — ignoring");
523
611
  const overrides = require_helpers.extractOverrides(response);
524
612
  const effectiveStrict = require_helpers.resolveStrictMode(defaults.strict, req.headers);
525
- const effReasoning = require_helpers.resolveReasoningForModel(response.reasoning, body.model, effectiveStrict, defaults.logger);
613
+ const effReasoning = require_helpers.resolveReasoningForModel(response.reasoning, responseModel, effectiveStrict, defaults.logger);
614
+ const streaming = body.stream === true;
615
+ const streamingBlocks = streaming && response.blocks && response.blocks.length > 0 ? require_helpers.resolveFixtureBlocks(response.blocks) : void 0;
526
616
  const journalEntry = journal.add({
527
617
  method: req.method ?? "POST",
528
618
  path: req.url ?? COMPLETIONS_PATH,
@@ -533,22 +623,15 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
533
623
  fixture
534
624
  }
535
625
  });
536
- if (body.stream !== true) {
537
- const completion = require_helpers.buildContentWithToolCallsCompletion(response.content ?? "", response.toolCalls ?? [], body.model, effReasoning, overrides, body.messages);
626
+ if (!streaming) {
627
+ const completion = require_helpers.buildContentWithToolCallsCompletion(response.content ?? "", response.toolCalls ?? [], responseModel, effReasoning, overrides, body.messages);
538
628
  res.writeHead(200, { "Content-Type": "application/json" });
539
- res.end(JSON.stringify(completion));
629
+ res.end(JSON.stringify(shapeORCompletion(completion, overrides)));
540
630
  } else {
541
- const chunks = require_helpers.buildContentWithToolCallsChunks(response.content ?? "", response.toolCalls ?? [], body.model, chunkSize, effReasoning, overrides, response.blocks);
542
- const completionText = (response.content ?? "") + (response.toolCalls ?? []).map((tc) => tc.name + tc.arguments).join("");
543
- const usageChunk = includeUsage ? require_helpers.buildUsageChunk(chunks[0]?.id ?? "chatcmpl-unknown", overrides?.model ?? body.model, chunks[0]?.created ?? Math.floor(Date.now() / 1e3), overrides?.usage ? {
544
- prompt_tokens: overrides.usage.prompt_tokens ?? 0,
545
- completion_tokens: overrides.usage.completion_tokens ?? 0,
546
- total_tokens: overrides.usage.total_tokens ?? (overrides.usage.prompt_tokens ?? 0) + (overrides.usage.completion_tokens ?? 0)
547
- } : {
548
- prompt_tokens: require_helpers.estimatePromptTokens(body.messages),
549
- completion_tokens: require_helpers.estimateTokens(completionText),
550
- total_tokens: require_helpers.estimatePromptTokens(body.messages) + require_helpers.estimateTokens(completionText)
551
- }, overrides?.systemFingerprint) : void 0;
631
+ const chunks = require_helpers.buildContentWithToolCallsChunks(response.content ?? "", response.toolCalls ?? [], responseModel, chunkSize, effReasoning, overrides, streamingBlocks);
632
+ const completionText = streamingBlocks ? streamingBlocks.map((b) => b.type === "text" ? b.text : b.name + b.arguments).join("") : (response.content ?? "") + (response.toolCalls ?? []).map((tc) => tc.name + tc.arguments).join("");
633
+ const usageChunk = emitStreamingUsage ? require_helpers.buildUsageChunk(chunks[0]?.id ?? "chatcmpl-unknown", overrides?.model ?? responseModel, chunks[0]?.created ?? Math.floor(Date.now() / 1e3), resolveStreamingUsageTokens(overrides, completionText), overrides?.systemFingerprint) : void 0;
634
+ shapeORChunks(chunks, usageChunk, overrides);
552
635
  const interruption = require_interruption.createInterruptionSignal(fixture);
553
636
  if (!await require_sse_writer.writeSSEStream(res, chunks, {
554
637
  latency,
@@ -557,7 +640,8 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
557
640
  onChunkSent: interruption?.tick,
558
641
  usageChunk,
559
642
  recordedTimings: fixture.recordedTimings,
560
- replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed
643
+ replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed,
644
+ openRouterProcessing
561
645
  })) {
562
646
  if (!res.writableEnded) res.destroy();
563
647
  journalEntry.response.interrupted = true;
@@ -571,7 +655,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
571
655
  if (response.webSearches?.length) defaults.logger.warn("webSearches in fixture response are not supported for Chat Completions API — ignoring");
572
656
  const overrides = require_helpers.extractOverrides(response);
573
657
  const effectiveStrict = require_helpers.resolveStrictMode(defaults.strict, req.headers);
574
- const effReasoning = require_helpers.resolveReasoningForModel(response.reasoning, body.model, effectiveStrict, defaults.logger);
658
+ const effReasoning = require_helpers.resolveReasoningForModel(response.reasoning, responseModel, effectiveStrict, defaults.logger);
575
659
  const journalEntry = journal.add({
576
660
  method: req.method ?? "POST",
577
661
  path: req.url ?? COMPLETIONS_PATH,
@@ -583,20 +667,13 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
583
667
  }
584
668
  });
585
669
  if (body.stream !== true) {
586
- const completion = require_helpers.buildTextCompletion(response.content, body.model, effReasoning, overrides, body.messages);
670
+ const completion = require_helpers.buildTextCompletion(response.content, responseModel, effReasoning, overrides, body.messages);
587
671
  res.writeHead(200, { "Content-Type": "application/json" });
588
- res.end(JSON.stringify(completion));
672
+ res.end(JSON.stringify(shapeORCompletion(completion, overrides)));
589
673
  } else {
590
- const chunks = require_helpers.buildTextChunks(response.content, body.model, chunkSize, effReasoning, overrides);
591
- const usageChunk = includeUsage ? require_helpers.buildUsageChunk(chunks[0]?.id ?? "chatcmpl-unknown", overrides?.model ?? body.model, chunks[0]?.created ?? Math.floor(Date.now() / 1e3), overrides?.usage ? {
592
- prompt_tokens: overrides.usage.prompt_tokens ?? 0,
593
- completion_tokens: overrides.usage.completion_tokens ?? 0,
594
- total_tokens: overrides.usage.total_tokens ?? (overrides.usage.prompt_tokens ?? 0) + (overrides.usage.completion_tokens ?? 0)
595
- } : {
596
- prompt_tokens: require_helpers.estimatePromptTokens(body.messages),
597
- completion_tokens: require_helpers.estimateTokens(response.content),
598
- total_tokens: require_helpers.estimatePromptTokens(body.messages) + require_helpers.estimateTokens(response.content)
599
- }, overrides?.systemFingerprint) : void 0;
674
+ const chunks = require_helpers.buildTextChunks(response.content, responseModel, chunkSize, effReasoning, overrides);
675
+ const usageChunk = emitStreamingUsage ? require_helpers.buildUsageChunk(chunks[0]?.id ?? "chatcmpl-unknown", overrides?.model ?? responseModel, chunks[0]?.created ?? Math.floor(Date.now() / 1e3), resolveStreamingUsageTokens(overrides, response.content), overrides?.systemFingerprint) : void 0;
676
+ shapeORChunks(chunks, usageChunk, overrides);
600
677
  const interruption = require_interruption.createInterruptionSignal(fixture);
601
678
  if (!await require_sse_writer.writeSSEStream(res, chunks, {
602
679
  latency,
@@ -605,7 +682,8 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
605
682
  onChunkSent: interruption?.tick,
606
683
  usageChunk,
607
684
  recordedTimings: fixture.recordedTimings,
608
- replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed
685
+ replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed,
686
+ openRouterProcessing
609
687
  })) {
610
688
  if (!res.writableEnded) res.destroy();
611
689
  journalEntry.response.interrupted = true;
@@ -619,7 +697,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
619
697
  if (response.webSearches?.length) defaults.logger.warn("webSearches in fixture response are not supported for Chat Completions API — ignoring");
620
698
  const overrides = require_helpers.extractOverrides(response);
621
699
  const effectiveStrict = require_helpers.resolveStrictMode(defaults.strict, req.headers);
622
- const effReasoning = require_helpers.resolveReasoningForModel(response.reasoning, body.model, effectiveStrict, defaults.logger);
700
+ const effReasoning = require_helpers.resolveReasoningForModel(response.reasoning, responseModel, effectiveStrict, defaults.logger);
623
701
  const journalEntry = journal.add({
624
702
  method: req.method ?? "POST",
625
703
  path: req.url ?? COMPLETIONS_PATH,
@@ -631,21 +709,14 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
631
709
  }
632
710
  });
633
711
  if (body.stream !== true) {
634
- const completion = require_helpers.buildToolCallCompletion(response.toolCalls, body.model, effReasoning, overrides, body.messages);
712
+ const completion = require_helpers.buildToolCallCompletion(response.toolCalls, responseModel, effReasoning, overrides, body.messages);
635
713
  res.writeHead(200, { "Content-Type": "application/json" });
636
- res.end(JSON.stringify(completion));
714
+ res.end(JSON.stringify(shapeORCompletion(completion, overrides)));
637
715
  } else {
638
- const chunks = require_helpers.buildToolCallChunks(response.toolCalls, body.model, chunkSize, effReasoning, overrides);
716
+ const chunks = require_helpers.buildToolCallChunks(response.toolCalls, responseModel, chunkSize, effReasoning, overrides);
639
717
  const completionText = response.toolCalls.map((tc) => tc.name + tc.arguments).join("");
640
- const usageChunk = includeUsage ? require_helpers.buildUsageChunk(chunks[0]?.id ?? "chatcmpl-unknown", overrides?.model ?? body.model, chunks[0]?.created ?? Math.floor(Date.now() / 1e3), overrides?.usage ? {
641
- prompt_tokens: overrides.usage.prompt_tokens ?? 0,
642
- completion_tokens: overrides.usage.completion_tokens ?? 0,
643
- total_tokens: overrides.usage.total_tokens ?? (overrides.usage.prompt_tokens ?? 0) + (overrides.usage.completion_tokens ?? 0)
644
- } : {
645
- prompt_tokens: require_helpers.estimatePromptTokens(body.messages),
646
- completion_tokens: require_helpers.estimateTokens(completionText),
647
- total_tokens: require_helpers.estimatePromptTokens(body.messages) + require_helpers.estimateTokens(completionText)
648
- }, overrides?.systemFingerprint) : void 0;
718
+ const usageChunk = emitStreamingUsage ? require_helpers.buildUsageChunk(chunks[0]?.id ?? "chatcmpl-unknown", overrides?.model ?? responseModel, chunks[0]?.created ?? Math.floor(Date.now() / 1e3), resolveStreamingUsageTokens(overrides, completionText), overrides?.systemFingerprint) : void 0;
719
+ shapeORChunks(chunks, usageChunk, overrides);
649
720
  const interruption = require_interruption.createInterruptionSignal(fixture);
650
721
  if (!await require_sse_writer.writeSSEStream(res, chunks, {
651
722
  latency,
@@ -654,7 +725,8 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
654
725
  onChunkSent: interruption?.tick,
655
726
  usageChunk,
656
727
  recordedTimings: fixture.recordedTimings,
657
- replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed
728
+ replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed,
729
+ openRouterProcessing
658
730
  })) {
659
731
  if (!res.writableEnded) res.destroy();
660
732
  journalEntry.response.interrupted = true;
@@ -674,12 +746,19 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
674
746
  fixture
675
747
  }
676
748
  });
677
- require_sse_writer.writeErrorResponse(res, 500, JSON.stringify({ error: {
749
+ require_sse_writer.writeErrorResponse(res, 500, openRouter ? require_openrouter_chat.serializeOpenRouterError(500, "Fixture response did not match any known type") : JSON.stringify({ error: {
678
750
  message: "Fixture response did not match any known type",
679
751
  type: "server_error"
680
752
  } }));
681
753
  }
682
754
  async function createServer(fixtures, options, mounts, serviceFixtures) {
755
+ return createServerWithResolvedAuth(fixtures, options, require_api_key_auth.resolveInboundAuth({
756
+ value: options?.auth,
757
+ label: "options.auth"
758
+ }), mounts, serviceFixtures);
759
+ }
760
+ /** @internal Config startup uses this to avoid parsing a selected auth source twice. */
761
+ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mounts, serviceFixtures) {
683
762
  const host = options?.host ?? "127.0.0.1";
684
763
  const port = options?.port ?? 0;
685
764
  const logger = new require_logger.Logger(options?.logLevel ?? "silent");
@@ -793,13 +872,10 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
793
872
  });
794
873
  });
795
874
  async function handleHttpRequest(req, res) {
796
- if (req.method === "OPTIONS") {
797
- handleOptions(res);
798
- return;
799
- }
800
875
  const startTime = registry ? process.hrtime.bigint() : 0n;
801
876
  const parsedUrl = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
802
877
  let pathname = parsedUrl.pathname;
878
+ const isOpenRouter = require_openrouter_chat.isOpenRouterPath(pathname);
803
879
  if (registry) res.on("finish", () => {
804
880
  try {
805
881
  const normalizedPath = require_metrics.normalizePathLabel(pathname);
@@ -819,6 +895,17 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
819
895
  defaults.logger.warn("metrics instrumentation error", err);
820
896
  }
821
897
  });
898
+ if (req.method === "OPTIONS" && (!resolvedAuth.policy.enabled || req.headers.origin !== void 0 && req.headers["access-control-request-method"] !== void 0)) {
899
+ handleOptions(res);
900
+ return;
901
+ }
902
+ if (!(req.method === "GET" && (pathname === HEALTH_PATH || pathname === READY_PATH || pathname === "/metrics"))) {
903
+ if (!require_api_key_auth.validateRequestApiKey(req, resolvedAuth.policy).ok) {
904
+ require_api_key_auth.writeApiKeyHttpRejection(res, setCorsHeaders);
905
+ return;
906
+ }
907
+ require_api_key_auth.markAuthenticatedRequest(req, resolvedAuth.policy);
908
+ }
822
909
  if (pathname.startsWith(CONTROL_PREFIX)) {
823
910
  await handleControlAPI(req, res, pathname, fixtures, journal, videoStates, openRouterVideoJobs, veoVideoJobs, grokVideoJobs, defaults);
824
911
  return;
@@ -1003,6 +1090,18 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
1003
1090
  }
1004
1091
  return;
1005
1092
  }
1093
+ if (pathname === "/api/v1/models" && req.method === "GET") {
1094
+ require_openrouter_chat.handleOpenRouterModels(req, res, fixtures, journal, defaults, setCorsHeaders);
1095
+ return;
1096
+ }
1097
+ if (pathname === "/api/v1/key" && req.method === "GET") {
1098
+ require_openrouter_chat.handleOpenRouterKey(req, res, journal, setCorsHeaders);
1099
+ return;
1100
+ }
1101
+ if (pathname === "/api/v1/credits" && req.method === "GET") {
1102
+ require_openrouter_chat.handleOpenRouterCredits(req, res, journal, setCorsHeaders);
1103
+ return;
1104
+ }
1006
1105
  let azureDeploymentId;
1007
1106
  const azureMatch = pathname.match(AZURE_DEPLOYMENT_RE);
1008
1107
  if (azureMatch && req.method === "POST") {
@@ -1728,17 +1827,17 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
1728
1827
  handleNotFound(res, "Not found");
1729
1828
  return;
1730
1829
  }
1731
- const completionsProvider = azureDeploymentId ? "azure" : "openai";
1830
+ const completionsProvider = azureDeploymentId ? "azure" : isOpenRouter ? "openrouter" : "openai";
1732
1831
  try {
1733
- await handleCompletions(req, res, fixtures, journal, defaults, azureDeploymentId, completionsProvider);
1832
+ await handleCompletions(req, res, fixtures, journal, defaults, azureDeploymentId, completionsProvider, isOpenRouter);
1734
1833
  } catch (err) {
1735
1834
  const msg = err instanceof Error ? err.message : "Internal error";
1736
- if (!res.headersSent) require_sse_writer.writeErrorResponse(res, 500, JSON.stringify({ error: {
1835
+ if (!res.headersSent) require_sse_writer.writeErrorResponse(res, 500, isOpenRouter ? require_openrouter_chat.serializeOpenRouterError(500, msg) : JSON.stringify({ error: {
1737
1836
  message: msg,
1738
1837
  type: "server_error"
1739
1838
  } }));
1740
1839
  else if (!res.writableEnded) try {
1741
- res.write(`data: ${JSON.stringify({ error: {
1840
+ res.write(`data: ${isOpenRouter ? require_openrouter_chat.serializeOpenRouterError(500, msg) : JSON.stringify({ error: {
1742
1841
  message: msg,
1743
1842
  type: "server_error"
1744
1843
  } })}\n\n`);
@@ -1759,6 +1858,10 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
1759
1858
  async function handleUpgradeRequest(req, socket, head) {
1760
1859
  const parsedUrl = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
1761
1860
  let pathname = parsedUrl.pathname;
1861
+ if (!require_api_key_auth.validateRequestApiKey(req, resolvedAuth.policy).ok) {
1862
+ require_api_key_auth.writeApiKeyUpgradeRejection(socket);
1863
+ return;
1864
+ }
1762
1865
  if (mounts) {
1763
1866
  for (const { path: mountPath, handler } of mounts) if ((pathname === mountPath || pathname.startsWith(mountPath + "/")) && handler.handleUpgrade) {
1764
1867
  const subPath = pathname.slice(mountPath.length) || "/";
@@ -1797,10 +1900,12 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
1797
1900
  upgradeHeaders: req.headers
1798
1901
  });
1799
1902
  else if (pathname === REALTIME_PATH) {
1800
- const model = parsedUrl.searchParams.get("model") ?? "gpt-realtime-2";
1903
+ const transcriptionIntent = parsedUrl.searchParams.get("intent") === "transcription";
1904
+ const model = transcriptionIntent ? "gpt-transcribe" : parsedUrl.searchParams.get("model") ?? "gpt-realtime-2";
1801
1905
  require_ws_realtime.handleWebSocketRealtime(ws, fixtures, journal, {
1802
1906
  ...defaults,
1803
1907
  model,
1908
+ transcriptionIntent,
1804
1909
  testId: wsTestId,
1805
1910
  upgradeHeaders: req.headers
1806
1911
  });
@@ -1850,4 +1955,6 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
1850
1955
 
1851
1956
  //#endregion
1852
1957
  exports.createServer = createServer;
1958
+ exports.createServerWithResolvedAuth = createServerWithResolvedAuth;
1959
+ exports.performFullReset = performFullReset;
1853
1960
  //# sourceMappingURL=server.cjs.map