Skip to content

Latest commit

 

History

History
987 lines (667 loc) · 86.8 KB

File metadata and controls

987 lines (667 loc) · 86.8 KB

@copilotkit/aimock

[1.37.2] - 2026-07-17

Fixed

  • Prevent Invalid string length crash on GET /__aimock/journal by capping retained request bodies at 64 KB (#308)

Changed

  • Log full error stack on unhandled request errors for prod diagnosis (#308)

[1.37.1] - 2026-07-16

Fixed

  • 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 they Buffer.concat(chunks).toString() for fixture construction to a configurable maxRecordBufferBytes (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)

[1.37.0] - 2026-07-15

Added

  • New match.toolResultContains string gate: passes when the request's LAST message is a role: "tool" result whose text content contains the substring (same last-message rule as toolCallId, 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 same tool_call_id but different result JSON. JSON-expressible, so fixture files can finally split those legs without a programmatic predicate. 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 the aimock-pytest match-level kwarg keys (aimock-pytest 0.5.0) (#299)

Fixed

  • The duplicate-userMessage shadow check no longer warns for fixtures that share a userMessage but 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; a predicate-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-pytest default server pin (_version.py) to 1.37.0 so the auto-download path (used when AIMOCK_CLI_PATH is unset) runs a server that supports the new toolResultContains match kwarg the client forwards (#299)
  • Guarded recordedTimings.interChunkDelaysMs against non-array values: a fixture whose interChunkDelaysMs is 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)

[1.36.1] - 2026-07-14

Fixed

  • aimock.json now honours llm.latency, llm.chunkSize, llm.replaySpeed and llm.logLevel, which the config loader previously accepted and ignored. Each behaves as its llmock CLI flag and createMockSuite({ llm }) equivalent. llm.replaySpeed divides 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 own replaySpeed still takes precedence. A non-positive llm.replaySpeed is ignored with a warning, matching the existing fixture-level guard (#294)

[1.36.0] - 2026-07-13

Added

  • 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>_KEY env var applied with the provider-correct wire scheme: Authorization: Bearer for OpenAI (_OPENAI_KEY), OpenRouter (_OPENROUTER_KEY), Cohere (_COHERE_KEY), Grok/xAI (_GROK_KEY), and Ollama (_OLLAMA_KEY); x-api-key for Anthropic (_ANTHROPIC_KEY); x-goog-api-key for Gemini (_GEMINI_KEY, also used for Gemini Interactions) and Veo (_VEO_KEY); api-key for Azure OpenAI (_AZURE_KEY); xi-api-key for ElevenLabs (_ELEVENLABS_KEY); and Authorization: Key for fal.ai (_FAL_KEY). Injection fires only when the caller credential is absent or dummy-prefixed (sk-aimock-, overridable via AIMOCK_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)

[1.35.1] - 2026-07-06

Fixed

  • 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)

[1.35.0] - 2026-06-27

Added

  • Native Google Veo async video lifecycle mock — POST /v1beta/models/{model}:predictLongRunning submit, GET /v1beta/operations/{name} poll through done:false → done:true, poll-count progression via veoVideo; the Files-API uri is 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 on done:true; captured operations replay later (#278)
  • Native xAI Grok Imagine async video lifecycle mock — POST /v1/videos/generations submit (JSON-only; multipart rejected with 400), GET /v1/videos/{request_id} poll through pending → done | failed | expired with synthesized progress, grokVideo progression, cost_in_usd_ticks units, 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 on done, failed persisted, expired passed through; captured jobs replay later (#278)
  • Optional blocks array on the combined content + toolCalls fixture 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_calls are separate channels the client merges) (#274)
  • Recorder captures block order and persists blocks only 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 blocks array is a complete response shape on its own, with no content/toolCalls required — builders derive the aggregate from the blocks and validateFixtures() 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 blocks array (#274)
  • validateBlocks rejects a malformed blocks array at load time — non-array, non-object entries, a type other than text/toolCall, a non-string or empty-string text block, or a toolCall block missing a name or carrying non-JSON arguments — and warns when a fixture carries both blocks and divergent content/toolCalls, so a bad array never reaches a builder mid-dispatch (#274)

[1.34.0] - 2026-06-24

Changed

  • Replay matching is content-anchored: turnIndex disambiguates, 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 turnIndexRelaxed match diagnostic + one-shot logger warn on a divergent relaxed serve (#276)
  • AIMOCK_STRICT_TURN_INDEX=1 restores the legacy strict turnIndex gate for replay (#276)

Fixed

  • Gemini Interactions mock now emits the SDK 2.x event protocol on both paths — streamed SSE (step.*, interaction.created/completed, tool args via arguments_delta) and non-streaming responses (steps/output_text); legacy 1.x recorded fixtures still parse (#279)

[1.33.0] - 2026-06-23

Added

  • --max-proxy-buffer-bytes / --max-proxy-buffer-frames flags to cap proxy buffering (#275)

Fixed

  • 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-Context to prevent fixture path traversal (#272)
  • Scope one-shot error injection, improve recorder fixture fidelity, fix matchesPattern lastIndex (#272)

[1.32.0] - 2026-06-22

Added

  • fal queue status/result responses now emit x-fal-request-id, and a billableUnits fixture field (e.g. onFalQueue(model, json, { billableUnits })) emits x-fal-billable-units on the completed result so adapters like @tanstack/ai-fal can surface usage.unitsBilled on replay. Record mode captures the upstream x-fal-billable-units header automatically, so recorded fixtures round-trip billing with no hand-editing (#269)

[1.31.0] - 2026-06-10

Added

  • 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)

Changed

  • Recording proxies now strip aimock-internal control headers on every provider path (#265)

Fixed

  • 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)

[1.30.0] - 2026-06-09

Added

  • 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 thinking block (or its signature, or a redacted_thinking block's data) is rejected with the real Anthropic 400, 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_delta wire event on the streaming paths; each thinking block's signature field non-streaming) and redacted_thinking block data are 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 Converse reasoningContent deltas and Cohere thinking content-deltas are captured into reasoning, and a recorded Anthropic reasoningSignature plus any redactedThinking payloads 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 recorded redactedThinking payloads are replayed as faithful leading redacted_thinking content blocks (streaming content_block_start/content_block_stop with the opaque data, non-streaming content-array blocks), across both streaming and non-streaming text, content+tool, and tool-only response shapes. The encrypted reasoning artifacts (reasoningSignature and redactedThinking) 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 the thinking, signature, and redacted_thinking channels together rather than leaving the encrypted blocks half-gated. Fidelity caveat: the interleaving of thinking and redacted_thinking blocks and per-block signatures are not preserved — all recorded redacted_thinking blocks replay as a leading group, and the merged thinking block carries the signature of the last thinking block that actually emitted one (a signature-less block does not clobber an earlier signature).

Changed

  • 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 / Responses reasoning_summary_text / Anthropic thinking / etc.) for models that would not emit reasoning against the real provider. A new isReasoningModel classifier and resolveReasoningForModel gate 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. The AIMOCK_REASONING_MODELS and AIMOCK_NONREASONING_MODELS env 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/turnIndex count state, the 503 message and error log read N candidate fixture(s) skipped by sequence/turn state instead of the generic no 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 — stateful match.predicate functions fire exactly once per request — and the hasToolResult shape predicate is evaluated before the sequenceIndex/turnIndex state gates, so the skip count only includes fixtures whose request shape actually matched.

Fixed

  • 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_thinking blocks with empty data are 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).

[1.29.0] - 2026-06-04

Added

  • 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() and reset_journal() client methods.
  • Control API reference documentation.

Changed

  • engines.node lowered to >=20.15.0. The runtime supports Node 20 (the published CLI runs on Node 20/22/24); the previous >=24.0.0 floor reflected an OIDC publish-time requirement, which is a CI concern handled by the publish workflow, not a runtime constraint for consumers.

Deprecated

  • POST /__aimock/reset — now a deprecated alias for /__aimock/reset/fixtures; it still performs a full reset but emits a Deprecation response header and a deprecated field in the body. Use the explicit /reset/fixtures or /reset/journal routes instead.

Fixed

  • VideoPOST /v1/videos (videos.create) now parses multipart/form-data bodies. The OpenAI SDK (>=6.28.0) sends video-create requests as multipart instead of JSON (even for File-less bodies), which previously returned a 400 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 via add_fixture/on_message/etc. (latency, chunkSize, sequenceIndex, chaos, …) are now forwarded in the correct wire shape. They were previously nested under an opts key the server ignored, so they were silently dropped.
  • aimock-pytest: _wait_for_ready now 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/_requests now clears only the request-journal entries, preserving fixture match-counts (so sequenced fixtures are not rewound) — consistent with POST /__aimock/reset/journal.

[1.28.0] - 2026-06-02

Added

  • 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.

Fixed

  • Recorder — decode streamed response chunks incrementally to prevent multibyte UTF-8 corruption; CRLF-tolerant frame-timing splitter; propagate webSearches and audio-companion fields (tool calls / content / reasoning) into recorded fixtures; log firstDroppedSample alongside 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.

[1.27.3] - 2026-05-27

Fixed

  • correct OpenAI images endpoint path from /v1/images/edit to /v1/images/edits (closes #221)
  • add Ollama /api/embed route as alias for /api/embeddings; /api/embed is the current documented endpoint (ollama/ollama docs/api.md) used by modern Ollama SDKs, while /api/embeddings is retained for backwards-compatibility. Both routes dispatch to the same handler.

[1.27.2] - 2026-05-26

Fixed

  • Fixture loader — full recursive directory traversal replaces 2-level cap; supports per-integration showcase layouts like d6/<integration>/<feature>.json.

Changed

  • Docker image — added git binary to production stage for sparse-checkout fixture fetching at boot.

[1.27.1] - 2026-05-22

Fixed

  • Router — systemMessage array exact-match logic was unsatisfiable for 2+ needles; collapsed to substring matching. Added elevenlabs-tts and translation to endpoint compatibility filter.
  • Recorder — Content-Type empty-string fallback (??||), derived EndpointType from FixtureMatch instead of duplicate union, negative guards on Gemini Interactions outputs detection, scoped turnIndex/hasToolResult to 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 around sendEvent for WebSocket close race safety.
  • WS-Gemini-Live — replaced deterministic call_gemini_${name}_${i} tool call IDs with random generateToolCallId() to prevent cross-turn collisions. Pre-computed resolvedToolCalls for wire/history ID consistency. Added unrecognized-role warning and ws.send try-catch with debug logging.
  • Gemini InteractionsinteractionsUsage honors Gemini-native field names (promptTokenCount/candidatesTokenCount/totalTokenCount). truncateAfterChunks only counts content.delta events. Added webSearches warning on tool-call branch.
  • fal-audio + ElevenLabs — all journal entries now use flattenHeaders(req.headers) instead of {}. handleSyncRun accepts RawJSONResponse fixtures from queue-walk recordings.
  • Helpers — extended resolveUsage with Gemini-native token fields. Preserved error cause in resolveResponse factory rethrow. buildEmbeddingResponse accepts optional usage. extractFormField escapes regex metacharacters.
  • Drift test infra — retry logging with body consumption, broadened redactUrl to cover api_key/apikey/token/access_token patterns, URL threaded into error messages with redaction, parseDataOnlySSE [DONE] filter fix, parseTypedSSE multi-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 when match.message is absent; fixtures without a message pattern no longer shadow other fixtures.
  • AG-UI input validation — runtime check that input.messages is an array after JSON parse; returns 400 instead of confusing downstream 404.
  • AG-UI SSE writerwriteAGUIEventStream uses logger abstraction instead of console.warn; handles non-Error throws.
  • Drift test helpersparseDataOnlySSE handles multi-line data blocks, aligned with providers.ts implementation.

[1.27.0] - 2026-05-20

Added

  • HITL continuation recording/replay supporttoolCallId matching for continuation fixtures. New toolCallId field on AGUIFixtureMatch and AGUIConfigFixture. getLastMessageIfToolResult helper and onToolResult fluent API. Recorder uses tool-result-first priority for continuation fixtures. (#233, closes #232)

Fixed

  • Walk structured content arrays in extractLastUserMessage — handle multimodal user content (AGUIMessageContentPart[]) by joining text parts and skipping non-text. Export NO_USER_MESSAGE_SENTINEL constant and AGUIMessageContentPart type. (#231)
  • Harden recorder against error responses, double-settle, and broken sentinel persistence — guard against recording fixtures from non-2xx upstream responses, add settled flag 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

[1.26.1] - 2026-05-19

Added

  • Context-based fixture routingX-AIMock-Context header scopes fixtures per integration. Fixtures with match.context only match requests carrying that context; fixtures without context remain shared. Recorder auto-captures context and routes recorded fixtures into context subdirectories.

[1.26.0] - 2026-05-18

Added

  • Timing-aware recording and replay — proxy recording captures per-frame arrival timestamps as recordedTimings on 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 N multiplier applies to all delay sources (recorded timings, streaming profiles, global latency). Per-fixture replaySpeed override. Covers SSE, NDJSON, Bedrock EventStream, and WebSocket protocols.

[1.25.0] - 2026-05-18

Added

  • Gemini embedContent endpointPOST /v1beta/models/{model}:embedContent with deterministic fallback embeddings and fixture matching
  • /v1/images/edit and /v1/images/variations endpoints — multipart form-data, same response format as generations. Closes #221
  • /v1/audio/translations endpoint — reuses transcription handler with endpoint: "translation" and task: "translate" in verbose mode
  • Ollama /api/embeddings endpoint — single-embedding response, supports both prompt and input (string or array) fields
  • Cohere /v2/embed endpoint — multi-text embedding with configurable embedding_types (float, int8, etc.)
  • ElevenLabs /v1/text-to-speech/{voice_id} endpoint — binary audio response with voice routing and onElevenLabsTTS helper
  • Streaming usage chunks — when stream_options.include_usage is set, emits a final SSE chunk with token usage before [DONE]
  • Automatic token usage estimation — responses without explicit fixture usage overrides now return estimated token counts (~4 chars/token) instead of zeros
  • Rate limiting headers on 429 responsesRetry-After, x-ratelimit-limit-*, x-ratelimit-remaining-*, x-ratelimit-reset-* headers on all error fixtures with status 429. Custom retryAfter override via fixture field
  • onTranslation convenience method — register translation fixtures with endpoint discrimination
  • onElevenLabsTTS convenience method — register ElevenLabs TTS fixtures
  • Configurable proxy timeoutsRecordConfig now accepts upstreamTimeoutMs (default 30s) and bodyTimeoutMs (default 30s). The body-idle timeout is the Node socket inactivity timer that fires req.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 no finish_reason. Lift to e.g. bodyTimeoutMs: 180_000 to record cleanly under that workload.

[1.24.1] - 2026-05-14

Fixed

  • Gemini tool-call response serializer dropped fixture-pinned tool_call.idparseToolCallPart emitted { functionCall: { name, args } } and omitted the id even when the fixture pinned one. Pairs with v1.23.1's INGEST-direction fix (#196) which preserves functionCall.id when 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 on functionCall.id to correlate follow-up functionResponse parts back to the originating call

[1.24.0] - 2026-05-14

Added

  • onFalImage(pattern, ImageResponse) — typed helper that wraps ImageResponse into fal's image envelope
  • onFalVideo(pattern, VideoResponse) — typed helper that wraps VideoResponse into fal's video envelope
  • MockServerOptions.falQueue — opt into realistic IN_QUEUE → IN_PROGRESS → COMPLETED polling progression with configurable thresholds
  • Queue status responses include logs[] (state-transition entries) and metrics.inference_time (once COMPLETED)
  • Cancel-before-completion returns 200 { status: "CANCELLED" }; cancel-after returns 400 { status: "ALREADY_COMPLETED" }
  • Result fetch before completion returns 202 with 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.timeoutMs for tuning upstream queue-walk recording cadence
  • Malformed JSON request bodies now return 400 invalid_json (consistent with all other handlers)

Fixed

  • pollsBeforeCompleted auto-defaults to pollsBeforeInProgress + 1 when 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

Changed

  • Default fal queue-walk timeout bumped from 2 min to 15 min (video generations routinely take 5–10 min)
  • persistFixture and buildFixtureMatch extracted from recorder internals and exported for reuse

[1.23.1] - 2026-05-14

Fixed

  • Gemini functionCall.id preservation — the Gemini conversation history converter generated new tool call IDs (call_gemini_*) instead of preserving the original IDs from functionCall.id. This broke toolCallId-based fixture matching on follow-up turns: the follow-up fixture couldn't match because the ID was overwritten, so the request fell through to userMessage fixtures 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)

[1.23.0] - 2026-05-13

Added

  • 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-20250514claude-opus-4) so fixtures survive version bumps. Disable with recordFullModelVersion: true. (#185)
  • Drift detection metadata — recorded fixtures include systemHash and toolsHash in a metadata block for detecting system prompt or tool definition changes since recording.
  • Prefix model matching — fixture router uses startsWith for string model matching, so model: "claude-opus-4" matches any claude-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 via OpenAI-Beta header. Default model changed to gpt-realtime-2.
  • GA Realtime modelsgpt-realtime, gpt-realtime-2, gpt-realtime-1.5, gpt-realtime-mini (and dated snapshots). Transcription/translation sessions use gpt-4o-transcribe, gpt-4o-mini-transcribe, or whisper-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.done and response.cancel events — 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.

Tests

  • 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.

[1.22.1] - 2026-05-12

Fixed

  • Strict mode checked before proxy attempt — in --proxy-only mode, the X-AIMock-Strict header had no effect because proxyAndRecord() 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

Tests

  • Bedrock mock consistency and CLI help text — corrected Bedrock mock test assertions and CLI --help output coverage

[1.22.0] - 2026-05-11

Added

  • Per-request strict mode via X-AIMock-Strict header — overrides the server-wide --strict flag per request (true/1 = strict, false/0 = lenient). When strict: fixture miss returns 503; when lenient: fixture miss proxies to real provider. Follows the X-AIMock-Chaos-* precedence pattern. Journal entries record strictOverride when the header overrides the server default. Enables the same aimock instance to serve both deterministic test probes and live demo traffic simultaneously.

Fixed

  • 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 typecompareSSESequences now 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 writableFinished before destroying upstream request
  • onHookBypassed and beforeWriteResponse callbacks 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 toolCall name/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 readBody consumption)
  • 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

Added

  • Fixture-level chaos evaluation for non-completions endpoints (ElevenLabs, fal)

Changed

  • 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

[1.21.0] - 2026-05-11

Added

  • match.systemMessage accepts string[] — 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 and RegExp forms continue to work unchanged. JSON form accepts string | string[]; programmatic form accepts string | string[] | RegExp.

[1.20.0] - 2026-05-11

Fixed

  • Drift tests passed vacuously with zero assertions — the shouldFail guard silently skipped all expect calls 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 unconditional expect(diffs.filter(...)).toEqual([])
  • Proxy relay leaked raw upstream HTTP status codes — 5 recorder relay paths in recorder.ts and agui-recorder.ts forwarded 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

Added

  • match.systemMessage fixture matcher — gate a fixture on a substring (or regexp) found inside the concatenated text of every system role 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 accepts string | 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

[1.19.5] - 2026-05-09

Fixed

  • Responses API request conversion — forward max_output_tokens and response_format from Responses requests to the underlying Chat Completions call
  • Gemini request conversion — forward maxOutputTokens, topP, topK from generationConfig; remove synthetic functionCall.id that real Gemini does not produce
  • Cohere request conversion — structured content (images, documents), native tool definitions, temperature, max_tokens, and stop_sequences now forwarded
  • Ollama request conversiontool_calls on assistant messages, base64 images on user messages, system parameter on /api/generate
  • Chat Completions error responses — add param field per OpenAI error spec
  • Moderation response shape — correct categories and category_scores to match the real OpenAI moderation object (boolean flags + float scores)
  • Transcription verbose response — add task, duration, segments, words fields for verbose_json format
  • Search response shape — add status field to search results
  • Rerank response shape — wrap results in { results: [...] } with relevance_score per result
  • Realtime WebSocket — add previous_item_id to conversation items, correct event ID prefixes, add missing fields on session and response events
  • Gemini Live WebSocketgenerationConfig alias for generation_config, turnComplete server event, correct gRPC status codes in error events, complete httpToGrpc mapper
  • Anthropic thinking blocks — add signature field to thinking content blocks and signature_delta event type for extended thinking with signatures

Added

  • 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 Responses reasoning, Anthropic thinking content blocks, Gemini thinking_config

[1.19.4] - 2026-05-08

Fixed

  • Converse stream: spurious type field in contentBlockDelta and contentBlockStartdelta objects contained a Claude Messages API type field (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 with ResponseParserError. Also fixed reasoning deltas to use reasoningContent (Converse format) instead of thinking (Claude format). (Issue #165, reported by @KMiya84377)
  • Converse: inferenceConfig.maxTokens silently droppedconverseToCompletionRequest now forwards maxTokens to max_tokens
  • Converse: non-streaming responses missing metrics — Added metrics: { latencyMs: 0 } to all 3 non-streaming converse response builders, matching the streaming path and AWS ConverseResponse spec

Added

  • 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 (toolUse start/delta) and reasoning (reasoningContent start/delta) variants. Three-way triangulate comparisons for all variants.

[1.19.3] - 2026-05-08

Fixed

  • Recorder: multi-turn runs collapsed to a single fixture. --record mode wrote only userMessage to the saved fixture's match block, 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 same userMessage substring, 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 writes turnIndex (count of assistant messages) and hasToolResult (any role:"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-userMessage fixtures continue to match unchanged. Surfaced as: CopilotKit's beautiful-chat showcase having to hot-patch dist/recorder.js inside its running aimock container to make --record produce usable multi-turn recordings.

[1.19.2] - 2026-05-07

Fixed

  • Converse stream: double-wrapped Event Stream payloadsbuildBedrockStreamTextEvents, buildBedrockStreamToolCallEvents, and buildBedrockStreamContentWithToolCallsEvents emitted payloads wrapped with the event type name (e.g. { messageStart: { role: "assistant" } }). The :event-type header already carries the event name, so AWS SDK (botocore) expected flat payloads (e.g. { role: "assistant" }). The redundant wrapping caused botocore's BaseEventStreamParser to silently return empty dicts, producing KeyError: 'role' in downstream frameworks like Strands Agents. (Issue #162, reported by @KMiya84377)
  • Responses API: missing item_id on 3 SSE event types — Added item_id to response.output_text.done, response.content_part.added, and response.content_part.done events, matching the real OpenAI Responses API shape. SDK drift shapes updated.
  • Chat Completions: missing logprobs on choices — Added logprobs: null to all streaming chunks and non-streaming choices. Removed logprobs from drift allowlist so future omissions are caught.
  • Ollama: missing created_at on /api/chat — Added created_at to all 6 /api/chat builder functions (text, tool call, content+tools, and their streaming variants). The /api/generate path 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 of rate_limit_error / ERROR.

[1.19.1] - 2026-05-07

Fixed

  • Fixture loader: snapshot-style subdirectories skipped on bootloadFixturesFromDir now recurses one level into subdirectories to load <testId>/<provider>.json files 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 aimock bin name (not just aimock-cli.js), fixing silent exit when invoked via npx aimock or bunx aimock. (Issue #160)

[1.19.0] - 2026-05-06

Added

  • 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-Id is present, recorded fixtures are saved to <fixturePath>/<slugified-testId>/<provider>.json instead 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)

Fixed

  • CORS: Firefox preflight blocked by restricted Allow-Headers — Changed Access-Control-Allow-Headers from Content-Type, Authorization to wildcard *, fixing Firefox's strict CORS enforcement when the OpenAI SDK sends User-Agent in the preflight. (Issue #158)
  • GitHub Action: cosmetic binary renameaction.yml fixtures branch referenced the legacy llmock binary (still functional); updated to aimock for consistency
  • GitHub Action: hardcoded URLs in docs examples — All workflow examples now use steps.<id>.outputs.url instead of hardcoded http://127.0.0.1:4010

[1.18.0] - 2026-05-04

Added

  • 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_total metric. Explicit source label (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-only fal-audio.ts. Routes by x-fal-target-host header. Queue lifecycle (submit→status→result→cancel), sync run, storage upload stub. RawJSONResponse type for verbatim JSON fixture preservation. Convenience methods onFalQueue() and onFalRun(). Record and replay support. (PR #153, tombeckenham)

Fixed

  • Fixture validation for RawJSONResponsevalidateFixtures() now recognizes RawJSONResponse, fixing the record→restart→replay cycle for fal.ai fixtures loaded from disk

[1.17.0] - 2026-05-04

Added

  • 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 typesAGUIInterrupt, AGUIResumeEntry, and AGUIRunFinishedOutcome (discriminated union: success | interrupt) from ag-ui PR #1569. outcome field on AGUIRunFinishedEvent, resume field on AGUIRunAgentInput.
  • AG-UI convenience buildersbuildActivityDelta, buildToolCallChunk, buildRawEvent, buildCustomEvent, buildReasoningChunk, buildReasoningEncryptedValue.
  • ./agui subpath exportagui-stub.ts wired into tsdown.config.ts and package.json exports, matching ./a2a, ./mcp, ./vector pattern.
  • Complete AG-UI type exports — All event types (reasoning, step, thinking, raw, custom, chunk), AGUIBuildOpts, matchesAGUIFixture, AGUIReasoningEncryptedValueSubtype, AGUIMessageRole now exported from package root.
  • "warn" log level — New log level between "silent" and "info" with proper hierarchy. AGUIMockOptions.logLevel option 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 via generateContent/streamGenerateContent with inlineData audio parts, and Gemini Live WebSocket audio. Convenience methods: onAudio(), onSoundEffect(), onMusic(), onFalAudio(). (PR #140, closes #118)
  • AudioResponse broadenedaudio field now supports both string (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-gen and fal-audio endpoint types

Fixed

  • AG-UI type alignmentAGUIMessage.id and AGUIRunAgentInput.threadId/runId now required (was optional). AGUIToolDefinition.description now required, metadata field added. encryptedValue and error fields added to AGUIMessage. AGUIMessageRole union covers all 7 protocol roles.
  • AG-UI handler hardeningwriteAGUIEventStream logs all caught errors (was silently swallowing non-TypeError/RangeError), preserves user-supplied timestamps (was always overwriting with Date.now()). buildCompositeResponse omits RUN_FINISHED when inner events contain RUN_ERROR (protocol compliance). matchesFixture resets lastIndex on regex before test().
  • 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 handlingreadBody wrapped 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 pipelinedrift-report-collector now clones canonical ag-ui repo and runs AG-UI schema drift test alongside HTTP API drift. test-drift.yml Slack notification distinguishes AG-UI schema vs HTTP API drift vs infra error; uses jq for safe JSON construction. fix-drift.yml clones ag-ui before collection, adds continue-on-error on autofix with success() guard on PR creation. fix-drift.ts corrected src/agui.ts references to src/agui-handler.ts, added SDK type to drift prompt, flexible changelog title matching, ENOENT-specific catch in syncDescriptionFromReadme, exit code 4 for no-changes (was collision with exit 2), version bump wrapped in try/catch.
  • AG-UI schema drift test hardeningskipIf guard 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.

[1.16.4] - 2026-04-30

Fixed

  • Router: toolCallId matched stale tool messages from history. The matcher previously used getLastMessageByRole(messages, "tool"), so once a conversation contained any prior tool result, every subsequent request still had a "last tool message" buried in history — a toolCallId fixture could win and shadow userMessage matchers 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.

[1.16.3] - 2026-04-29

Fixed

  • Responses API: response.function_call_arguments.done events now include item_id field, matching the real OpenAI Responses API shape. Without this, TanStack AI's OpenAI adapter emits TOOL_CALL_END with toolCallId: undefined, breaking ag-ui verify middleware.

[1.16.2] - 2026-04-28

Fixed

  • Bedrock invoke native stream formatinvoke-with-response-stream now emits Anthropic-native snake_case payloads (content_block_delta, input_json_delta) wrapped in Bedrock EventStream chunk frames, 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 = true in streaming handler; use deterministic tool_use_${index} fallback IDs; change textContent || null to ?? null to 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 contentBlockStop and messageStop payloads to match real AWS Converse API; remove duplicate top-level contentBlockIndex from contentBlockStart/contentBlockDelta; add trailing metadata events (usage + latencyMs) to all stream builders
  • Converse request conversion — Filter empty-string text blocks in all paths; unwrap inputSchema from { json: {...} } Converse API wrapper; set completionReq.stream = true in streaming handler; add content-loss warnings for non-text blocks; fix error type || to ??

Changed

  • Extract shared test helpers (createMockReq/createMockRes/createDefaults) into helpers/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)

[1.16.1] - 2026-04-28

Fixed

  • Responses API: item_reference droppedresponsesInputToMessages() now synthesizes an assistant message with a matching function_call when a function_call_output has no prior matching call, preventing item_reference loss
  • Responses API: annotations missing — Added annotations: [] to all four output_text content items (streaming .added, .done, prefix, and non-streaming) for schema conformance
  • Responses API: item_id missing on reasoning events — Added item_id to reasoning_summary_part.added, reasoning_summary_part.done, and reasoning_summary_text.done events
  • Responses API: web_search_call action missing type — Changed action: { query } to action: { 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

Added

  • Debug logging across all LLM handlers — Added logger.debug("Fixture matched: ...") on match and logger.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

1.16.0

Added

  • turnIndex match criterion: Stateless conversation-depth matching — counts role: "assistant" messages in the request's message array. Use for multi-turn conversation flows in shared/deployed instances where sequenceIndex counters break under concurrency. turnIndex: 0 matches the first turn (no prior assistant messages), turnIndex: 1 the second, etc.
  • hasToolResult match criterion: Stateless boolean — true when any role: "tool" message exists in the request, false when 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.

1.15.1

Fixed

  • 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.

1.14.9

Fixed

  • Removed the preinstall: npx only-allow pnpm script 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 during npm install -g @copilotkit/aimock, aborting the install with sh: only-allow: not found before 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).

1.14.8

Fixed

  • --proxy-only mode now accepts URL-only --fixtures sources without requiring a local filesystem path. Previously the first --fixtures value 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 --record mode where a writable destination is actually required. Same fix applied to the parallel --agui-proxy-only CLI path. Unblocks the showcase-aimock Railway service which runs aimock in proxy-only mode with remote GitHub raw fixture URLs and no local fallback.

1.14.7

Added

  • --fixtures now accepts https:// and http:// 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 lying Content-Length cannot bypass it). Only https:// and http:// 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 --fixtures URLs: 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. Set AIMOCK_ALLOW_PRIVATE_URLS=1 to opt out (required for local dev / tests that target 127.0.0.1).
  • HTTP redirects are rejected (fail-loud) for remote --fixtures URLs to prevent scheme-bypass (a 3xx Location: pointing at file:// or javascript: 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.

1.14.6

Changed

  • README DevX: Quick Start sets OPENAI_BASE_URL + OPENAI_API_KEY before SDK construction with an inline ordering warning; Docker one-liner uses absolute $(pwd)/fixtures:/fixtures path; LLMock class 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); finishReason Responses-API mapping fully documented; toolName scope clarified; shadowing-warning format matches actual validator output; Azure-inherits-OpenAI override support footnoted.
  • Record & Replay page: Docker examples use absolute $(pwd) paths; Rust async-openai example corrected to Client::with_config(OpenAIConfig::new().with_api_base(...)) form; enableRecording({ proxyOnly: true }) disambiguated; pseudocode annotated as simplified; enableRecording example includes mock.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=-1 rejection and createServer() 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.node raised to >=24.0.0 to match OIDC publish requirement; preinstall: only-allow pnpm guard added; deprecated @google/generative-ai swapped for @google/genai; files includes CHANGELOG.md; repository.url canonicalized; typesVersions gains .d.cts entries; optional peerDependencies for vitest/jest added; prepare: husky || true tightened to husky; release script gains pnpm test && pnpm lint pre-check.

Removed

  • Stray package-lock.json — repo is pnpm-only, now enforced via preinstall.

1.14.5

Fixed

  • Recorder no longer buffers SSE (text/event-stream) upstream responses before relaying to the client. proxyAndRecord accumulated all upstream chunks and replayed them via a single res.end(), collapsing multi-frame streams into one client-visible write and breaking progressive rendering for downstream consumers (notably showcase --proxy-only deployments). SSE responses now stream chunk-by-chunk to the client while still being tee'd into the recording buffer; non-SSE behavior is unchanged.

1.14.4

Added

  • 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 llmock CLI bin is a legacy alias pointing at a narrower flag-driven CLI without --config or convert support.

Fixed

  • Docker examples in the Record & Replay guide no longer prefix npx @copilotkit/aimock before 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).
  • requestTransform example 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 toolCallId matching semantics on the Fixtures page to describe the "last role: "tool" message" rule from router.ts (not "last message being a tool").
  • Added -h 0.0.0.0 to every Docker example in the README and Record & Replay page so the default 127.0.0.1 host bind doesn't silently break -p port 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.0 so docker -p port mapping works.
  • Updated --journal-max default wording on the Record & Replay page to reflect post-v1.14.2 behavior (finite 1000 cap for both serve and createServer(); only direct new Journal() instantiation remains unbounded).
  • Stripped redundant npx @copilotkit/aimock / aimock prefixes 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's node dist/cli.js entrypoint.
  • Replaced --config Docker examples across docs/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 the llmock CLI 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 sequenceIndex gotcha on /multi-turnvalidateFixtures does not factor sequenceIndex, toolCallId, model, or predicate into the duplicate-userMessage warning; the warning is advisory when a runtime differentiator is present.
  • Fixed the Programmatic Recording example on /record-replay to stop contradicting itself by pairing proxyOnly: true with fixturePath; 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 validateFixtures shadowing 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 correct class="cm" across docs/cohere, docs/test-plugins, docs/vertex-ai, docs/ollama, docs/record-replay, and docs/chaos-testing code blocks (21 occurrences) — .cmt is not defined in docs/style.css, so these code-block comments were rendering as default text instead of the dimmed comment color.

1.14.3

Added

  • Microsoft Agent Framework (MAF) integration guide with Python and .NET examples.
  • Generic .code-tabs language switcher with cross-section sync and localStorage persistence.

Changed

  • Updated Claude model references from claude-sonnet-4-20250514 (retiring 2026-06-15) to claude-sonnet-4-6.

1.14.2

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=0 if you intended unbounded retention.

⚠ BEHAVIOR CHANGE (should have been MINOR per SemVer)createServer() programmatic defaults for journalMaxEntries and fixtureCountsMaxTestIds flipped 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 passing 0 if that was the prior relied-upon behavior. Released as a PATCH; in retrospect this warranted a MINOR bump.

Fixed

  • 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-max and --fixture-counts-max with a clear error (previously silently treated as unbounded). Breaking for anyone passing -1 expecting unbounded — see note above.

Changed

  • createServer() programmatic default: journalMaxEntries and fixtureCountsMaxTestIds now default to finite caps (1000 / 500) instead of unbounded. Long-running embedders that relied on unbounded retention must now opt in explicitly by passing 0. Back-compat with test harnesses using new 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.

Added

  • New --fixture-counts-max <n> CLI flag (default 500) to cap the fixture-match-counts map by testId.

1.14.1

Fixed

  • Cap in-memory journal (and fixture-match-counts map) to prevent heap OOM under sustained load. Journal.entries was 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; programmatic createServer() remains unbounded by default (back-compat). See --journal-max flag.

1.14.0

Added

  • Response template merging — override id, created, model, usage, finishReason, role, systemFingerprint on fixture responses across all 4 provider formats (OpenAI, Claude, Gemini, Responses API) (#111)
  • JSON auto-stringify — fixture arguments and content fields 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)
  • ResponseOverrides field validation in validateFixtures — catches invalid types for id, created, model, usage, finishReason, role, systemFingerprint

Fixed

  • onTranscription docs now show correct 1-argument signature
  • validateFixtures now recognizes ContentWithToolCalls and multimedia response types

1.13.0

Added

  • GitHub Action for one-line CI setup — uses: CopilotKit/aimock@v1 with fixtures, config, port, args, and health check (#102)
  • Fixture converters wired into the CLI — npx @copilotkit/aimock convert vidaimock and npx @copilotkit/aimock convert mockllm as 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)

Changed

  • Strip video URLs from README for npm publishing (#102)

1.12.0

Added

  • Multimedia endpoint support: image generation (OpenAI DALL-E + Gemini Imagen), text-to-speech, audio transcription, and video generation with async polling (#101)
  • match.endpoint field 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)
  • _endpointType explicit field on ChatCompletionRequest for type safety (#101)
  • Comparison matrix and drift detection rules updated for multimedia (#101)
  • 54 new tests (32 integration, 11 record/replay, 12 type/routing)

1.11.0

Added

  • 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-only mode for demos (#100)
  • AG-UI schema drift detection — compares aimock event types against canonical @ag-ui/core Zod schemas to catch protocol changes (#100)
  • --agui-record, --agui-upstream, --agui-proxy-only CLI flags (#100)

Removed

  • Section bar from docs pages (cleanup)

1.10.0

Added

  • --proxy-only flag — 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)

1.9.0

Added

  • Per-test sequence isolation via X-Test-Id header — 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 + toolCalls in fixture responses — new ContentWithToolCallsResponse type and type guard, supported across OpenAI Chat, OpenAI Responses, Anthropic Messages, and Gemini, with stream collapse support (#92)
  • OpenRouter reasoning_content support 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

Fixed

  • web_search_call items now use action.query matching real OpenAI API format (#89)
  • Homepage URL cleaned up (remove /index.html suffix) (#90)
  • Record & Replay section title now centered and terminal panel top-aligned (#87)
  • CI: use pull_request_target for fork PR Slack alerts

1.8.0

Added

  • requestTransform option 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 — reasoning field in fixtures generates reasoning_content in responses and streaming reasoning deltas (#62 by @erezcor)
  • Reasoning support for Gemini (thoughtParts), AWS Bedrock InvokeModel + Converse (thinking blocks), and Ollama (think tags) (#81)
  • Web search result events for OpenAI Responses API (#62)
  • Open Graph image and meta tags for social sharing
  • CI: npm environment to release workflow for deployment tracking; workflow_dispatch added to Python test workflow

Changed

  • 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

Fixed

  • Migration page examples: replaced fragile time.sleep with health check loops against /__aimock/health; fixed Python npx example stderr=subprocess.PIPE deadlock (#80)
  • Stream collapse now handles reasoning events correctly

1.7.0

Added

  • 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 management
  • aimock CLI 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-pytest pip package for native Python testing
  • Converter scripts: convert-vidaimock (Tera → JSON) and convert-mockllm (YAML → JSON)
  • Drift automation skill updates — fix-drift.ts now updates skills/write-fixtures/SKILL.md alongside 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

Changed

  • Renamed package from @copilotkit/llmock to @copilotkit/aimock
  • Renamed Prometheus metrics to aimock_* with new MCP/A2A/Vector counters
  • Rebranded logger [aimock], chaos headers x-aimock-chaos-*, CLI startup message
  • Helm chart renamed to charts/aimock/
  • Homepage redesigned (Treatment 3: Progressive Disclosure)

1.6.1

Fixed

  • Record proxy now preserves upstream URL path prefixes — base URLs like https://gateway.company.com/llm now correctly resolve to gateway.company.com/llm/v1/chat/completions instead of losing the /llm prefix (PR #57)
  • Record proxy now forwards all request headers to upstream, not just Content-Type and 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 as proxy_error fixtures (PR #64)
  • Guarded base64 embedding decode against corrupted data (non-float32-aligned buffers fall through gracefully instead of crashing)

Added

  • --summary flag on the competitive matrix update script for markdown-formatted change summaries

1.6.0

Added

  • 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: ChaosConfig type with drop, malformed, and disconnect actions; supports per-fixture chaos via chaos config on each fixture and server-wide chaos via --chaos-drop, --chaos-malformed, and --chaos-disconnect CLI flags
  • Metrics: GET /metrics endpoint exposing Prometheus text format with request counters and latency histograms per provider and route
  • Record-and-replay: --record flag and proxyAndRecord helper that proxies requests to real LLM APIs, collapses streaming responses, and writes fixture JSON to disk for future playback

1.5.1

Fixed

  • Documentation URLs now use the correct domain (llmock.copilotkit.dev)

1.5.0

Added

  • Embeddings API: POST /v1/embeddings endpoint, onEmbedding() convenience method, inputText match field, EmbeddingResponse type, deterministic fallback embeddings from input hash, Azure embedding routing
  • Structured output / JSON mode: responseFormat match field, onJsonOutput() convenience method
  • Sequential responses: sequenceIndex match field for stateful multi-turn fixtures, per-fixture-group match counting, resetMatchCounts() method
  • Streaming physics: StreamingProfile type with ttft, tps, jitter fields for realistic timing simulation
  • AWS Bedrock: POST /model/{modelId}/invoke endpoint, Anthropic Messages format translation
  • Azure OpenAI: provider routing for /openai/deployments/{id}/chat/completions and /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.ts and scripts/fix-drift.ts for CI-driven drift fixes
  • CI automation: competitive matrix update workflow, drift fix workflow
  • FixtureOpts and EmbeddingFixtureOpts type aliases exported for external consumers
  • .worktrees/ to eslint ignores

Changed

  • 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

Fixed

  • Gemini Live handler no longer crashes on malformed clientContent.turns and toolResponse.functionResponses
  • Added isClosed guard before WebSocket finalization events (prevents writes to closed connections)
  • streamingProfile now present on convenience method opts types (on, onMessage, etc.)
  • skills/ symlink direction corrected so npm pack includes the write-fixtures skill
  • .claude removed 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)

Removed

  • Dead @keyframes sseLine CSS from docs site

1.4.0

Added

  • --watch (-w): File-watching with 500ms debounced reload. Keeps previous fixtures on validation failure.
  • --log-level: Configurable log verbosity (silent, info, debug). Default info for CLI, silent for 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 use
  • Logger class exported for programmatic use

1.3.3

Added

  • 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-preview is deprecated and suggests GA replacement
  • Gemini Live documented as unverified (no text-capable bidiGenerateContent model exists yet)

Fixed

  • Responses WS handler now accepts flat response.create format matching the real OpenAI API (previously required a non-standard nested response: { ... } envelope)
  • README Gemini Live response shape example corrected (modelTurn.parts, not modelTurnComplete)

1.3.2

Added

  • 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.md documentation for the drift detection system

Fixed

  • Missing refusal field on OpenAI Chat Completions responses — both the SDK and real API return refusal: null on non-refusal messages, but llmock was omitting it

1.3.1

Added

  • 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)

Changed

  • README and docs site updated with Claude Code integration instructions

1.3.0

Added

  • Mid-stream interruption: truncateAfterChunks and disconnectAfterMs fixture fields to simulate abrupt server disconnects
  • AbortSignal-based cancellation primitives (createInterruptionSignal, signal-aware delay())
  • Backward-compatible writeSSEStream overload with StreamOptions returning completion status
  • Interruption support across all HTTP SSE and WebSocket streaming paths
  • destroy() method on WebSocketConnection for abrupt disconnect simulation
  • Journal records interrupted and interruptReason on interrupted streams
  • LLMock convenience API extended with interruption options (truncateAfterChunks, disconnectAfterMs)

1.2.0

Added

  • 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

Fixed

  • WebSocket close-frame lifecycle
  • Improved error visibility across WebSocket handlers

1.1.1

Added

  • Function call IDs on Gemini tool call responses

Removed

  • Changesets (simplified release workflow)

1.1.0

Added

  • 9948a8b: prependFixture() and getFixtures() public API methods

1.0.1

Added

  • getTextContent for array-format message content handling