Releases: ArthurDEV44/paneflow
v0.3.9
Paneflow v0.3.9
This release rebuilds the native terminal engine on upstream alacritty_terminal and lands a deep hardening pass over the whole codebase: two full security and correctness audits remediated, a hardened self-update path, bounded IPC and subprocesses, and blocking work moved off the render thread.
Terminal
- Native terminal engine rebuilt on upstream
alacritty_terminalwith rendering parity: OSC 8 hyperlinks, configurable cursor shapes, and a live scrollbar. - Faithful cursor and alt-screen input handling, with PTY teardown and exit-status reporting so a closed shell reports how it ended.
- A new Terminal settings tab plus a terminal block in the config schema and loader.
- Golden snapshot tests lock terminal rendering against regressions.
Self-update and trust
- The self-update path is hardened end to end: the downloaded candidate is verified before it can replace the live binary, updates swap in atomically with crash recovery, and integrity diagnostics are surfaced instead of swallowed.
- Per-platform verification was added: macOS codesign and spctl gating with Team ID pinning, Windows Authenticode through WinVerifyTrust, hardened tar.gz and AppImage extraction, and native host architecture detection for Rosetta and WOW64.
- The release pipeline gained an in-CI minisign signing step and a dual-key rotation runbook, so artifacts can be verified against a public key embedded in the binary once the key is provisioned.
Reliability and security
- Panics on untrusted input are eliminated across session restore, config parsing, IPC, date handling, and layout, with fail-safe accessors replacing defensive indexing.
- Every external surface is bounded against resource exhaustion: the IPC server caps line size, concurrency, and idle time; external subprocesses run under a timeout with a watchdog; ingress and DoS caps live in one module.
- Untrusted content is sanitized: markdown strips bidi and zero-width characters, git refs lose control bytes before they reach agent prompts, and session ids are validated to block argument injection.
- Persisted config and session input is validated and clamped, with atomic write-and-rename for
paneflow.jsonand symmetric bounds across session, IPC, and the schema. - Terminal and shim lifecycle hardening: PID-reuse guards, an environment deny-list and scrollback sanitization on restore, codex flock serialization, and correct orphan cleanup under systemd.
Performance
- Blocking work moved off the render thread: session saves, git diff stats, config loads, font enumeration, and the recursive file watcher run in the background, with a cached config feeding every frame.
- Lower per-frame allocations in terminal paint, sidebar recompute, and layout, with memoized derivations and zero-allocation leaf lookups.
Cross-platform
- Windows portability work in the code: portable shell launches, correct LOCALAPPDATA casing, Git for Windows PATH augmentation, and
dirs-based home resolution. - Non-US keyboard input fixed, Alt-on-arrows decoupled from the option-as-meta setting, and the keybindings editor reworked to be action-indexed with collision detection.
Install
- macOS (Homebrew, Apple Silicon):
brew install --cask paneflow(the cask auto-updates to 0.3.9). The.dmgis Developer ID signed and notarized. - Linux (
.deb/.rpm/ AppImage /.tar.gz, x86_64 and aarch64) and macOS Apple Silicon (.dmg) direct downloads are attached below, each with a SHA-256 sidecar.
Full Changelog: v0.3.8...v0.3.9
v0.3.8
Paneflow v0.3.8
The Agents view becomes terminal-only. "New thread" now launches a CLI coding agent straight into a real terminal pane with a pre-filled prompt, so you stay in the agent's native interface with permission bypass respected exactly as the tab-bar buttons do. The in-app ACP chat, its conversation timeline and composer, and the separate sign-in page are gone: agents authenticate in their own terminal.
Agents
- Terminal-only Agents view: each thread launches a CLI agent directly in a terminal pane, no in-app chat.
- Eleven new launchable agents alongside Claude Code, Codex, OpenCode, Pi, and Hermes: Grok, Amp, Cursor, Gemini, Kiro, Antigravity, Copilot, CodeBuddy, Factory, Qoder, and Openclaw. Each ships a tab-bar button, an icon, and a Settings visibility toggle.
- Terminal Threads remember which agent they launched and restore it on the next session.
Diff viewer
- Safer working-tree reads: an unreadable file now renders as a binary stub instead of a phantom deletion.
- A shared generated-file skip-list so the file watcher and the diff builder cannot drift.
- A watcher-refresh race fix.
Project
- Open-source onboarding polish: community-health files, issue templates, and README positioning on the agent cockpit and the cross-platform story.
Install
- macOS (Homebrew):
brew install --cask paneflow(the cask auto-updates to 0.3.8). - Linux (.deb / .rpm / AppImage / tar.gz), Windows (.msi), and macOS (.dmg) direct downloads are attached below, with SHA-256 sidecars.
Full Changelog: v0.3.7...v0.3.8
v0.3.7 — In-app diffs and agent branch reviews
Highlights
Paneflow now reviews code where you already work. v0.3.7 brings a full Git diff viewer inside the app, and lets you hand any branch to a coding agent for review in a real terminal pane, without leaving the window or pasting a diff anywhere.
Git diff viewer
Open the diff viewer from the sidebar and read your changes inside Paneflow, no context switch to a browser or a separate tool. A sidebar lists your projects and branches; pick one or several and each shows as its own column side by side. Files get a folders first tree, sticky headers that keep the current file pinned as you scroll, quick jumps between hunks, line numbers in the gutter, and a per file diffstat. Changes are word level and syntax aware, so what actually moved stands out.
Review a branch with an agent
Click Review on a branch and Paneflow opens a real terminal pane for each agent you pick (Claude Code, Codex, OpenCode, Pi), in that branch's worktree, with a compact review prompt already filled in. You press Enter and watch it run, in a real terminal, nothing hidden. Pick more than one agent for a second opinion and the second one is told to play the skeptic. While a review is running, hover any changed line and click to send that exact line straight into the agent's input.
Copy a hunk as a diff
Ctrl+Shift+C over the diff copies the hunk under your cursor as a clean unified diff, or grab the whole file from the right click menu. The diff is generated by Paneflow itself, so when you paste it into an agent there is never a wrong @@ header to trip it up.
Worktree columns
In the Worktree view, the × on a branch column now deselects that branch instead of leaving it hidden in a "N hidden" state. Re check it in the branches picker and it comes back. Shown or not, nothing in between.
Install
See the Installation section in the README. Existing installs update in place via the title bar pill.
Full Changelog: v0.3.6...v0.3.7
v0.3.6
Highlights
Paneflow gains a real shell around your agents: two docked right side panels and drag to reorder tabs. The things you reach for during an all day session are now one click away and stay open while you work.
Agent sessions sidebar
The agent sessions list moves from a popover that closed the moment you looked away to a docked panel that stays beside your panes. It lists every past session scoped to the active terminal's working directory, grouped by agent (Claude Code, Codex, opencode). Click a row and Paneflow resumes that session right in the bound pane. Toggle it from the sessions button in the tab bar.
Files sidebar
A new Files panel renders a folders first tree of the active workspace, expanded lazily as you open folders. Markdown files are full color and click to open straight into the active pane, so a PRD or a README sits next to the agent reading it. Every other file is shown but greyed; gitignored and hidden entries are dimmed further. The folders you leave open are remembered across restarts.
Drag to reorder tabs
Grab a tab and drag it to reorder it within a pane, or drop it onto another pane to move it there. A floating preview follows the cursor. The same move is available from the tab context menu under "Move to pane".
Install
See the Installation section in the README. Existing installs update in place via the title bar pill.
Full Changelog: v0.3.5...v0.3.6
v0.3.5
Highlights
Your CLI agent can now read any other pane's output. Run Claude Code, Codex, Gemini, or opencode in one pane and cargo run (or any server) in another, then ask the agent to read the logs. No more copying and pasting by hand.
- A new MCP bridge exposes every terminal surface to MCP capable agents, read only:
list_panes,read_pane,search_pane. Output is wrapped as untrusted so the agent treats it as data, never as instructions. - One command wires it into every agent installed on your machine:
paneflow mcp install(plusuninstallandstatus). It is idempotent, backs up each config before writing, and never touches anything but its own entry. - Target a pane by meaning, not by id. Panes get readable names derived from what they run (
cargo-run,vite). Double click a tab to rename it; the name persists across restarts and is what the agent targets. A new tab bar button copies a pane reference to the clipboard.
The bridge binary ships inside Paneflow and is extracted to a stable path that survives updates, so paneflow mcp install is a one time step.
Install
See the Installation section in the README. Existing installs update in place via the title bar pill.
Setup per agent: docs/mcp-bridge.md.
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_textfreed 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_markdownanddiff_scroll_handlesonly pruned on Keep-All / Reject-All, so Read / Search / Execute tool calls leakedEntity<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_promptsqueue bounded by size + byte budget.
Robustness — no panics, no silent error sinks
- IPC thread spawn no longer panics the app.
ipc.rspreviously.expect()-ed the spawn; underRLIMIT_NPROC/EAGAINthat killed every active agent thread. It now degrades gracefully. wait_for_exitbounded with a 30 s deadline — aSIGKILLrace that missedwaitpidused to leak one blocking-pool thread (max 128) perterminal/create.RuntimeEventchannel bounded at capacity 256 with producer back-pressure (migrated totokio::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_envmoved tomain()before anythread::spawn; the previousunsafe remove_varfrom a non-main thread raced concurrentgetenvin the tokio / smol / IPC threads since Rust 1.85. surface.send_textblast radius documented + gated behind an opt-in flag — it was an undocumented same-UID RCE primitive.- API-key-shaped strings redacted before
trace_wire_lineso*_API_KEY=…prefix tokens stop landing inpaneflow-debug.logunderRUST_LOG=trace. workspace.createcwd canonicalized and non-directories rejected;file_opsreads bounded with line streaming + a max-bytes cap.- Kill-on-parent-death hardened cross-platform —
PR_SET_PDEATHSIGin thepaneflow-shimpre_execon 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::appendO(n²) root cause fixed. Streaming previously rebuiltSharedString::new(source.to_string() + text)every tick — 30–50% main-thread CPU on 5 concurrent agents. Fixed via theArthurDEV44/zed@paneflow/markdown-append-fixfork (one additiveString-buffer patch oncrates/markdown, ~38× throughput on the streaming path; 9 downstream Zed consumers compile unchanged). Reverts to upstream once the PR merges.highlight_codememoized by(language, content-hash)— syntect was running synchronously insideRender::renderat 3–8 ms per visible code block per frame.compute_activity_stateis now O(1) (incremental counters, was a linear scan), markdown element IDs cached at parse time, runtime command channel migrated off the per-commandspawn_blocking+Mutex+ join pattern, dirty-flag persistence incollect_persisted_items.
Quality gates & tooling
cargo denywired 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 (rsaMarvin sidechannel via LSP TLS, not exposed;async-stddiscontinued, 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, andhighlight_codeto 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.
v0.3.3
Paneflow v0.3.3 — Multi-session agents, code-path scanner, IPC singleton guard
Fourth drop in the Agents view (cmux-port-2026-Q2) and the largest one yet — 14 atomic commits across the runtime, the agent UI, the terminal, the config schema, and the IPC layer. The release is organised around three big themes:
- Multi-session agent tracking — two Claude Codes or two Codex sessions in the same workspace now show up as two distinct rows in the sidebar instead of collapsing into one. Driven by a full refactor of the agent-state model from a single enum to a per-PID map, plus PID stamping on every IPC frame from the shim.
- Editor jumps from terminal output — Cmd/Ctrl-click on
path/foo.rs:42:7in any terminal pane (compiler error, test runner output, stack trace) opens the file at that line + column in your preferred editor (Zed / Cursor / VS Code / Neovim / Vim / Helix / Emacs). Same handler runs for markdown links in assistant messages. - ACP capabilities upgrade — Paneflow now declares the same client capabilities Zed declares to Codex / Claude Code. Result: Codex stops skipping
AgentThoughtChunkreasoning streams, "Thinking" blocks render properly, and turns no longer end prematurely.
Plus a long tail of terminal polish (10K scrollback, OSC fixes, Ctrl+click throttle, prompt-mark scrollbar ticks), an IPC singleton guard that stops two Paneflow instances from clobbering each other's socket, a thread-view overhaul with throttled persistence, the default mono font switched to IBM Plex Mono, and the CLI/Agents toggle moved from the title bar into the sidebar footer.
Multi-session agent tracking (per-PID)
Previously Workspace held a single AiToolState enum and a HashMap<String, u32> keyed by tool name. Two Claude Codes in the same workspace meant the second ai.session_start overwrote the first PID, and the sidebar showed one "Claude thinking" badge regardless of how many sessions were really live. Updates from the second session were silently routed to the wrong PID.
The new model:
Workspace::agent_sessions: HashMap<u32, AgentSession>— one entry per live PID.AgentStateenum (Thinking/WaitingForInput/Finished);Inactiveis implicit (no entry).AgentSession { tool, state, active_tool_name }captures the per-PID lifecycle.aggregate_by_tool()collapses N concurrent sessions of the same tool into one sidebar row with a+Nsuffix and the most salient state (Waiting > Thinking > Finished). One row per tool, deterministic Claude-first ordering.
On the shim/hook side, PANEFLOW_AI_PID is now exported on every child the shim spawns (Claude Code, Codex, JSONL-tee, interrupt/Stop, SessionEnd) and paneflow-ai-hook promotes it to a top-level params.pid field on every lifecycle frame — not just SessionStart. The IPC handlers (ai.prompt_submit, ai.tool_use, ai.stop, ai.session_end) route through PID-keyed helpers; ai.session_start is now a no-op (a freshly-spawned shell with no prompt in flight shouldn't show any badge — the first prompt creates the row).
The stale-PID sweep skips synthetic PIDs (allocated in the upper half of u32 for back-compat with shims that don't stamp pid on every frame), so a legacy session isn't killed mid-turn by a kill(pid, 0) probe that would always say "dead" for an out-of-range PID.
ACP client capabilities (Zed parity)
Two related fixes in paneflow-acp that together unlock the same response richness Zed gets:
InitializeRequest now declares full ClientCapabilities:
fs.read_text_file(true)+fs.write_text_file(true)terminal(true)auth(AuthCapabilities::new().terminal(true))metaflagsterminal_outputandterminal-authclient_info("paneflow", VERSION).title("Paneflow")
Byte-for-byte matches Zed's crates/agent_servers/src/acp.rs:888-904. Empirically observed: a bare InitializeRequest::new(V1) (no caps) makes Codex skip AgentThoughtChunk reasoning streams entirely — "Thinking" blocks never render and turns end prematurely. With the full caps declaration, the reasoning streams come through.
NewSessionRequest now sends explicit empty mcp_servers + additional_directories arrays (instead of omitting the fields). The ACP wrappers (codex-acp, claude-code-acp) treat the absence of these fields as a stripped-down host signal and adopt a terser response style; sending them as empty arrays says "I implement these surfaces, just nothing configured right now".
Required two new agent-client-protocol = "0.12" features: unstable_auth_methods and unstable_session_additional_directories. Features mirrored across paneflow-acp and src-app so Cargo unifies on one compiled artifact.
Streaming-buffer micro-perf bonus: pending_chars is now maintained incrementally on every push/tick/flush instead of calling pending.chars().count() (O(n)) per tick. Drops the hottest non-allocation cost in the agents UI under sustained streams.
Cmd+click on file:line:col opens your editor
New crate-local module src-app/src/editor.rs plus a code-path scanner in terminal/element/hyperlink.rs. Recognises any of path/foo.rs, path/foo.rs:42, path/foo.rs:42:7 style references (40+ recognised extensions; .md deliberately excluded so the markdown viewer keeps that surface; Windows drive-letter-safe). Ctrl/Cmd-click in any terminal pane emits TerminalEvent::OpenCodePath { path, line, col } which the app routes to the editor on the background executor.
Three-stage open strategy:
$VISUALenv, then$EDITORenv. The string is parsed as a shell command (binary + flags) so users runningEDITOR="code --wait"get their pre-set flags carried over.- Probed fallback chain:
code→cursor→zed→subl→nvim→vim→hx→emacs. First binary found on PATH wins. - Last-resort
open::that(path)so the OS launcher hands the file to its registered handler (loses the line/col target but always does something useful).
Per-family argv shape so the location actually lands:
code -g path:L:C(VS Code / Cursor / Codium clones)nvim +L path/vim +L path(Vim family)emacsclient +L:C path(Emacs)- bare
path:L:C(Zed, Helix, Sublime)
12 unit tests cover the regex splitter, scanner end-to-end, editor argv construction, and the fallback chain. Markdown link clicks in assistant messages route through the same module (via the new agents/external_editor.rs), so a click on [foo](src/foo.rs:42) opens at the right line just like a terminal click does.
New external_editor config field ("auto" / "system" / "zed" / "cursor" / "windsurf" / "code") lets the user pin a specific editor instead of relying on the auto-probe order.
IPC singleton guard
Without the guard, two parallel Paneflow processes entered an endless mutual-clobber loop. Each one's 5 s health check noticed the other's rebind, dropped its listener, and recreated the socket. During every micro-window between drop and re-create, the AI shim's connect() failed, IPC messages were silently lost, and a session's Thinking / Done / session_start status stayed stale forever.
start_server now probes the socket with system.identify before the IPC thread spawns and before bind_socket blindly remove_files any existing socket. If a live Paneflow already responds with "PaneFlow", the new process logs an error and exits 1 with a clear message:
paneflow: another Paneflow instance is already running on /run/user/1000/paneflow/paneflow.sock.
Existing instance: PaneFlow 0.3.3
Close the open window first, or set PANEFLOW_ALLOW_MULTIPLE=1 to override.
Other outcomes (missing file, stale socket from a SIGKILL'd prior run, non-Paneflow listener, parse failure, timeout) let the caller proceed normally. Probe is resilient to the legacy rebind race (3 attempts, 70 ms gap, 300 ms timeout per attempt — comfortably crosses the ~10 ms remove_file + create_sync + chmod window).
PANEFLOW_ALLOW_MULTIPLE=1 escape hatch for intentional side-by-side debug instances.
Terminal: scrollback, OSC fixes, Ctrl+hover throttle, mouse/input polish
A grab-bag of terminal-surface improvements that landed during the agents work. Kept atomic at the module level (terminal/ + mouse + keys) because the changes interlock.
PTY + lifecycle:
terminal.scrollback_linesconfig (default 10_000, clamp[100, 100_000]) replaces a fixed 1024-line buffer. Long compiler / log output no longer truncates mid-session.- Drop
SHLVLfrom the child env (Zed parity) so the shell starts at level 1, not nested under the GUI launcher. - Drop-time force-kill timer routed through GPUI's background executor instead of a detached OS thread — removes a thread leak per closed pane under heavy use.
- PTY message loop coalesces consecutive
Resizemessages so a drag-resize no longer floods the child with SIGWINCH storms. - PTY failure falls back to a display-only
TerminalStatethat surfaces the error in the pane instead of panicking the whole app.
OSC + escape sequences:
- OSC 4 palette-color queries (vim, nvim, python-rich) now answered.
- OSC 133 / OSC 7 scanners accept C1 ST (
\x9c) as a terminator (fish on some locales). - OSC 52 Load capped at 100 KiB to match the Store cap.
- Scrollbar gutter paints amber ticks for OSC 133
PromptStartmarkers — skim by command boundary like in modern terminals. - OSC 8 cells always underline regardless of the cell flags.
Mouse / input:
mouse_button_codereturnsOption<u8>—MouseButton::Navigate(side / back / forward buttons) no longer injects phantom Left clicks into TUI apps with mouse mode active.- Modifier+Delete now sends
\x1b[3;{m}~(matches xterm). - Ctrl+hover hyperlink rescan throttled to fire only when the hovered cell changes (was ~60 regex sweeps/s under continuous hover).
Rendering:
- APCA contrast adjustment skipped for truecolor + xterm-256 palette cells so `b...
v0.3.2 — Terminal Threads, editable rename, auto-titled threads
Paneflow v0.3.2 — Terminal Threads, editable rename, auto-titled threads
Third drop in the Agents view (cmux-port-2026-Q2). Three coherent pieces:
Terminal Thread surface
The project context menu now ships a "New terminal thread" action that opens a raw PTY surface in the main area instead of a chat. Run claude, codex, amp, opencode, pi, gh dash, lazygit, htop — anything CLI — and it lives as a first-class sidebar entry next to your ACP-backed agent threads.
- OSC 0/2 title updates from the running process flow into the sidebar row label, so a
claudeauto-summary (or any process that sets its terminal title) replaces the genericTerminalplaceholder live. - Closing a thread tears down the PTY immediately (alacritty
Msg::ShutdowninDrop); closing a project cascades cleanup for all its terminal threads. - The thread row's
kindis persisted insession.jsonvia a newkinddiscriminant inThreadSession— older sessions round-trip byte-identically (#[serde(default)]), so a downgrade after upgrading doesn't corrupt anything.
Mirrors Zed's AgentPanelEntryKind::Terminal semantics with Paneflow's own PTY stack (portable-pty + alacritty_terminal 0.26).
Editable rename for project and thread rows
Renamed the rename. The old {text}| fake-shimmer in the sidebar is gone — both project headers and thread rows now use a real TextArea entity, the same widget the composer uses, with full editing affordances:
- Cursor + selection.
- IME, clipboard, system shortcuts.
- Click-to-position, double-click word select, triple-click line select.
- Submit on Enter, cancel on Escape, commit-on-blur when you click away.
Thread rows were previously not user-renameable — they relied on either an agent-pushed SessionInfoUpdate.title (Claude Code) or the client-side auto-derive on the first prompt. That left Codex threads stuck with whatever the auto-derive produced. With this release, every thread is renameable via context menu Rename thread or by double-click on the row label, and the rename is mirrored into threads.db (set_summary) so the new title survives a session.json wipe.
Per-thread agent icons (the small claude-color.svg / codex-color.svg next to each thread title) are now hidden in favor of an invisible 14px placeholder that keeps the title's X-position aligned with the project header above it. The agent kind is already conveyed by the project header — the per-thread glyph was visual noise that also broke down for Terminal Threads (no agent kind to display).
Background thread-title summarizer (Zed parity)
After the first clean turn of a thread, Paneflow now generates a sharp 3-7 word title and applies it to the sidebar row — spiritual port of Zed's Thread::generate_title flow. Specifically for Codex threads (and any future agent that doesn't push titles via ACP):
- The composer captures the title at the moment of the first user prompt.
- On the first
EndTurn, it shells out toclaude -p --dangerously-skip-permissions --model haikuwith the user/assistant pair and a short summarization prompt (verbatim from Zed'ssummarize_thread_prompt.txt). - The result lands as a new
TitleSuggestedevent, gated by a newTitleReplacePolicy::OnlyIfStillEqualTo(snapshot)policy — if the user renamed the thread or the agent pushed aSessionInfoUpdate.titleduring the (~1-3 s) subprocess round trip, the summarizer's result is silently dropped.
Key divergence from Zed's literal implementation: Zed drives summarization through an Arc<dyn LanguageModel> HTTP backend. We use the user's existing claude CLI to avoid managing any provider credentials of our own — works out of the box for Claude Max subscribers (OAuth keychain) and API users (ANTHROPIC_API_KEY).
Claude Code threads still get titles directly from the agent's own ACP push; the summarizer no-ops there.
Title hygiene
New clean_sidebar_title helper that strips leading CLI-status decoration glyphs from sidebar titles, applied at three sites:
- Background summarizer output (in case the model hallucinates a
Title:prefix). - ACP-pushed
SessionInfoUpdate.title(Claude Code's✻spinner, Codex's braille spinner, generic●/•, zero-width characters). - Session restore (
thread_from_session), so threads cleaned up in this release stay clean across launches.
The strip is whitelist-based — letters, digits, and the legitimate title-leading punctuation are kept, everything else is stripped from the front in one pass. Future CLI status glyphs are caught without code changes.
Replace-policy enum
TitleSuggested now carries a tri-state TitleReplacePolicy instead of a single boolean, modeled after Zed's Thread::set_title / generate_title guards:
| Policy | When | Behavior |
|---|---|---|
Always |
ACP push (SessionInfoUpdate.title) |
Replaces unconditionally — the agent's authoritative summary wins. |
OnlyIfDefault |
Client-side auto-derive on first prompt | Replaces only if the current title is still "New thread". |
OnlyIfStillEqualTo(snapshot) |
Background summarizer | Replaces only if the title still matches the snapshot captured at trigger time. |
Compatibility
session.jsonformat is forward-compatible. Older sessions restore asThreadKind::Agent(the legacy chat path). Restoring a v0.3.2 session in v0.3.1 will silently drop thekindfield — Terminal Threads come back as Agent threads (broken until you delete them). One-way upgrade for sessions with Terminal Threads.threads.dbis unchanged.- Cross-platform: full parity across Linux (Wayland + X11), macOS (Intel + Apple Silicon), Windows (x64).
Commits
feat(threads):add ThreadKind discriminant +clean_sidebar_titlehelper (266db74)feat(agents):Terminal Thread surface + editable rename for sidebar rows (0503d62)feat(agents):background thread-title summarizer (Zed parity) (b9c5b49)chore(release):v0.3.2 -- Terminal Threads, editable rename, title summarizer (0362287)
Full Changelog: v0.3.1...v0.3.2
v0.3.1
Full Changelog: v0.3.0...v0.3.1
v0.3.0
Full Changelog: v0.2.17...v0.3.0