- Prevent
Invalid string lengthcrash onGET /__aimock/journalby capping retained request bodies at 64 KB (#308)
- Log full error stack on unhandled request errors for prod diagnosis (#308)
- Capped the uncapped in-memory record buffers that could grow a string past V8's ~512 MiB max string length and throw
RangeError: Invalid string length(the ~1/sec production crash, amplified once the real-key fixture-miss passthrough landed). The AG-UI recorder, the generic recorder, and the stream-collapse path now bound the buffer theyBuffer.concat(chunks).toString()for fixture construction to a configurablemaxRecordBufferBytes(default 64 MiB) clamped to a 256 MiB hard ceiling. Once the cap is crossed the recorder frees the buffer, marks the recording truncated, and skips fixture construction — the full upstream response is still relayed to the client byte-for-byte in real time, so capping never truncates what the client receives ("don't journal", not "don't answer") (#307)
- New
match.toolResultContainsstring gate: passes when the request's LAST message is arole: "tool"result whose text content contains the substring (same last-message rule astoolCallId, and composable with it). Discriminates fixtures whose requests differ only inside the tool-result payload — e.g. a human-in-the-loop suspend tool where approve and cancel resume with the sametool_call_idbut different result JSON. JSON-expressible, so fixture files can finally split those legs without a programmaticpredicate. Validated at load time (non-empty string — an empty substring would silently act as a catch-all), included in the duplicate-userMessage dedup key and the catch-all discriminator set, and routed through theaimock-pytestmatch-level kwarg keys (aimock-pytest 0.5.0) (#299)
- The duplicate-userMessage shadow check no longer warns for fixtures that share a
userMessagebut are legitimately disambiguated by a matcher the dedup key previously omitted (systemMessage,model,toolName,toolCallId,inputText,responseFormat,endpoint). The dedup key now covers every discriminator the router actually matches on and serialises RegExp / array matchers kind-aware so they don't collide; apredicate-bearing fixture is keyed by identity so it is never a false duplicate. This only suppresses spurious warnings — the check never dropped or deduped fixtures (#299) - Bumped the
aimock-pytestdefault server pin (_version.py) to 1.37.0 so the auto-download path (used whenAIMOCK_CLI_PATHis unset) runs a server that supports the newtoolResultContainsmatch kwarg the client forwards (#299) - Guarded
recordedTimings.interChunkDelaysMsagainst non-array values: a fixture whoseinterChunkDelaysMsis malformed (null, missing, or not an array) no longer throws during load and silently drops every fixture in that file — the value is coerced to an empty array (#299)
aimock.jsonnow honoursllm.latency,llm.chunkSize,llm.replaySpeedandllm.logLevel, which the config loader previously accepted and ignored. Each behaves as itsllmockCLI flag andcreateMockSuite({ llm })equivalent.llm.replaySpeeddivides every delay source (recorded timings, streaming profiles,latency), so a fixture suite can replay recorded inter-chunk timings faster than they were recorded; a fixture's ownreplaySpeedstill takes precedence. A non-positivellm.replaySpeedis ignored with a warning, matching the existing fixture-level guard (#294)
- On a fixture-miss passthrough (record/proxy mode), aimock can inject its own configured upstream provider key instead of forwarding a caller's dummy placeholder key. Every static-key provider aimock proxies is wired end-to-end, each with an independent
AIMOCK_PROVIDER_<PROVIDER>_KEYenv var applied with the provider-correct wire scheme:Authorization: Bearerfor OpenAI (_OPENAI_KEY), OpenRouter (_OPENROUTER_KEY), Cohere (_COHERE_KEY), Grok/xAI (_GROK_KEY), and Ollama (_OLLAMA_KEY);x-api-keyfor Anthropic (_ANTHROPIC_KEY);x-goog-api-keyfor Gemini (_GEMINI_KEY, also used for Gemini Interactions) and Veo (_VEO_KEY);api-keyfor Azure OpenAI (_AZURE_KEY);xi-api-keyfor ElevenLabs (_ELEVENLABS_KEY); andAuthorization: Keyfor fal.ai (_FAL_KEY). Injection fires only when the caller credential is absent or dummy-prefixed (sk-aimock-, overridable viaAIMOCK_DUMMY_KEY_MARKER); a real caller key is always forwarded unchanged, an empty-string env var is treated as unset, and with no built-in key configured the feature is inert. Signed/exchanged credentials — AWS Bedrock (SigV4) and Vertex AI (OAuth) — are never rewritten (#293)
- Record mode no longer silently drops fixtures when an SDK closes its socket immediately after
data: [DONE](e.g. the OpenAI Python SDK); the completed upstream response is now persisted. Genuine mid-stream aborts (client disconnect before[DONE]) still abort upstream and write no fixture (#288)
- Native Google Veo async video lifecycle mock —
POST /v1beta/models/{model}:predictLongRunningsubmit,GET /v1beta/operations/{name}poll throughdone:false → done:true, poll-count progression viaveoVideo; the Files-APIuriis served as-is (aimock never proxies or downloads video bytes) (#278) - Record-mode live proxying for the Veo surface (
record.providers.veo) — submit and poll forwarded 1:1, eager fixture capture of the Files-API uri ondone:true; captured operations replay later (#278) - Native xAI Grok Imagine async video lifecycle mock —
POST /v1/videos/generationssubmit (JSON-only; multipart rejected with 400),GET /v1/videos/{request_id}poll throughpending → done | failed | expiredwith synthesizedprogress,grokVideoprogression,cost_in_usd_ticksunits, and a Sora-safe/v1/videos/{id}dispatch that leaves the OpenAI video surface unchanged (#278) - Record-mode live proxying for the Grok surface (
record.providers.grok) — submit and poll forwarded 1:1, eager fixture capture of url/duration/cost ondone,failedpersisted,expiredpassed through; captured jobs replay later (#278) - Optional
blocksarray on the combinedcontent+toolCallsfixture shape lets a fixture express ordered text/tool-call blocks ({type:"text",text}|{type:"toolCall",name,arguments,id?}); when present it takes precedence over{content, toolCalls}for stream order, enabling tool-first and interleaved ordering. Legacy{content, toolCalls}fixtures are unchanged (#274) - All five providers stream combined responses in fixture block order: Anthropic, OpenAI Responses, and Gemini are fully observable; Ollama is best-effort (clients may reassemble positionally); OpenAI chat-completions emits in order but is degenerate (
delta.content/delta.tool_callsare separate channels the client merges) (#274) - Recorder captures block order and persists
blocksonly when the recorded upstream stream was genuinely tool-first or interleaved; text-first streams keep the legacy{content, toolCalls}shape so golden recordings round-trip byte-identically (#274) - Blocks-only fixtures are first-class: a non-empty
blocksarray is a complete response shape on its own, with nocontent/toolCallsrequired — builders derive the aggregate from the blocks andvalidateFixtures()accepts the shape (#274) - Block ordering is now honored on replay across the remaining providers — Cohere (streaming), Bedrock invoke, Bedrock Converse, and Gemini Interactions — so a tool-first or interleaved fixture streams its tool call ahead of its text wherever the wire protocol can express it (#274)
- Record-side block capture extends to the Cohere and Bedrock collapsers; Gemini Interactions normalizes tool-call arguments only and does not reorder blocks on capture (its step-index protocol can't reconcile arrival-order blocks at record time), while replay still honors a hand-authored
blocksarray (#274) validateBlocksrejects a malformedblocksarray at load time — non-array, non-object entries, atypeother thantext/toolCall, a non-string or empty-string text block, or atoolCallblock missing a name or carrying non-JSON arguments — and warns when a fixture carries bothblocksand divergentcontent/toolCalls, so a bad array never reaches a builder mid-dispatch (#274)
- Replay matching is content-anchored:
turnIndexdisambiguates, no longer a hard reject gate (#276) - Empirical over 9769 real requests: 3213 false-miss fixes, 0 new misses, 0 wrong-fixture (#276)
- Diverges only on off-by-N assistant count in either direction (behind OR ahead of turn) (#276)
- New
turnIndexRelaxedmatch diagnostic + one-shot logger warn on a divergent relaxed serve (#276) AIMOCK_STRICT_TURN_INDEX=1restores the legacy strict turnIndex gate for replay (#276)
- Gemini Interactions mock now emits the SDK 2.x event protocol on both paths — streamed SSE (
step.*,interaction.created/completed, tool args viaarguments_delta) and non-streaming responses (steps/output_text); legacy 1.x recorded fixtures still parse (#279)
--max-proxy-buffer-bytes/--max-proxy-buffer-framesflags to cap proxy buffering (#275)
- Proxy path no longer leaks memory on long-lived upstream streams (#275)
- Oversized proxied responses no longer crash with
Invalid string length(#275) - Sanitize
X-AIMock-Contextto prevent fixture path traversal (#272) - Scope one-shot error injection, improve recorder fixture fidelity, fix
matchesPatternlastIndex (#272)
- fal queue
status/resultresponses now emitx-fal-request-id, and abillableUnitsfixture field (e.g.onFalQueue(model, json, { billableUnits })) emitsx-fal-billable-unitson the completed result so adapters like@tanstack/ai-falcan surfaceusage.unitsBilledon replay. Record mode captures the upstreamx-fal-billable-unitsheader automatically, so recorded fixtures round-trip billing with no hand-editing (#269)
- OpenRouter async video job lifecycle mock — submit, poll, content download, model listing (#262)
- Record-mode live proxying for the OpenRouter video surface; captured videos replay later (#265)
- Recording proxies now strip aimock-internal control headers on every provider path (#265)
- Recorder and fal record paths hardened — timeouts, threshold sanitizing, persist errors (#265)
- attw ^0.18 fixes the test:exports crash (#263); OpenAI /v1/videos docs corrected (#264)
- Extended-thinking request invariants — aimock now validates Anthropic extended-thinking continuations on the tool-use loop. When extended thinking is enabled, a continuation whose prior assistant turn drops the leading
thinkingblock (or itssignature, or aredacted_thinkingblock'sdata) is rejected with the real Anthropic400, instead of producing a false-green replay — under strict mode; otherwise the violation warns and replay proceeds. Emitted thinking blocks now carry a non-empty placeholder signature so record→replay round-trips stay green across text, content+tool, and tool-only response shapes. - Faithful reasoning capture + replay — the recorder now preserves provider reasoning artifacts that recording previously dropped. Anthropic thinking-block signatures (the
signature_deltawire event on the streaming paths; each thinking block'ssignaturefield non-streaming) andredacted_thinkingblockdataare captured across all three Anthropic capture paths — the SSE stream collapser, the Anthropic-native binary frames on Bedrock invoke-with-response-stream (thinking_delta/signature_delta/redacted_thinking), and the non-streaming JSON recording path. Bedrock ConversereasoningContentdeltas and Coherethinkingcontent-deltas are captured into reasoning, and a recorded AnthropicreasoningSignatureplus anyredactedThinkingpayloads are written into the saved fixture (TextResponse/ToolCallResponse/ContentWithToolCallsResponse). On replay, Anthropic emits the recorded signature when one was captured and otherwise falls back to the round-trip-safe placeholder, and recordedredactedThinkingpayloads are replayed as faithful leadingredacted_thinkingcontent blocks (streamingcontent_block_start/content_block_stopwith the opaquedata, non-streaming content-array blocks), across both streaming and non-streaming text, content+tool, and tool-only response shapes. The encrypted reasoning artifacts (reasoningSignatureandredactedThinking) are gated on the same model-capability resolution as the plaintext reasoning channel, so replaying a reasoning-model fixture against a non-reasoning model under strict mode suppresses thethinking,signature, andredacted_thinkingchannels together rather than leaving the encrypted blocks half-gated. Fidelity caveat: the interleaving ofthinkingandredacted_thinkingblocks and per-block signatures are not preserved — all recordedredacted_thinkingblocks replay as a leading group, and the merged thinking block carries thesignatureof the last thinking block that actually emitted one (a signature-less block does not clobber an earlier signature).
- Reasoning emission — replaying a reasoning channel is now gated on the requested model's capability. aimock no longer synthesizes a reasoning channel (chat
reasoning_content/ Responsesreasoning_summary_text/ Anthropic thinking / etc.) for models that would not emit reasoning against the real provider. A newisReasoningModelclassifier andresolveReasoningForModelgate are applied across OpenAI chat + Responses, Anthropic, Ollama, Gemini, Cohere, Bedrock (invoke + Converse), and WebSocket Responses: a non-reasoning model paired with a reasoning fixture has its reasoning suppressed under strict mode, or warns-and-emits otherwise. TheAIMOCK_REASONING_MODELSandAIMOCK_NONREASONING_MODELSenv vars override the classifier. - Tool-only reasoning emission — the capability gate now also covers the tool-call-only response path of every provider (OpenAI chat + Responses, Cohere, Bedrock invoke + Converse, Ollama, WebSocket Responses, and the Gemini chat tool-only path). Previously a tool-only fixture carrying reasoning dropped the reasoning channel entirely; it now emits the provider-native reasoning channel for reasoning-capable models and is suppressed under strict mode (or warns-and-emits otherwise), matching the text and content+tool paths, with leading reasoning blocks shifting streaming tool/output indices by one where applicable. The Gemini audio companion's reasoning is now capability-gated as well.
- Strict-mode 503 now distinguishes sequence/turn exhaustion from a true no-match: when candidate fixtures matched the request shape but were skipped by
sequenceIndex/turnIndexcount state, the 503 message and error log readN candidate fixture(s) skipped by sequence/turn stateinstead of the genericno fixture matched. HTTP status (503) and error envelope shape are unchanged. Applied across all strict-mode emission sites via a shared helper. Design notes: the skip diagnostic is captured from the same single matcher pass that performs the match — statefulmatch.predicatefunctions fire exactly once per request — and thehasToolResultshape predicate is evaluated before thesequenceIndex/turnIndexstate gates, so the skip count only includes fixtures whose request shape actually matched.
- Recorder — pathological-but-real Anthropic turns (thinking-only with empty text, redacted-only, redacted blocks with empty
data) now record as normal empty-content fixtures instead of "Could not detect response format" error fixtures, with streaming/non-streaming parity. redacted_thinkingblocks with emptydataare filtered at every capture site via one shared predicate, matching the strict replay validator — a recorded fixture can no longer 400 on replay for data the validator rejects (record-green ⇒ replay-green).
POST /__aimock/reset/fixtures— full reset (clears fixtures, generation state, and journal).POST /__aimock/reset/journal— clears only the request journal, leaving fixtures intact.aimock-pytest:reset_fixtures()andreset_journal()client methods.- Control API reference documentation.
engines.nodelowered to>=20.15.0. The runtime supports Node 20 (the published CLI runs on Node 20/22/24); the previous>=24.0.0floor reflected an OIDC publish-time requirement, which is a CI concern handled by the publish workflow, not a runtime constraint for consumers.
POST /__aimock/reset— now a deprecated alias for/__aimock/reset/fixtures; it still performs a full reset but emits aDeprecationresponse header and adeprecatedfield in the body. Use the explicit/reset/fixturesor/reset/journalroutes instead.
- Video —
POST /v1/videos(videos.create) now parsesmultipart/form-databodies. The OpenAI SDK (>=6.28.0) sends video-create requests as multipart instead of JSON (even for File-less bodies), which previously returned a400 invalid_json. The handler reuses the existing transcription multipart field parser and preserves the JSON path for older SDKs. aimock-pytest: per-fixture options passed viaadd_fixture/on_message/etc. (latency,chunkSize,sequenceIndex,chaos, …) are now forwarded in the correct wire shape. They were previously nested under anoptskey the server ignored, so they were silently dropped.aimock-pytest:_wait_for_readynow honors its startup timeout (a background reader thread drains the child's stdout, so a no-output child can no longer hang past the deadline or deadlock on a full pipe buffer), tears down the subprocess on a startup failure, and surfaces the child's captured output (the health-check failure path also reports accurate elapsed time).DELETE /v1/_requestsnow clears only the request-journal entries, preserving fixture match-counts (so sequenced fixtures are not rewound) — consistent withPOST /__aimock/reset/journal.
- Harmony channel format — parse OpenAI "harmony" channel tokens (
<|channel|>… <|message|>… <|call|>) emitted by local gpt-oss models (Ollama / vLLM / OpenRouter) so their tool calls, reasoning, and content are captured when recording (hosted OpenAI pre-parses harmony, so only local runtimes pass it through raw). Implemented as a lexer + state-machine parser with a uniform all-or-nothing verbatim fail-safe, wired as fallback-only so it never produces phantom tool calls.
- Recorder — decode streamed response chunks incrementally to prevent multibyte UTF-8 corruption; CRLF-tolerant frame-timing splitter; propagate
webSearchesand audio-companion fields (tool calls / content / reasoning) into recorded fixtures; logfirstDroppedSamplealongside dropped-chunk warnings. - Stream collapsers — multi-line and CRLF SSE handling; missing/uncorrelated tool-call index guards with symmetric dropped-chunk accounting across OpenAI / Anthropic / Bedrock / Cohere; bound Bedrock EventStream header parsing against malformed frames.
- Gemini — replay audio-companion tool calls / content / reasoning on audio turns instead of dropping them.
- correct OpenAI images endpoint path from
/v1/images/editto/v1/images/edits(closes #221) - add Ollama
/api/embedroute as alias for/api/embeddings;/api/embedis the current documented endpoint (ollama/ollama docs/api.md) used by modern Ollama SDKs, while/api/embeddingsis retained for backwards-compatibility. Both routes dispatch to the same handler.
- Fixture loader — full recursive directory traversal replaces 2-level cap; supports per-integration showcase layouts like
d6/<integration>/<feature>.json.
- Docker image — added
gitbinary to production stage for sparse-checkout fixture fetching at boot.
- Router — systemMessage array exact-match logic was unsatisfiable for 2+ needles; collapsed to substring matching. Added
elevenlabs-ttsandtranslationto endpoint compatibility filter. - Recorder — Content-Type empty-string fallback (
??→||), derivedEndpointTypefromFixtureMatchinstead of duplicate union, negative guards on Gemini Interactions outputs detection, scopedturnIndex/hasToolResultto chat endpoints only. - WS-Realtime — session.update rollback now captures full snapshot instead of just model/type. Added Beta flat fields for noise reduction, transcription, and turn_detection. Joined all text content parts in
realtimeItemsToMessages. Added try-catch with debug logging aroundsendEventfor WebSocket close race safety. - WS-Gemini-Live — replaced deterministic
call_gemini_${name}_${i}tool call IDs with randomgenerateToolCallId()to prevent cross-turn collisions. Pre-computedresolvedToolCallsfor wire/history ID consistency. Added unrecognized-role warning and ws.send try-catch with debug logging. - Gemini Interactions —
interactionsUsagehonors Gemini-native field names (promptTokenCount/candidatesTokenCount/totalTokenCount).truncateAfterChunksonly countscontent.deltaevents. AddedwebSearcheswarning on tool-call branch. - fal-audio + ElevenLabs — all journal entries now use
flattenHeaders(req.headers)instead of{}.handleSyncRunacceptsRawJSONResponsefixtures from queue-walk recordings. - Helpers — extended
resolveUsagewith Gemini-native token fields. Preserved error cause inresolveResponsefactory rethrow.buildEmbeddingResponseaccepts optional usage.extractFormFieldescapes regex metacharacters. - Drift test infra — retry logging with body consumption, broadened
redactUrlto coverapi_key/apikey/token/access_tokenpatterns, URL threaded into error messages with redaction,parseDataOnlySSE[DONE] filter fix,parseTypedSSEmulti-line data handling with null guards. - Drift collector — invoke vitest directly via npx to avoid pnpm stdout prefix breaking JSON parse; classify raw stack traces as infrastructure errors instead of crashing.
- AG-UI config loader — removed
/.*/catch-all regex fallback whenmatch.messageis absent; fixtures without a message pattern no longer shadow other fixtures. - AG-UI input validation — runtime check that
input.messagesis an array after JSON parse; returns 400 instead of confusing downstream 404. - AG-UI SSE writer —
writeAGUIEventStreamuses logger abstraction instead ofconsole.warn; handles non-Error throws. - Drift test helpers —
parseDataOnlySSEhandles multi-line data blocks, aligned withproviders.tsimplementation.
- HITL continuation recording/replay support —
toolCallIdmatching for continuation fixtures. NewtoolCallIdfield onAGUIFixtureMatchandAGUIConfigFixture.getLastMessageIfToolResulthelper andonToolResultfluent API. Recorder uses tool-result-first priority for continuation fixtures. (#233, closes #232)
- Walk structured content arrays in
extractLastUserMessage— handle multimodal user content (AGUIMessageContentPart[]) by joining text parts and skipping non-text. ExportNO_USER_MESSAGE_SENTINELconstant andAGUIMessageContentParttype. (#231) - Harden recorder against error responses, double-settle, and broken sentinel persistence — guard against recording fixtures from non-2xx upstream responses, add
settledflag to prevent error+end race, skip disk write for predicate fixtures (sentinel was semantically broken on reload), include parse error reason in SSE warning log
- Context-based fixture routing —
X-AIMock-Contextheader scopes fixtures per integration. Fixtures withmatch.contextonly match requests carrying that context; fixtures withoutcontextremain shared. Recorder auto-captures context and routes recorded fixtures into context subdirectories.
- Timing-aware recording and replay — proxy recording captures per-frame
arrival timestamps as
recordedTimingson fixtures. Replay uses recorded timings for approximate timing reproduction based on recorded TTFT and inter-frame cadence instead of the synthetic model. Replay chunk count may differ from recording chunk count — TTFT and average pace are preserved, not per-token fidelity.--replay-speed Nmultiplier applies to all delay sources (recorded timings, streaming profiles, global latency). Per-fixturereplaySpeedoverride. Covers SSE, NDJSON, Bedrock EventStream, and WebSocket protocols.
- Gemini
embedContentendpoint —POST /v1beta/models/{model}:embedContentwith deterministic fallback embeddings and fixture matching /v1/images/editand/v1/images/variationsendpoints — multipart form-data, same response format as generations. Closes #221/v1/audio/translationsendpoint — reuses transcription handler withendpoint: "translation"andtask: "translate"in verbose mode- Ollama
/api/embeddingsendpoint — single-embedding response, supports bothpromptandinput(string or array) fields - Cohere
/v2/embedendpoint — multi-text embedding with configurableembedding_types(float, int8, etc.) - ElevenLabs
/v1/text-to-speech/{voice_id}endpoint — binary audio response with voice routing andonElevenLabsTTShelper - Streaming usage chunks — when
stream_options.include_usageis set, emits a final SSE chunk with token usage before[DONE] - Automatic token usage estimation — responses without explicit fixture
usageoverrides now return estimated token counts (~4 chars/token) instead of zeros - Rate limiting headers on 429 responses —
Retry-After,x-ratelimit-limit-*,x-ratelimit-remaining-*,x-ratelimit-reset-*headers on all error fixtures with status 429. CustomretryAfteroverride via fixture field onTranslationconvenience method — register translation fixtures with endpoint discriminationonElevenLabsTTSconvenience method — register ElevenLabs TTS fixtures- Configurable proxy timeouts —
RecordConfignow acceptsupstreamTimeoutMs(default 30s) andbodyTimeoutMs(default 30s). The body-idle timeout is the Node socket inactivity timer that firesreq.destroy()mid-stream; under concurrent load against reasoning models (e.g. Grok 4.3 + structured output), token-emission gaps can routinely exceed 30s during the thinking phase, causing record-mode runs to truncate SSE responses mid-stream with no[DONE]and nofinish_reason. Lift to e.g.bodyTimeoutMs: 180_000to record cleanly under that workload.
- Gemini tool-call response serializer dropped fixture-pinned
tool_call.id—parseToolCallPartemitted{ functionCall: { name, args } }and omitted the id even when the fixture pinned one. Pairs with v1.23.1's INGEST-direction fix (#196) which preservesfunctionCall.idwhen aimock parses an incoming Gemini request — that fix only helps when the id is already in the response body. Without this EGRESS-direction fix, aimock never emits one for clients to preserve in the first place, so the round-trip silently breaks for any client that depends onfunctionCall.idto correlate follow-upfunctionResponseparts back to the originating call
onFalImage(pattern, ImageResponse)— typed helper that wraps ImageResponse into fal's image envelopeonFalVideo(pattern, VideoResponse)— typed helper that wraps VideoResponse into fal's video envelopeMockServerOptions.falQueue— opt into realisticIN_QUEUE → IN_PROGRESS → COMPLETEDpolling progression with configurable thresholds- Queue status responses include
logs[](state-transition entries) andmetrics.inference_time(once COMPLETED) - Cancel-before-completion returns
200 { status: "CANCELLED" }; cancel-after returns400 { status: "ALREADY_COMPLETED" } - Result fetch before completion returns
202with current status body - Queue-walk recording: recorder now walks the upstream fal queue (submit → poll → result) and persists the FINAL job body, not the submit envelope
RecordConfig.fal.pollIntervalMs/fal.timeoutMsfor tuning upstream queue-walk recording cadence- Malformed JSON request bodies now return
400 invalid_json(consistent with all other handlers)
pollsBeforeCompletedauto-defaults topollsBeforeInProgress + 1when only the in-progress threshold is set- URL extension extraction no longer produces invalid MIME types for URLs with query strings, fragments, or no extension
- Double-cancel no longer pushes duplicate log entries
- Legacy fal audio queue recording now uses the same queue-walk approach
- Default fal queue-walk timeout bumped from 2 min to 15 min (video generations routinely take 5–10 min)
persistFixtureandbuildFixtureMatchextracted from recorder internals and exported for reuse
- Gemini functionCall.id preservation — the Gemini conversation history converter generated new tool call IDs (
call_gemini_*) instead of preserving the original IDs fromfunctionCall.id. This broketoolCallId-based fixture matching on follow-up turns: the follow-up fixture couldn't match because the ID was overwritten, so the request fell through touserMessagefixtures which returned another tool call — creating an infinite loop for all Gemini/ADK showcase integrations. LangGraph-python (OpenAI format) was unaffected because it preserves IDs natively. (#196)
- Model-aware fixture recording — recorded fixtures now include the model name in match criteria, preventing collisions when an app makes multiple LLM calls with the same user message but different models. Model names are normalized by stripping date/version suffixes (e.g.,
claude-opus-4-20250514→claude-opus-4) so fixtures survive version bumps. Disable withrecordFullModelVersion: true. (#185) - Drift detection metadata — recorded fixtures include
systemHashandtoolsHashin ametadatablock for detecting system prompt or tool definition changes since recording. - Prefix model matching — fixture router uses
startsWithfor string model matching, somodel: "claude-opus-4"matches anyclaude-opus-4-*version. - GA Realtime protocol migration with Beta compatibility shim — handler emits GA event names natively;
sendEvent()wrapper translates back for Beta clients detected viaOpenAI-Betaheader. Default model changed togpt-realtime-2. - GA Realtime models —
gpt-realtime,gpt-realtime-2,gpt-realtime-1.5,gpt-realtime-mini(and dated snapshots). Transcription/translation sessions usegpt-4o-transcribe,gpt-4o-mini-transcribe, orwhisper-1. - Transcription and translation session types — dedicated session configurations for translation and transcription workloads on the Realtime API.
- Image input support — Realtime sessions accept image content parts alongside text and audio.
- Commentary phase — Realtime handler supports the GA commentary phase for model-generated annotations.
conversation.item.doneandresponse.cancelevents — new GA Realtime event types for item completion tracking and response cancellation.- Endpoint type routing for Realtime — router distinguishes GA vs Beta Realtime endpoints for fixture matching.
- Drift detection for GA Realtime — drift test suite extended with GA protocol shapes, Beta conformance shapes, and three-way triangulation.
- 73 GA Realtime integration tests — comprehensive test coverage for all GA event types, Beta compatibility, session management, model routing, image input, translate/whisper, commentary, and cancellation.
- GA and Beta Realtime conformance suites — API conformance tests validating event shapes against both GA and Beta protocol specs.
- GA Realtime drift detection — SDK shape tests and provider triangulation for the GA Realtime protocol.
- Strict mode checked before proxy attempt — in
--proxy-onlymode, theX-AIMock-Strictheader had no effect becauseproxyAndRecord()returned before the strict check. Now all 17 handlers check strict mode first: when strict + no fixture → 503 immediately, no proxy attempt - Helper utilities and error serialization — hardened helper functions and error serialization paths for correctness and robustness
- Journal and fixture-loader correctness — fixed journal entry handling and fixture-loader edge cases
- WebSocket handler consistency and strict-mode journal — aligned WebSocket handler behavior and ensured strict-mode journal entries are recorded correctly
- Provider handler consistency and proxy outcomes — unified provider handler error paths and proxy outcome reporting
- Media handler hardening and chaos injection — strengthened media handler validation and chaos injection reliability
- Bedrock mock consistency and CLI help text — corrected Bedrock mock test assertions and CLI
--helpoutput coverage
- Per-request strict mode via
X-AIMock-Strictheader — overrides the server-wide--strictflag per request (true/1= strict,false/0= lenient). When strict: fixture miss returns 503; when lenient: fixture miss proxies to real provider. Follows theX-AIMock-Chaos-*precedence pattern. Journal entries recordstrictOverridewhen the header overrides the server default. Enables the same aimock instance to serve both deterministic test probes and live demo traffic simultaneously.
- Progressive relay for NDJSON and Bedrock binary event streams — Ollama NDJSON and Bedrock binary event streams were fully buffered before relay, triggering downstream idle timeouts; now relayed progressively as chunks arrive
- JSON.parse error detail in bare catch blocks — capture and surface parse-error detail in all bare catch blocks across 25+ provider/WebSocket/stream-collapse handlers instead of swallowing context
- Unguarded stream write/end calls — wrap stream write/end in try/catch (recorder.ts, agui-recorder.ts) to prevent unhandled exceptions on client disconnect
- Response termination for headers-already-sent paths — add response termination in error paths where headers were already sent (server.ts, a2a-mock.ts, mcp-mock.ts), preventing connection hangs
- Vector-mock double body consumption — fix route passthrough consuming the request body twice, causing empty-body forwarding
- Drift detection compared only first event per type —
compareSSESequencesnow compares ALL events per type, not just the first, catching previously invisible divergences - Ollama drift tests used broken async describe.skipIf — replaced with synchronous env-var gate so tests are correctly skipped or executed
- 12 unrestored spy/mock leaks and misleading assertions — fix spy/mock leaks across test files and correct assertions that passed for the wrong reasons
- Proxy relay hardcoded POST method — now forwards the original HTTP method
- Response timeout timer leak — cleared after successful upstream completion
- Client disconnect handler race — checks
writableFinishedbefore destroying upstream request onHookBypassedandbeforeWriteResponsecallbacks not wrapped in try/catch- Audio error relay sent non-2xx responses with audio content-type instead of application/json
- Snapshot-mode fixture writes not atomic — concurrent requests could corrupt the file
- Undefined
toolCallname/arguments silently dropped during fixture save - Video detection heuristic false-positives on LLM provider responses with
{id, status}shape - One-shot error fixture splice during iteration (deferred via microtask)
- Azure model injection catch swallowed non-SyntaxError exceptions
- fal request body lost on passthrough (double
readBodyconsumption) - fal queue handler dropped PUT request body
- Recorder test: tmpDir leak on strict-mode reassignment, global fetch dependency, fragile fixturePath cleanup, duplicate helpers, spy leak on assertion failure
- Fixture-level chaos evaluation for non-completions endpoints (ElevenLabs, fal)
- Anti-buffering headers on all progressive stream relay paths — standard headers (Cache-Control, Connection, X-Accel-Buffering) added to all progressive stream relay paths to prevent intermediate proxy buffering
- Stream-collapse returns firstDroppedSample — stream-collapse functions now return the first dropped sample for forensic debugging of collapsed streams
match.systemMessageacceptsstring[]— array form requires ALL substrings to be present in the joined system-message text (AND semantics). Use this when the gate must combine multiple non-adjacent tokens that may appear in any order — e.g., a host that serialises agent-context entries into a system message whose entry order is not stable, but where a fixture should only match when every default value is present (["\"value\": \"Atai\"", "[\"Viewed the pricing page\",\"Watched the product demo video\"]"]). Single-string andRegExpforms continue to work unchanged. JSON form acceptsstring | string[]; programmatic form acceptsstring | string[] | RegExp.
- Drift tests passed vacuously with zero assertions — the
shouldFailguard silently skipped allexpectcalls when no critical diffs were found, so broken extraction logic or warning-level drift went completely undetected. Replaced every guarded assertion across all 21 drift test files (89 instances) with unconditionalexpect(diffs.filter(...)).toEqual([]) - Proxy relay leaked raw upstream HTTP status codes — 5 recorder relay paths in
recorder.tsandagui-recorder.tsforwarded raw upstream codes (429, 503, 401, 201, etc.) to aimock clients, exposing provider implementation details. Normalized to 200 for success and 502 for errors; fixture recording preserves the original status for fidelity
match.systemMessagefixture matcher — gate a fixture on a substring (or regexp) found inside the concatenated text of everysystemrole message in the request. Hosts that plumb dynamic context (persona, agent-context entries, dynamic config) through system messages can now narrow a fixture to a specific context state; when the caller changes that state the fixture stops matching and the request falls through to the next fixture or upstream proxy instead of silently returning a stale baked response. JSON form:"match": { "userMessage": "Who am I?", "systemMessage": "name=Atai" }. Programmatic form acceptsstring | RegExp.- Status code normalization tests — 5 tests verifying proxy relay normalization (201→200, 429→502, 503→502, 401→502, SSE 429→502) with fixture preservation assertions; 2 existing tests updated to expect normalized 502
- Responses API request conversion — forward
max_output_tokensandresponse_formatfrom Responses requests to the underlying Chat Completions call - Gemini request conversion — forward
maxOutputTokens,topP,topKfromgenerationConfig; remove syntheticfunctionCall.idthat real Gemini does not produce - Cohere request conversion — structured content (images, documents), native tool definitions,
temperature,max_tokens, andstop_sequencesnow forwarded - Ollama request conversion —
tool_callson assistant messages, base64imageson user messages,systemparameter on/api/generate - Chat Completions error responses — add
paramfield per OpenAI error spec - Moderation response shape — correct
categoriesandcategory_scoresto match the real OpenAI moderation object (boolean flags + float scores) - Transcription verbose response — add
task,duration,segments,wordsfields forverbose_jsonformat - Search response shape — add
statusfield to search results - Rerank response shape — wrap results in
{ results: [...] }withrelevance_scoreper result - Realtime WebSocket — add
previous_item_idto conversation items, correct event ID prefixes, add missing fields on session and response events - Gemini Live WebSocket —
generationConfigalias forgeneration_config,turnCompleteserver event, correct gRPC status codes in error events, completehttpToGrpcmapper - Anthropic thinking blocks — add
signaturefield tothinkingcontent blocks andsignature_deltaevent type for extended thinking with signatures
- Drift tests for 9 multimedia/auxiliary providers — images, speech/TTS, transcription/STT, moderation, ElevenLabs audio, fal.ai, fal.ai queue lifecycle, video, rerank
- Error shape drift tests — OpenAI Chat, Anthropic Claude, Gemini, Cohere error response shapes validated against SDK types
- Reasoning/thinking drift tests — OpenAI Chat
reasoning_effort, OpenAI Responsesreasoning, Anthropicthinkingcontent blocks, Geminithinking_config
- Converse stream: spurious
typefield in contentBlockDelta and contentBlockStart —deltaobjects contained a Claude Messages APItypefield (text_delta,thinking_delta) that is not a member of the Converse API's tagged union. botocore's single-member union parser rejected the extra field withResponseParserError. Also fixed reasoning deltas to usereasoningContent(Converse format) instead ofthinking(Claude format). (Issue #165, reported by @KMiya84377) - Converse:
inferenceConfig.maxTokenssilently dropped —converseToCompletionRequestnow forwardsmaxTokenstomax_tokens - Converse: non-streaming responses missing
metrics— Addedmetrics: { latencyMs: 0 }to all 3 non-streaming converse response builders, matching the streaming path and AWS ConverseResponse spec
- Bedrock drift test expansion — invoke-with-response-stream drift test with binary frame parsing and Anthropic-native event shape comparison. Converse-stream SDK shapes for tool call (
toolUsestart/delta) and reasoning (reasoningContentstart/delta) variants. Three-way triangulate comparisons for all variants.
- Recorder: multi-turn runs collapsed to a single fixture.
--recordmode wrote onlyuserMessageto the saved fixture'smatchblock, so two LLM calls in the same agent run that share the user message (the canonical tool-call → tool-result → follow-up shape) collided in the in-memory fixture cache: as soon as turn 0 was recorded, turn 1 matched it on the sameuserMessagesubstring, the recorder was skipped, and the follow-up text turn was silently lost. Replaying the resulting single fixture against the same run looped on the tool call until the framework's recursion limit fired. Recorder now also writesturnIndex(count ofassistantmessages) andhasToolResult(anyrole:"tool"present) — the matcher in 1.16.0 already accepts both — so each call in a run produces a distinct, deterministic match key. Existing single-userMessagefixtures continue to match unchanged. Surfaced as: CopilotKit's beautiful-chat showcase having to hot-patchdist/recorder.jsinside its running aimock container to make--recordproduce usable multi-turn recordings.
- Converse stream: double-wrapped Event Stream payloads —
buildBedrockStreamTextEvents,buildBedrockStreamToolCallEvents, andbuildBedrockStreamContentWithToolCallsEventsemitted payloads wrapped with the event type name (e.g.{ messageStart: { role: "assistant" } }). The:event-typeheader already carries the event name, so AWS SDK (botocore) expected flat payloads (e.g.{ role: "assistant" }). The redundant wrapping caused botocore'sBaseEventStreamParserto silently return empty dicts, producingKeyError: 'role'in downstream frameworks like Strands Agents. (Issue #162, reported by @KMiya84377) - Responses API: missing item_id on 3 SSE event types — Added
item_idtoresponse.output_text.done,response.content_part.added, andresponse.content_part.doneevents, matching the real OpenAI Responses API shape. SDK drift shapes updated. - Chat Completions: missing logprobs on choices — Added
logprobs: nullto all streaming chunks and non-streaming choices. Removedlogprobsfrom drift allowlist so future omissions are caught. - Ollama: missing created_at on /api/chat — Added
created_atto all 6/api/chatbuilder functions (text, tool call, content+tools, and their streaming variants). The/api/generatepath already had it. - Gemini: error fixtures used Anthropic-style error codes — Test fixtures and the Gemini Live WebSocket handler now use Google canonical gRPC status codes (
RESOURCE_EXHAUSTED,INTERNAL) instead ofrate_limit_error/ERROR.
- Fixture loader: snapshot-style subdirectories skipped on boot —
loadFixturesFromDirnow recurses one level into subdirectories to load<testId>/<provider>.jsonfiles written by the snapshot recorder. Previously the loader skipped all subdirectories with a warning, so recorded fixtures could never be replayed. (Issue #161, reported by @jantimon) - CLI: immediate exit on npx/bunx — Entry-point guard now matches the
aimockbin name (not justaimock-cli.js), fixing silent exit when invoked vianpx aimockorbunx aimock. (Issue #160)
- Async fixture responses — Fixture responses can now be sync or async functions that receive the request and return the response dynamically. Enables awaiting side effects (database writes, API calls) before constructing the response — eliminating race conditions in complex multi-turn E2E tests. Works with all providers, streaming, and convenience methods (
on(),onMessage(),onTurn(),onToolCall(),onToolResult()). (Feature request by @5ebastianMeier, issue #154) - Snapshot-style recording — When
X-Test-Idis present, recorded fixtures are saved to<fixturePath>/<slugified-testId>/<provider>.jsoninstead of timestamp-based filenames. Multiple fixtures for the same test+provider merge into one file. Stable paths enable meaningful PR diffs and easy test-to-fixture mapping. (Feature request by @jantimon, issue #155)
- CORS: Firefox preflight blocked by restricted
Allow-Headers— ChangedAccess-Control-Allow-HeadersfromContent-Type, Authorizationto wildcard*, fixing Firefox's strict CORS enforcement when the OpenAI SDK sendsUser-Agentin the preflight. (Issue #158) - GitHub Action: cosmetic binary rename —
action.ymlfixtures branch referenced the legacyllmockbinary (still functional); updated toaimockfor consistency - GitHub Action: hardcoded URLs in docs examples — All workflow examples now use
steps.<id>.outputs.urlinstead of hardcodedhttp://127.0.0.1:4010
- Chaos testing in proxy mode — Pre-flight chaos (drop/disconnect) prevents upstream contact; post-response chaos (malformed) corrupts relay body after recording the real upstream response. SSE bypass tracked via
aimock_chaos_bypassed_totalmetric. Explicitsourcelabel (fixture/proxy/internal) on all chaos Prometheus counters and journal entries. - fal.ai as first-class provider — General fal.ai handler (
src/fal.ts) supporting arbitrary JSON request/response payloads (image, video, motion, music, etc.) alongside the existing audio-onlyfal-audio.ts. Routes byx-fal-target-hostheader. Queue lifecycle (submit→status→result→cancel), sync run, storage upload stub.RawJSONResponsetype for verbatim JSON fixture preservation. Convenience methodsonFalQueue()andonFalRun(). Record and replay support. (PR #153, tombeckenham)
- Fixture validation for RawJSONResponse —
validateFixtures()now recognizesRawJSONResponse, fixing the record→restart→replay cycle for fal.ai fixtures loaded from disk
- Gemini Interactions API — 12th LLM provider. Full record/replay support for Google's Gemini Interactions streaming API (
/v1beta/models/{model}:streamGenerateContent), including multi-turn conversations, function calling, streaming text, and safety metadata. Drift tests, integration tests, and documentation included. (PR #139) - AG-UI interrupt-aware types —
AGUIInterrupt,AGUIResumeEntry, andAGUIRunFinishedOutcome(discriminated union: success | interrupt) from ag-ui PR #1569.outcomefield onAGUIRunFinishedEvent,resumefield onAGUIRunAgentInput. - AG-UI convenience builders —
buildActivityDelta,buildToolCallChunk,buildRawEvent,buildCustomEvent,buildReasoningChunk,buildReasoningEncryptedValue. ./aguisubpath export —agui-stub.tswired intotsdown.config.tsandpackage.jsonexports, matching./a2a,./mcp,./vectorpattern.- Complete AG-UI type exports — All event types (reasoning, step, thinking, raw, custom, chunk),
AGUIBuildOpts,matchesAGUIFixture,AGUIReasoningEncryptedValueSubtype,AGUIMessageRolenow exported from package root. "warn"log level — New log level between"silent"and"info"with proper hierarchy.AGUIMockOptions.logLeveloption added; AG-UI mock defaults to"warn"instead of"silent".- Non-speech audio generation — Mock support for ElevenLabs sound effects (
/v1/sound-generation) and music (/v1/music/*), fal.ai queue-based audio (/fal/queue/submit/*,/fal/queue/requests/*,/fal/run/*), Gemini HTTP audio viagenerateContent/streamGenerateContentwithinlineDataaudio parts, and Gemini Live WebSocket audio. Convenience methods:onAudio(),onSoundEffect(),onMusic(),onFalAudio(). (PR #140, closes #118) - AudioResponse broadened —
audiofield now supports bothstring(base64) and{ b64Json, contentType }object form - Gemini audio recording — Record and replay Gemini audio responses (both streaming SSE and non-streaming JSON)
- Router audio-gen filtering — Bidirectional endpoint filtering for
audio-genandfal-audioendpoint types
- AG-UI type alignment —
AGUIMessage.idandAGUIRunAgentInput.threadId/runIdnow required (was optional).AGUIToolDefinition.descriptionnow required,metadatafield added.encryptedValueanderrorfields added toAGUIMessage.AGUIMessageRoleunion covers all 7 protocol roles. - AG-UI handler hardening —
writeAGUIEventStreamlogs all caught errors (was silently swallowing non-TypeError/RangeError), preserves user-supplied timestamps (was always overwriting withDate.now()).buildCompositeResponseomitsRUN_FINISHEDwhen inner events containRUN_ERROR(protocol compliance).matchesFixtureresetslastIndexon regex beforetest(). - AG-UI recorder SSE parsing — Handle
data:lines without space after colon per SSE spec. Serialize predicate-match fixtures as__NO_USER_MESSAGE__sentinel on disk instead of producing catch-all{}. Return actual HTTP status from proxy (was always boolean/200). Forward upstream content-type on non-2xx responses instead of SSE headers. - AG-UI mock error handling —
readBodywrapped in try/catch (was unguarded await producing opaque 500s). JSON parse errors now include detail in 400 response. Proxy status correctly journaled. - Drift auto-remediation pipeline —
drift-report-collectornow clones canonical ag-ui repo and runs AG-UI schema drift test alongside HTTP API drift.test-drift.ymlSlack notification distinguishes AG-UI schema vs HTTP API drift vs infra error; usesjqfor safe JSON construction.fix-drift.ymlclones ag-ui before collection, addscontinue-on-erroron autofix withsuccess()guard on PR creation.fix-drift.tscorrectedsrc/agui.tsreferences tosrc/agui-handler.ts, added SDK type to drift prompt, flexible changelog title matching, ENOENT-specific catch insyncDescriptionFromReadme, exit code 4 for no-changes (was collision with exit 2), version bump wrapped in try/catch. - AG-UI schema drift test hardening —
skipIfguard checks both source files (was only canonical). Field regex handles multi-arg Zod types. Recursive parent field resolution for multi-level inheritance. Comment lines stripped before field matching. Base fields parsed dynamically from source with hardcoded fallback.
- Router:
toolCallIdmatched stale tool messages from history. The matcher previously usedgetLastMessageByRole(messages, "tool"), so once a conversation contained any prior tool result, every subsequent request still had a "last tool message" buried in history — atoolCallIdfixture could win and shadowuserMessagematchers for new user turns. Tightened to require the tool message to be the last message in the request (which is the only state in which the LLM is being asked to respond to a tool result). Surfaced as: in CopilotKit's beautiful-chat showcase, clicking a second suggestion replayed the first chart's content fixture instead of producing a new tool call.
- Responses API:
response.function_call_arguments.doneevents now includeitem_idfield, matching the real OpenAI Responses API shape. Without this, TanStack AI's OpenAI adapter emitsTOOL_CALL_ENDwithtoolCallId: undefined, breaking ag-ui verify middleware.
- Bedrock invoke native stream format —
invoke-with-response-streamnow emits Anthropic-native snake_case payloads (content_block_delta,input_json_delta) wrapped in Bedrock EventStreamchunkframes, instead of Converse-style camelCase events. Converse-stream retains camelCase format. (PR #144, sf-jin-ku) - Bedrock invoke false-green test — Reasoning negative test used wrong event filters, masking a real bug; corrected to match actual stream shape (PR #144)
- Bedrock invoke/stream hardening — Set
completionReq.stream = truein streaming handler; use deterministictool_use_${index}fallback IDs; changetextContent || nullto?? nullto preserve empty strings; warn on unsupported content block types and unexpected roles; add webSearches warning on tool-call-only responses - Converse stream shape alignment — Wrap
contentBlockStopandmessageStoppayloads to match real AWS Converse API; remove duplicate top-levelcontentBlockIndexfromcontentBlockStart/contentBlockDelta; add trailingmetadataevents (usage + latencyMs) to all stream builders - Converse request conversion — Filter empty-string text blocks in all paths; unwrap
inputSchemafrom{ json: {...} }Converse API wrapper; setcompletionReq.stream = truein streaming handler; add content-loss warnings for non-text blocks; fix error type||to??
- Extract shared test helpers (
createMockReq/createMockRes/createDefaults) intohelpers/mock-res.ts - Convert reasoning-all-providers tests to per-test server lifecycle
- Add content+toolCalls streaming integration coverage for both invoke and converse paths (PR #144)
- Responses API: item_reference dropped —
responsesInputToMessages()now synthesizes an assistant message with a matchingfunction_callwhen afunction_call_outputhas no prior matching call, preventing item_reference loss - Responses API: annotations missing — Added
annotations: []to all fouroutput_textcontent items (streaming.added,.done, prefix, and non-streaming) for schema conformance - Responses API: item_id missing on reasoning events — Added
item_idtoreasoning_summary_part.added,reasoning_summary_part.done, andreasoning_summary_text.doneevents - Responses API: web_search_call action missing type — Changed
action: { query }toaction: { type: "search", query }in both streaming events and output prefix - Responses API: item_reference for text messages — Extended item_reference handling to cover assistant text messages, not just function_call_output compensation
- Responses API: multi-fco assistantCount inflation — Fixed backward scan in
responsesInputToMessages()to find and append to existing assistant messages with tool_calls instead of creating duplicates
- Debug logging across all LLM handlers — Added
logger.debug("Fixture matched: ...")on match andlogger.debug("No fixture matched...")on no-match to: server.ts, responses.ts, messages.ts, gemini.ts, bedrock.ts, bedrock-converse.ts, cohere.ts, ollama.ts, embeddings.ts, images.ts, speech.ts, transcription.ts, video.ts
turnIndexmatch criterion: Stateless conversation-depth matching — countsrole: "assistant"messages in the request's message array. Use for multi-turn conversation flows in shared/deployed instances wheresequenceIndexcounters break under concurrency.turnIndex: 0matches the first turn (no prior assistant messages),turnIndex: 1the second, etc.hasToolResultmatch criterion: Stateless boolean —truewhen anyrole: "tool"message exists in the request,falsewhen none do. Simplest option for 2-step HITL flows (tool call → tool result → follow-up).onTurn(turn, pattern, response, opts)convenience method on the programmatic API.
- Recorder: crash hardening (headersSent guards, clientDisconnected tracking), preserve content alongside toolCalls, Cohere v2 native detection, tool-call ID extraction from 5 providers, reasoning/thinking extraction from 4 providers, multi-block text join (filter+join instead of find), thinking-only and empty-content response handling, Ollama /api/generate format detection, streaming collapse reasoning propagation.
- Bedrock/Converse: ContentWithToolCallsResponse support, ResponseOverrides wired into all non-streaming and streaming builders, Converse-wrapped stream event format, text_delta type field on text deltas, proper error envelope on Converse errors, webSearches warnings.
- Cohere v2: reasoning in all builders + streaming, webSearches warnings, response_format forwarding, assistant tool_calls preservation, full ResponseOverrides (finish_reason, usage, id) in non-streaming and streaming paths.
- Server: readBody 10MB size limit, control API error detail, one-shot error fixture race fix, normalizeCompatPath clarity, fixtures_loaded gauge updates on mutations.
- Competitive matrix: HTML pipeline fixed (computeChanges, applyChanges, updateProviderCounts, extractFeatures all aligned with actual DOM structure).
- CI workflows: --auto merge (respects branch protection), Slack secrets via env vars, script injection prevention in notify-pr.yml, portable grep.
- Router: RegExp g-flag lastIndex reset prevents alternating match/no-match.
- Jest/Vitest: save/restore pre-existing env vars in afterAll, loadFixtures console.warn on failure.
- Gemini: tool_call_id collision fix (shared callCounter), thought-part filtering.
- Ollama: ContentWithToolCallsResponse support, default stream:true, field validation.
- Removed the
preinstall: npx only-allow pnpmscript from the published package. That hook was intended to guide contributors cloning the monorepo toward pnpm, but it got bundled into the published tarball and fired duringnpm install -g @copilotkit/aimock, aborting the install withsh: only-allow: not foundbefore npm could even resolve the binary. The guard belongs on the monorepo root, not inside the shipped package. Unblocks CopilotKit's docs CI (and any other consumer installing aimock via npm).
--proxy-onlymode now accepts URL-only--fixturessources without requiring a local filesystem path. Previously the first--fixturesvalue was always checked as a record-destination base path, which rejected all-URL invocations even though proxy-only mode doesn't write recordings to disk. The check now fires only for--recordmode where a writable destination is actually required. Same fix applied to the parallel--agui-proxy-onlyCLI path. Unblocks the showcase-aimock Railway service which runs aimock in proxy-only mode with remote GitHub raw fixture URLs and no local fallback.
--fixturesnow acceptshttps://andhttp://URLs to JSON fixture files in addition to filesystem paths. Fetches at boot, parses, and registers the remote fixture as if loaded from disk. On-disk cache at~/.cache/aimock/fixtures/<sha256-of-url>/(honoring$XDG_CACHE_HOME) provides resilience against transient upstream failures: with--validate-on-load, a fetch failure with a valid cached copy logs a warning and continues; without a cache, the process exits non-zero. HTTP fetch has a hard-coded 10s timeout and a 50 MB body size cap (enforced incrementally so a lyingContent-Lengthcannot bypass it). Onlyhttps://andhttp://schemes are accepted —file://,ftp://, etc. are rejected with a clear error. The flag is now repeatable; multiple sources are loaded and concatenated. Tarball (.tar.gz) and zip URL support intentionally deferred to a future release.- Private-address denylist for remote
--fixturesURLs: fetches to loopback (127.0.0.0/8,::1), link-local (169.254.0.0/16,fe80::/10), RFC1918 (10/8,172.16/12,192.168/16), CGNAT (100.64/10), cloud-metadata (169.254.169.254), ULA (fc00::/7), multicast, and other reserved ranges are rejected with a clear fail-loud error. Hostnames are resolved and every returned address is checked. SetAIMOCK_ALLOW_PRIVATE_URLS=1to opt out (required for local dev / tests that target127.0.0.1). - HTTP redirects are rejected (fail-loud) for remote
--fixturesURLs to prevent scheme-bypass (a 3xxLocation:pointing atfile://orjavascript:would otherwise sidestep the scheme gate and SSRF denylist). Configure the upstream to serve the final URL directly — GitHub raw content URLs already do this.
- README DevX: Quick Start sets
OPENAI_BASE_URL+OPENAI_API_KEYbefore SDK construction with an inline ordering warning; Docker one-liner uses absolute$(pwd)/fixtures:/fixturespath;LLMockclass name asymmetry after the v1.7.0 package rename is explained inline; Multimedia and Protocol-Mock feature bullets now link to each individual feature page. - Fixtures page: Vertex AI added to Provider Support Matrix; Ollama Reasoning marked as supported (was incorrectly "—" since v1.8.0);
finishReasonResponses-API mapping fully documented;toolNamescope clarified; shadowing-warning format matches actual validator output; Azure-inherits-OpenAI override support footnoted. - Record & Replay page: Docker examples use absolute
$(pwd)paths; Rustasync-openaiexample corrected toClient::with_config(OpenAIConfig::new().with_api_base(...))form;enableRecording({ proxyOnly: true })disambiguated; pseudocode annotated as simplified;enableRecordingexample includesmock.stop()cleanup; stale 2025 timestamp replaced with generic placeholder. - Sidebar: TOC id-assignment now runs unconditionally (previously skipped on pages with fewer than 4 headings, silently breaking cross-page anchor links to short pages).
- Historical CHANGELOG: v1.14.1 Railway-specific language scrubbed; v1.14.2
--journal-max=-1rejection andcreateServer()default flip annotated with BREAKING / BEHAVIOR CHANGE markers; all 15 historical version entries standardized on Keep-a-Changelog categories (Added/Changed/Fixed/Removed) instead of mixed Changesets-style. - package.json:
engines.noderaised to>=24.0.0to match OIDC publish requirement;preinstall: only-allow pnpmguard added; deprecated@google/generative-aiswapped for@google/genai;filesincludesCHANGELOG.md;repository.urlcanonicalized;typesVersionsgains.d.ctsentries; optionalpeerDependenciesforvitest/jestadded;prepare: husky || truetightened tohusky;releasescript gainspnpm test && pnpm lintpre-check.
- Stray
package-lock.json— repo is pnpm-only, now enforced viapreinstall.
- Recorder no longer buffers SSE (
text/event-stream) upstream responses before relaying to the client.proxyAndRecordaccumulated all upstream chunks and replayed them via a singleres.end(), collapsing multi-frame streams into one client-visible write and breaking progressive rendering for downstream consumers (notably showcase--proxy-onlydeployments). SSE responses now stream chunk-by-chunk to the client while still being tee'd into the recording buffer; non-SSE behavior is unchanged.
- Multi-turn conversations documentation page covering the tool-round idiom, matching semantics across turns, and how to author/record multi-turn fixtures.
- Matching Semantics section on the Fixtures page documenting last-message-only matching, first-wins file order, substring-vs-exact matching, and shadowing warnings.
- Recording guidance for multi-turn conversations on the Record & Replay page.
- CLI Flags table on the Record & Replay page expanded to cover
-f/--fixtures,--journal-max,--fixture-counts-max,--agui-*,--chaos-*,--watch,-p,-h,--log-level,--validate-on-load. - README note clarifying that the
llmockCLI bin is a legacy alias pointing at a narrower flag-driven CLI without--configorconvertsupport.
- Docker examples in the Record & Replay guide no longer prefix
npx @copilotkit/aimockbefore the image ENTRYPOINT (the four snippets would have failed with strict parseArgs rejecting positional args). - Auth Header Forwarding documentation now reflects the strip-list behavior that has been in place since v1.6.1 (all headers forwarded except hop-by-hop and client-set).
requestTransformexample fixture key no longer carries an undocumented load-bearing trailing space.- Completed the Claude model-id migration (v1.14.3) for the remaining test fixtures that still referenced
claude-sonnet-4-20250514. - README LLM Providers count and migration-page comparisons restored to the "11+" form with accurate enumeration (OpenAI Chat / Responses / Realtime, Claude, Gemini REST / Live WS, Azure, Bedrock, Vertex AI, Ollama, Cohere). The earlier "8" collapse was incorrect: competitors count endpoint/protocol variants separately, and "8" undersold aimock's actual coverage. Provider Support Matrix on the Fixtures page gains a dedicated Vertex AI column.
- Corrected
toolCallIdmatching semantics on the Fixtures page to describe the "lastrole: "tool"message" rule fromrouter.ts(not "last message being a tool"). - Added
-h 0.0.0.0to every Docker example in the README and Record & Replay page so the default127.0.0.1host bind doesn't silently break-pport mapping when user args override the image CMD. - Extended the Docker host-bind fix across all migration guides, tutorials, and the Docker/aimock-cli/metrics/chaos-testing pages — every Docker example that passes user args now includes
-h 0.0.0.0sodocker -pport mapping works. - Updated
--journal-maxdefault wording on the Record & Replay page to reflect post-v1.14.2 behavior (finite1000cap for bothserveandcreateServer(); only directnew Journal()instantiation remains unbounded). - Stripped redundant
npx @copilotkit/aimock/aimockprefixes from Docker examples in migration pages (mokksy, vidaimock, mock-llm, piyook, openai-responses); all were silently broken under strict parseArgs because the prefix became a positional arg to the image'snode dist/cli.jsentrypoint. - Replaced
--configDocker examples acrossdocs/aimock-cli,docs/metrics,docs/chaos-testing, and migration guides with flag-driven Docker equivalents or explicit npx/local-install notes (the published image's ENTRYPOINT runs thellmockCLI which does not support--config). - Synchronized LLM provider counts across all migration pages to the "11+" form with accurate variant-level enumeration, restoring competitor-equivalent counting (e.g. VidaiMock "11+", Mokksy "11 vs 5").
- Corrected the
sequenceIndexgotcha on/multi-turn—validateFixturesdoes not factorsequenceIndex,toolCallId,model, orpredicateinto the duplicate-userMessagewarning; the warning is advisory when a runtime differentiator is present. - Fixed the Programmatic Recording example on
/record-replayto stop contradicting itself by pairingproxyOnly: truewithfixturePath; now shows record mode and proxy-only mode as two distinct examples. - Reconciled provider-count phrasing across migration pages — mock-llm lead paragraph no longer says "9 more providers", enumerated lists no longer trail the count with "and OpenAI-compatible providers" / "and more". Aligned the
validateFixturesshadowing wording between the Fixtures and Multi-Turn pages (both now correctly describe the warning as advisory when a runtime differentiator is present). - Replaced broken
class="cmt"CSS class with correctclass="cm"acrossdocs/cohere,docs/test-plugins,docs/vertex-ai,docs/ollama,docs/record-replay, anddocs/chaos-testingcode blocks (21 occurrences) —.cmtis not defined indocs/style.css, so these code-block comments were rendering as default text instead of the dimmed comment color.
- Microsoft Agent Framework (MAF) integration guide with Python and .NET examples.
- Generic
.code-tabslanguage switcher with cross-section sync and localStorage persistence.
- Updated Claude model references from
claude-sonnet-4-20250514(retiring 2026-06-15) toclaude-sonnet-4-6.
BREAKING — CLI flag parsing:
--journal-max=-1(and--fixture-counts-max=-1) no longer silently maps to "unbounded"; it is now rejected with a clear error. Migration: drop the flag entirely, or pass--journal-max=0/--fixture-counts-max=0if you intended unbounded retention.⚠ BEHAVIOR CHANGE (should have been MINOR per SemVer) —
createServer()programmatic defaults forjournalMaxEntriesandfixtureCountsMaxTestIdsflipped from unbounded to finite caps (1000 / 500). Auto-update consumers on long-running embedders: review your retention assumptions and opt in to unbounded explicitly by passing0if that was the prior relied-upon behavior. Released as a PATCH; in retrospect this warranted a MINOR bump.
Journal.getFixtureMatchCount()is now read-only: calling it with an unknown testId no longer inserts an empty map or triggers FIFO eviction of a live testId. Reads never mutate cache state.- CLI rejects negative values for
--journal-maxand--fixture-counts-maxwith a clear error (previously silently treated as unbounded). Breaking for anyone passing-1expecting unbounded — see note above.
createServer()programmatic default:journalMaxEntriesandfixtureCountsMaxTestIdsnow default to finite caps (1000 / 500) instead of unbounded. Long-running embedders that relied on unbounded retention must now opt in explicitly by passing0. Back-compat with test harnesses usingnew Journal()directly is preserved (they still default to unbounded). Note: this is a behavior change that in retrospect warranted a MINOR bump rather than PATCH.
- New
--fixture-counts-max <n>CLI flag (default 500) to cap the fixture-match-counts map by testId.
- Cap in-memory journal (and fixture-match-counts map) to prevent heap OOM under sustained load.
Journal.entrieswas unbounded, causing heap growth ~3.8MB/sec to 4GB → OOM in ~18 minutes on long-running production deployments. Default cap for CLI (serve) is now 1000 entries; programmaticcreateServer()remains unbounded by default (back-compat). See--journal-maxflag.
- Response template merging — override
id,created,model,usage,finishReason,role,systemFingerprinton fixture responses across all 4 provider formats (OpenAI, Claude, Gemini, Responses API) (#111) - JSON auto-stringify — fixture
argumentsandcontentfields accept objects that are auto-stringified by the loader, eliminating escaped JSON pain (#111) - Migration guide from openai-responses-python (#111)
- All fixture examples and docs converted to object syntax (#111)
ResponseOverridesfield validation invalidateFixtures— catches invalid types forid,created,model,usage,finishReason,role,systemFingerprint
onTranscriptiondocs now show correct 1-argument signaturevalidateFixturesnow recognizes ContentWithToolCalls and multimedia response types
- GitHub Action for one-line CI setup —
uses: CopilotKit/aimock@v1with fixtures, config, port, args, and health check (#102) - Fixture converters wired into the CLI —
npx @copilotkit/aimock convert vidaimockandnpx @copilotkit/aimock convert mockllmas first-class subcommands (#102) - 30 npm keywords for search discoverability (#102)
- Fixture gallery with 11 examples covering all mock types, plus browsable docs page at /examples (#102)
- Vitest and jest plugins for zero-config testing —
import { useAimock } from "@copilotkit/aimock/vitest"(#102)
- Strip video URLs from README for npm publishing (#102)
- Multimedia endpoint support: image generation (OpenAI DALL-E + Gemini Imagen), text-to-speech, audio transcription, and video generation with async polling (#101)
match.endpointfield for fixture isolation — prevents cross-matching between chat, image, speech, transcription, video, and embedding fixtures (#101)- Bidirectional endpoint filtering — generic fixtures only match compatible endpoint types (#101)
- Convenience methods:
onImage,onSpeech,onTranscription,onVideo(#101) - Record & replay for all multimedia endpoints — proxy to real APIs, save fixtures with correct format/type detection (#101)
_endpointTypeexplicit field onChatCompletionRequestfor type safety (#101)- Comparison matrix and drift detection rules updated for multimedia (#101)
- 54 new tests (32 integration, 11 record/replay, 12 type/routing)
AGUIMock— mock the AG-UI (Agent-to-UI) protocol for CopilotKit frontend testing. All 33 event types, 11 convenience builders, fluent registration API, SSE streaming with disconnect handling (#100)- AG-UI record & replay with tee streaming — proxy to real AG-UI agents, record event streams as fixtures, replay on subsequent requests. Includes
--proxy-onlymode for demos (#100) - AG-UI schema drift detection — compares aimock event types against canonical
@ag-ui/coreZod schemas to catch protocol changes (#100) --agui-record,--agui-upstream,--agui-proxy-onlyCLI flags (#100)
- Section bar from docs pages (cleanup)
--proxy-onlyflag — proxy unmatched requests to upstream providers without saving fixtures to disk or caching in memory. Every unmatched request always hits the real provider, preventing stale recorded responses in demo/live environments (#99)
- Per-test sequence isolation via
X-Test-Idheader — each test gets its own fixture match counters, wired through all 12 HTTP handlers and 3 WebSocket handlers. No more test pollution from shared sequential state (#93) - Combined
content + toolCallsin fixture responses — newContentWithToolCallsResponsetype and type guard, supported across OpenAI Chat, OpenAI Responses, Anthropic Messages, and Gemini, with stream collapse support (#92) - OpenRouter
reasoning_contentsupport in chat completions (#88) - Demo video in README (#91)
- CI: Slack notifications for drift tests, competitive matrix updates, and new PRs (#86)
- Docs: reasoning and webSearches rows in Response Types table
web_search_callitems now useaction.querymatching real OpenAI API format (#89)- Homepage URL cleaned up (remove
/index.htmlsuffix) (#90) - Record & Replay section title now centered and terminal panel top-aligned (#87)
- CI: use
pull_request_targetfor fork PR Slack alerts
requestTransformoption for deterministic matching and recording — normalizes requests before matching (strips timestamps, UUIDs, session IDs) and switches to exact equality when set. Applied across all 15 provider handlers and the recorder. (#79, based on design by @iskhakovt in #63)- Reasoning/thinking support for OpenAI Chat Completions —
reasoningfield in fixtures generatesreasoning_contentin responses and streamingreasoningdeltas (#62 by @erezcor) - Reasoning support for Gemini (
thoughtParts), AWS Bedrock InvokeModel + Converse (thinkingblocks), and Ollama (thinktags) (#81) - Web search result events for OpenAI Responses API (#62)
- Open Graph image and meta tags for social sharing
- CI:
npmenvironment to release workflow for deployment tracking;workflow_dispatchadded to Python test workflow
- Updated all GitHub repo URLs from CopilotKit/llmock to CopilotKit/aimock
- Reframed drift detection docs for users ("your mocks never go stale") with restored drift report output
- Migration page examples: replaced fragile
time.sleepwith health check loops against/__aimock/health; fixed Python npx examplestderr=subprocess.PIPEdeadlock (#80) - Stream collapse now handles reasoning events correctly
- MCPMock — Model Context Protocol mock with tools, resources, prompts, session management
- A2AMock — Agent-to-Agent protocol mock with SSE streaming
- VectorMock — Pinecone, Qdrant, ChromaDB compatible vector DB mock
- Search (Tavily), rerank (Cohere), and moderation (OpenAI) service mocks
/__aimock/*control API for external fixture managementaimockCLI with JSON config file support- Mount composition for running multiple protocol handlers on one server
- JSON-RPC 2.0 transport with batch and notifications
aimock-pytestpip package for native Python testing- Converter scripts:
convert-vidaimock(Tera → JSON) andconvert-mockllm(YAML → JSON) - Drift automation skill updates —
fix-drift.tsnow updatesskills/write-fixtures/SKILL.mdalongside source fixes - Docker: dual-push
ghcr.io/copilotkit/aimock+ghcr.io/copilotkit/llmock(compat) - 6 migration guides: MSW, VidaiMock, mock-llm, piyook, Python mocks, Mokksy
- Docs: sidebar.js, cli-tabs.js, section bar, competitive matrix with 25 rows
- Renamed package from
@copilotkit/llmockto@copilotkit/aimock - Renamed Prometheus metrics to
aimock_*with new MCP/A2A/Vector counters - Rebranded logger
[aimock], chaos headersx-aimock-chaos-*, CLI startup message - Helm chart renamed to
charts/aimock/ - Homepage redesigned (Treatment 3: Progressive Disclosure)
- Record proxy now preserves upstream URL path prefixes — base URLs like
https://gateway.company.com/llmnow correctly resolve togateway.company.com/llm/v1/chat/completionsinstead of losing the/llmprefix (PR #57) - Record proxy now forwards all request headers to upstream, not just
Content-Typeand auth headers. Hop-by-hop headers (connection,keep-alive,transfer-encoding, etc.) and client-set headers (host,content-length,cookie,accept-encoding) are still stripped (PR #58) - Recorder now decodes base64-encoded embeddings when
encoding_format: "base64"is set in the request. Python's openai SDK uses this by default. Previously these were saved asproxy_errorfixtures (PR #64) - Guarded base64 embedding decode against corrupted data (non-float32-aligned buffers fall through gracefully instead of crashing)
--summaryflag on the competitive matrix update script for markdown-formatted change summaries
- Provider-specific endpoints: dedicated routes for Bedrock (
/model/{modelId}/invoke), Ollama (/api/chat,/api/generate), Cohere (/v2/chat), and Azure OpenAI deployment-based routing (/openai/deployments/{id}/chat/completions) - Chaos injection:
ChaosConfigtype withdrop,malformed, anddisconnectactions; supports per-fixture chaos viachaosconfig on each fixture and server-wide chaos via--chaos-drop,--chaos-malformed, and--chaos-disconnectCLI flags - Metrics:
GET /metricsendpoint exposing Prometheus text format with request counters and latency histograms per provider and route - Record-and-replay:
--recordflag andproxyAndRecordhelper that proxies requests to real LLM APIs, collapses streaming responses, and writes fixture JSON to disk for future playback
- Documentation URLs now use the correct domain (llmock.copilotkit.dev)
- Embeddings API:
POST /v1/embeddingsendpoint,onEmbedding()convenience method,inputTextmatch field,EmbeddingResponsetype, deterministic fallback embeddings from input hash, Azure embedding routing - Structured output / JSON mode:
responseFormatmatch field,onJsonOutput()convenience method - Sequential responses:
sequenceIndexmatch field for stateful multi-turn fixtures, per-fixture-group match counting,resetMatchCounts()method - Streaming physics:
StreamingProfiletype withttft,tps,jitterfields for realistic timing simulation - AWS Bedrock:
POST /model/{modelId}/invokeendpoint, Anthropic Messages format translation - Azure OpenAI: provider routing for
/openai/deployments/{id}/chat/completionsand/openai/deployments/{id}/embeddings - Health & models endpoints:
GET /health,GET /ready,GET /v1/models(auto-populated from fixtures) - Docker & Helm: Dockerfile, Helm chart for Kubernetes deployment
- Documentation website: full docs site at llmock.copilotkit.dev with feature pages and competitive comparison matrix
- Automated drift remediation:
scripts/drift-report-collector.tsandscripts/fix-drift.tsfor CI-driven drift fixes - CI automation: competitive matrix update workflow, drift fix workflow
FixtureOptsandEmbeddingFixtureOptstype aliases exported for external consumers.worktrees/to eslint ignores
- Default to non-streaming for Claude Messages API and Responses API (matching real API defaults)
- README rewritten as concise overview with links to docs site
- Write-fixtures skill updated for all v1.5.0 features
- Docs site: Get Started links to docs, comparison above reliability, npm version badge
- Gemini Live handler no longer crashes on malformed
clientContent.turnsandtoolResponse.functionResponses - Added
isClosedguard before WebSocket finalization events (prevents writes to closed connections) streamingProfilenow present on convenience method opts types (on,onMessage, etc.)- skills/ symlink direction corrected so
npm packincludes the write-fixtures skill .clauderemoved from package.json files (was dead weight — symlink doesn't ship)- Watcher cleanup on error (clear debounce timer, null guard)
- Empty-reload guard (keep previous fixtures when reload produces 0)
- Dead
@keyframes sseLineCSS from docs site
--watch(-w): File-watching with 500ms debounced reload. Keeps previous fixtures on validation failure.--log-level: Configurable log verbosity (silent,info,debug). Defaultinfofor CLI,silentfor programmatic API.--validate-on-load: Fixture schema validation at startup — checks response types, tool call JSON, numeric ranges, shadowing, and catch-all positioning.validateFixtures()exported for programmatic useLoggerclass exported for programmatic use
- WebSocket drift detection tests: TLS client for real provider WS endpoints, 4 verified drift tests (Responses WS + Realtime), Gemini Live canary for text-capable model availability
- Realtime model canary: detects when
gpt-4o-mini-realtime-previewis deprecated and suggests GA replacement - Gemini Live documented as unverified (no text-capable
bidiGenerateContentmodel exists yet)
- Responses WS handler now accepts flat
response.createformat matching the real OpenAI API (previously required a non-standard nestedresponse: { ... }envelope) - README Gemini Live response shape example corrected (
modelTurn.parts, notmodelTurnComplete)
- Live API drift detection test suite: three-layer triangulation between SDK types, real API responses, and llmock output across OpenAI (Chat + Responses), Anthropic Claude, and Google Gemini
- Weekly CI workflow for automated drift checks
DRIFT.mddocumentation for the drift detection system
- Missing
refusalfield on OpenAI Chat Completions responses — both the SDK and real API returnrefusal: nullon non-refusal messages, but llmock was omitting it
- Claude Code fixture authoring skill (
/write-fixtures) — comprehensive guide for match fields, response types, agent loop patterns, gotchas, and debugging - Claude Code plugin structure for downstream consumers (
--plugin-dir,--add-dir, or manual copy)
- README and docs site updated with Claude Code integration instructions
- Mid-stream interruption:
truncateAfterChunksanddisconnectAfterMsfixture fields to simulate abrupt server disconnects - AbortSignal-based cancellation primitives (
createInterruptionSignal, signal-awaredelay()) - Backward-compatible
writeSSEStreamoverload withStreamOptionsreturning completion status - Interruption support across all HTTP SSE and WebSocket streaming paths
destroy()method onWebSocketConnectionfor abrupt disconnect simulation- Journal records
interruptedandinterruptReasonon interrupted streams - LLMock convenience API extended with interruption options (
truncateAfterChunks,disconnectAfterMs)
- Zero-dependency RFC 6455 WebSocket framing layer
- OpenAI Responses API over WebSocket (
/v1/responses) - OpenAI Realtime API over WebSocket (
/v1/realtime) — text + tool calls - Gemini Live BidiGenerateContent over WebSocket — text + tool calls
- Future Direction section in README
- WebSocket close-frame lifecycle
- Improved error visibility across WebSocket handlers
- Function call IDs on Gemini tool call responses
- Changesets (simplified release workflow)
- 9948a8b:
prependFixture()andgetFixtures()public API methods
getTextContentfor array-format message content handling