Skip to content

0.4.0 - 2026-11-02

Choose a tag to compare

@github-actions github-actions released this 02 Nov 22:03

Release Notes

Added

  • --model-for works with --harness pi: each session's Pi is launched
    with its role's model, and the run's models.json lists every model of
    the map, keeping the settings your ~/.pi/agent/models.json has for
    each. A role's model without --context-window-for takes the window
    that file lists for it, and its inputs unless --vision is given.
    --swap-models works with Pi too. See ADR 0011.
  • tenaz init --swap-models keeps one model of a --model-for map
    loaded at a time, for models that do not fit in memory together: before
    each session Tenaz unloads the others and loads that session's model,
    so each review costs two loads. At every tenaz run start it loads
    each model once, alone, and stops the run if one will not load. Needs
    --engine load-api (Sushi, mlx-serve). tenaz run --swap-models and
    --no-swap-models change it; it is kept as launch.swapModels in
    run.json, status --json has swap_models, and tenaz status says
    the models are loaded one at a time. When swapping, the
    journal's lease records name the model of each load and unload,
    and record a model the engine dropped by itself as evicted. See ADR
    0015.

Changed

  • With fx, an engine error that came with an error status now starts
    with that status, as Pi's errors do, then the engine's message:
    503: <message>, or 500 alone when there is no message. The message
    of an SSE body is that of its first error chunk. Pi puts the raw body
    after the status instead. It shows in the session's provider-error
    watchdog event and image_refusal, the journal's image-continuation
    refusal, the image continuation note and tenaz watch.
  • The agent skill no longer offers to pipe the install script into sh:
    it gives Homebrew, a release archive with its checksum and attestation,
    or cargo install, and points to the README for the rest. skills.sh's
    Snyk audit flagged the script's URL as an unverifiable dependency.
  • The journal's lease records end with ready whenever the lease loads
    the LLMs back: also at the start of a run, after an engine error, and
    after a failed reload or unload, not only after a media or heavy job.
  • A context budget held at the ceiling a failed raise set, for twice the
    sessions a raise waits for then (8 to 32 with the default
    capacityRecoverSessions), is raised past it, halfway to the largest
    configured budget, and the log says so; an overflow sets the ceiling
    again. The ceiling used to last the whole run: in the Digger below the
    Mine run of 2026-10-30 it was set at 58419 while memory was short, and
    the budget stayed there for hours after the pressure was gone, with
    sessions reaching 77257 tokens unharmed. See ADR 0014.

Fixed

  • When the engine cannot take the preflight's requests, tenaz run now
    stops with the engine's error and says it is the engine's. It used to
    report no reasoning, no cached prefix and a failed tool call, and advise
    another model. This covers an engine out of memory (mlx-serve answers
    503), one that answers 502, 503 or 504, and one not reached.
    --allow-weak-engine does not accept such a preflight, so the next run
    measures it again. For an engine out of memory it says to free memory
    or reload the model, as the run's retries do. Other engine errors, such
    as a refused image or a 400 or 500 not about memory, are still left to
    the checks and the flag. run.json's preflight keeps the engine's
    errors in engine_errors. The log and tenaz status show such a
    preflight as not judged, with the count of errors, instead of its
    checks, and the log names the first error. A failed preflight points
    to .tenaz/private/preflight.fx-acp.log only with fx: Pi keeps no log
    of it. See ADR 0016.
  • The preflight line of the log and of tenaz status shows the image
    check (vision=true or vision=false) for a vision model. A preflight
    that failed only on the image used to read like a pass.
  • The log's preflight line shows (accepted weak) when
    --allow-weak-engine runs past a failed preflight, as tenaz status
    does. The log used to be written before the run took the flag into
    account.
  • The preflight line of the log and of tenaz status starts with
    settled=false when the preflight's session did not finish. Such a
    preflight fails, but its line used to read like a pass.
  • The engine proxy ends a streamed event at a blank line written with
    \r\n\r\n or \n\r\n, as well as \n\n. With an engine that framed
    its events that way, the proxy used to report an error inside the
    stream only when the stream ended. When the request offered a tool to
    repair, the whole reply reached the harness only then, not repaired,
    and when its first event held arguments of a call to that tool, the
    rest of the reply was dropped.

Install tenaz 0.4.0

Install prebuilt binaries via shell script

curl --proto '=https' --tlsv1.2 -LsSf https://github.com/aartiles/tenaz/releases/download/v0.4.0/tenaz-installer.sh | sh

Install prebuilt binaries via Homebrew

brew install aartiles/tap/tenaz

Download tenaz 0.4.0

File Platform Checksum
tenaz-aarch64-apple-darwin.tar.gz Apple Silicon macOS checksum
tenaz-x86_64-unknown-linux-gnu.tar.gz x64 Linux checksum

Verifying GitHub Artifact Attestations

The artifacts in this release have attestations generated with GitHub Artifact Attestations. These can be verified by using the GitHub CLI:

gh attestation verify <file-path of downloaded artifact> --repo aartiles/tenaz

You can also download the attestation from GitHub and verify against that directly:

gh attestation verify <file-path of downloaded artifact> --bundle <file-path of downloaded attestation>