Skip to content

v2.0.0-beta.1

Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 04 Sep 06:05

Zeraix v2.0.0-beta.1

🐛 Fixes

Sub-agents can now be stopped individually, at any time

  • Added a stop button so users can terminate a single running sub-agent without waiting for it to finish or having to kill the whole parent run.
  • Touches the sub-agent scheduler, the execution registry (executionRegistry.ts), and a new abortSignals.ts that centralizes how cancellation signals propagate.

Installed skills couldn't be used in chat

  • Root cause: the skills panel writes to storage but nothing re-reads it — the browser storage event only fires in other windows, not the one that did the writing — so a skill installed on /agent/skills stayed invisible in chat until the app was restarted.
  • Fixed by re-reading the installed list both when returning to the chat route and every time the skills panel is opened.

refine_question tool broken due to an unconfigured LLM endpoint

  • Root cause: refine_question runs in the main process and needs to make its own secondary model call to rewrite the question, but the renderer-side call that was supposed to hand over that endpoint/model/key config had gone missing during an earlier refactor — so the tool failed every single time with "LLM endpoint is not configured."
  • Fixed by re-syncing that config to the main process every time the active model changes (fire-and-forget; a sync failure doesn't block sending a message).

Large results caused a chain of problems (fixed in the actual order they were addressed)

These issues were causally linked, and the real commit order matters for understanding the fix:

Step 1 — root cause: reading a large file froze the app outright

  • The freeze wasn't caused by read_file itself, but by two downstream consumers that couldn't handle large text:
    • The tokenizer (js-tiktoken)'s byte-pair-merge is O(n²) on a long run of text with no whitespace — a 2MB file with no line breaks (base64, a minified bundle) would send tokenization time quadratic and lock the main thread.
    • The transcript UI laid out a tool call's full arguments/result verbatim inside a single <pre> block — same problem, bigger text, bigger freeze.
  • Fix: the tokenizer now encodes in fixed 512-character windows, and falls back to sampling instead of a full pass above 65,536 characters. The UI now clips displayed text to 32KB with a "N more hidden" note (the full text still reaches the model and gets saved to disk as before — only the DOM stops rendering the whole thing).
    Step 2 — only after the freeze was fixed did it become safe to remove read_file's original limits
  • Removed three hard caps: refusing files over 2MB, truncating results at 200,000 characters, and capping a bare read at 2,000 lines. A bare read_file call (no offset/limit) now returns the entire file; offset/limit remain available as opt-in paging.
  • Also fixed a performance bug in the sidecar's stdout parser, which was re-scanning its entire buffer on every incoming chunk — quadratic in line length (32MB result: 11.6s → 0.19s).
    Step 3 — removing those limits then exposed new downstream problems, which this release also closes
  • Saving a project got slow: a 100MB tool result meant every save had to re-serialize the entire 100MB document. Strings ≥256KB are now split out of the project document and written once, encrypted, to a blob file (conversations/<pid>.blobs/<sha256>). Saves are now serialized per project, written atomically, and orphaned blobs get swept. Same scenario: 389ms + 100MB heap growth → 68ms with no large allocation.
  • Oversized results no longer go into the model's request wholesale: a tool result larger than the compaction target (half the context window, or a budget-scaled target) is now withheld from the outgoing request and replaced with a note describing the call and its size (lines/chars/tokens), instead of blowing out the context window on one oversized read.
  • Startup got slow / memory spiked: at launch, the app was reading a 200MB leftover test result back into memory in full. Blobs over 4MB (or a project total over 64MB) are no longer loaded at startup — the conversation keeps a reference note instead, which resolves back to the same file on save.
  • A handful of related memory/perf issues found during an internal audit, fixed alongside:
    • The project-memory observer and the sub-agent Inspector both used to hold substring views that pinned entire results in memory for the session; both now keep owned copies instead.
    • The transcript used to run a diff-extraction regex over every tool result on every render; it now only runs for the three tools that actually produce diffs.
    • The token estimator used to re-tokenize the entire conversation on every turn (~1.4MB/s); counts are now memoized per message and per text fingerprint.
  • Added scripts/bench-large-result.mjs: a benchmark confirming a 100MB result now loads in ~20ms with nothing pinned in memory.

Sandbox status dialog showed the wrong stage on a warm start

  • Symptom: the sandbox engine would report the image as ready, then immediately overwrite that status with a boot message — so a dialog attaching afterward showed "checking runtime environment" for the entire boot on warm starts, looking like it had hung.
  • Fixed: the boot message now reports progress 100, so the dialog correctly shows the image as done and the boot step as in progress.

Model was writing its own internal placeholder marker to disk as file content

  • Symptom: the model copied the app's context-trimming marker verbatim and wrote it as file content, so the file on disk became the marker itself — and the model then tried increasingly desperate shell workarounds (echo, PowerShell, Set-Content) to "fix" it, making things worse.
  • Fix (four coordinated changes, all cross-platform):
    • write_file/edit_file/append_file now refuse any content that is entirely one of the app's "[…… … ……]" markers, with a message explaining what the marker is.
    • The elision text substituted into trimmed arguments now reads clearly as "context was removed here" rather than looking like a plausible value.
    • Host commands now run in a guaranteed UTF-8 environment on every platform: a UTF-8 console code page on Windows, and a UTF-8 locale on macOS/Linux when the app inherited none.
    • The environment hint now correctly names the shell actually running host commands (cmd.exe on Windows, /bin/sh elsewhere), and the prompt now explicitly tells the model to only create/edit files via the file tools.

🧪 Tests

Added 10 new test files covering everything above: conversation-store, oversized-results, placeholder, read-span, result-blobs, sandbox-progress, shell-encoding, subagent-loop-convergence, subagent-observability, tokenizer-large-text, plus expanded the existing subagent-scheduler tests. Rust crate tests, the full JS suite, typecheck, and lint all pass.

Full Changelog: v2.0.0-beta.0...v2.0.0-beta.1