@copilotkit/aimock 1.29.0 → 1.31.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 (207) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +34 -0
  4. package/README.md +1 -1
  5. package/dist/agui-types.d.ts.map +1 -1
  6. package/dist/bedrock-converse.cjs +63 -31
  7. package/dist/bedrock-converse.cjs.map +1 -1
  8. package/dist/bedrock-converse.d.cts.map +1 -1
  9. package/dist/bedrock-converse.d.ts.map +1 -1
  10. package/dist/bedrock-converse.js +65 -33
  11. package/dist/bedrock-converse.js.map +1 -1
  12. package/dist/bedrock.cjs +95 -33
  13. package/dist/bedrock.cjs.map +1 -1
  14. package/dist/bedrock.d.cts.map +1 -1
  15. package/dist/bedrock.d.ts.map +1 -1
  16. package/dist/bedrock.js +97 -35
  17. package/dist/bedrock.js.map +1 -1
  18. package/dist/cli.cjs +18 -1
  19. package/dist/cli.cjs.map +1 -1
  20. package/dist/cli.js +18 -1
  21. package/dist/cli.js.map +1 -1
  22. package/dist/cohere.cjs +49 -15
  23. package/dist/cohere.cjs.map +1 -1
  24. package/dist/cohere.d.cts.map +1 -1
  25. package/dist/cohere.d.ts.map +1 -1
  26. package/dist/cohere.js +51 -17
  27. package/dist/cohere.js.map +1 -1
  28. package/dist/config-loader.d.ts.map +1 -1
  29. package/dist/elevenlabs-audio.cjs +8 -4
  30. package/dist/elevenlabs-audio.cjs.map +1 -1
  31. package/dist/elevenlabs-audio.d.cts.map +1 -1
  32. package/dist/elevenlabs-audio.d.ts.map +1 -1
  33. package/dist/elevenlabs-audio.js +10 -6
  34. package/dist/elevenlabs-audio.js.map +1 -1
  35. package/dist/embeddings.cjs +4 -3
  36. package/dist/embeddings.cjs.map +1 -1
  37. package/dist/embeddings.d.cts.map +1 -1
  38. package/dist/embeddings.d.ts.map +1 -1
  39. package/dist/embeddings.js +6 -5
  40. package/dist/embeddings.js.map +1 -1
  41. package/dist/fal-audio.cjs +18 -8
  42. package/dist/fal-audio.cjs.map +1 -1
  43. package/dist/fal-audio.d.cts.map +1 -1
  44. package/dist/fal-audio.d.ts.map +1 -1
  45. package/dist/fal-audio.js +22 -12
  46. package/dist/fal-audio.js.map +1 -1
  47. package/dist/fal.cjs +55 -46
  48. package/dist/fal.cjs.map +1 -1
  49. package/dist/fal.d.cts +8 -0
  50. package/dist/fal.d.cts.map +1 -1
  51. package/dist/fal.d.ts +8 -0
  52. package/dist/fal.d.ts.map +1 -1
  53. package/dist/fal.js +58 -49
  54. package/dist/fal.js.map +1 -1
  55. package/dist/gemini-embeddings.cjs +4 -3
  56. package/dist/gemini-embeddings.cjs.map +1 -1
  57. package/dist/gemini-embeddings.js +6 -5
  58. package/dist/gemini-embeddings.js.map +1 -1
  59. package/dist/gemini-interactions.cjs +3 -3
  60. package/dist/gemini-interactions.cjs.map +1 -1
  61. package/dist/gemini-interactions.d.cts.map +1 -1
  62. package/dist/gemini-interactions.d.ts.map +1 -1
  63. package/dist/gemini-interactions.js +5 -5
  64. package/dist/gemini-interactions.js.map +1 -1
  65. package/dist/gemini.cjs +55 -24
  66. package/dist/gemini.cjs.map +1 -1
  67. package/dist/gemini.d.cts.map +1 -1
  68. package/dist/gemini.d.ts.map +1 -1
  69. package/dist/gemini.js +57 -26
  70. package/dist/gemini.js.map +1 -1
  71. package/dist/helpers.cjs +120 -2
  72. package/dist/helpers.cjs.map +1 -1
  73. package/dist/helpers.d.cts +43 -3
  74. package/dist/helpers.d.cts.map +1 -1
  75. package/dist/helpers.d.ts +43 -3
  76. package/dist/helpers.d.ts.map +1 -1
  77. package/dist/helpers.js +117 -3
  78. package/dist/helpers.js.map +1 -1
  79. package/dist/images.cjs +12 -6
  80. package/dist/images.cjs.map +1 -1
  81. package/dist/images.d.cts.map +1 -1
  82. package/dist/images.d.ts.map +1 -1
  83. package/dist/images.js +14 -8
  84. package/dist/images.js.map +1 -1
  85. package/dist/index.cjs +13 -2
  86. package/dist/index.d.cts +4 -3
  87. package/dist/index.d.ts +4 -3
  88. package/dist/index.js +6 -5
  89. package/dist/llmock.cjs +2 -1
  90. package/dist/llmock.cjs.map +1 -1
  91. package/dist/llmock.d.cts.map +1 -1
  92. package/dist/llmock.d.ts.map +1 -1
  93. package/dist/llmock.js +2 -1
  94. package/dist/llmock.js.map +1 -1
  95. package/dist/messages.cjs +325 -85
  96. package/dist/messages.cjs.map +1 -1
  97. package/dist/messages.d.cts.map +1 -1
  98. package/dist/messages.d.ts.map +1 -1
  99. package/dist/messages.js +327 -87
  100. package/dist/messages.js.map +1 -1
  101. package/dist/metrics.cjs +9 -0
  102. package/dist/metrics.cjs.map +1 -1
  103. package/dist/metrics.d.cts.map +1 -1
  104. package/dist/metrics.d.ts.map +1 -1
  105. package/dist/metrics.js +7 -1
  106. package/dist/metrics.js.map +1 -1
  107. package/dist/model-utils.cjs +68 -0
  108. package/dist/model-utils.cjs.map +1 -1
  109. package/dist/model-utils.js +68 -1
  110. package/dist/model-utils.js.map +1 -1
  111. package/dist/ollama.cjs +58 -21
  112. package/dist/ollama.cjs.map +1 -1
  113. package/dist/ollama.d.cts.map +1 -1
  114. package/dist/ollama.d.ts.map +1 -1
  115. package/dist/ollama.js +60 -23
  116. package/dist/ollama.js.map +1 -1
  117. package/dist/openrouter-video.cjs +1609 -0
  118. package/dist/openrouter-video.cjs.map +1 -0
  119. package/dist/openrouter-video.d.cts +154 -0
  120. package/dist/openrouter-video.d.cts.map +1 -0
  121. package/dist/openrouter-video.d.ts +154 -0
  122. package/dist/openrouter-video.d.ts.map +1 -0
  123. package/dist/openrouter-video.js +1601 -0
  124. package/dist/openrouter-video.js.map +1 -0
  125. package/dist/recorder.cjs +97 -15
  126. package/dist/recorder.cjs.map +1 -1
  127. package/dist/recorder.d.cts.map +1 -1
  128. package/dist/recorder.d.ts.map +1 -1
  129. package/dist/recorder.js +96 -17
  130. package/dist/recorder.js.map +1 -1
  131. package/dist/responses.cjs +26 -12
  132. package/dist/responses.cjs.map +1 -1
  133. package/dist/responses.d.cts +1 -1
  134. package/dist/responses.d.cts.map +1 -1
  135. package/dist/responses.d.ts +1 -1
  136. package/dist/responses.d.ts.map +1 -1
  137. package/dist/responses.js +28 -14
  138. package/dist/responses.js.map +1 -1
  139. package/dist/router.cjs +37 -8
  140. package/dist/router.cjs.map +1 -1
  141. package/dist/router.d.cts +30 -1
  142. package/dist/router.d.cts.map +1 -1
  143. package/dist/router.d.ts +30 -1
  144. package/dist/router.d.ts.map +1 -1
  145. package/dist/router.js +37 -9
  146. package/dist/router.js.map +1 -1
  147. package/dist/server.cjs +173 -19
  148. package/dist/server.cjs.map +1 -1
  149. package/dist/server.d.cts +2 -0
  150. package/dist/server.d.cts.map +1 -1
  151. package/dist/server.d.ts +2 -0
  152. package/dist/server.d.ts.map +1 -1
  153. package/dist/server.js +176 -22
  154. package/dist/server.js.map +1 -1
  155. package/dist/speech.cjs +4 -2
  156. package/dist/speech.cjs.map +1 -1
  157. package/dist/speech.d.cts.map +1 -1
  158. package/dist/speech.d.ts.map +1 -1
  159. package/dist/speech.js +6 -4
  160. package/dist/speech.js.map +1 -1
  161. package/dist/stream-collapse.cjs +44 -1
  162. package/dist/stream-collapse.cjs.map +1 -1
  163. package/dist/stream-collapse.d.cts +28 -0
  164. package/dist/stream-collapse.d.cts.map +1 -1
  165. package/dist/stream-collapse.d.ts +28 -0
  166. package/dist/stream-collapse.d.ts.map +1 -1
  167. package/dist/stream-collapse.js +44 -2
  168. package/dist/stream-collapse.js.map +1 -1
  169. package/dist/transcription.cjs +4 -2
  170. package/dist/transcription.cjs.map +1 -1
  171. package/dist/transcription.d.cts.map +1 -1
  172. package/dist/transcription.d.ts.map +1 -1
  173. package/dist/transcription.js +6 -4
  174. package/dist/transcription.js.map +1 -1
  175. package/dist/types.d.cts +123 -8
  176. package/dist/types.d.cts.map +1 -1
  177. package/dist/types.d.ts +123 -8
  178. package/dist/types.d.ts.map +1 -1
  179. package/dist/vector-types.d.cts.map +1 -1
  180. package/dist/vector-types.d.ts.map +1 -1
  181. package/dist/video.cjs +4 -2
  182. package/dist/video.cjs.map +1 -1
  183. package/dist/video.d.cts.map +1 -1
  184. package/dist/video.d.ts.map +1 -1
  185. package/dist/video.js +6 -4
  186. package/dist/video.js.map +1 -1
  187. package/dist/ws-gemini-live.cjs +4 -3
  188. package/dist/ws-gemini-live.cjs.map +1 -1
  189. package/dist/ws-gemini-live.d.cts +2 -2
  190. package/dist/ws-gemini-live.d.cts.map +1 -1
  191. package/dist/ws-gemini-live.d.ts.map +1 -1
  192. package/dist/ws-gemini-live.js +6 -5
  193. package/dist/ws-gemini-live.js.map +1 -1
  194. package/dist/ws-realtime.cjs +4 -3
  195. package/dist/ws-realtime.cjs.map +1 -1
  196. package/dist/ws-realtime.d.cts +2 -2
  197. package/dist/ws-realtime.d.cts.map +1 -1
  198. package/dist/ws-realtime.d.ts.map +1 -1
  199. package/dist/ws-realtime.js +6 -5
  200. package/dist/ws-realtime.js.map +1 -1
  201. package/dist/ws-responses.cjs +8 -6
  202. package/dist/ws-responses.cjs.map +1 -1
  203. package/dist/ws-responses.d.cts.map +1 -1
  204. package/dist/ws-responses.d.ts.map +1 -1
  205. package/dist/ws-responses.js +10 -8
  206. package/dist/ws-responses.js.map +1 -1
  207. package/package.json +2 -2
