跳到内容
所有版本

v0.3.4

发布于

Paneflow v0.3.4 — Agent CLI hardening: security, robustness, memory, performance

The largest hardening drop yet. Two back-to-back 4-axis audits (memory, security, performance, robustness) on the CLI-agent subsystem, shipped as two PRDs landing together: prd-agents-hardening-2026-Q3 (19 stories, closing the post-visual-parity audit) and prd-cli-hardening-followup-2026-Q3 (30 stories across 6 epics, closing the 4 deferred follow-ups plus 26 fresh findings). Every fix is grounded in a file:line audit citation, not conjecture. No new .unwrap() / .expect(), no new abstraction layers — surgical fixes through the project's panic = deny / unwrap_used = warn clippy gate.

Why it matters: Paneflow is the only AI-agents IDE built in pure Rust + GPUI, and the pitch is "Rust native = leaner than the wrapper." This release makes that honest — bounded memory on all-day sessions, zero main-thread panics under resource exhaustion, and a locked-down IPC surface before more external integrations come online.


Memory — bounded footprint on long sessions

  • DiffSnapshot.old_text freed on review completion. A 500-turn refactor session was retaining 10–100 MB of pre-edit file content for already-reviewed edits. The diff body is now dropped after review and the renderer short-circuits to a [diff body cleared after review, N lines] placeholder.
  • Per-tool-call UI state pruned on terminal status. tool_label_markdown and diff_scroll_handles only pruned on Keep-All / Reject-All, so Read / Search / Execute tool calls leaked Entity<Markdown> GPUI registry entries for the whole thread lifetime. They are now purged as each tool call reaches a terminal state.
  • Session cache capped at 10 LRU entries; pending_prompts queue bounded by size + byte budget.

Robustness — no panics, no silent error sinks

  • IPC thread spawn no longer panics the app. ipc.rs previously .expect()-ed the spawn; under RLIMIT_NPROC / EAGAIN that killed every active agent thread. It now degrades gracefully.
  • wait_for_exit bounded with a 30 s deadline — a SIGKILL race that missed waitpid used to leak one blocking-pool thread (max 128) per terminal/create.
  • RuntimeEvent channel bounded at capacity 256 with producer back-pressure (migrated to tokio::sync::mpsc).
  • Six silent Err(Closed) sinks now logged across the title summarizer and composer — a window closing mid-task no longer makes a file insertion or generated title vanish without a breadcrumb.
  • Mutex-poison recovery and NTP backwards-step sentinels added to the session cache / relative-time path.

Security — IPC surface and parser bounds

  • JSONL parsers capped at 64 KiB per line across the Claude / Codex / OpenCode / agent session readers — a malicious 500 MB single-line JSONL planted in ~/.claude/projects/<slug>/ no longer OOMs the process.
  • Rust 1.85 env-race fixed. scrub_claudecode_env moved to main() before any thread::spawn; the previous unsafe remove_var from a non-main thread raced concurrent getenv in the tokio / smol / IPC threads since Rust 1.85.
  • surface.send_text blast radius documented + gated behind an opt-in flag — it was an undocumented same-UID RCE primitive.
  • API-key-shaped strings redacted before trace_wire_line so *_API_KEY=… prefix tokens stop landing in paneflow-debug.log under RUST_LOG=trace.
  • workspace.create cwd canonicalized and non-directories rejected; file_ops reads bounded with line streaming + a max-bytes cap.
  • Kill-on-parent-death hardened cross-platformPR_SET_PDEATHSIG in the paneflow-shim pre_exec on Linux (Windows JobObject already shipped, macOS no-op), and the shim self-exclusion now uses Unix (dev, ino) instead of a dir-string compare.

Performance — streaming hot paths

  • Markdown::append O(n²) root cause fixed. Streaming previously rebuilt SharedString::new(source.to_string() + text) every tick — 30–50% main-thread CPU on 5 concurrent agents. Fixed via the ArthurDEV44/zed@paneflow/markdown-append-fix fork (one additive String-buffer patch on crates/markdown, ~38× throughput on the streaming path; 9 downstream Zed consumers compile unchanged). Reverts to upstream once the PR merges.
  • highlight_code memoized by (language, content-hash) — syntect was running synchronously inside Render::render at 3–8 ms per visible code block per frame.
  • compute_activity_state is now O(1) (incremental counters, was a linear scan), markdown element IDs cached at parse time, runtime command channel migrated off the per-command spawn_blocking + Mutex + join pattern, dirty-flag persistence in collect_persisted_items.

Quality gates & tooling

  • cargo deny wired into CI — a daily security-audit cron (audit.yml) opens an issue when the lockfile gains a new advisory overnight, complementing the blocking PR-gate. Two known transitive advisories (rsa Marvin sidechannel via LSP TLS, not exposed; async-std discontinued, not in the current fork tree) are whitelisted with explicit rationale.
  • Criterion baselines added for blob_compress (zstd roundtrip on the persist path), markdown_append, and highlight_code to catch perf regressions on those exact hot paths.
  • Heaptrack runbook documented for the 5-agent streaming scenario (see tasks/heaptrack-runbook.md).

Breaking: claude_code_bypass_permissions now defaults to false (was true). The Agents panel asks before each Claude Code tool call on a fresh install — the previous default was a latent vulnerability. Set it back to true in config if you scripted around the old behavior.

Build note: this release builds against the ArthurDEV44/zed@paneflow/markdown-append-fix fork (pinned by exact sha in Cargo.lock) rather than upstream zed-industries/zed, for the Markdown::append fix. It reverts to an upstream rev once the PR merges.