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