Skip to content

v2.14.1

Choose a tag to compare

@github-actions github-actions released this 07 Sep 03:51
· 24 commits to main since this release
763a0ed

Forge v2.14.1

CLI/TUI and desktop release v2.14.1; mobile remains on its compatible native version.

  • CLI / TUI — binaries below (*.tar.gz / *.zip) or brew upgrade forge
  • Desktop (macOS · Windows · Linux) — app bundles below + in-app auto-update
  • Mobile (iOS) — production OTA by default; native/TestFlight builds are manual when required

v2.14.0 was tagged but never published: its release build failed 24 seconds in, before compiling
anything, because Debian Bullseye is EOL and its security pool rotated out from under the live
mirror — the apt index advertised libc-dev-bin 2.31-13+deb11u14 while deb.debian.org had
already deleted that .deb. Both Linux targets died identically. This release is the same work
plus the build fix; per RELEASING.md a release tag is never moved, so it ships as a new version.

Fixed

  • The portable Linux release build could not install its toolchain packages. The container now
    pins apt to a timestamped snapshot.debian.org view, which keeps the security suite and cannot
    rotate underneath a build — making the step reproducible the same way the image digest and Rust
    toolchain already are (.github/workflows/release.yml).

Added

  • shell can start something that keeps running. Every attempt before this died the moment the
    call returned, and nothing said why — a session spent hours concluding "the sandbox kills
    background processes" and reaching for transient systemd units. The cause was three lines: after a
    command exits, the tool SIGKILLs its whole process group so a leaked descendant cannot hold the
    output pipes open, and nohup cmd & does not escape that (nohup detaches from the terminal, not
    the process group), nor does systemd-run --scope. shell{background:true} now spawns into its
    own session (setsid) with output on a log file, so the job outlives the call, the turn, and
    Forge itself; the new shell_job tool lists, tails, inspects and stops those jobs from state on
    disk under .forge/jobs/, so a later turn — or a whole new session — can find what an earlier one
    started. Jobs deliberately survive shutdown: an emulator that took two minutes to boot must not
    die because a turn ended (crates/forge-tools/src/shell/background.rs,
    crates/forge-tools/src/shell.rs).
  • A foreground call now says when it killed what the command left running, naming the processes
    and pointing at background:true. The silent kill is what turned a three-line problem into hours
    of dead ends (crates/forge-tools/src/shell/background.rs).
  • A local SearXNG is now the default search backend, with scripts/searxng-setup.sh to stand it
    up in one command and FORGE_SEARXNG_URL to point at another instance
    (crates/forge-tools/src/web/search.rs, scripts/searxng-setup.sh).

Fixed

  • web_search was effectively down without a key. The keyless DuckDuckGo default answered the
    FIRST query from an IP and then returned HTTP 202 with an empty body — one query per session is
    not a search tool — and the error it produced advised setting a Brave key "for reliable results",
    advice that expired when Brave retired its free tier in February 2026. Search is now a chain
    (local SearXNG → DuckDuckGo → keyless Bing) so one engine being throttled no longer takes it down,
    and every result says which engine answered. That attribution is load-bearing: keyless Bing never
    throttles but returns confident nonsense — asked for tokio select macro it returned ten
    well-formed results for plumbers near 1 Microsoft Way — so it is last and labelled, never trusted
    silently. Bing's /ck/a tracking redirects are decoded to real URLs
    (crates/forge-tools/src/web/search.rs).
  • The emulator booted on a renderer that segfaults it. A headless emulator_start picked
    -gpu swiftshader_indirect; SwiftShader renders in JIT-compiled shader code, so a bad guest draw
    call faults inside it — an out-of-bounds SIMD load — and the SIGSEGV takes the whole emulator
    down mid-test. It killed the local AVD twice in a row. Headless now uses -gpu auto-no-window,
    the emulator's own renderer selection, which also drops the CPU cost of compositing a phone-sized
    screen (crates/forge-device/src/emulator.rs, crates/forge-tools/src/device.rs).
  • A language server that could never succeed was retried forever. clear_failure() ran on a
    successful handshake, but rust-analyzer initializes in milliseconds and dies minutes later while
    indexing — so the counter reset to zero before every failure and the exponential backoff never
    once doubled. One project logged 386 identical "retrying in 30s" lines over three days, ~90% of
    its session log, each cycle also injecting an unactionable "diagnostics unavailable" notice into
    the model's context after every write. Only delivered diagnostics clear the failure state now, a
    (language, root) pair is given up on after five consecutive failures with a reason naming
    lsp.memory_limit_mb, and outage notices stay in the log where they belong
    (crates/forge-lsp/src/registry.rs, crates/forge-core/src/lsp_hints.rs).
  • A hard guard abandoned the work it was ending. The error named 60 modified files and stopped,
    leaving a human to reconstruct what a 400-step turn had been in the middle of. Hard guards now
    snapshot tracked edits with git stash create under refs/forge/aborted-turns — nothing moves,
    the files stay exactly where they are — and the guard messages print billable and cache-inclusive
    input, because printing only the latter made a working token ceiling look broken
    (crates/forge-core/src/turn_guards.rs, crates/forge-core/src/lib.rs).
  • forge run --output-format stream-json answered with prose on any machine running a daemon.
    The fleet-publish check ran before the stream-json branch, so an explicit machine-readable format
    was silently replaced by two human-readable lines and exit 0 — every parsing caller saw a
    successful run and no events. It went unnoticed because CI has no daemon: the e2e test passed
    everywhere except a developer's own box. An explicit machine-readable format now opts out of the
    fleet (crates/forge-cli/src/cli/commands/run/one_shot.rs).
  • The emulator inherited Forge's process group, so any group-directed kill threw away a
    two-minute boot for a reason invisible from the device side (crates/forge-device/src/emulator.rs).

What's Changed

Full Changelog: v2.14.0...v2.14.1