Changelog
Source:
CHANGELOG.mdat the repo root.
All notable changes to tapedeck are documented here. The format follows Keep a Changelog; this project adheres to semantic versioning once it reaches 1.0.0.
0.4.0 - 2026-08-11
Added
mode: 'compare'(drift detection). A fourth mode that calls the live model and loads the recorded cassette, then reports how the two diverged. A cassette is a static fixture, and a static fixture rots silently: the provider retunes the model and the recorded trajectory quietly stops being what the model does. Works fordoGenerateanddoStream, hash-addressed and named cassettes.- Three signals, all explainable: the tool-call trajectory (same names,
same order, same inputs, compared as canonical JSON so key order is
never drift), the unified finish reason, and text
(
exact/normalized/different). No similarity score to argue with. - Never writes. The cassette is left byte-identical and the caller
receives the live result, so
compareis a live run with a drift check stapled on. See compare never writes. onCompare(result)fires once per compared call with the structuredCassetteCompareResult. With no handler, the first diverging call throws the newCassetteDriftError, so drift can never pass silently; register a handler to own the policy.tapedeck compare <script> [args...]runs a command withCASSETTE_MODE=compareand propagates its exit code: a CI drift gate.- New exports:
compareCassetteResponses,summarizeResponse,formatCompareResult,CassetteDriftError, and the report types (CassetteCompareResult,ResponseSummary,ToolCallSummary, …). - Compare spans carry a
tapedeck.compare_equalattribute.
- Three signals, all explainable: the tool-call trajectory (same names,
same order, same inputs, compared as canonical JSON so key order is
never drift), the unified finish reason, and text
(
Changed
- Language-model spec v4 is now typed, not merely supported at
runtime.
cassetteMiddlewarereturned aLanguageModelV3Middleware, soai@7consumers (spec v4) had to writeas unknown as LanguageModelMiddlewareatwrapLanguageModeleven though the runtime path was green in the weekly cron. The middleware is now typed structurally (TapedeckMiddleware): it describes the fields tapedeck actually reads and stays generic in the result type, so one object is assignable toai@6's spec v3 middleware and toai@7's spec v4 middleware with no cast on either side.test/types.test-d.tsasserts assignability to both spec surfaces and towrapLanguageModelfrom the installed major, in both legs of the CI matrix.specificationVersionstays'v3': v4 hosts accept any string, v3 hosts accept only'v3'.- Runtime behaviour, cassette format, and hashes are untouched. No
CASSETTE_VERSIONbump: v1 and v2 cassettes replay byte-identically.
0.3.1 - 2026-08-10
Changed
aipeer range widened to>=6.0.0 <8.ai@7keeps the language-model middleware surface tapedeck wraps: the weekly compat cron passed typecheck, the full suite, and the record/replay round trip against 7.0.37 and 7.0.58, but the<7pin made the package uninstallable next to a freshnpm i ai. No runtime changes; cassette formats and hashes are untouched.- CI runs the suite against the latest of each supported
aimajor (6 and 7) on every push and PR, instead of only the lockfile's 6.x plus the weeklyai@latestcron.
0.3.0 — 2026-06-10
Added
- Multi-interaction named cassettes. A named cassette
(
withCassette('checkout-flow.json', …)orcassetteName) now stores every model call the test makes, keyed by request hash — a multi-step agent records all its calls into one file and replays each one distinctly, in any order. New v2 file shape:{ version: "tapedeck@0.3.0", recordedAt, interactions: [{ hash, request, response }] }. EachwithCassetterun is one recording session (re-recording starts the file fresh); a staticcassetteNameupserts by hash. Legacy v1 named cassettes keep their serve-as-is replay; hash-addressed files unchanged. - New exports:
MultiCassette,CassetteInteraction,CassetteFile,isMultiCassette,MULTI_CASSETTE_VERSION,diffCassetteFiles/formatCassetteFileDiff.tapedeck diffandtapedeck lsunderstand both formats.
Fixed
withCassettehad no effect on the published package. The two dist entry points are separate bundles, each with its own copy of the ambient-context module —withCassettepublished into oneAsyncLocalStoragewhile the middleware read another, silently falling back tolivemode. The context registry now lives onglobalThisunderSymbol.for('tapedeck.cassette-context'), and a post-build cross-bundle smoke test in CI guards the regression.
0.2.0 — 2026-06-10
Published as @nkwib/tapedeck from this release — the unscoped tapedeck name on npm belongs to an unrelated 2022 package. The CLI bin
is still tapedeck.
Added
tapedeckCLI (npx tapedeck …):record/replaya script withCASSETTE_MODEset,lscassettes, semanticdiff(exit 1 on difference), andmergewith conflict reporting (--forceoverwrites).- OTel span emission.
cassetteMiddleware({ tracer })accepts any OpenTelemetry-compatible tracer, typed structurally — still zero runtime dependencies. Spans carry mode, hash, cassette path, model, hit/miss, and chunk-count attributes; misses record the exception. - Pluggable storage.
cassetteMiddleware({ store })takes aCassetteStore; ships withfileCassetteStore()(default) andmemoryCassetteStore()for tests and edge runtimes. toFollowRoute()matcher in@nkwib/tapedeck/vitest— asserts a tool-call trajectory follows a toolroute router; structurally typed, no dependency either way.- Diff/merge as library functions:
diffCassettes,formatCassetteDiff,mergeCassetteDirs, plusparseCassette/serializeCassette.
Changed
- Edge-safe core. No static
node:fs/node:path/node:crypto: hashing uses WebCrypto (identical digests) and the file store loadsnode:fslazily. Onlynode:async_hooksremains (Cloudflare Workersnodejs_compatflag). computeCassetteHashis now async (returnsPromise<string>). Digests unchanged; existing cassettes stay valid.
0.1.0 — 2026-06-10
Initial public release. Treated as a pre-1.0 calling card; a 1.0.0 cut will follow once the API has been used in anger.
cassetteMiddleware({ mode, cassetteDir, redact, cassetteName })— a Vercel AI SDKLanguageModelV3Middlewarethat intercepts bothdoGenerateanddoStream. Modes:record|replay|live.- Streaming is first-class:
recorddrains and captures ordered stream parts;replayre-serves them as a genuineReadableStreamvia the SDK's ownsimulateReadableStream. - Hash-addressed cassettes keyed by a stable SHA-256 of
{ modelProvider, modelId, prompt, toolSchemas, maxOutputTokens, temperature, topP }. Tool schemas are normalized (descriptions stripped, keys sorted). - Secret redaction at record time. Default matchers:
apiKey,authorization,x-api-key,bearer,token(case-insensitive). Configurable viaredact: (string | RegExp)[]. A replayed cassette that still contains a value a matcher would strip throwsCassetteSecretError. withCassette(name, testFn, options?)from@nkwib/tapedeck/vitest— pins a test to a named cassette and forcesreplayfor its duration viaAsyncLocalStorage.- Error family —
CassetteMissError,CassetteSecretError,CassetteCorruptError,CassetteModeError, all extendingCassetteError. COMPATIBILITY.mdrow stamped againstai@6.0.0. Zero runtime dependencies beyond theaipeer.