package/dist/server.d.cts CHANGED
@@ -4,6 +4,7 @@ import { SearchFixture } from "./search.cjs";
4
4
  import { RerankFixture } from "./rerank.cjs";
5
5
  import { ModerationFixture } from "./moderation.cjs";
6
6
  import { VideoStateMap } from "./video.cjs";
7
+ import { OpenRouterVideoJobMap } from "./openrouter-video.cjs";
7
8
  import * as http$1 from "node:http";
8
9
 
9
10
  //#region src/server.d.ts
@@ -13,6 +14,7 @@ interface ServerInstance {
13
14
  url: string;
14
15
  defaults: HandlerDefaults;
15
16
  videoStates: VideoStateMap;
17
+ openRouterVideoJobs: OpenRouterVideoJobMap;
16
18
  }
17
19
  interface ServiceFixtures {
18
20
  search: SearchFixture[];
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.cts","names":[],"sources":["../src/server.ts"],"sourcesContent":[],"mappings":";;;;;;;;;UAyEiB,cAAA;UACP,MAAA,CAAK;EADE,OAAA,EAEN,OAFM;EAAc,GAAA,EAAA,MAAA;UACrB,EAGE,eAHG;aACJ,EAGI,aAHJ;;AAGI,UA85BE,eAAA,CA95BF;EAAa,MAAA,EA+5BlB,aA/5BkB,EAAA;EA85BX,MAAA,EAEP,aAFsB,EAAA;EAAA,UAAA,EAGlB,iBAHkB,EAAA;;AAEtB,iBAOY,YAAA,CAPZ,QAAA,EAQE,OARF,EAAA,EAAA,OAAA,CAAA,EASE,iBATF,EAAA,MAOV,CAPU,EAUC,KAVD,CAAA;MACI,EAAA,MAAA;EAAiB,OAAA,EASW,SATX;AAM/B,CAAA,CAAA,EAAsB,eAAY,CAAA,EAId,eAJc,CAAA,EAK/B,OAL+B,CAKvB,cALuB,CAAA"}
1
+ {"version":3,"file":"server.d.cts","names":[],"sources":["../src/server.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;UA0FiB,cAAA;UACP,MAAA,CAAK;EADE,OAAA,EAEN,OAFM;EAAc,GAAA,EAAA,MAAA;UACrB,EAGE,eAHG;aACJ,EAGI,aAHJ;qBAEC,EAEW,qBAFX;;AAEW,UAw8BN,eAAA,CAx8BM;EAAqB,MAAA,EAy8BlC,aAz8BkC,EAAA;EAw8B3B,MAAA,EAEP,aAFsB,EAAA;EAAA,UAAA,EAGlB,iBAHkB,EAAA;;AAEtB,iBAOY,YAAA,CAPZ,QAAA,EAQE,OARF,EAAA,EAAA,OAAA,CAAA,EASE,iBATF,EAAA,MAOV,CAPU,EAUC,KAVD,CAAA;MACI,EAAA,MAAA;EAAiB,OAAA,EASW,SATX;AAM/B,CAAA,CAAA,EAAsB,eAAY,CAAA,EAId,eAJc,CAAA,EAK/B,OAL+B,CAKvB,cALuB,CAAA"}
package/dist/server.d.ts CHANGED
@@ -4,6 +4,7 @@ import { SearchFixture } from "./search.js";
4
4
  import { RerankFixture } from "./rerank.js";
5
5
  import { ModerationFixture } from "./moderation.js";
6
6
  import { VideoStateMap } from "./video.js";
7
+ import { OpenRouterVideoJobMap } from "./openrouter-video.js";
7
8
  import * as http$1 from "node:http";
8
9
 
9
10
  //#region src/server.d.ts
@@ -13,6 +14,7 @@ interface ServerInstance {
13
14
  url: string;
14
15
  defaults: HandlerDefaults;
15
16
  videoStates: VideoStateMap;
17
+ openRouterVideoJobs: OpenRouterVideoJobMap;
16
18
  }
17
19
  interface ServiceFixtures {
18
20
  search: SearchFixture[];
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","names":[],"sources":["../src/server.ts"],"sourcesContent":[],"mappings":";;;;;;;;;UAyEiB,cAAA;UACP,MAAA,CAAK;EADE,OAAA,EAEN,OAFM;EAAc,GAAA,EAAA,MAAA;UACrB,EAGE,eAHG;aACJ,EAGI,aAHJ;;AAGI,UA85BE,eAAA,CA95BF;EAAa,MAAA,EA+5BlB,aA/5BkB,EAAA;EA85BX,MAAA,EAEP,aAFsB,EAAA;EAAA,UAAA,EAGlB,iBAHkB,EAAA;;AAEtB,iBAOY,YAAA,CAPZ,QAAA,EAQE,OARF,EAAA,EAAA,OAAA,CAAA,EASE,iBATF,EAAA,MAOV,CAPU,EAUC,KAVD,CAAA;MACI,EAAA,MAAA;EAAiB,OAAA,EASW,SATX;AAM/B,CAAA,CAAA,EAAsB,eAAY,CAAA,EAId,eAJc,CAAA,EAK/B,OAL+B,CAKvB,cALuB,CAAA"}
1
+ {"version":3,"file":"server.d.ts","names":[],"sources":["../src/server.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;UA0FiB,cAAA;UACP,MAAA,CAAK;EADE,OAAA,EAEN,OAFM;EAAc,GAAA,EAAA,MAAA;UACrB,EAGE,eAHG;aACJ,EAGI,aAHJ;qBAEC,EAEW,qBAFX;;AAEW,UAw8BN,eAAA,CAx8BM;EAAqB,MAAA,EAy8BlC,aAz8BkC,EAAA;EAw8B3B,MAAA,EAEP,aAFsB,EAAA;EAAA,UAAA,EAGlB,iBAHkB,EAAA;;AAEtB,iBAOY,YAAA,CAPZ,QAAA,EAQE,OARF,EAAA,EAAA,OAAA,CAAA,EASE,iBATF,EAAA,MAOV,CAPU,EAUC,KAVD,CAAA;MACI,EAAA,MAAA;EAAiB,OAAA,EASW,SATX;AAM/B,CAAA,CAAA,EAAsB,eAAY,CAAA,EAId,eAJc,CAAA,EAK/B,OAL+B,CAKvB,cALuB,CAAA"}
package/dist/server.js CHANGED
@@ -1,7 +1,7 @@
1
- import { buildContentWithToolCallsChunks, buildContentWithToolCallsCompletion, buildTextChunks, buildTextCompletion, buildToolCallChunks, buildToolCallCompletion, buildUsageChunk, estimatePromptTokens, estimateTokens, extractOverrides, flattenHeaders, getContext, getTestId, isAudioResponse, isContentWithToolCallsResponse, isErrorResponse, isTextResponse, isToolCallResponse, readBody, resolveResponse, resolveStrictMode, serializeErrorResponse, strictOverrideField } from "./helpers.js";
1
+ import { buildContentWithToolCallsChunks, buildContentWithToolCallsCompletion, buildTextChunks, buildTextCompletion, buildToolCallChunks, buildToolCallCompletion, buildUsageChunk, estimatePromptTokens, estimateTokens, extractOverrides, flattenHeaders, getContext, getTestId, isAudioResponse, isContentWithToolCallsResponse, isErrorResponse, isTextResponse, isToolCallResponse, readBody, resolveReasoningForModel, resolveResponse, resolveStrictMode, serializeErrorResponse, strictNoMatchLogLine, strictNoMatchMessage, strictOverrideField } from "./helpers.js";
2
2
  import { Logger } from "./logger.js";
3
3
  import { Journal } from "./journal.js";
4
- import { matchFixture } from "./router.js";
4
+ import { matchFixtureDiagnostic } from "./router.js";
5
5
  import { entryToFixture, validateFixtures } from "./fixture-loader.js";
6
6
  import { writeErrorResponse, writeSSEStream } from "./sse-writer.js";
7
7
  import { createInterruptionSignal } from "./interruption.js";
@@ -19,9 +19,10 @@ import { handleTranscription } from "./transcription.js";
19
19
  import { handleImageEdit, handleImageVariations, handleImages } from "./images.js";
20
20
  import { handleSpeech } from "./speech.js";
21
21
  import { VideoStateMap, handleVideoCreate, handleVideoStatus } from "./video.js";
22
- import { handleElevenLabsAudio, handleElevenLabsTTS } from "./elevenlabs-audio.js";
23
- import { falQueueStates, handleFal } from "./fal.js";
24
22
  import { falJobs, handleFalQueue } from "./fal-audio.js";
23
+ import { falQueueStates, handleFal } from "./fal.js";
24
+ import { OPENROUTER_VIDEO_DEFAULT_MAX_CONTENT_BYTES, OpenRouterVideoJobMap, handleOpenRouterVideoContent, handleOpenRouterVideoCreate, handleOpenRouterVideoModels, handleOpenRouterVideoStatus } from "./openrouter-video.js";
25
+ import { handleElevenLabsAudio, handleElevenLabsTTS } from "./elevenlabs-audio.js";
25
26
  import { handleOllama, handleOllamaEmbeddings, handleOllamaGenerate } from "./ollama.js";
26
27
  import { handleCohere, handleCohereEmbed } from "./cohere.js";
27
28
  import { handleSearch } from "./search.js";
@@ -31,7 +32,7 @@ import { upgradeToWebSocket } from "./ws-framing.js";
31
32
  import { handleWebSocketResponses } from "./ws-responses.js";
32
33
  import { handleWebSocketRealtime } from "./ws-realtime.js";
33
34
  import { handleWebSocketGeminiLive } from "./ws-gemini-live.js";
34
- import { createMetricsRegistry, normalizePathLabel } from "./metrics.js";
35
+ import { OPENAI_VIDEO_STATUS_RE, OPENROUTER_VIDEO_CONTENT_RE, OPENROUTER_VIDEO_STATUS_RE, createMetricsRegistry, normalizePathLabel } from "./metrics.js";
35
36
  import * as http$1 from "node:http";
36
37
 
37
38
  //#region src/server.ts
@@ -53,7 +54,6 @@ const SPEECH_PATH = "/v1/audio/speech";
53
54
  const TRANSCRIPTIONS_PATH = "/v1/audio/transcriptions";
54
55
  const TRANSLATIONS_PATH = "/v1/audio/translations";
55
56
  const VIDEOS_PATH = "/v1/videos";
56
- const VIDEOS_STATUS_RE = /^\/v1\/videos\/([^/]+)$/;
57
57
  const GEMINI_PREDICT_RE = /^\/v1beta\/models\/([^:]+):predict$/;
58
58
  const ELEVENLABS_SOUND_GENERATION_PATH = "/v1/sound-generation";
59
59
  const ELEVENLABS_TTS_RE = /^\/v1\/text-to-speech\/([^/]+)$/;
@@ -104,6 +104,8 @@ const OLLAMA_GENERATE_PATH = "/api/generate";
104
104
  const OLLAMA_EMBEDDINGS_PATH = "/api/embeddings";
105
105
  const OLLAMA_EMBED_PATH = "/api/embed";
106
106
  const OLLAMA_TAGS_PATH = "/api/tags";
107
+ const OPENROUTER_VIDEOS_PATH = "/api/v1/videos";
108
+ const OPENROUTER_VIDEO_MODELS_PATH = "/api/v1/videos/models";
107
109
  const HEALTH_PATH = "/health";
108
110
  const READY_PATH = "/ready";
109
111
  const MODELS_PATH = "/v1/models";
@@ -142,10 +144,11 @@ const CONTROL_PREFIX = "/__aimock";
142
144
  * `aimock_fixtures_loaded` gauge. Shared by `/reset/fixtures` and the
143
145
  * deprecated `/reset` alias.
144
146
  */
145
- function performFixturesReset(fixtures, journal, videoStates, defaults) {
147
+ function performFixturesReset(fixtures, journal, videoStates, openRouterVideoJobs, defaults) {
146
148
  fixtures.length = 0;
147
149
  journal.clear();
148
150
  videoStates.clear();
151
+ openRouterVideoJobs.clear();
149
152
  falJobs.clear();
150
153
  falQueueStates.clear();
151
154
  resetInteractionCounter();
@@ -156,7 +159,7 @@ function performFixturesReset(fixtures, journal, videoStates, defaults) {
156
159
  * Handle requests under `/__aimock/`. Returns `true` if the request was
157
160
  * handled, `false` if the path doesn't match the control prefix.
158
161
  */
159
- async function handleControlAPI(req, res, pathname, fixtures, journal, videoStates, defaults) {
162
+ async function handleControlAPI(req, res, pathname, fixtures, journal, videoStates, openRouterVideoJobs, defaults) {
160
163
  if (!pathname.startsWith(CONTROL_PREFIX)) return false;
161
164
  const subPath = pathname.slice(9);
162
165
  setCorsHeaders(res);
@@ -220,7 +223,7 @@ async function handleControlAPI(req, res, pathname, fixtures, journal, videoStat
220
223
  return true;
221
224
  }
222
225
  if (subPath === "/reset/fixtures" && req.method === "POST") {
223
- performFixturesReset(fixtures, journal, videoStates, defaults);
226
+ performFixturesReset(fixtures, journal, videoStates, openRouterVideoJobs, defaults);
224
227
  res.writeHead(200, { "Content-Type": "application/json" });
225
228
  res.end(JSON.stringify({ reset: true }));
226
229
  return true;
@@ -232,7 +235,7 @@ async function handleControlAPI(req, res, pathname, fixtures, journal, videoStat
232
235
  return true;
233
236
  }
234
237
  if (subPath === "/reset" && req.method === "POST") {
235
- performFixturesReset(fixtures, journal, videoStates, defaults);
238
+ performFixturesReset(fixtures, journal, videoStates, openRouterVideoJobs, defaults);
236
239
  const deprecation = "POST /__aimock/reset is deprecated; use POST /__aimock/reset/fixtures (full reset) or POST /__aimock/reset/journal (journal only)";
237
240
  defaults.logger.warn("POST /__aimock/reset is deprecated; use /__aimock/reset/fixtures or /__aimock/reset/journal");
238
241
  res.writeHead(200, {
@@ -374,7 +377,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
374
377
  body._endpointType = "chat";
375
378
  body._context = getContext(req);
376
379
  const testId = getTestId(req);
377
- const fixture = matchFixture(fixtures, body, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform);
380
+ const { fixture, skippedBySequenceOrTurn } = matchFixtureDiagnostic(fixtures, body, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform);
378
381
  if (fixture) {
379
382
  journal.incrementFixtureMatchCount(fixture, fixtures, testId);
380
383
  defaults.logger.debug(`Fixture matched: ${JSON.stringify(fixture.match).slice(0, 120)}`);
@@ -401,8 +404,8 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
401
404
  if (!fixture) {
402
405
  if (resolveStrictMode(defaults.strict, req.headers)) {
403
406
  const strictStatus = 503;
404
- const strictMessage = "Strict mode: no fixture matched";
405
- defaults.logger.error(`STRICT: No fixture matched for ${req.method ?? "POST"} ${req.url ?? COMPLETIONS_PATH}`);
407
+ const strictMessage = strictNoMatchMessage(skippedBySequenceOrTurn);
408
+ defaults.logger.error(strictNoMatchLogLine(req.method ?? "POST", req.url ?? COMPLETIONS_PATH, skippedBySequenceOrTurn));
406
409
  journal.add({
407
410
  method: req.method ?? "POST",
408
411
  path: req.url ?? COMPLETIONS_PATH,
@@ -512,6 +515,8 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
512
515
  if (isContentWithToolCallsResponse(response)) {
513
516
  if (response.webSearches?.length) defaults.logger.warn("webSearches in fixture response are not supported for Chat Completions API — ignoring");
514
517
  const overrides = extractOverrides(response);
518
+ const effectiveStrict = resolveStrictMode(defaults.strict, req.headers);
519
+ const effReasoning = resolveReasoningForModel(response.reasoning, body.model, effectiveStrict, defaults.logger);
515
520
  const journalEntry = journal.add({
516
521
  method: req.method ?? "POST",
517
522
  path: req.url ?? COMPLETIONS_PATH,
@@ -523,11 +528,11 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
523
528
  }
524
529
  });
525
530
  if (body.stream !== true) {
526
- const completion = buildContentWithToolCallsCompletion(response.content, response.toolCalls, body.model, response.reasoning, overrides, body.messages);
531
+ const completion = buildContentWithToolCallsCompletion(response.content, response.toolCalls, body.model, effReasoning, overrides, body.messages);
527
532
  res.writeHead(200, { "Content-Type": "application/json" });
528
533
  res.end(JSON.stringify(completion));
529
534
  } else {
530
- const chunks = buildContentWithToolCallsChunks(response.content, response.toolCalls, body.model, chunkSize, response.reasoning, overrides);
535
+ const chunks = buildContentWithToolCallsChunks(response.content, response.toolCalls, body.model, chunkSize, effReasoning, overrides);
531
536
  const completionText = response.content + response.toolCalls.map((tc) => tc.name + tc.arguments).join("");
532
537
  const usageChunk = includeUsage ? buildUsageChunk(chunks[0]?.id ?? "chatcmpl-unknown", overrides?.model ?? body.model, chunks[0]?.created ?? Math.floor(Date.now() / 1e3), overrides?.usage ? {
533
538
  prompt_tokens: overrides.usage.prompt_tokens ?? 0,
@@ -559,6 +564,8 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
559
564
  if (isTextResponse(response)) {
560
565
  if (response.webSearches?.length) defaults.logger.warn("webSearches in fixture response are not supported for Chat Completions API — ignoring");
561
566
  const overrides = extractOverrides(response);
567
+ const effectiveStrict = resolveStrictMode(defaults.strict, req.headers);
568
+ const effReasoning = resolveReasoningForModel(response.reasoning, body.model, effectiveStrict, defaults.logger);
562
569
  const journalEntry = journal.add({
563
570
  method: req.method ?? "POST",
564
571
  path: req.url ?? COMPLETIONS_PATH,
@@ -570,11 +577,11 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
570
577
  }
571
578
  });
572
579
  if (body.stream !== true) {
573
- const completion = buildTextCompletion(response.content, body.model, response.reasoning, overrides, body.messages);
580
+ const completion = buildTextCompletion(response.content, body.model, effReasoning, overrides, body.messages);
574
581
  res.writeHead(200, { "Content-Type": "application/json" });
575
582
  res.end(JSON.stringify(completion));
576
583
  } else {
577
- const chunks = buildTextChunks(response.content, body.model, chunkSize, response.reasoning, overrides);
584
+ const chunks = buildTextChunks(response.content, body.model, chunkSize, effReasoning, overrides);
578
585
  const usageChunk = includeUsage ? buildUsageChunk(chunks[0]?.id ?? "chatcmpl-unknown", overrides?.model ?? body.model, chunks[0]?.created ?? Math.floor(Date.now() / 1e3), overrides?.usage ? {
579
586
  prompt_tokens: overrides.usage.prompt_tokens ?? 0,
580
587
  completion_tokens: overrides.usage.completion_tokens ?? 0,
@@ -605,6 +612,8 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
605
612
  if (isToolCallResponse(response)) {
606
613
  if (response.webSearches?.length) defaults.logger.warn("webSearches in fixture response are not supported for Chat Completions API — ignoring");
607
614
  const overrides = extractOverrides(response);
615
+ const effectiveStrict = resolveStrictMode(defaults.strict, req.headers);
616
+ const effReasoning = resolveReasoningForModel(response.reasoning, body.model, effectiveStrict, defaults.logger);
608
617
  const journalEntry = journal.add({
609
618
  method: req.method ?? "POST",
610
619
  path: req.url ?? COMPLETIONS_PATH,
@@ -616,11 +625,11 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
616
625
  }
617
626
  });
618
627
  if (body.stream !== true) {
619
- const completion = buildToolCallCompletion(response.toolCalls, body.model, overrides, body.messages);
628
+ const completion = buildToolCallCompletion(response.toolCalls, body.model, effReasoning, overrides, body.messages);
620
629
  res.writeHead(200, { "Content-Type": "application/json" });
621
630
  res.end(JSON.stringify(completion));
622
631
  } else {
623
- const chunks = buildToolCallChunks(response.toolCalls, body.model, chunkSize, overrides);
632
+ const chunks = buildToolCallChunks(response.toolCalls, body.model, chunkSize, effReasoning, overrides);
624
633
  const completionText = response.toolCalls.map((tc) => tc.name + tc.arguments).join("");
625
634
  const usageChunk = includeUsage ? buildUsageChunk(chunks[0]?.id ?? "chatcmpl-unknown", overrides?.model ?? body.model, chunks[0]?.created ?? Math.floor(Date.now() / 1e3), overrides?.usage ? {
626
635
  prompt_tokens: overrides.usage.prompt_tokens ?? 0,
@@ -690,6 +699,9 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
690
699
  },
691
700
  get falQueue() {
692
701
  return serverOptions.falQueue;
702
+ },
703
+ get openRouterVideo() {
704
+ return serverOptions.openRouterVideo;
693
705
  }
694
706
  };
695
707
  if (options?.chaos) {
@@ -709,11 +721,31 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
709
721
  ];
710
722
  for (const { name, value } of chaosRates) if (value !== void 0 && (value < 0 || value > 1)) logger.warn(`Chaos ${name} (${value}) is outside 0-1 range — will be clamped at runtime`);
711
723
  }
724
+ for (const { name, config } of [{
725
+ name: "falQueue",
726
+ config: options?.falQueue
727
+ }, {
728
+ name: "openRouterVideo",
729
+ config: options?.openRouterVideo
730
+ }]) {
731
+ if (!config) continue;
732
+ for (const field of ["pollsBeforeInProgress", "pollsBeforeCompleted"]) {
733
+ const value = config[field];
734
+ if (value === void 0) continue;
735
+ if (!Number.isFinite(value)) logger.warn(`${name}.${field} (${value}) is not a finite number — treating as unset`);
736
+ else if (!Number.isInteger(value) || value < 0) logger.warn(`${name}.${field} (${value}) is not a non-negative integer — flooring/clamping to a non-negative integer`);
737
+ }
738
+ }
739
+ {
740
+ const cap = options?.record?.openRouterVideo?.maxContentBytes;
741
+ if (cap !== void 0 && (!Number.isInteger(cap) || cap < 0)) logger.warn(`record.openRouterVideo.maxContentBytes (${cap}) is not a non-negative integer — using the default cap (${OPENROUTER_VIDEO_DEFAULT_MAX_CONTENT_BYTES})`);
742
+ }
712
743
  const journal = new Journal({
713
744
  maxEntries: options?.journalMaxEntries ?? 1e3,
714
745
  fixtureCountsMaxTestIds: options?.fixtureCountsMaxTestIds ?? 500
715
746
  });
716
747
  const videoStates = new VideoStateMap();
748
+ const openRouterVideoJobs = new OpenRouterVideoJobMap();
717
749
  if (mounts) for (const { handler } of mounts) {
718
750
  if (handler.setJournal) handler.setJournal(journal);
719
751
  if (registry && handler.setRegistry) handler.setRegistry(registry);
@@ -760,7 +792,7 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
760
792
  }
761
793
  });
762
794
  if (pathname.startsWith(CONTROL_PREFIX)) {
763
- await handleControlAPI(req, res, pathname, fixtures, journal, videoStates, defaults);
795
+ await handleControlAPI(req, res, pathname, fixtures, journal, videoStates, openRouterVideoJobs, defaults);
764
796
  return;
765
797
  }
766
798
  if (mounts) {
@@ -824,6 +856,125 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
824
856
  res.end(JSON.stringify({ models }));
825
857
  return;
826
858
  }
859
+ const openRouterVideoContentMatch = pathname.match(OPENROUTER_VIDEO_CONTENT_RE);
860
+ if (openRouterVideoContentMatch && req.method === "GET") {
861
+ try {
862
+ await handleOpenRouterVideoContent(req, res, openRouterVideoContentMatch[1], journal, defaults, setCorsHeaders, openRouterVideoJobs);
863
+ } catch (err) {
864
+ const msg = err instanceof Error ? err.message : "Internal error";
865
+ defaults.logger.error(`openrouter-video content: ${msg}`);
866
+ if (!res.headersSent) {
867
+ try {
868
+ journal.add({
869
+ method: req.method ?? "GET",
870
+ path: req.url ?? pathname,
871
+ headers: flattenHeaders(req.headers),
872
+ body: null,
873
+ response: {
874
+ status: 500,
875
+ fixture: null
876
+ }
877
+ });
878
+ } catch (jErr) {
879
+ defaults.logger.warn(`openrouter-video content: journal write failed after handler error: ${jErr instanceof Error ? jErr.message : String(jErr)}`);
880
+ }
881
+ writeErrorResponse(res, 500, JSON.stringify({ error: {
882
+ message: msg,
883
+ type: "server_error"
884
+ } }));
885
+ } else if (!res.writableEnded) res.destroy();
886
+ }
887
+ return;
888
+ }
889
+ if (pathname === OPENROUTER_VIDEO_MODELS_PATH && req.method === "GET") {
890
+ try {
891
+ await handleOpenRouterVideoModels(req, res, fixtures, journal, defaults, setCorsHeaders);
892
+ } catch (err) {
893
+ const msg = err instanceof Error ? err.message : "Internal error";
894
+ defaults.logger.error(`openrouter-video models: ${msg}`);
895
+ if (!res.headersSent) {
896
+ try {
897
+ journal.add({
898
+ method: req.method ?? "GET",
899
+ path: req.url ?? pathname,
900
+ headers: flattenHeaders(req.headers),
901
+ body: null,
902
+ response: {
903
+ status: 500,
904
+ fixture: null
905
+ }
906
+ });
907
+ } catch (jErr) {
908
+ defaults.logger.warn(`openrouter-video models: journal write failed after handler error: ${jErr instanceof Error ? jErr.message : String(jErr)}`);
909
+ }
910
+ writeErrorResponse(res, 500, JSON.stringify({ error: {
911
+ message: msg,
912
+ type: "server_error"
913
+ } }));
914
+ } else if (!res.writableEnded) res.destroy();
915
+ }
916
+ return;
917
+ }
918
+ const openRouterVideoStatusMatch = pathname.match(OPENROUTER_VIDEO_STATUS_RE);
919
+ if (openRouterVideoStatusMatch && req.method === "GET") {
920
+ try {
921
+ await handleOpenRouterVideoStatus(req, res, openRouterVideoStatusMatch[1], fixtures, journal, defaults, setCorsHeaders, openRouterVideoJobs);
922
+ } catch (err) {
923
+ const msg = err instanceof Error ? err.message : "Internal error";
924
+ defaults.logger.error(`openrouter-video status: ${msg}`);
925
+ if (!res.headersSent) {
926
+ try {
927
+ journal.add({
928
+ method: req.method ?? "GET",
929
+ path: req.url ?? pathname,
930
+ headers: flattenHeaders(req.headers),
931
+ body: null,
932
+ response: {
933
+ status: 500,
934
+ fixture: null
935
+ }
936
+ });
937
+ } catch (jErr) {
938
+ defaults.logger.warn(`openrouter-video status: journal write failed after handler error: ${jErr instanceof Error ? jErr.message : String(jErr)}`);
939
+ }
940
+ writeErrorResponse(res, 500, JSON.stringify({ error: {
941
+ message: msg,
942
+ type: "server_error"
943
+ } }));
944
+ } else if (!res.writableEnded) res.destroy();
945
+ }
946
+ return;
947
+ }
948
+ if (pathname === OPENROUTER_VIDEOS_PATH && req.method === "POST") {
949
+ setCorsHeaders(res);
950
+ try {
951
+ await handleOpenRouterVideoCreate(req, res, await readBody(req), fixtures, journal, defaults, setCorsHeaders, openRouterVideoJobs);
952
+ } catch (err) {
953
+ const msg = err instanceof Error ? err.message : "Internal error";
954
+ defaults.logger.error(`openrouter-video submit: ${msg}`);
955
+ if (!res.headersSent) {
956
+ try {
957
+ journal.add({
958
+ method: req.method ?? "POST",
959
+ path: req.url ?? pathname,
960
+ headers: flattenHeaders(req.headers),
961
+ body: null,
962
+ response: {
963
+ status: 500,
964
+ fixture: null
965
+ }
966
+ });
967
+ } catch (jErr) {
968
+ defaults.logger.warn(`openrouter-video submit: journal write failed after handler error: ${jErr instanceof Error ? jErr.message : String(jErr)}`);
969
+ }
970
+ writeErrorResponse(res, 500, JSON.stringify({ error: {
971
+ message: msg,
972
+ type: "server_error"
973
+ } }));
974
+ } else if (!res.writableEnded) res.destroy();
975
+ }
976
+ return;
977
+ }
827
978
  let azureDeploymentId;
828
979
  const azureMatch = pathname.match(AZURE_DEPLOYMENT_RE);
829
980
  if (azureMatch && req.method === "POST") {
@@ -1099,7 +1250,7 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
1099
1250
  }
1100
1251
  return;
1101
1252
  }
1102
- const videoStatusMatch = pathname.match(VIDEOS_STATUS_RE);
1253
+ const videoStatusMatch = pathname.match(OPENAI_VIDEO_STATUS_RE);
1103
1254
  if (videoStatusMatch && req.method === "GET") {
1104
1255
  const videoId = videoStatusMatch[1];
1105
1256
  handleVideoStatus(req, res, videoId, journal, defaults, setCorsHeaders, videoStates);
@@ -1521,6 +1672,8 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
1521
1672
  server.close = function(callback) {
1522
1673
  for (const ws of activeConnections) ws.close(1001, "Server shutting down");
1523
1674
  activeConnections.clear();
1675
+ videoStates.clear();
1676
+ openRouterVideoJobs.clear();
1524
1677
  originalClose(callback);
1525
1678
  return this;
1526
1679
  };
@@ -1541,7 +1694,8 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
1541
1694
  journal,
1542
1695
  url,
1543
1696
  defaults,
1544
- videoStates
1697
+ videoStates,
1698
+ openRouterVideoJobs
1545
1699
  });
1546
1700
  });
1547
1701
  });