Releases and upgrading
Two kinds of change matter differently here.
A new feature you can ignore until you want it. A change to an evidence gate you cannot: it decides which findings your next run is allowed to promote, and a session started under an older build can carry evidence the current build no longer accepts. Those are the ones this page leads with.
Versioning is semver, with one caveat while locac is below 1.0: a gate can tighten in a patch release. Tightening a gate never invents a finding, it only declines to promote one, so it is a fix rather than a feature - but it is still something you have to know about before you resume a week-old session and wonder where its Criticals went.
Upgrading to 0.5.2
This release changed what counts as proof. Three things follow from that, and all three can surprise a resumed session.
execute no longer proves exploitability
execute mints an artifact, and that artifact is now labelled a diagnostic:
[diagnostic artifact: ev_… — NOT proof: execute did not evaluate an exploit assertion]
An exit code is not an oracle. A script exiting zero proves the script ran; a nonzero exit may just be the script's own bug. Neither observed a security effect, so neither can promote a severity.
If your workflow built PoCs as execute scripts, move them to reproduce with a claim and either
a crash or a sentinel the PoC causes to appear:
// before - recorded, auditable, and no longer proof
execute({ script: "…" })
// after — a claim, and an assertion the oracle checks
reproduce({
cmd: "…",
sentinel: "LOCAC-PWNED-8f21",
claim: {
location: "src/upload/handler.ts",
line: 143,
vuln_class: "CWE-78",
effect: "attacker-controlled argument executed; sentinel printed"
}
})
The same applies to a plain replay_request (now diagnostic unless you pass claim + assertion),
a run_module scan that confirmed nothing, and a reproduce run whose oracle did not trigger.
All are still recorded. None is proof.
A High or Critical finding must say where it is
record_finding now requires file for anything at High, Critical or evidence_kind=proof, and
requires line too unless file is an exact http(s) URL. Without a location there is nothing to
bind the proof to, and an artifact that binds to nothing can be reused for the next bug in the same
file.
Artifacts from before 0.5.2 are not grandfathered
The schema migration adds the claim and proof columns and loses no data. Old rows keep every byte
they had, and recall still finds them. But an old row has no structured claim and no checked
assertion, so it is a diagnostic, and a finding citing one is refused at promotion:
Cannot promote finding f_…: it has no semantic execution proof bound to this file/URL.
Re-run the proof under the current gate and attach the new artifact with
record_finding(artifact_ref=…). Honouring the old rows would have kept alive exactly the class of
finding the release exists to stop.
A cross-verify vote must name the finding it votes on
A verifier's artifact now has to clear four checks, not one: minted during this round, bound to
the exact finding_id, matching that finding's location, and distinct from the other two lenses'.
A URL can host several bug classes at once, so a claim naming only the location would let one of them
vouch for another. Re-verifying a DAST finding means calling run_module with its finding_id.
If you have written your own role that submits verdicts, its prompt needs to pass claim.finding_id
through. The built-in exploit-verifier already does.
Confined commands now serialize across roots
Auditing two repositories in one process - which the dashboard allows, and which eval ab does by
design - used to let the second root re-pin the process-global sandbox while the first root's command
was still running. A process-wide lease now covers the whole command lifetime. Confined commands from
different roots run one at a time. A single-root run is unaffected.
Details in Tools and Security model.
Release history
0.6.11 - 2026-08-15
Fixed. The DAST egress guard now blocks a cloud-metadata IP hidden in the NAT64 well-known prefix.
The guard already recovered an IPv4-mapped IPv6 (::ffff:169.254.169.254) before range-checking it,
but a metadata address encoded in 64:ff9b::/96 (RFC 6052) -
64:ff9b::a9fe:a9fe, which a NAT64 gateway routes straight at 169.254.169.254 - slipped past as an
ordinary public IPv6. The embedded IPv4 is now decoded out of the prefix and faces the same
169.254.0.0/16 block, on both the IP-literal path and the DNS-resolved path (a hostname whose AAAA is
a NAT64-wrapped metadata address). Public and RFC1918 IPv4s wrapped in NAT64 stay reachable, matching
how a bare IPv4 is treated - authorized internal DAST still works; only the link-local/metadata escape
closes.
0.6.10 - 2026-08-13
Added. ask_user lets the agent hand a decision back to the operator instead of guessing: which
lead to chase first, whether a behaviour is in scope, which credential set to assume. A call carries
up to four questions, each with 2-4 options and an optional multiSelect, plus an always-present
free-text "Other" row for an answer none of the options cover. In the TUI it renders as a modal that
blocks the turn until answered, the same way the dangerous-tool approval prompt does. Run without an
interactive terminal - piped, --print, a role child, CI - there is no operator to ask, so it
degrades deterministically: it auto-selects each question's first option, notes that it did, and the
run never stalls waiting on input that will not arrive. It is orchestrator-only, so a subagent role
(which has no operator) never sees it. See the tool reference. A mid-session
/model switch is now also remembered per session and restored on resume - the TUI /resume and the
cross-process locac resume <id> both reopen on the session's stored model, while an explicit
--model/--provider still wins.
Fixed. A round of resume fixes so a session comes back whole. In-TUI /resume, /new and /fork
now rebind the research tool state (recall, findings, memory, the coverage/verdict/evidence repos),
not just the transcript - previously the run was re-driven while recall stayed blind to the resumed
session's rows, the "resumed but lost context" operators reported. Resume also repaints tool calls and
their results, not only prose, so a resumed hunt shows the grep/read/record_finding work instead of
two lonely prose bubbles; only calls with a persisted result are replayed, so an interrupted turn
leaves no spinner that never resolves. Separately, a Vietnamese IME burst (char + backspace + char in
one stdin read) no longer sticks characters in the editor, and the web Providers form keeps its cost
/1M row inside the panel border.
0.6.9 - 2026-08-13
Fixed. Ctrl+C in the TUI stopped tearing the process down mid-turn. Ink's default exitOnCtrlC
intercepted the keystroke and unmounted before any handler ran, so a Ctrl+C during a running turn
slammed locac shut with no confirmation and abandoned the in-flight turn - the message-boundary abort
was wired only to Esc. It is now disabled and handled in the input box: while the agent runs, Ctrl+C
aborts the turn the way Esc does (the session is preserved); idle, it clears a non-empty draft, and on
an empty draft it quits only on a second press, with a "press Ctrl+C again to exit" hint - the same
courtesy the headless SIGINT path already had. Ctrl+D remains an immediate quit. Separately, /resume
no longer lists empty sessions: persistence is transactional per message, so an interrupt only ever
drops the single in-flight turn, but relaunching created a fresh empty session that sorted to the top of
the picker by updated_at and shadowed the session the operator came back for - which read as lost
context. The picker now draws only sessions with at least one entry, so a still-empty session can never
outrank real work.
0.6.8 - 2026-08-10
Added. trace_callees traces a symbol down the call graph toward sinks (forward BFS) - the
mirror of trace_callers. From a function, each hop groups the callees invoked inside it, so an
untrusted source can be walked down to a dangerous sink in one call instead of hand-chaining
grok_sink. It is read-only, budget- and coverage-wrapped, and realpath-confined to the target root
like every name-based edge in the harness. It joins the read-only tool set, the role read-only
allow-list and the compaction evidence set, and the bundled srcwalk skill now drives
meet-in-the-middle with the two tracers as a pair.
0.6.7 - 2026-08-10
Added. The DAST module runner injects beyond query and form params: JSON body leaves (dotted and
indexed paths like user.roles[0]), a curated set of request headers, and cookie pairs - a JSON
probe re-serializes only the targeted leaf and a cookie probe rewrites only the targeted pair, so a
mutated request is never corrupted. Two active modules join it. ssti (CWE-1336) injects arithmetic
in the four common template syntaxes and confirms only when the computed product appears in the
response while the raw payload does not, so a page that merely echoes the payload is never flagged.
cmdi (CWE-78) times a benign baseline, then requires two sleep-bearing sends to each exceed it by
a wide margin, rejecting a one-off stall and skipping an already-slow baseline. Authenticated scans
read static same-host credentials (bearer headers, cookies) from LOCAC_HOME config only - never the
target repo - attach them only to their listed hosts (never an off-host redirect or SSRF target), and
register them as operator secrets so the output guard scrubs them from probe evidence. locac sarif
exports a session's findings as SARIF 2.1.0 for GitHub code scanning.
0.6.6 - 2026-08-07
Fixed. A file a scanner skips - oversized, binary, or unreadable - can no longer pass for clean
coverage. Every source scanner threads a coverage record through its walk, so a skipped file is
reported in-band with the reason, persisted as a note_coverage_gap, and folded into a
⚠ PARTIAL COVERAGE warning; gaps are relativized from the canonical target root, so a macOS
/var → /private/var or a Windows junction is not mistaken for an escape and dropped. read streams
a file of any size in bounded pages that each name what remains unchecked, sniffing only the head for
a binary marker before any line offset.
0.6.5 - 2026-08-06
Added. A key-toggled live subagent overlay (Ctrl+T): a fullscreen view of every running
subagent with its status, role, turn count, token spend, current activity, and elapsed time. Up and
down move the selection, s steers the selected agent a one-line message, and x aborts it. The
view is display-only and its elapsed clock is injected at the manager boundary, so the deterministic
verdict path is never touched by a wall-clock read.
0.6.0 - 2026-08-06
Added. note_coverage_gap - a voluntary self-report an agent files when it declines or cannot
fully analyze a region of the target (too large, binary, unparseable, refused, out of scope), so a
silent skip cannot read as clean coverage. It is a diagnostic only: it never promotes, blocks, or
downgrades a finding and takes no part in the proof gates. Available to the orchestrator and every
subagent (each gap is tagged with the agent that logged it), deduplicated on location and category,
and surfaced as a count-only segment on the run-state digest and in full on the session-detail API.
The model- and target-authored note is stored but never reaches an unfenced harness surface.
0.5.3 - 2026-08-06
Added. An opt-in thinkingTokenBudget for vLLM-style openai-completions endpoints, where the
reasoning phase and the answer draw from the same output-token cap. Without it, an unbounded reasoning
turn can spend the entire budget and return no answer and no tool call; with it locac sends a top-level
thinking_token_budget, derived from the reasoning level and clamped to leave room for the answer. Off
by default (a plain OpenAI endpoint rejects the field) and effective only alongside a reasoning level.
0.5.2 - 2026-08-05
Fixed. A successful command was accepted as proof of exploitability; evidence now carries a
structured vulnerability claim plus a machine-checked assertion, bound to the same session and the
exact file/URL and line. Cross-verifiers could cite proof unrelated to the candidate; every counted
vote must now mint a fresh artifact, name the exact finding_id, and stay distinct from the other
lenses. Concurrent audits could re-pin the process-global sandbox underneath a running child.
Changed. DAST proof is detection-scoped rather than scan-scoped: run_module emits one artifact
per confirmed detection and can re-verify an existing finding without recording a duplicate;
replay_request proves something only when a marker appears in the mutated response and not in its
benign baseline.
0.5.1 - 2026-08-04
Added. Drag a file into the terminal and it attaches - the other half of image support, since a drop arrives as a pasted path rather than as its own input channel. A 3 MiB ceiling on an attachment, refused rather than resized.
Fixed. allowRead confined nothing on macOS or Linux: the profile emitted a blanket
(allow file-read*) on macOS and re-bound allowed paths over a tmpfs mask on Linux, so target code
could read ~/.locac/config.json and the API key in it. Reads are deny-first now.
0.5.0 - 2026-08-04
Added. The editor keys that were missing (Home, End, Delete, Ctrl+D, Ctrl+L,
Shift+Tab), Alt+Up to pull queued messages back into the editor, clipboard images, and /tree to
draw the session picker as the fork tree.
Fixed. The Delete key deleted backwards. Alt+Up destroyed a queued attachment-only message.
0.4.0 - 2026-08-04
Added. An installed skill remembers where it came from, which is what skills update <name> and
--all needed to exist. skills add <url> --all installs a whole repo behind one gate.
Fixed. A source tree could forge its own provenance sidecar and choose what a later update cloned from.
0.3.1 - 2026-08-03
Changed. Release artifacts ship compressed - 116 MB becomes 39 MB - byte-reproducibly, and both installers verify the checksum before extracting rather than after.
0.3.0 - 2026-08-03
Added. The Agent Skills format as published, verified against all 17 skills in
anthropics/skills; a skill's bundled scripts/ actually runs (read-only grants, never write);
locac skills add <url>.
Fixed. Four of those 17 skills loaded mangled. The sandbox pin ignored everything but the root, so skill read grants silently never applied.
0.2.0 - 2026-08-03
Added. Cross-compilation from one host to all three OS artifacts, locac --version, and the
provider's own rawStopReason when the five-member enum lost information.
Fixed. The Linux sandbox lost its seccomp layer in a compiled binary, so a shipped Linux binary
ran at sandbox=os with the AF_UNIX block absent. appendMessage wrote three rows untransacted,
leaving entries recall could not see.
0.1.0 - 2026-08-01
Initial release: the harness, the evidence gate, the five invariants, the tool set, the provider matrix, the sandbox, the TUI, the web dashboard, sessions and forking, skills and roles.
Next
Troubleshooting has the message-by-message index, including the exact error each tightened gate prints.