Skip to content
Warlock.js v4.7.0

Change Log

Shipped releases across the @warlock.js/* packages — one shared version line, newest first.

Filter
4.7.0
July 6, 2026

@warlock.js/ai gains the prompt compiler — systemPrompt().refined({ model, criteria, store }) lazily rewrites human-authored prompt text into a model-optimized version, pinned like a lockfile with machine-enforced placeholder parity and an always-safe fallback to the original — plus ai.prompts.validate({ criteria }) to grade a prompt against your own rules. @warlock.js/fs gains an ergonomic async fs facade — fs.files.* / fs.dirs.* grouping, lazy File / Directory handles, read-modify-write helpers (edit / editJson / mergeJson), recursive walk, and zero-dependency schema-validated JSON. @warlock.js/cascade lands a driver-correctness pass — a dozen real Postgres/MongoDB driver fixes (multi-row findAndUpdate, where-scoped update / unset / deleteOne, pivot detach, MongoDB with() eager-loading, per-subclass global scopes) plus new lockForUpdate({ skipLocked }) row locking (FOR UPDATE SKIP LOCKED).

@warlock.js/ai Added 2
  • Added systemPrompt().refined({ model, criteria, store }) — the prompt compiler. Humans keep writing human prompt text; the refined wrapper lazily rewrites it into a model-optimized version on first agent use and pins the result like a lockfile (re-compiled only when the source text, refiner model, criteria, or recipe version change — never silently). await refined.refine() returns the compiled template string (placeholders intact — routes / previews / warmup / CI; throws PromptRefinementError on failure) and await refined.refinePrompt() returns a composable prompt with meta.refinedFrom / meta.refinerModel provenance (register it to diff original vs refined). Placeholder parity is machine-enforced (one repair re-ask, then rejected); the lazy agent path never throws — it warns once and serves the original.
  • Added ai.prompts.validate({ criteria }) — validate a prompt against your own rules. Pass criteria (a string or a list of short rules) and, when a judge model is supplied, it replaces the built-in quality rubric so the judge's score / issues reflect your criteria (a failed rule is named in issues). Advisory only — never flips the deterministic ok; folded into the judgeCache key so different rules re-run.
@warlock.js/fs Added 7
  • Added fs shorthand facade — an async, ergonomic surface over the primitives: fs.files.* (file ops), fs.dirs.* (directory ops), lazy fs.file(path) / fs.dir(path) handles (File / Directory classes), and fs.exists(path) (type-agnostic). Delegates to the existing *Async primitives; synchronous callers keep using the bare primitives (the bare = sync / *Async = async charter is unchanged)
  • Added New file ops on the facade: append / prepend / appendLine / appendJsonLine (NDJSON), size, isEmpty, ensure (create-if-missing, never truncates), touch, edit (read → transform → write), editJson, mergeJson (shallow, or { deep: true }), ensureJson (get-or-create), checksumMatches, readLines (streaming async iterator), and an EXDEV-safe move (creates the destination parent, falls back to copy+unlink across devices)
  • Added New directory ops on the facade: empty (emptyDir), size (recursive byte sum), count, isEmpty, walk (constant-memory async iterator of { path, name, type }), a recursive option on list / listFiles / listDirs, and hash (stable directory fingerprint)
  • Added fs.files.getJson(path, { schema }) — validate parsed JSON against any Standard Schema validator (seal / zod / valibot) with zero dependency (calls the schema's own ~standard.validate); throws JsonSchemaValidationError on failure. { default } returns a fallback when the file is missing
  • Added File / Directory handles are lazy (no IO in the constructor) and immutable (copy / move / rename / copyTo / moveTo return a NEW handle); pure-path helpers name / basename / extension / parent() and child file(...) / dir(...); Directory.listFiles() / listDirs() return File[] / Directory[]
  • Added fs.hash namespace — fs.hash.string / fs.hash.buffer (sync, pure/in-memory) and fs.hash.file / fs.hash.dir (async, read from disk)
  • Added fs.files.get() is overloaded: a text read returns string (no cast); pass { encoding: null } for a Buffer
@warlock.js/cascade Added 2 Fixed 11
  • Added lockForUpdate({ skipLocked?, noWait? }) — row locking on SELECT (FOR UPDATE [SKIP LOCKED | NOWAIT]), the concurrent job-queue claim shape; Postgres-only, the MongoDB driver throws
  • Added DatabaseDriverContract.supportsSqlSerialization — capability flag (default true); false routes the MigrationRunner through direct migration-driver execution
  • Fixed Postgres model-level sum/avg/min/max/distinct/countDistinct/pluck/value no longer return 0/undefined — the hydration callback is reset before reading, matching MongoDB
  • Fixed Postgres Model.findAndUpdate / Model.atomic now update every matching row instead of one arbitrary row (a hidden LIMIT 1; MongoDB was already multi-row)
  • Fixed Postgres query-builder update() / unset() now honor the chained where filter — previously they updated the whole table
  • Fixed Postgres query-builder deleteOne() deletes exactly one row — the internal limit(1) was silently ignored, deleting every matching row
  • Fixed Postgres pivot detach(ids) (and sync / toggle) works — the driver translates Mongo-style filter operators ($in, $nin, $eq, $ne, $gt, $gte, $lt, $lte) instead of binding the operator object literally
  • Fixed CHECK constraints are no longer silently dropped on the MigrationRunner SQL path — the Postgres serializer emits ADD CONSTRAINT ... CHECK for this.check(...) and column .check(...)
  • Fixed MongoDB with() eager loading is no longer a silent no-op — get() runs the relation loader, same wiring as Postgres
  • Fixed MongoDB pipelines order $match before $project (SQL semantics), so select() before where() no longer strips the filter column and returns [] — fixes pivot attach de-duplication and sync / toggle deltas
  • Fixed The MigrationRunner works on MongoDB — migrations execute directly through the migration driver; exportSQL stays SQL-only with a clear unsupported error
  • Fixed MongoDB dropIndex(table, name) honors the literal index name — the string form is no longer rewritten to <name>_1 (the columns-array form keeps the convention name)
  • Fixed addGlobalScope / addLocalScope register per-subclass — a scope added on one model (e.g. a soft-delete notDeleted) no longer leaks onto every other model
create-warlock Added 2 Changed 1
  • Added Non-interactive scaffolding — create-warlock <name> --yes (with --db, --pm, --features, --ai, --git, --jwt) scaffolds the entire app in a single command, no prompts
  • Added --db=none / --no-db and a None option in the database prompt — scaffold with no database: the driver, its package, and src/config/database.ts are all skipped
  • Changed Starter models drop the baked-in globalColumnsSchema audit columns (createdBy / updatedBy / deletedBy / isActive) — global columns are left to the developer
4.6.1
July 1, 2026

A production-hardening patch across @warlock.js/core, @warlock.js/logger, and @warlock.js/cascade — a fatal boot error now fails loudly instead of exiting 0, native Postgres array columns (TEXT[] / JSONB[]) work with no configuration, and a nested transaction() joins the active transaction instead of opening an isolated one that can't see its writes.

@warlock.js/cascade Fixed 2
  • Fixed Native Postgres array columns (TEXT[] / JSONB[], from arrayText() / arrayJson()) are now auto-detected by introspecting the schema on connect and bound as raw arrays — no more "malformed array literal" on insert and no need to hand-list nativeArrayColumns (which stays as an optional per-connection override, now consulted per-table)
  • Fixed transaction() now flat-nests: a nested transaction() joins the active one (same session, sees its uncommitted writes) instead of opening a second, independent transaction — fixes phantom foreign-key violations when a service that opens its own transaction is called inside an outer one (e.g. a seeder creating a row, then a service inserting a child that references it). MongoDB joins too, replacing its "nested not supported" throw
@warlock.js/core Fixed 1
  • Fixed a fatal uncaughtException at production boot (e.g. a config file that throws) is no longer swallowed into a silent exit 0 — bootstrap now wires the crash handler to exit non-zero in production so warlock start surfaces the failure; the dev server still logs-and-continues for HMR
@warlock.js/logger Changed 1
  • Changed captureAnyUnhandledRejection() now exits the process non-zero after an uncaughtException (and prints the stack to console.error when no terminal channel is configured) so a fatal error at boot is never silently swallowed into a clean exit 0 — opt out with { exitOnUncaughtException: false } where the process recovers on its own (e.g. a dev server using HMR). unhandledRejection is unchanged (logged at error, never exits).
4.6.0
July 1, 2026

The AI framework closes every remaining capability gap. Output modalities land — ai.image(), ai.speech(), and ai.transcribe(), plus the new @warlock.js/ai-live package for ai.realtime() duplex voice + ai.video(). RAG gains first-party vector stores and document loaders (ai.rag.pgVectorStore on Postgres/pgvector, ai.rag.loadWeb / loadPdf / loadHtml / loadText). Durable mid-run crash-resume comes to agents and planners (agent.resume() / planner.resume() via an opt-in durable store). And provider breadth doubles with four new OpenAI-compatible adapters — @warlock.js/ai-mistral, ai-groq, ai-deepseek, and ai-xai.

@warlock.js/ai Added 9
  • Added ai.image(params) — image generation, the first verb of the output-modality track (Theme I). Wraps an ImageModelContract in the uniform never-throws { data, error, usage, report } envelope, with cost-truth (per-token for gpt-image, per-image for DALL·E / Imagen) folded into the same Usage.cost rollup and a type: "image" report routed to observers. Ships on the OpenAI + Google adapters.
  • Added SDKAdapterContract.image?(config) — the image-model capability seam, mirroring embedder?(). Adds ImageModelContract, GeneratedImage (discriminated base64 | url), ImageModelPricing, and ImageGenerationOptions.
  • Added MockSDK().image(...) + MockImageModel — deterministic image doubles (scriptable responses, recorded calls, pricing) for tests.
  • Added ai.speech(params) + ai.transcribe(params) — text-to-speech and speech-to-text, the audio verbs of the modality track. Same uniform never-throws envelope + cost-truth (per-character / per-minute / per-token). New SpeechModelContract / TranscriptionModelContract on SDKAdapterContract.speech?() / transcribe?(), plus MockSpeechModel / MockTranscriptionModel.
  • Added ai.audioFromFile(path) / ai.audioFromBuffer(bytes, mediaType) / ai.audioMediaTypeForFilename(name) — non-AI utilities that package audio (WhatsApp .ogg/.opus, iOS .m4a, …) into the AudioInput shape ai.transcribe consumes.
  • Added ai.rag.pgVectorStore({ client }) — a Postgres + pgvector vector store satisfying VectorStoreContract (upsert / query / removeNamespace), with an ensureSchema() DDL helper and a lazy pg optional peer.
  • Added ai.rag.loadText / loadHtml / loadWeb / loadPdf — document loaders producing RagDocuments for .index(). loadWeb is SSRF-safe (routes through guardedFetch / OutboundPolicy); loadPdf uses a lazy pdf-parse optional peer.
  • Added Durable mid-run crash-resume — opt-in durable: { store, deleteOnComplete? } on ai.agent / ai.planner with a stable runId + agent.resume(runId) / planner.resume(runId). Per-trip (agent) / per-node (planner) checkpoints reuse ai.snapshot.{memory,pg,redis}; drift detection via AgentDriftError / PlannerDriftError (bypass with { force: true }); completed work never re-runs its tools and usage is never double-counted.
  • Added **ai.rag.* namespace** now also carries chunk, cacheVectorStore, pgVectorStore, loadText/loadHtml/loadWeb/loadPdf, bm25Rank, reciprocalRankFusion, hybridRank, multiQuery (previously standalone-only exports), for ai.*-namespace consistency.
@warlock.js/ai-live Added 5
  • Added First release — the live & generative rich-media add-on for @warlock.js/ai, kept in its own package so core text/image/speech stay dependency-light. A side-effect import (import "@warlock.js/ai-live") mounts ai.video + ai.realtime onto the shared Ai facade.
  • Added ai.video(params) — text-to-video (Sora / Veo / Kling-class); the provider's async submit→poll job hidden behind the uniform never-throws { data, error, usage, report } envelope, with per-second cost-truth folded into Usage.cost and a type: "video" report routed to observers.
  • Added ai.realtime(options) — a stateful duplex voice session over a pluggable RealtimeTransport: sendAudio / sendText / events() out, close()RealtimeReport for the cost/observability surfaces.
  • Added ContractsVideoModelContract, GeneratedVideo, VideoModelPricing, VideoOptions; RealtimeSession, RealtimeTransport, RealtimeConnection, RealtimeEvent, RealtimeReport, RealtimeOptions.
  • Added MocksMockVideoModel + MockRealtimeTransport for deterministic, HTTP- and socket-free tests (scripted responses / event streams, recorded calls).
@warlock.js/ai-openai Added 4 Fixed 1
  • Added openai.image({ name }) — image generation for the gpt-image-* (token-metered) and dall-e-* (per-image) families, for use with ai.image(). A non-image model id is rejected at construction.
  • Added PDF + audio input. pdf and audio content parts now map to OpenAI file (base64 file_data) and input_audio (wav / mp3) parts — opt in with model({ pdf: true }) / { audio: true }. A remote-URL pdf/audio source raises a typed InvalidRequestError up front.
  • Added openai.speech({ name }) — text-to-speech for the tts-1 / tts-1-hd / gpt-4o-mini-tts families (audio.speech.create), for use with ai.speech().
  • Added openai.transcribe({ name }) — speech-to-text for the whisper-1 / gpt-4o-transcribe families (audio.transcriptions.create), for use with ai.transcribe(). whisper-1 defaults to verbose_json (duration + segments); a non-TTS/STT model id is rejected at construction.
  • Fixed Non-text content parts are no longer coerced to image_url. The message mapper now branches per modality (image → image_url, pdf → file, audio → input_audio) instead of forcing every attachment through the image path.
@warlock.js/ai-mistral Added 3
  • Added First release. MistralSDK — a thin wrapper over @warlock.js/ai-openai that points one internal OpenAISDK at Mistral's OpenAI-compatible endpoint (https://api.mistral.ai/v1) with provider: "mistral", delegating transport, streaming, tool calls, structured output, error wrapping, and token accounting to the battle-tested adapter. Exposes .model(), .embedder() (mistral-embed), and .count().
  • Added Mistral-aware capability inferencevision is auto-set for the pixtral family and recent multimodal generations (mistral-large, mistral-medium, ministral-3); reasoning for the magistral family and the hybrid mistral-small generation. An explicit vision / reasoning on .model() always wins. Exported as inferVisionCapability / inferReasoningCapability, with the -latest aliases grouped under MISTRAL_MODELS.
  • Added Default pricing registry (MISTRAL_DEFAULT_PRICING, USD per 1,000,000 tokens) merged under any caller-supplied pricing so cost truth works out of the box; per-model > SDK-level > default > undefined. No image() — Mistral has no OpenAI-compatible image endpoint.
@warlock.js/ai-google Added 1 Fixed 1
  • Added google.image({ name }) — Imagen (imagen-*) image generation for use with ai.image(). Per-image-metered; when every candidate is safety-filtered the run surfaces a typed ContentFilterError. A non-Imagen model id is rejected at construction.
  • Fixed PDF + audio input are now explicitly mapped and tested. The content-part mapper documents and proves that pdf / audio parts route to Gemini inlineData (the pdf / audio capabilities the adapter advertises are backed by a real mapper, not an accident of the image path), and the remote-URL rejection now names the actual modality instead of always saying "images".
@warlock.js/core Added 11 Changed 1 Fixed 4
  • Added release-hygiene tests: version↔changelog invariant + generator-stub import check
  • Added router.routeCount() exposes the number of registered routes as a boot/readiness signal
  • Added health.addRoutesRegisteredCheck(getRouteCount) registers a readiness check that reports not-ready when a booted HTTP app has zero routes
  • Added seeders now receive a { track } context — track(model), track(models[]), and track(table, id) register created records (each call returns its argument so it can be chained inline); recordsCreated is auto-derived from the track count
  • Added seed_records table (created via the new SeedRecordsTableMigration) records every tracked seed reference within the same transaction the seed runs in; only the last run's refs are kept per seeder
  • Added warlock seed --drop [name] undoes a seed: deletes its tracked records in reverse run/insertion order inside a transaction, then resets the matching seeds-log rows so once: true seeds re-run; scope to one seeder with --drop=<name>
  • Added Seeder.dependsOn is now resolved — seeders are topologically sorted so dependencies run before dependents, layered over the numeric order tie-break; throws UnknownSeederDependencyError for a missing dependency and SeederDependencyCycleError for a cycle
  • Added seeders receive an injectable clock and a meaningful batch size — run({ track, now, batchSize }); now() (default () => new Date()) drives both seed data and the seeds-log timestamps so historical/back-fill runs are deterministic, and batchSize surfaces the seeder's own batchSize for Model.createMany(rows, { batchSize })
  • Added repository-level aggregation — aggregate(), sum(), avg(), min(), max(), and groupBy() on RepositoryManager, each reusing filterBy (and its operator-injection guard), where, and scopes before the aggregate, exactly like count()
  • Added warlock doctor — a read-only diagnostics command that runs routes / config / connectors / optional-peers / health / release-hygiene checks and prints a pass/warn/fail report (exits non-zero on any failure, never opens a DB/cache/socket connection)
  • Added warlock routes — a read-only command that lists the registered HTTP routes as a verb-colored table (method / path / name / action / middleware-count / source); filter with --method / --path / --name, or emit the normalized rows as JSON with --json. Boots app code to register routes but starts no connectors
  • Changed Seeder.run now receives a SeedContext (run(ctx)) — backward compatible, an existing zero-arg run() keeps working unchanged
  • Fixed route-module load/registration failures are no longer swallowed: a route file that throws on import or registration now surfaces loudly instead of silently 404'ing the whole surface
  • Fixed ModuleLoader.loadModule rethrows after logging (wrapped in a new ModuleLoadError carrying the failing file + cause), so a broken module aborts boot and is caught loudly by the HMR batch-reload handler in dev
  • Fixed ModuleLoader.loadAll aggregates per-file failures and throws an AggregateError at the end, so one broken module no longer hides the others
  • Fixed router.withSourceFile rethrows the callback error after logging instead of consuming it with a bare console.log (the try/finally source-file stack cleanup is preserved)
@warlock.js/cascade Added 10 Changed 1 Fixed 4
  • Added Fast bulk Model.createMany(data, options?: { batchSize?; bulk? }) — both paths chunk by batchSize (default 500); bulk: true routes each chunk to the driver's native multi-row insertMany for 10–100× throughput (skips per-row hooks/events; default path preserves them)
  • Added IdGeneratorContract.generateNextIds({ table, count }) — reserve a contiguous block of auto-increment ids in a SINGLE atomic op (MongoDB). Model.createMany (default + bulk) now reserves one id block per chunk instead of one counter round-trip per row; engages only for fixed-increment, auto-generated, id-less rows (random-increment or caller-supplied-id rows fall back to per-row generation)
  • Added QueryBuilder.groupByDate(column, unit, aggregates?) — portable date-bucketed GROUP BY (day/week/month/year) across Postgres date_trunc and MongoDB $dateTrunc
  • Added $agg.sum(expr) now also accepts a typed column expression ($expr.mul/$expr.add/$expr.sub/$expr.div/$expr.col/$expr.lit) so you can sum price * quantity; bare-string payload is unchanged. Added $agg.sumRaw(expression) raw escape hatch (Postgres SUM(<raw>); throws on MongoDB)
  • Added Column-expression DSL grouped under a single $expr object (mirroring $agg) — $expr.col / $expr.lit / $expr.mul / $expr.add / $expr.sub / $expr.div / $expr.raw — plus isColumnExpression / toColumnExpression and the ColumnExpression / ColumnExpressionInput types
  • Added MongoDB id counter (MasterMind) now has a lazily-ensured unique index on { collection: 1 } plus a bounded retry on duplicate-key (E11000), closing the cold-start race where two concurrent first inserts into a new collection could reserve overlapping ids/blocks
  • Added $agg.countDistinct(field) — a cross-driver grouped distinct-count aggregate (Postgres COUNT(DISTINCT col); MongoDB $addToSet in $group finalized with $size in the renaming $project)
  • Added Model.raw<T>(sql, params) — typed, transaction-aware raw query that auto-joins the active transaction() scope and returns RawQueryResult<T>
  • Added DataSource.raw<T>(sql, params) — thin transaction-aware passthrough to driver.query
  • Added Postgres connection option nativeArrayColumns — opt out listed columns (JSONB[]/TEXT[]/…) from JSON-text encoding so genuine native-array columns keep their {...} literal form
  • Changed DriverContract.query<T>() is now typed Promise<RawQueryResult<T>> (new rows + rowCount result type) instead of Promise<any>
  • Fixed Postgres json/jsonb columns no longer corrupt: object-arrays, string-arrays, mixed arrays, empty [] (previously stored as {}), and plain objects are now JSON-encoded before binding instead of falling through to a Postgres array literal; the same encoding is applied on the UPDATE $set path. The pgvector all-number array form is preserved.
  • Fixed Insert no longer overwrites a caller-supplied createdAt — a backdated value (imports/migrations) is now honored, mirroring the upsert guard, while updatedAt is always stamped at persist time
  • Fixed Insert validation now whitelists the system columns (id/_id/timestamps/deletedAt) like the update path, so a backdated createdAt survives strict strip/fail mode instead of being dropped before reaching the writer
  • Fixed Corrected the MongoDB id-generator docs that falsely claimed the counter write "participates in active transactions" — it is a standalone, immediately-durable write (no transaction session is attached), so a rolled-back insert leaves the consumed id as a gap, exactly like SQL SERIAL
@warlock.js/ai-deepseek Added 5
  • Added First release. DeepSeek adapter for @warlock.js/ai — a thin wrapper over @warlock.js/ai-openai's OpenAISDK pinned to https://api.deepseek.com (provider: "deepseek"), so all wire behavior (streaming, tool calls, structured output, error wrapping) is inherited unchanged.
  • Added DeepSeekSDK.model() / .embedder() / .image() / .count() delegated to the wrapped client. baseURL and provider are optional (default to DeepSeek's endpoint / label); every other openai ClientOptions value is forwarded verbatim.
  • Added DeepSeek-specific capability inference (inferReasoningCapability / inferVisionCapability) — reasoning is auto-true for deepseek-reasoner and the *-pro tier, auto-false for deepseek-chat / *-flash; vision is false for every id (no documented vision surface). An explicit reasoning / vision per model always wins.
  • Added Built-in DeepSeek pricing defaults (USD per 1M tokens) for deepseek-chat, deepseek-reasoner, deepseek-v4-flash, deepseek-v4-pro, so usage.cost is computed out of the box; overridable per model or per SDK.
  • Added DEEPSEEK_CHAT_MODELS — informational list of the documented chat model ids.
@warlock.js/ai-xai Added 5
  • Added First release. xAI Grok adapter for @warlock.js/ai — a thin wrapper over @warlock.js/ai-openai's OpenAISDK pinned to https://api.x.ai/v1 (provider: "xai"), so all wire behavior (streaming, tool calls, structured output, error wrapping) is inherited unchanged.
  • Added XaiSDK.model() / .embedder() / .image() / .count() delegated to the wrapped client. baseURL and provider are optional (default to xAI's endpoint / label); every other openai ClientOptions value is forwarded verbatim.
  • Added xAI-specific capability inference (inferVisionCapability / inferReasoningCapability, exported alongside XAI_VISION_MODEL_PREFIXES / XAI_REASONING_MODEL_PREFIXES) — Grok ids don't match OpenAI's gpt-* / o* prefixes, so vision is auto-true for grok-4 / grok-2-vision and reasoning for grok-4 / grok-3-mini. An explicit vision / reasoning per model always wins.
  • Added XAI_CHAT_MODELS — convenience list of current public Grok chat ids (grok-4, grok-3, grok-3-mini, grok-2-vision, grok-2).
  • Added Optional per-model pricing registry — resolution at model() time is per-model pricing > SDK registry > undefined.
@warlock.js/ai-groq Added 3
  • Added First release. GroqSDK — a thin wrapper over @warlock.js/ai-openai that points one internal OpenAISDK at Groq's OpenAI-compatible endpoint (https://api.groq.com/openai/v1) with provider: "groq", delegating transport, streaming, structured output, error wrapping, and token accounting to the battle-tested adapter. Serves Groq-hosted open models (llama-3.3-70b-versatile, llama-3.1-8b-instant, openai/gpt-oss-*, deepseek-r1-distill-llama-70b) on LPU hardware via .model() and .count(). GROQ_BASE_URL / GROQ_PROVIDER / GROQ_KNOWN_MODELS exported.
  • Added Groq-aware capability inference — because Groq ids are upstream open-weight names, not OpenAI's, the wrapper carries its own lists: vision is auto-set for gpt-oss / llama-4 / llama-3.2-*-vision; reasoning for gpt-oss / deepseek-r1 / qwq / qwen3. An explicit vision / reasoning / structuredOutput always wins. Exported as inferVisionCapability / inferReasoningCapability.
  • Added Default pricing registry (USD per 1,000,000 tokens) for the known Groq models as the final fallback; resolution per-model > SDK-level pricing[name] > built-in default > undefined. No embeddings endpoint on Groq (.embedder() is delegated for symmetry but calls fail upstream) and no image().
@warlock.js/scheduler Fixed 1
  • Fixed Warn in development when jobs are registered but start() is never called — a one-shot deferred check logs N job(s) registered but scheduler.start() was never called, is suppressed once start() runs or in production (NODE_ENV=production), and is unref'd so it never holds the process open.
4.5.0
July 1, 2026

The biggest AI release yet — the agent-platform, prompt-unification, and team-identity milestones plus a security-hardening pass, consolidated into one release. @warlock.js/ai grows from a primitive ladder into a full agent platform: ai.rag(), ai.team(), ai.skills(), a unified ai.prompts registry (ai.prompt is now a facade over it), ai.dataset() / ai.vcr(), a planner that runs DAGs and can pause for approval, a generic observe seam, the former ai-human + ai-guard satellites folded into core, and a security pass (SSRF-safe outbound I/O, redaction, attachment policy, SSE serving, streaming structured output). @warlock.js/ai-panoptic becomes batteries-included with a redesigned, auth-gated local dashboard (group-by-type, cost heatmap, timeline, search / filter, restart-persistent store) and first-class team-type traces. The first releases of @warlock.js/ai-tools (ready-made tools + MCP) and @warlock.js/ai-workspace (a policy-jailed coding workspace), plus a broad @warlock.js/core hardening pass.

@warlock.js/ai Added 18 Changed 3 Fixed 3 Security 4
  • Added ai.rag(config) — retrieval-augmented generation in core: a chunk → embed → retrieve → cite pipeline that reuses your existing embedder and cache, with zero new dependencies. Includes hybrid retrieval (dense + BM25 reciprocal-rank fusion), keyword / LLM rerankers, and multi-query expansion.
  • Added ai.team(config) — manager-led multi-agent teams: thin sugar over ai.supervisor for the review-then-fix and test-then-fix shapes.
  • Added ai.skills(config) — runtime agent skills with progressive disclosure: a cheap always-injected catalog plus an on-demand loadSkill tool. Adds a skills option on ai.agent.
  • Added ai.streamObject(...) — structured-output streaming: partial-object snapshots as tokens arrive, with a strict final parse against the response schema.
  • Added ai.serve(executable, options) — serve any agent / workflow / supervisor as an SSE HTTP endpoint.
  • Added Multimodal attachmentsContentPart gains pdf and audio variants alongside text / image, resolved to provider-ready parts (PDF wired on the Anthropic and Bedrock adapters).
  • Added Planner DAG execution, re-planning, and plan-only approval — run independent steps concurrently, revise the plan when a step fails, or return a plan for approval before it executes.
  • Added Generic Observer seam — route any flow's run report to pluggable observers (e.g. @warlock.js/ai-panoptic) without coupling core to a backend.
  • Added ai.prompts + ai.prompt — a process-wide registry of named, versioned systemPrompt(...) builders (resolved by name@version / name@tag) with define / tag / diff / export / import and a unified validate (deterministic missing-placeholder check plus an optional Nova-safe LLM-judge); ai.prompt is a thin facade over it.
  • Added SystemPromptContract identity + provenance.meta({ name, version, description, required }) (a name auto-registers in ai.prompts), .merge(...blocks) / .merge(contract) / .merge(name, { fromVersion }), and deterministic meta.composedFrom labels.
  • Added ai.dataset(options) — filterable, shardable evaluation case sets that feed agent.eval, with baseline / regression detection and CI reporters.
  • Added ai.vcr(model, options) — record / replay any model against an on-disk cassette for deterministic, offline tests, with recordRequest modes and redactRequest / redactResponse / redactError hooks.
  • Added ai.agent.judge(config) — judge-safe agent preset (also ai.agent({ judge: true })): lenient JSON parsing, bounded repair re-asks, and never-throw verdicts on Nova-class models.
  • Added Human-in-the-loop approval now ships in core (ai.human.*, formerly @warlock.js/ai-human) — a tool-approval gate plus durable interrupt / resume.
  • Added Content guardrails now ship in core (ai.guardrail.*, formerly @warlock.js/ai-guard) — PII / topic / injection / moderation detectors.
  • Added Orchestrator sessionLock — per-session turn serialization (default in-process mutex keyed by sessionId, pluggable distributed lock) so concurrent same-session turns can't lose a checkpoint update.
  • Added Sub-agent trace nesting — a supervisor / team / orchestrator callback that calls agent.execute() directly now nests callback → agent → tool with rolled-up usage / cost.
  • Added AgentReport.systemPrompt — the resolved system prompt sent to the model is now recorded on the agent report.
  • Changed ai.team runs report type: "team" — a first-class ReportType (was "supervisor") so observers distinguish team runs on the wire.
  • Changed Deterministic parallel workflow state merge — parallel children merge into the parent in declaration order (last-declared wins on a conflicting key) instead of completion order; an optional per-step mergeState reducer overrides it.
  • Changed Safer batch / RAG defaultsai.batch warns once on a large unbounded run (pass an explicit concurrency or "unbounded"); ai.rag accepts limits (maxDocuments / maxChunks / maxBytes) that fail before any embedding spend.
  • Fixed Cancellation propagates through composite tools — a cancelled outer agent now aborts a nested agent / workflow / supervisor invoked via .asTool() (the run signal threads into the nested execute).
  • Fixed Observer / event-handler errors are surfaced, not swallowed — a throwing observer or on handler stays isolated (never crashes the run) but is now warned once / routed to a hook instead of disappearing silently.
  • Fixed budget({ maxCostUSD }) fail-open closed — a cost cap with no matching model pricing now warns once (naming the model) instead of silently never tripping.
  • Security Shared OutboundPolicy + redact() — one SSRF-safe outbound-fetch guard (scheme + host allowlist, post-DNS private-IP deny, max-bytes, timeout, injectable fetch) and one redaction utility, consumed across attachments, URL skills, VCR, and the error path.
  • Security Attachment trust boundary (AttachmentPolicy) — remote-text attachment fetch is default-deny (opt in with a policy), local reads honor an allowedRoots sandbox, and bare-string local paths warn (staged deprecation).
  • Security URL skill sources hardened — the manifest fetch runs through OutboundPolicy and every record is runtime-validated before it enters model context; adds cache-TTL controls.
  • Security Guardrail coverage documented — input detectors inspect text only; non-text attachment content needs an attachment-level policy.
@warlock.js/ai-panoptic Added 9 Changed 1 Fixed 1 Security 2
  • Added Zero-setup local dashboarddashboard(store, options) serves a loopback-only mini-Langfuse (default 127.0.0.1:4319) with no Docker and no account: light / dark / system theme, a two-pane drawer (nested call tree / detail), a metadata panel, colour-coded Title-case type labels, arrow-coded token rollups (↓input · ↑output · total), per-node rollup cost, and the Warlock logo.
  • Added Dashboard search / filter / grouping — client-side free-text search, status / type / session / prompt filter chips, an errors-only toggle, and group-by Session / Prompt / Type with a per-type aggregate-stats panel (count, failure rate, p50 / p95 latency, tokens, cost).
  • Added Cost heatmap, timeline view, and deep-links — each node carries a cost-tinted accent; the drawer toggles between the call tree and a Gantt timeline (critical path highlighted); the open trace + span are reflected in the URL hash for shareable views.
  • Added Prompt-version linkage — agent spans stamp agent.promptName / agent.promptVersion; the dashboard filters and groups by the resolved name@version key.
  • Added Cache-backed persistent trace storecreateCacheTraceStore(cache, options) persists traces through any @warlock.js/cache driver, serves reads from an in-memory mirror, and re-hydrates on ready() so traces survive a restart; ai.config({ panoptic: { cache } }) wires it.
  • Added Declarative ai.config({ panoptic }) — configure panoptic once; it registers on core's observer registry and starts the dashboard. Per-flow observe + observeAll opt flows into observation.
  • Added ContentCaptureOptions.fullHistory — capture the agent's complete message history on the span; agent content is emitted as a [system, user] chat array; Langfuse gets trace-level input / output.
  • Added onError hook — handle isolated exporter failures on panoptic() / createCollector().
  • Added Pure trace-list helpers exported from the package root (filterTraces / groupBySession / groupByPrompt / groupByType / aggregateByType / rollupCost / …) so your own views mirror the dashboard's rules.
  • Changed Title-case status & type labels across rows, drawer, chips, and group headers (underlying filter keys stay lowercase); team is a first-class dashboard type; type chips show only present types; drawer metadata keys are humanized; the group-by toggles became a single Group dropdown.
  • Fixed Isolated exporter failures no longer fail silently — a failing exporter still never crashes the run, but now warns once (or calls onError).
  • Security Dashboard hardening — bearer-token auth (authToken, required when binding off-loopback), a Host-header allowlist (DNS-rebinding guard), and security response headers (nosniff / X-Frame-Options: DENY / locked-down CSP) on every response.
  • Security Error redaction — captured error message / stack are scrubbed of secrets (Bearer tokens, API keys) and a retained cause is deep-redacted (auth / cookie headers stripped) before a trace is stored or exported.
@warlock.js/ai-tools Added 5
  • Added **Five ready-made agent tools, attached to the shared ai object under ai.tools.* via a declare module "@warlock.js/ai" augmentation, so a bare import "@warlock.js/ai-tools" makes them available and statically typed. Each returns a ToolContract that drops straight into ai.agent({ tools: [...] }): - ai.tools.webSearch(options) (web_search) — web search via a chosen provider (tavily / brave / serpapi) over the global fetch; the API key falls back to TAVILY_API_KEY / BRAVE_API_KEY / SERPAPI_API_KEY; maxResults is clamped per call. - ai.tools.fetchUrl(options?) (fetch_url) — fetch a URL and return its content as readability-extracted text (default), raw html, or markdown, with a host allowlist (SSRF guardrail), a byte cap (truncated flag), and a request timeout. - ai.tools.http(options?) (http_request) — a guarded HTTP/REST client: method + host allowlists enforced before the network call, optional baseUrl join, static-header merge, byte cap, and timeout; JSON-parses a JSON response body. - ai.tools.calculator(options?)** (calculator) — a SAFE arithmetic evaluator (+ - * / % ^, unary signs, parentheses, decimal/scientific literals) implemented with a shunting-yard pass — it never calls eval/Function. - ai.tools.dateTime(options?) (date_time) — clock/calendar operations: now / add / diff / format over ISO-8601 instants, with millisecond-based units and IANA time-zone rendering. diff accepts from as an alias for the start instant (iso wins when both are set).
  • Added MCP client — ai.mcp(server, options?) (Direction A). Connects to an external MCP server over a stdio (node:child_process + node:readline, no dependency) or Streamable HTTP transport, runs the initialize handshake + tools/list, and adapts each remote tool into a native ToolContract (its JSON Schema wrapped as a Standard Schema, tools/call as execute). Supports namePrefix, filter, and a per-call timeoutMs; an isError result surfaces as { error } data.
  • Added MCP server — ai.mcp.serve(source, options) (Direction B). Exposes a built agent / supervisor / orchestrator (or a raw ToolContract[]) AS an MCP server: tools/list emits each tool's inputSchema via extractJsonSchema at the configured schemaTarget (default draft-2020-12), and tools/call routes to contract.invoke(), mapping data to a text content block and error to an isError: true result. The stdio transport is auto-pumped over process.stdin / process.stdout; the pure protocol core is also exported as createServeHandler for a host's own HTTP wiring.
  • Added Typed error classesWebToolError, HttpPolicyError, CalculatorError, DateTimeError, and McpTransportError, each extending the @warlock.js/ai AIError base with a type discriminator. Every tool follows the errors-as-data contract: failures are thrown inside execute, wrapped by tool(), and reach the model as { error } so the agent self-corrects instead of crashing the run.
  • Added Optional peers, lazily imported. Heavy dependencies — a search provider (@tavily/core), the readability scraper (@mozilla/readability + jsdom), the MCP SDK (@modelcontextprotocol/sdk), and the JSON-Schema validator (ajv) — are optional peers, import()ed only when the relevant path runs, surfacing a curated npm install string when absent rather than crashing at import time. The only required runtime peer is @warlock.js/ai; everything else is Node built-ins + the global fetch (Node 18+).
@warlock.js/ai-workspace Added 16
  • Added ai.workspace(policy) — the workspace verb, registered on the shared ai object via a declare module "@warlock.js/ai" augmentation + a runtime side-effect on import (no edit to @warlock.js/ai's own source). Exported as the workspace factory; WorkspaceCapableAi is the typed view consumers cast ai through.
  • Added Policy jail — every path is realpath-resolved and must sit under cwd (or an allowPaths root); denyPaths globs are blocked even inside cwd; the shell allow/deny list gates each command's leading executable basename (deny wins, fail-closed); per-command timeout + output byte cap; and process.env is never inherited wholesale (opt-in shell.inheritEnv, plus explicit shell.env).
  • Added Seven agent-facing tools under ws.tools.*, each a ToolContract built on the core tool() factory: read_file, edit_file, write_file, run_shell, run_tests, grep, glob. Each factory takes an optional { name } (and run_tests a { command }) override.
  • Added tools.all() — every tool in canonical order — and tools.pick(...names) — a least-privilege subset (e.g. pick("readFile", "grep", "glob") for a reviewer).
  • Added Direct programmatic methods sharing the same policy seam: readFile / writeFile / editFile / exec / grep / glob / exists / mkdir / remove.
  • Added readonly() — a projection that vends only the read/grep/glob tools and rejects every mutating direct method with a WorkspacePolicyError.
  • Added scope(subdir) — a sub-jailed workspace rooted at subdir (narrowed cwd, same sub-policies and backend selection).
  • Added Read-before-edit guardread_file / readFile return a SHA-256 content hash (via @warlock.js/fs hashString); edit_file requires an exact, unique oldString (or replaceAll) and rejects a mismatched expectHash as stale.
  • Added BackendscreateLocalBackend (default "local"; @warlock.js/fs for IO + node:child_process for the shell) and createMockBackend (in-memory Map + scripted exec, for hermetic disk-free tests), behind the WorkspaceBackend contract.
  • Added Policy engine seam exports: resolveInJail, isCommandAllowed, buildEnv, and the ResolvedPath type.
  • Added Ops layer export createOps — the single policy-enforced operation layer both the tools and the direct methods funnel through.
  • Added Typed errors WorkspacePolicyError (type: "path-escape" | "denied-command") and WorkspaceEditError (type: "not-found" | "not-unique" | "stale-hash"), both extending the @warlock.js/ai AIError base (code TOOL_EXEC_FAILED) so failures surface to the agent as tool-error data, never thrown run-killers.
  • Added Public type surface re-exported from the barrel: Workspace, WorkspaceTools, WorkspacePolicy, WorkspaceShellPolicy, WorkspaceReadPolicy, WorkspaceBackendType, WorkspaceToolName, WorkspaceOps, WorkspaceBackend (+ WorkspaceBackendExecOptions / WorkspaceBackendExecResult), and the per-tool IO shapes (ReadFileInput/Result, EditFileInput/Result, WriteFileInput/Result, RunShellInput/Result, RunTestsInput, GrepInput/Match/Result, GlobInput/Result).
  • Added scripts/generate-llms.mjs and the generated llms.txt / llms-full.txt projections of skills/.
  • Added skills/use-a-workspace/SKILL.md — building a jailed workspace and operating it (policy, the seven tools, the direct methods, readonly / scope).
  • Added skills/build-loop-agent/SKILL.md — wiring ws.tools.all() into a coding agent that reads → edits → runs tests until green.
@warlock.js/core Changed 4 Fixed 24 Removed 1
  • Changed dev-server update notice now fires immediately on warlock dev — the check is spawned in parallel with server startup instead of awaiting it, so the notice surfaces as soon as npm responds
  • Changed raised the update check's npm registry timeout from 2.5s to 30s, so a slow connection no longer drops the notice
  • Changed repository lifecycle hooks (onCreating / onCreate / onUpdating / onSaving / onDeleting / …) now run on create / update / delete — they were defined but never invoked
  • Changed repository.list() / all() now honor the sortBy, sortDirection, and purgeCache options — previously accepted but silently ignored
  • Fixed response.sendFile({ filename }) and response.download() no longer 500 on non-ASCII file names — the Content-Disposition header is now RFC 6266-encoded (a sanitized ASCII filename fallback plus an RFC 5987 filename*=UTF-8''…), so an Arabic / emoji / UTF-8 download name streams correctly instead of throwing Node's ERR_INVALID_CHAR
  • Fixed local storage paths are contained to their disk root — ../ traversal segments and absolute paths can no longer escape the configured directory
  • Fixed storage.putFromUrl adds SSRF guards — private / loopback / link-local hosts are rejected and the fetched body is size-capped
  • Fixed S3 / R2 / DigitalOcean Spaces url() no longer produces a malformed double-host URL when urlPrefix is set
  • Fixed cloud deleteDirectory paginates via the list continuation cursor instead of re-listing the first page
  • Fixed local-storage metadata cache is invalidated on write / delete — it was serving a stale size / modified-time
  • Fixed the cloud driver no longer reports a misleading "SDK not installed" error when the AWS SDK is in fact present (driver load race)
  • Fixed repository countCached / countActiveCached now cache and return correctly — a null cache miss was being returned as the count
  • Fixed repository firstCached / lastCached no longer fetch and cache the entire table to return a single row
  • Fixed repository boolean filters no longer coerce false / 0 to true
  • Fixed repository cache keys are now order-independent (stable key serialization)
  • Fixed router groups restore prefix / name / middleware state via try/finally even when the group callback throws
  • Fixed router.any() / all routes now match every HTTP verb under the dev server — they previously matched only GET and POST, diverging from production
  • Fixed the HTTP concurrency limiter releases its slot on every response path (noContent, redirect, file, buffer) — a throwing or non-send handler no longer leaks a permit and permanently 429s the route
  • Fixed the cached-response middleware replays a hit through response.replay() instead of re-sending an already-sent reply, preserving status and content-type
  • Fixed onSent cache writes in the idempotency and cache middleware are error-handled — a cache-backend failure no longer surfaces as an unhandled rejection
  • Fixed X-Forwarded-For is parsed to its first hop, so IP-filter / rate-limit / idempotency scoping cannot be spoofed with extra header hops
  • Fixed the maintenance middleware allowlist matches request paths that carry a query string
  • Fixed use-cases run their after middleware and broadcast for a void handler, and a failed history write no longer fails an otherwise-successful call
  • Fixed the use-case retry counter reports the correct count on total failure
  • Fixed the socket connector no longer double-closes the shared HTTP server during graceful shutdown
  • Fixed the cache connector disconnects its drivers on shutdown — an open Redis connection was left dangling
  • Fixed generator stubs import v / Infer from @warlock.js/seal (core never re-exported them), so generated models compile and run
  • Fixed warlock dev hot-reloads when a file is emptied or saved with no trailing newline — a stale no-op-change check was silently dropping those saves before they reached HMR
  • Removed presigned-upload maxSize option — a presigned PUT URL cannot enforce a size cap, so the option was a false guarantee
@warlock.js/ai-anthropic Fixed 1
  • Fixed All upstream ClientOptions now reach the Anthropic client. The SDK constructor peels off the framework-only provider / pricing keys and forwards the rest (timeout, maxRetries, defaultHeaders, custom fetch, baseURL, …) verbatim, instead of dropping everything but apiKey / baseURL.
@warlock.js/ai-openai Fixed 1
  • Fixed All upstream ClientOptions now reach the OpenAI client. The SDK constructor peels off the framework-only provider / pricing keys and forwards the rest (timeout, maxRetries, defaultHeaders, custom fetch, organization, project, …) verbatim, instead of dropping everything but apiKey / baseURL.
4.4.0
June 21, 2026

@warlock.js/core completes the production lifecycle — a booted hook, a shutdown hook, graceful HTTP draining, and built-in /health + /ready endpoints for zero-downtime deploys. Plus AI fixes across @warlock.js/ai, @warlock.js/ai-openai, and @warlock.js/ai-panoptic (opt-in content capture, corrected Langfuse token accounting).

@warlock.js/core Added 8 Fixed 1
  • Added Application.onceBooted(cb) — run a callback once the app is fully booted (fires immediately if already booted)
  • Added Application.whenBooted() — promise that resolves with the boot context when the app is fully booted
  • Added Application.isBooted — whether the app has finished booting
  • Added Application.onShutdown(cb) — run teardown once on shutdown, before connectors stop (mirror of onceBooted)
  • Added Application.isShuttingDown — whether shutdown has begun
  • Added built-in /health (liveness) and /ready (readiness) endpoints with a health check registry (health.addCheck)
  • Added graceful HTTP shutdown — drains in-flight requests on shutdown, bounded by http.gracefulShutdown.timeout
  • Added http.health.* config to toggle or rename the health endpoints
  • Fixed connector shutdown no longer reverses the connector list in place (could corrupt order on a repeated shutdown)
@warlock.js/ai Fixed 2
  • Fixed Planner: OpenAI strict structured-output 400. The generated plan schema now lists every property in required and drops minItems / maxItems, so ai.planner() no longer fails against OpenAI strict json_schema mode.
  • Fixed Report / result types no longer collapse to never under strict TypeScript. The narrowing report / result types now override the discriminant via Omit<…> instead of intersection. Type-only — no runtime change.
@warlock.js/ai-panoptic Added 1 Fixed 1
  • Added Opt-in content capture. panoptic({ captureContent, redactContent }) copies the agent prompt / response and each tool's args / result onto spans, surfaced by the console (io), file, OTel (gen_ai.prompt / gen_ai.completion), and Langfuse exporters. Off by default; a ContentRedactor masks each value.
  • Fixed Langfuse token accounting — generations now meter their own usage (rolled-up minus children) and the root execution is metered, so the trace total no longer double-counts nested spans.
@warlock.js/ai-openai Fixed 1
  • Fixed Strict structured-output compatibility check is now recursive. A schema that omits a required property anywhere in the tree degrades to loose json_object instead of 400-ing; client-side validation still enforces the full shape.
@warlock.js/cascade Changed 1
  • Changed Documented model.uuid — the accessor returns the model's primary id as string (where model.id is string | number); the name is historical and performs no UUID validation.
4.3.0
June 21, 2026

Self-update tooling — @warlock.js/core gains the warlock update command and a new-release notice in warlock dev. @warlock.js/ai caps the primitive ladder with ai.orchestrator(), ai.planner(), and ai.memory(), plus a cost-truth pass across every provider. The new @warlock.js/ai-panoptic package adds observability. ⚠ Breaking: snapshot persistence moves from a CacheDriver to the dedicated SnapshotStore.

@warlock.js/core Added 4 Fixed 1
  • Added warlock update — update every @warlock.js/* package in package.json to its latest version (operator preserved), then run the detected package manager's install
  • Added dev-server update notice — warlock dev checks npm on start and prints a one-line notice when a newer @warlock.js/core is published
  • Added devServer.checkForUpdates config flag (default true) to toggle the dev-server update notice
  • Added fetchLatestVersion() and isNewerVersion() registry/version utilities
  • Fixed warlock dev --skip-typings and --skip-health long-form flags now work (were silently ignored)
@warlock.js/ai ⚠ BREAKING 1 Added 8
  • ⚠ BREAKING Supervisor + workflow snapshot persistence moved from CacheDriver to the dedicated SnapshotStore contract. The per-primitive fallback is now ai.config({ defaultSnapshotStore }). Migration: replace snapshotStore: cache.driver("redis", { client }) with snapshotStore: ai.snapshot.redis({ client }) (and ai.snapshot.{memory,pg} for the other tiers).
  • Added ai.orchestrator() — stateful session manager over a supervisor: durable session / history / context, drift detection, history compaction, resume, and a command surface (orchestrator.asTool(), a 3-tier event surface, and OrchestratorContract / config / error types).
  • Added ai.checkpoint.{memory,pg,redis}() and ai.snapshot.{memory,pg,redis}() — durable orchestrator-session and supervisor / workflow run stores, with matching defaultCheckpointStore / defaultSnapshotStore config fields.
  • Added ai.memory() — agent-memory store with four tiers: working (in-run scratch), semantic (durable facts), episodic (durable, recency-blended events), and procedural (durable, reinforcement-blended how-tos). Wired into the orchestrator via a memory? field.
  • Added ai.planner() — an LLM generates an ordered plan over your registered capabilities, then executes it step-by-step.
  • Added ai.spawnSubAgent() — one-shot delegation to a fresh single-use agent with an optional per-task budget; usable from a planner step, a tool, or a workflow.
  • Added Cost-truth contract surface across all five adapters — Usage.reasoningTokens, per-channel ModelPricing, and ModelCallOptions.{reasoning, cacheControl} (ignored by adapters that lack the capability).
  • Added DX helpers — ai.router(), ai.fanOut(), ai.batch(), ai.fallbackModel(), ai.mockRouter(), agent.eval() + built-in ai.eval.* scorers, Vitest matchers (registerAiMatchers()), supervisor-level middleware, and ai.systemPrompt.fromFile(path).
  • Added Executables passed in an agent's tools: [...] are auto-adapted into tools (workflows / supervisors / orchestrators compose directly via .asTool()).
@warlock.js/ai-panoptic Added 4 Fixed 3
  • Added panoptic() — the one-call subscriber factory: builds a collector, registers exporters, and feeds traces via attach(), middleware(), or collect().
  • Added Exporters — consoleExporter(), fileExporter() (JSON-Lines), otelExporter() (GenAI semantic conventions), and langfuseExporter(). @opentelemetry/* and langfuse are optional peers, lazily imported.
  • Added createInMemoryTraceStore() — a queryable in-memory trace store (query / aggregate by runId, sessionId, status, time window; optional capacity FIFO cap) that doubles as an exporter.
  • Added Vendor-neutral trace contracts (Trace / TraceSpan / CollectorContract / ExporterContract) derived 1:1 from the core BaseReport tree, plus skills for observing, exporting, and querying traces.
  • Fixed Failed root runs now carry their error in every export (threaded from the result envelope onto the root span).
  • Fixed Per-span exporters now receive every span (the collector walks the finalized tree).
  • Fixed panoptic().middleware() now works on a supervisor (the middleware declares a supervisor hook map).
@warlock.js/ai-anthropic Added 4
  • Added Usage accountingusage.cacheWriteTokens is populated from Anthropic's cache_creation_input_tokens (alongside cachedTokens); reasoningTokens is left unset because Anthropic bills thinking inside output_tokens.
  • Added Extended thinkingModelCallOptions.reasoning maps to Anthropic's thinking budget (reasoning.effort → a tiered budget, floored at 1024); temperature is dropped when thinking is enabled.
  • Added System-prompt prompt cachingcacheControl.breakpoints >= 1 emits the system prompt with cache_control: { type: "ephemeral" }.
  • Added Capabilitiesreasoning, promptCaching, and pdf are now advertised; audio stays absent.
@warlock.js/ai-bedrock Added 4
  • Added Cost-truth capabilitiesreasoning, promptCaching, pdf, and audio are reported truthfully per model family (inferred from the model id, overridable via bedrock.model(...)).
  • Added Reasoning / extended thinkingModelCallOptions.reasoning maps to Converse thinking for reasoning-capable models, and no-ops elsewhere so unsupported params never reach the wire.
  • Added Prompt-cache write breakpointscacheControl.breakpoints appends a Converse cachePoint block for caching-capable models.
  • Added Usage.cacheWriteTokens populated from Converse cacheWriteInputTokens; reasoningTokens is left unset (Bedrock reports no reasoning channel).
@warlock.js/ai-google Added 3
  • Added Usage.reasoningTokens is populated from Gemini's thoughtsTokenCount (alongside cachedTokens), surfaced only when reported > 0.
  • Added ModelCallOptions.reasoning maps to Gemini's thinkingConfig (maxTokensthinkingBudget, effort → a bucketed budget) for reasoning-capable models.
  • Added ModelCapabilities now reports reasoning, promptCaching, audio, and pdf; cacheControl is accepted as a graceful no-op.
@warlock.js/ai-openai Added 3
  • Added Usage.reasoningTokens is populated from completion_tokens_details.reasoning_tokens (o-series / gpt-5 hidden reasoning channel), emitted only when > 0.
  • Added ModelCallOptions.reasoning.effort maps to the native reasoning_effort param for reasoning-capable models; reasoning.maxTokens has no Chat Completions equivalent.
  • Added ModelCapabilities.reasoning is inferred from the model name (overridable via .model(...)); promptCaching is always true (OpenAI caches automatically), and cacheControl write breakpoints are a no-op.
@warlock.js/ai-ollama Added 2
  • Added ModelCapabilities.reasoning is inferred from thinking-capable model tags (overridable via ollama.model({ name, reasoning })); promptCaching / audio / pdf report false.
  • Added ModelCallOptions.reasoning maps onto Ollama's native think flag for reasoning-capable models; reasoning.maxTokens and cacheControl are graceful no-ops.
4.2.11
June 17, 2026

Soft deletes go end-to-end in @warlock.js/cascade. @warlock.js/core adds warlock add notifications, and @warlock.js/notifications gains model-driven column mapping, read-state, and multi-tenant support.

@warlock.js/cascade Added 1 Changed 1 Fixed 2
  • Added Migration.create auto-wires the deletedAt column when the model's delete strategy is "soft" (opt out with { softDeletes: false })
  • Changed Require @mongez/reinforcements ≥ 3.3.0 — the update validator now uses its new when helper for conditional schema fields
  • Fixed Soft destroy() now sets deletedAt on the in-memory model — the instance was left stale before
  • Fixed Update validation no longer strips or rejects the deletedAt column under strict mode (now whitelisted like the timestamps)
@warlock.js/core Added 3 Changed 3 Fixed 1
  • Added lowerStage3Decorators() — Vite/Vitest plugin that lowers TC39 Stage-3 decorators with esbuild before oxc / the SSR rewrite mangles them; drop it first in plugins so model-decorated files load under Vitest 4 / Vite 8.
  • Added warlock add notifications — installs @warlock.js/notifications (+ the mail feature), ejects config/notifications.ts, and scaffolds the app-owned Notification model + migration (idempotent).
  • Added Notifications connector — a built-in, config-gated connector that lazy-imports @warlock.js/notifications, so core keeps no hard dependency on it.
  • Changed warlock add test now scaffolds a vite.config.ts that includes lowerStage3Decorators(), so a fresh project can test decorated models out of the box.
  • Changed warlock add test test / test:coverage scripts now run one-shot (vitest run) instead of watch mode — CI-safe by default.
  • Changed Bumped @mongez/reinforcements to 3.3.0
  • Fixed startHttpTestServer now starts early-phase connectors (database, cache, logger, …) before app modules, then late-phase (http, socket) after — mirroring dev/prod boot order; fixes a MissingDataSourceError under the Vitest integration harness.
@warlock.js/notifications Changed 5
  • Changed In-app column mapping moved onto the model as static columnMap (recipient / tenant / readAt / isRead); accessors, repository, and channels all derive from it. New NotificationColumnMap type.
  • Changed Read-state is presence-based — declaring readAt, isRead, or both selects the representation (default read_at); the mode-agnostic unread filter replaces isRead.
  • Changed Multi-tenant support — when the model declares a tenant column, the database channel reads it off the recipient and createFor(...) writes it.
  • Changed inApp.list / inApp.listUnread now forward full list options (page / limit / orderBy + filters).
  • Changed notificationColumns(model) derives its columns from columnMap; the SQL-vs-MongoDB dataSource branch is removed.
@warlock.js/auth Changed 1
  • Changed Bumped @mongez/reinforcements to 3.3.0
@warlock.js/cache Changed 1
  • Changed Bumped @mongez/reinforcements to 3.3.0
@warlock.js/herald Changed 1
  • Changed Bumped @mongez/reinforcements to 3.3.0
@warlock.js/logger Changed 1
  • Changed Bumped @mongez/reinforcements to 3.3.0
@warlock.js/seal Changed 1
  • Changed Bumped @mongez/reinforcements to 3.3.0
create-warlock Changed 1
  • Changed Bumped @mongez/reinforcements to 3.3.0 (package dependency + project template)
4.2.10
June 17, 2026

Patch: @warlock.js/auth moves its internal @mongez/* utilities from peer to regular dependencies, clearing the install warnings. @warlock.js/logger softens its console timestamp to gray.

@warlock.js/auth Fixed 1
  • Fixed @mongez/copper, @mongez/events, and @mongez/reinforcements are now regular dependencies instead of peerDependencies — they're framework-internal utilities your app never imports, so declaring them as peers produced unmet peer dependency warnings on install.
@warlock.js/logger Changed 1
  • Changed ConsoleLog's timestamp (and the context arrow) switch from bright-black gray to the 256-color slate — recessive but cleanly legible where bright-black read muddy.
create-warlock Changed 1
  • Changed The project template now pins the latest @mongez/* versions (@mongez/reinforcements@^3.2.0, @mongez/agent-kit@^1.2.0) so freshly scaffolded apps start on current dependencies. (@warlock.js/* versions are still rewritten to the scaffolder's own version at install time.)
4.2.9
June 17, 2026

Patch: @warlock.js/logger's console output is retuned for scannability — a dimmed time-only timestamp, aligned level columns, and a restored white-on-red fatal badge.

@warlock.js/logger Changed 1
  • Changed ConsoleLog output retuned for scannability — a time-only HH:mm:ss.SSS timestamp dimmed to gray, fixed-width level tags so the columns align, and fatal restored to a white-on-bright-red background badge. (FileLog / JSONFileLog keep the full ISO timestamp.)
4.2.8
June 17, 2026

Patch: @warlock.js/logger now prints each level's name beside its icon (ℹ info, ⚠ warn, ✗ error, …) for at-a-glance reading.

@warlock.js/logger Changed 1
  • Changed ConsoleLog now prints each level's name beside its icon (⚙ debug, ℹ info, ⚠ warn, ✗ error, ✓ success, ☠ fatal) for at-a-glance reading.
4.2.7
June 17, 2026

Patch: create-warlock now ships its templates/ folder, fixing the "Something went wrong" error when scaffolding a new project.

create-warlock Fixed 1
  • Fixed The published package now ships its templates/ folder, so scaffolding a new project works from the installed package — it was missing from the build, which failed the wizard with "Something went wrong" at the template-copy step.
4.2.6
June 17, 2026

Patch: create-warlock ships its bin folder again, restoring the CLI that was dropped from 4.2.5.

create-warlock Fixed 1
  • Fixed The published package now ships its bin folder again, so the create-warlock CLI works from the installed package — it was omitted from the 4.2.5 build.
4.2.5
June 15, 2026

Patch: warlock add notifications now scaffolds the in-app notifications HTTP surface — routes plus a list / mark-read / clear controller, gated by auth.

@warlock.js/core Added 1
  • Added warlock add notifications now scaffolds the in-app read/dismiss HTTP surface — routes.ts + a notifications.controller.ts (list / unread-count / mark-read / mark-all-read / clear / delete), gated by authMiddleware and recipient-scoped via inApp. Pulls @warlock.js/auth.
4.2.4
June 15, 2026

Patch: corrects a worker-loader build path in @warlock.js/core that 4.2.3 left broken.

@warlock.js/core Fixed 1
  • Fixed Fix the worker-loader path in the build entry points — a wrong path in 4.2.3 left the worker entry broken (and blocked the 4.2.3 publish for some packages).
4.2.3
June 15, 2026

Patch: ships the worker scripts as @warlock.js/core build entry points. A wrong path here blocked publishing for some packages — fixed in 4.2.4.

@warlock.js/core Fixed 1
  • Fixed Add the worker scripts as build entry points so they ship in the published package.
4.2.2
June 15, 2026

Patch: ships the warlock CLI entry (cli/start) in @warlock.js/core's build.

@warlock.js/core Fixed 1
  • Fixed Add cli/start to the build entry points so the warlock CLI entry ships in the published package.
4.2.1
June 15, 2026

Patch: @warlock.js/core and @warlock.js/cascade ship their bin folders, restoring the warlock and cascade CLIs dropped from 4.2.0.

@warlock.js/core Fixed 1
  • Fixed Ship the bin folder so the warlock CLI works from the published package — it was omitted from the 4.2.0 build.
@warlock.js/cascade Fixed 1
  • Fixed Ship the bin folder so the cascade CLI works from the published package — it was omitted from the 4.2.0 build.
4.2.0
June 15, 2026

A security overhaul of @warlock.js/auth — brute-force throttling, atomic refresh-token rotation, and CSPRNG secrets (the jwt config gives way to accessToken / refreshToken). Introduces two packages: @warlock.js/notifications (multi-channel notifications) and @warlock.js/access (RBAC + ABAC authorization).

@warlock.js/notifications New 1
  • New Shipped Warlock.js Notifications Package.
@warlock.js/access New 1
  • New Shipped Warlock.js Access Package.
@warlock.js/auth Added 5 Fixed 6 Deprecated 1 Removed 2 Security 2
  • Added loginThrottleMiddleware — failure-aware brute-force / credential-stuffing protection: counts only failed logins, locks per-account and per-IP, and rejects pre-controller with 429 (cache-backed, fails open). Adds AuthErrorCodes.TooManyAttempts (EC004).
  • Added accessToken / refreshToken configuration blocks, making a separate refresh-token secret first-class.
  • Added Overridable token storage — register a custom model under config.auth.accessToken.model / refreshToken.model and .extend() the exported schemas to add columns (e.g. a multi-tenant organization_id).
  • Added tokenType (access | refresh) claim, stamped on issue and verified on read, so an access token can't be presented as a refresh token.
  • Added expires_at on access tokens; warlock auth.cleanup now purges expired access tokens too.
  • Fixed Default access-token lifetime was ~3.6 seconds (a numeric expiresIn read as milliseconds) and is now 1 hour.
  • Fixed Targeted revocation queried userId instead of the user_id column, so logout / refresh-token removal threw on Postgres and silently no-oped on MongoDB; token queries now route through named model statics.
  • Fixed Token deletions were fire-and-forget (false success for callers, uncatchable rejections) and are now awaited.
  • Fixed The route middleware matched on userType instead of the user_type column.
  • Fixed revokeAllTokens / revokeTokenFamily reported an empty set, so token.revoked / token.familyRevoked never fired; the revoked rows are now captured before revocation.
  • Fixed A throwing synchronous auth-event listener no longer turns a completed login into a 500.
  • Deprecated The auth.jwt.* configuration block. Use accessToken / refreshToken instead — the legacy shape is still read and mapped forward with a one-time deprecation warning.
  • Removed Unread access_tokens columns is_active and last_access.
  • Removed The unused auth.password.salt configuration key.
  • Security warlock jwt.generate now derives JWT_SECRET / JWT_REFRESH_SECRET from a CSPRNG (Random.token) instead of Math.random().
  • Security Refresh-token rotation is atomic — a guarded conditional UPDATE means two concurrent rotations can't both succeed, and a replayed token revokes its entire family.
@warlock.js/logger Added 4 Changed 3 Fixed 1
  • Added log.flush() — awaitable async counterpart to flushSync(), draining every channel via Promise.allSettled with per-channel isolation. Implemented by FileLog / JSONFileLog.
  • Added SentryLog channel — forwards entries to Sentry (eventLevels become events, others breadcrumbs; module / action as tags). @sentry/node is an optional, lazily-imported peer.
  • Added log.fatal() + fatal level — ranked strictly above error for unrecoverable failures; does not auto-flush or exit.
  • Added ConsoleLog renders fatal with a icon on a bright-red background, distinct from error's .
  • Changed captureAnyUnhandledRejection() now escalates uncaughtException to log.fatal (was error); unhandledRejection stays at error.
  • Changed LoggingData.type is now typed as LogLevel (was a duplicated inline union).
  • Changed LogContract / LogChannel now expose an optional flush?() alongside flushSync?().
  • Fixed @sentry/node is referenced only via local types + an indirect dynamic import, so source-served consumers no longer get TS2307: Cannot find module '@sentry/node' when they don't install the optional peer.
@warlock.js/cascade Changed 1 Fixed 1
  • Changed MongoDB and PostgreSQL drivers now log a failed initial connect() at log.fatal (was log.error) — a boot-time database connection failure is unrecoverable, so fatal keeps "page on fatal only" alerting clean. Per-query and disconnect failures stay at error.
  • Fixed PostgreSQL increment / decrement (and the *Many variants) bound the amount as $1, colliding with the first filter placeholder (SET n = n + $1 WHERE id = $1) so every filtered counter update wrote the wrong number; the amount now binds after the filter params.
@warlock.js/ai Fixed 1
  • Fixed No-argument tools (declared without an input schema) no longer crash on invocation — tool.invoke now skips validation when no schema is present and passes the raw input to the handler.
@warlock.js/ai-anthropic Added 1
  • Added Opt-in promptCaching flag on the model config — marks tool definitions with cache_control: { type: "ephemeral" } so multi-trip agents reuse the static tool schemas at the cache-read rate. Off by default.
@warlock.js/cache Changed 1
  • Changed Redis driver now logs a failed initial connect() at log.fatal (was log.error) — a boot-time cache connection failure is unrecoverable, so fatal keeps "page on fatal only" alerting clean.
@warlock.js/core Changed 1
  • Changed herald-connector and http-connector now log a failed boot-time connection at log.fatal (was log.error) — an unrecoverable broker connection or HTTP port-bind failure makes "page on fatal only" alerting clean; the HTTP connector flushes logs before process.exit(1). Disconnect / shutdown failures stay at error.
4.1.15
June 4, 2026

The first public release of Warlock.js — 17 packages published together at 4.1.15. Every change from here on is recorded per package and aggregated on this page.