Skip to content

v5.40.0

Choose a tag to compare

@hassanzohdy hassanzohdy released this 29 Oct 14:14
· 21 commits to main since this release

Released 2026-10-29.

All 33 packages (@warlock.js/* and create-warlock) move to 5.40.0 together;
keep every family package on the same version. No breaking changes. This release makes
warlock check finish on large apps, makes the development server steadier and quieter,
brings Windows up to a tested platform, and adds turn control, context editing, spend
limits and approvals to the AI agent loop.


1. Breaking changes

  • None. Two additions can surface in strict code; see the upgrade notes:
    FinishReason has a new member "tool_stop", and http.log: true now writes one summary
    line per request instead of one line per phase.

2. warlock check finishes on large apps (core)

  • On a large app the conventions step could run for tens of minutes at full CPU without
    printing anything. Two causes are fixed:
    • The .client boundary rule (WL9022) re-read, re-parsed and re-resolved every shared
      file for each controller, service, model and route it checked. Import chains are now read
      once per run, and files known to reach no .client module are never walked again.
    • Parsed syntax trees kept their parent links in shared lookup tables. With thousands of
      trees alive, memory cleanup stalled for seconds at a time. The links now live on the nodes,
      and the import rules reuse one parse per file.
  • On an app with about 7,500 source files, the conventions step now takes about 14 seconds.
  • Progress lines. warlock check prints one line to stderr when each step starts and
    ends, and the conventions step reports its milestones. stdout is unchanged, so --json
    still prints only the report.
  • Per-rule time budget. Each convention rule may use 30 seconds over a run. A rule that
    goes over is skipped for the remaining files and reported as a WL9026 warning, instead
    of hanging the command. Set ruleBudgetMs in .warlock-check.json; 0 turns it off.

3. A steadier, quieter development server (core, web, fs)

  • Spec and test files are never auto-loaded. Command discovery, main, routes,
    worker, events and locale files, and the standalone cascade migrate loader skip
    *.spec.* and *.test.* files, so a vi.mock() beside a command no longer breaks the CLI.
  • The first page after a restart waits for the compile. In development the page loader
    timeout defaults to 90 seconds and starts only after a reload in progress has settled.
    Production keeps 15 seconds; an explicit web.loaderTimeout still wins.
  • Smaller reloads. Importers that reach a changed file only through import type are no
    longer reloaded, only the changed files are re-read, and the +N modules count is per file.
    Adding files at boot no longer re-scans every tracked file.
  • Requests survive reloads. New requests wait while a reload runs; requests already
    running get up to 5 seconds to finish. A request held longer than 15 seconds gets a 503
    with Retry-After: 2. putJsonFile and the typings manifest are written atomically.
  • Module singletons. appSingleton(key, create, { dispose, disposeOnReload }) keeps one
    instance per process across reloads and across the copy that pages load through Vite, and
    disposes it on shutdown. A second supervisor() for the same registry and owner in one
    process returns the running supervisor instead of failing with a held lock.
  • Less log noise.
    • With web.moduleBoundary: "warn", each boundary violation prints once per dev session,
      then a count of repeats.
    • With http.log: true, each request writes one info line
      (GET /orders 200 12ms [request id]); the per-phase lines move to debug.
      http.logPhases: "info" restores the old output.
    • AI tool schema lint takes lint: { ignore: [codes] } per tool, in
      ai.config({ toolLint }) and on ai.mcp(server, { lint }), and prints each finding once
      per process.
  • App ready hook. Application.onceBooted() / whenBooted() runs after every connector
    has started; the docs now say when it fires.

4. Windows (process, core, fs, ai, web)

  • Process supervisor.
    • The guardian answers the resume request before it starts the program, so a slow start
      can no longer time out the ownership check.
    • observationTimeoutMs is a supervisor option: 2 seconds on Linux, 15 on Windows and
      macOS. The timeout error names the option.
    • pollIntervalMs defaults per platform: 100 ms on Linux, 500 ms on macOS, 1.5 s on
      Windows.
    • On Windows one long-lived helper answers every process lookup in batches, instead of two
      PowerShell processes per lookup, and the launch gate polls the guardian pipe directly.
  • Fixed on Windows: image variants answered 500 when the cache path went over 260
    characters; warlock typecheck, warlock check --changed and warlock inspect could not
    match the compiler's paths; the development model registry keyed a deleted model
    differently; end-to-end browsers ignored their profile; warlock release linked
    node_modules with a directory symlink, which needs administrator rights.
  • ai.snapshot.sqlite stores have close(), so their database file can be removed.
  • The framework's own test suites for fs, process, core, web and ai now run on
    Windows in CI.

5. AI agent loop (ai, ai-anthropic)

  • End a turn after a tool. stopAfterTools: [...], stopWhen(ctx) on the agent, or
    endsTurn on a tool ends the run with no further model call; the finish reason is
    "tool_stop".
  • Context editing. contextEditing: { clearToolResults } replaces old tool results with
    a placeholder before each trip, for every provider. Anthropic models use the provider's
    native clearing. It runs before compaction, which still summarises.
  • Prices and spend. ai.pricing({ tables }) builds one overridable price list from the
    provider tables. budget({ onSpend }) reports spend after every call, and
    scoped: { window: "all" } caps a session or a lifetime.
  • Approvals. ai.human.approval() takes per-tool modes (allow, deny, ask) and
    remember, so an approval can cover the rest of the session for that tool or those
    arguments.

6. AI providers and tools

  • xAI: calls that use native web or X search go through the Responses API; other calls
    stay on Chat Completions (api: "chat" | "responses" to choose).
  • OpenAI: a call with web_search is sent to the Responses API automatically; a model
    that cannot search fails with a clear error before any request.
  • Google: googleSearchBudget(n) stops sending search once n queries were used. The
    Gemini API has no per-call cap, so one running call can still go over.
  • Model listing: every first-party adapter implements models(), a live listing in one
    shape (ModelInfo).
  • Workspace patches: @warlock.js/ai-workspace adds an atomic apply_patch tool for
    unified diffs (modify, create, delete, rename), confined to the workspace.

7. Other fixes

  • Manual beginTransaction() covers the caller's queries (cascade). After
    await beginTransaction(), the queries the caller runs use the transaction until commit or
    rollback. Before, they ran outside it on MongoDB and on other pool connections on
    PostgreSQL. transaction(fn) is unchanged.
  • New API-only projects typecheck (create-warlock). A project created without web pages
    no longer receives src/config/web.ts, which imported a package it does not install.
  • New projects tell agents how to upgrade (create-warlock). The generated AGENTS.md has an
    "Upgrading Warlock.js" section: read npx warlock release-notes --from <current> (bundled with
    core, so it works offline), run npx warlock update, follow the upgrade notes, then
    npx warlock check. It also explains that each package's skills arrive as one router skill.

8. Upgrade notes

  1. Update the family. Run npx warlock update.
  2. Read what changed. Run npx warlock release-notes --from 5.39.2.
  3. Run npx warlock check. It should now finish on a large app. A WL9026 warning names
    a rule that ran out of time; raise ruleBudgetMs if your app needs more.
  4. Exhaustive FinishReason switches. Add a "tool_stop" case where you switch over
    every finish reason.
  5. Request logs. If you rely on per-phase request lines at info, set
    http.logPhases: "info".
  6. Replace app workarounds. Module-level singletons kept on globalThis can use
    appSingleton(). A pinned longer ownership timeout or poll interval on Windows can usually
    go. Specs moved away from command folders can come back.
  7. Review manual transactions. Code that calls beginTransaction() now runs its later
    queries inside the transaction, as the API always promised. Make sure each one commits or
    rolls back.