Changelog
This page summarizes user-facing changes. The full commit log is on GitHub, and the project also ships a generated CHANGELOG.md at the repo root that groups commits by conventional-commit type (added in 0.3.145).
0.10.8 — An answered question stops claiming an approval
Section titled “0.10.8 — An answered question stops claiming an approval”Follow-on to 0.10.7, found by verifying that fix on a live session rather than only in tests. Answering a question emits no “resolved” event either, so the permission notification the question raised outlived the question: the session sat at “waiting for approval” while actively working, the sidebar pill claimed an approval was pending, and — the real problem — a bare ht claude approve would have selected it and typed Enter into a pane showing no prompt at all.
- τ-mux now records whether a pending announcement belongs to a choice modal, and retracts it when the modal closes — phase back to working/idle, message cleared — while leaving a genuine tool prompt untouched.
- Deliberate limitation: a notification arriving while a modal is up is attributed to the modal. If that ever misfires, the result is a missed auto-approval (you press Enter yourself), never a stray keystroke — the safe direction.
- No new hooks and no re-install needed; restart the app.
0.10.7 — Auto-approve no longer answers questions meant for you
Section titled “0.10.7 — Auto-approve no longer answers questions meant for you”Claude Code raises the same permission-prompt hook for an AskUserQuestion or ExitPlanMode modal as it does for “may I run this command”, with the same generic message — so on the hook stream alone the two are indistinguishable, and auto-approve was answering multiple-choice questions addressed to you by picking their default option.
- Two new hooks scoped to
AskUserQuestion|ExitPlanModetell τ-mux when a choice modal is open. Both auto-approve and the manualht claude approvenow refuse while one is up: pressing Enter on a choice modal picks a default, which is not what “approve” means. - A missed close event can’t wedge a session — the next prompt, turn end, or session end clears the flag.
- Requires
ht claude installto wire the two hooks, then a restart of running Claude Code sessions.ht claude doctorreports them as missing until then.
0.10.6 — Auto-approve answers every prompt in a turn
Section titled “0.10.6 — Auto-approve answers every prompt in a turn”Fix: with auto-approve on, a turn that asked permission more than once had only its first prompt answered; the second hung indefinitely with Do you want to proceed? on screen. Claude Code ships no “prompt resolved” hook, so the session stays in “waiting for approval” between back-to-back prompts and the second announcement looked identical to the first still being up. τ-mux now counts prompt announcements rather than state transitions, so it fires once per prompt. The burst guard counts every prompt too.
0.10.5 — Plan panel: clear control + step detail
Section titled “0.10.5 — Plan panel: clear control + step detail”- Plan cards can be dismissed from the UI. Each card carries a clear control — a hover-revealed
×while work is in flight, promoted to a labelled Clear button once every step is done. It routes through the same handler asht plan clear, so the CLI, the native panel and the web mirror can never disagree. - Step descriptions render inline. Steps that carry a description (every mirrored Claude Code task does) are now toggles: click to expand the full text under the row, click again to collapse. Previously the description existed only as a hover tooltip, which is why a plan “showed only that there is a plan”.
- Cards gained a progress bar and an
updated Nm agostamp, so a stale plan is visible as stale. - Accessibility: the card was one big button, which made the new controls illegal nested buttons. It is now an inert container holding three real controls, with
aria-expandedon the step toggles. - Bug fix:
ht plan update <id> --state donesilently deleted the step’s description, blanking the new detail row exactly when a step completed.
0.10.4 — Claude session registry survives restarts
Section titled “0.10.4 — Claude session registry survives restarts”The per-session registry is now persisted, so phases, pane attribution and mirrored task lists survive an app restart instead of resetting to unknown. Repeated “skipped” decisions collapse in the log instead of flooding it.
0.10.3 — ht claude auto-approve on|off
Section titled “0.10.3 — ht claude auto-approve on|off”Auto-approve was settings-only, which made it awkward to flip for a single unattended run. It is now a first-class CLI verb — ht claude auto-approve on|off|status — routed through the same settings pipeline as the toggle.
0.10.2 — The docs are now verified against the code
Section titled “0.10.2 — The docs are now verified against the code”A full code-vs-docs audit, plus a CI gate so it cannot rot again. The audit found 42 undocumented RPC methods (six whole domains), 22 undocumented CLI commands, 38 undocumented settings, 11 documented settings that did not exist, and 14 wrong defaults. All fixed in EN and FR. tests/docs-coverage.test.ts now fails the build if a registered method, an ht command, or an AppSettings field goes undocumented, if a documented setting is a ghost, if a documented default disagrees with the code, if a surface kind is missing from the concepts page, or if EN and FR drift apart in page inventory.
0.10.1 — Approve targets the blocked pane
Section titled “0.10.1 — Approve targets the blocked pane”Fix: ht claude approve answered the prompt in the caller’s pane rather than the one actually blocked. It now resolves the longest-waiting session, or an explicit --surface.
0.10.0 — Accept Claude Code permission prompts
Section titled “0.10.0 — Accept Claude Code permission prompts”When Claude Code runs in a terminal pane and asks permission to run a command, τ-mux can now answer it for you.
- Manual approve, always available — command palette “Approve Claude Code permission prompt” or
ht claude approvepresses Enter on the longest-waiting prompt (or--surface). - Auto-approve, opt-in — Settings → Auto-approve Claude Code prompts (off by default) accepts them as they appear, after a short configurable delay.
- Deliberately narrow, because this is a consent gate: it fires only for Claude Code’s own prompt in that pane’s terminal — never for the τ-mux approval modal, never for the Claude Code pane; it re-checks the prompt is still on screen after the delay so it can’t send a stray Enter into a pane you already answered; it pauses and notifies after more than eight prompts in a minute; and every approval is written to that pane’s sidebar log. See accepting terminal prompts.
0.9.0 — The Claude Code pane, on the τ-mux design system
Section titled “0.9.0 — The Claude Code pane, on the τ-mux design system”The pane now looks and behaves like a native τ-mux agent surface rather than a guest app.
- Amber agent identity (§7). A Claude session is a robot’s session, so the pane now carries the same amber identity signal as the pi agent pane — identity dot, focused pane border, sidebar workspace card, and status-bar entry. At a glance you can tell an agent pane from your own cyan shells. (It previously rendered in the human cyan, with a Catppuccin palette inherited from the v1 hook bridge.)
- Full palette/shape/motion alignment. Every colour now comes from a TAU design token, radii use the token scale, telemetry is monospace with tabular numerals, and animation is limited to the canonical τ-mux keyframes. A new conformance test suite pins all of it so the palette can’t drift again.
- Grouped control strip. Identity + status badge + model + permission mode on the left, live token/cost/elapsed meters and New · Sessions · Stop on the right. The phase is now spelled out in a status badge (
idle,working,approval needed,ended) instead of being encoded only in a dot colour. - Working directory everywhere. The pane shows its cwd immediately on open — in the header and as the same cwd chip terminal panes get — and it feeds the sidebar workspace card, so a Claude pane no longer sits at “resolving…”.
- Queued-message feedback. Sending while a turn is in flight marks the message as queued and shows a footer chip, instead of appearing to do nothing.
- Copy affordance on finished assistant messages (hover), alongside the existing copy buttons on tool input/output.
ht claude pane— open a Claude Code pane from the CLI or a script (--cwd,--split,--direction,--resume), mirroringagent.createfor the pi pane. Also exposed asclaude.paneover JSON-RPC and in the extension SDK.
0.8.0 — The Claude Code pane, full-featured
Section titled “0.8.0 — The Claude Code pane, full-featured”The native pane grows from a working v1 into the flagship surface:
- Markdown transcript with O(N) live streaming, code blocks, inline code, headers and lists; thinking blocks stream into a collapsed, pulsing block.
- Tool cards v2 — status dot (running → ok/failed), one-line summaries, expandable full input and matched output (
tool_use_idpairing) with copy buttons. - In-place sessions — New starts a fresh session in the same pane; Sessions resumes (or forks) a previous one with its transcript replayed under a divider. The pane rebinds its SDK stream; the layout slot never changes.
- Model switcher (mid-session, via the SDK) next to the permission-mode switcher;
bypassPermissionsis highlighted red. - Inline approval status — “Waiting for approval: Bash” appears in the transcript while the τ-mux modal / Telegram question is open; denials and timeouts leave a red record.
- Meters + state — token / cost / elapsed pills, a pulsing state dot, smart stick-to-bottom autoscroll with a ↓ latest pill, a proper empty state, and cwd inheritance: new panes start in the directory of the pane you were focused on.
- Composer: auto-growing textarea; sending mid-turn queues the message.
0.7.1 — Claude Code integration (milestones 1–3)
Section titled “0.7.1 — Claude Code integration (milestones 1–3)”τ-mux becomes a first-class harness for Claude Code — plan: doc/august-plan.md. Three releases in one wave; see the rewritten Claude Code integration page.
- Full-lifecycle awareness (0.5.0). The hook bridge now forwards fourteen Claude Code events (was four): session start/end, prompt/stop, API failures, subagent start/stop, compaction, cwd changes, task created/completed, idle/permission notifications. A per-session registry tracks each session’s phase — working / waiting for input / approval needed / compacting / error — attributed to the pane it runs in. API errors get their own red state and an actionable notification (“Rate limited”).
ht claude statusline(0.5.0). One line in~/.claude/settings.jsongives Claude Code a τ-mux-styled statusline (model, effort, dir, git branch, permission mode, PR badge, color-coded context bar, cost, ±lines, rate-limit warnings ≥80%) and feeds cost / context % / rate limits / session title into the sidebar ticker (Opus · 42% ctx · $0.31). These are numbers Claude Code computes itself — the old transcript parsing, hand-maintained pricing table, and thepi-based title generator are deleted; the pills now always match/costand/context.- Remote approvals, opt-in (0.6.0).
ht claude install --features approvalsroutes Claude Code permission prompts to a τ-mux ask-user modal — and to Telegram — with Allow / Deny / “Answer in terminal”. Fail-safe by construction: any failure (τ-mux down, timeout, error) falls back to Claude Code’s own prompt; the gate can only add an answer path. Approve aBashcommand from your phone. - Automatic task mirror (0.6.0). Claude Code’s native task list projects into the plan panel deterministically (hooks, not model cooperation), per session, cleared on session end, coexisting with pi plans. Together with turn-end notifications this plugs Claude Code sessions into the existing auto-continue engine.
- One-command install (0.6.0).
ht claude install / uninstall / doctor— timestamped backups, additive merge, idempotence, refuse-on-parse-failure, and a doctor that names exactly what’s missing. - Native Claude Code pane (0.7.0). A first-class surface hosting an Agent SDK session: streamed responses, tool cards, permission-mode switcher, interrupt, cost pills, and a Sessions picker that resumes previous sessions. Tool permissions ride the same modal + Telegram path. See Claude Code pane.
- Agent-teams pill (0.7.1). With Claude Code’s experimental agent teams enabled, a passive sidebar pill shows
3 members · 2/6 tasksfrom the on-disk team state. - New
claude.*JSON-RPC domain +claudenamespace in the extension SDK.
0.4.12 — Audit remediation
Section titled “0.4.12 — Audit remediation”Everything actionable from the 2026-08 whole-repo audit (doc/full_app_review_2026-08.md) except the web-mirror defaults (deferred).
- Security — RPC socket token ON by default.
rpcSocketRequireTokennow defaults totrue: state-mutating socket calls require the per-boot token. Every first-party client (the bundledht, the pi bridge, the extension SDK, the Claude bridge) reads it automatically, so nothing changes in normal use; read-only diagnostics stay open soht doctorstill works. See auth & hardening. - Security — extension trust boundary. The
bun xnetwork fallback for dev servers was removed; theenabledflag is now actually enforced (a disabled extension refuses to open — previously it silently launched); newht extension enable / disableverbs; backends get a real SIGTERM→SIGKILL escalation so they can’t outlive the app. The trust model is now stated plainly: extensions are fully trusted code — install only what you would pipe to a shell. - Correctness. CPU-sample pruning actually runs (a guard made it a no-op — the sample map never shrank); the GPU-renderer palette toggle no longer shows inverted before settings load;
system.identifyreportsnullinstead of a plausible-but-wrong socket path when unwired; extension dev-server ports are allocated per instance (two devPort-less extensions — or any unrelated Vite project on 5173 — could previously collide and load the wrong UI into a pane). - Fixed for real this time — blank GPU panes. v0.4.11 reverted the renderer default to
dom, but anyone who ran v0.4.9/v0.4.10 hadwebglpersisted in settings, so their panes stayed blank across two releases. A one-time settings migration (schema v1 → v2) resets a persistedwebglback todom; re-enabling it afterwards sticks. The renderer remains experimental, and the settings panel now shows a live “running on DOM —” hint when the GPU renderer has fallen back. - Performance. The webview’s 1 Hz status-bar tick is skipped while the window is hidden (it was rebuilding the whole status-key subtree every second even when occluded).
- Tooling. The coverage gate now reports files it isn’t gating (it had been blind to ~2,000 LOC of new files since May); a new module-size ratchet fails CI when an oversized module grows further.
0.4.11 — Desktop performance
Section titled “0.4.11 — Desktop performance”The doc/desktop-perf-plan.md wave (v0.4.8 → v0.4.11).
- Metadata poller rewritten on libSystem FFI (0.4.8).
ps+ twolsofcalls per 1 Hz tick (~200 ms of subprocess CPU every second) replaced by directsysctl(KERN_PROC_ALL)+proc_pidinfo/proc_pidfdinfocalls: measured 135.8 ms → 2.42 ms per tick (56×); steady-state CPU of the main process dropped from 7–10% to ~1%. The module self-validates its kernel struct offsets at startup (own pid/cwd, a throwaway listener) and falls back tops/lsofcleanly on any mismatch or off macOS. - More accurate CPU% (0.4.8). Chips, Process Manager, and sidebar now derive CPU from cumulative CPU-time deltas instead of
ps’s decaying average — a process that just finished a burst no longer lingers at a high reading. - Adaptive stdout coalescing (0.4.10). Keystroke echo on a quiet terminal no longer waits out the batching window; batching engages only under sustained output.
- Optional GPU terminal renderer (0.4.9, opt-in since 0.4.11). New
terminalRenderersetting (domdefault,webglopt-in) plus a command-palette toggle. Experimental — it shipped enabled in 0.4.9 and rendered panes blank on some setups; see the 0.4.12 note above for the persisted-setting migration.
0.4.7 — Nebula, the full SDK surface & extension-platform fixes
Section titled “0.4.7 — Nebula, the full SDK surface & extension-platform fixes”The extension platform’s first hardening wave, plus a flagship example.
- Nebula — a 3D HTTP API explorer (0.4.4). A showcase extension: a full Postman-style HTTP client rendered as a living three.js scene with a glassmorphism HUD. It discovers the dev servers running in your terminals (via the process-metadata listening ports) and turns each into a one-click orbiting endpoint, fires requests through the scene (status-colored response rings, latency-mapped animation), and drives τ-mux from your API workflow — open a URL in a browser pane, send the request as
curlinto a new terminal split, live latency sparkline in the sidebar, notifications on failures.ht extension install …/examples/extensions/nebula. See Extension apps. @tau-mux/sdknow types the complete control surface (0.4.7). The typed facade grew from 6 curated namespaces to all 17 RPC domains (~120 methods) — including the full browser driver (click / type / eval / snapshot / cookies / console), agents (incl.askUsermodals), telegram, editor panes, plans, auto-continue, audits, screenshots, and the extension platform itself — identical from the Bun backend and the Vite frontend. A two-directional coverage test keeps the SDK and the host registry in lockstep. (Also fixed:sidebar.setStatuspreviously targeted a non-existent wire name — it now correctly callssidebar.set_status.)- Extensions install anywhere (0.4.6). The SDK is vendored into each bundled example (
file:./vendor/tau-mux-sdk), sobun installresolves offline in dev, installed, and packaged builds — previously a repo-relative path broke once the extension was copied into the config dir, leaving the pane blank. - Extension pane fixes (0.4.2 – 0.4.5). The pane close button now stops the extension’s backend + dev server (no process leaks); the status pill flips to “running” when the iframe actually loads; dev mode waits for the Vite server to listen before pointing the iframe at it; and a webview-init regression that disabled the command palette + title-bar double-click (a TDZ throw during module init) was fixed with a structural regression test.
0.4.0 — Extension apps
Section titled “0.4.0 — Extension apps”A new surface type: extension apps. An extension is a Bun backend (a real child process that can bun install its own deps) + a Vite frontend rendered in an <iframe> (hot-module reload while editing, built static once installed) + a typed @tau-mux/sdk that drives every τ-mux control surface — create panes, open browser surfaces, push notifications, set sidebar status, and more. Extensions are saved on disk, restored with your layout, and created / edited / removed from inside the app.
extensionsurface (0.4.0). Open one in a pane like any other surface. Each running surface gets its own Bun backend (started fresh, stopped on close) and an iframe pointed at the Vite dev URL (HMR) or a built bundle served over a tiny loopback host. Saved with the workspace by extension id; on restart the surface + a fresh backend are restored (the extension reloads its ownstate.json), or the slot degrades to a terminal if the extension was uninstalled. See Extension apps.@tau-mux/sdk(0.4.0). One typed surface from both halves of an extension —notification,sidebar,surface,workspace,browser,system, plus a rawcall(method, params)to reach any JSON-RPC method. The backend talks over the unix socket; the frontend over apostMessagebridge the host dispatches through the same RPC the CLI uses.ht extensionCLI +extension.*API (0.4.0).list,templates,open,split,new,install,remove,reload,stop. Seeht extensionand theextension.*API.- Bundled examples (0.4.0).
hello(zero-dependency static app — the fastest way to see the bridge),three-demo(Vite + three.js with HMR; backend drives the sidebar + notifications), andhttp-client(a Postman-style request builder whose backend runsfetchwith no CORS and persists history). They double as scaffold templates. - In-app editor (0.4.1). The command palette (
⌘⇧P, “Extensions”) now offers, per installed extension, Open, Edit (opens its backend source — ormanifest.json— in the editor surface, the live edit → HMR loop), and Remove, plus New Extension… to scaffold from a template.
Extensions are fully trusted — there is no sandbox; manifest permissions are advisory. Install only what you trust, exactly as you would a shell script.
0.3.188 — UI polish: zero-flicker rendering, smoother resize & live settings
Section titled “0.3.188 — UI polish: zero-flicker rendering, smoother resize & live settings”A focused quality pass on rendering churn, long-session memory, service resilience, and the Settings panel — plus ten new shareBin utilities.
Rendering & flicker
Section titled “Rendering & flicker”- Status-key charts redesigned + flicker-free (0.3.184 – 0.3.185). The
ht set-statuschart renderers got a visual overhaul (smooth curved line/area graphs with gradient fills, baseline grid + latest-value headline, value-centered gauges, rounded bars/heatmap cells), and status grids now reconcile the DOM minimally in place — a 1 Hzset-statustick repaints only the entries whose value actually changed, on native and the web mirror.shareBin/demo_status_keys --liveis a good showcase. - Zero-flicker sidebar workspace cards (0.3.187). CPU bar, % / RAM / process chips, and the sparkline update in place on each metadata tick; the CPU-bar fill keeps its node identity so its transition animates instead of snapping. Cards now also refresh on live CPU/MEM movement, while a truly idle workspace still costs nothing.
- No notification-overlay strobe (0.3.187). The on-terminal notification stack reconciles in place, so existing cards keep their slide-in state and auto-dismiss countdown when new ones arrive.
- Sideband panels never strand transparent (0.3.187). Panels created while the window was backgrounded no longer depend on a rAF WKWebView may suspend — resting opacity is set synchronously, with a self-healing CSS entrance fade.
Feel & resilience
Section titled “Feel & resilience”- Smooth sidebar resize (0.3.187). Dragging the sidebar divider only repositions panes during the drag; the authoritative terminal refit runs once on release — no more per-frame reflow jank.
- Terminal no longer jumps on resize (0.3.187). Refits (sidebar/pane resize, sideband-panel triggers) preserve your scrollback position; alt-screen TUIs (vim, htop) are untouched.
- Lower idle CPU + memory (0.3.187). rAF-coalesced web-mirror status bar, in-place sidebar log/stat strips, metadata polling backs off when the window loses focus, and several long-session leaks were plugged (web-mirror per-pane observers/timers, browser-pane retry timer, auto-continue per-surface state, dead-workspace status in the web store, the ask-user title cache).
- More resilient services (0.3.187). A crash inside a Telegram handler can no longer demote the long-poll loop; a failed agent spawn surfaces an
agent_exitbanner instead of an inert pane; the PTY stdout pipeline is guarded against a misbehaving output sink. - Live, smooth Settings (0.3.188). Dragging a slider no longer stalls per step — value-based change detection, rAF-coalesced apply, debounced persistence, and
applySettingsskips per-pane refit/layout work when the changed fields don’t need it. Settings apply instantly, no Apply button.
CLI & shareBin
Section titled “CLI & shareBin”htworks from any shell (0.3.187). The CLI’s default socket path is now the app’s real config-dir socket (~/Library/Application Support/hyperterm-canvas/hyperterm.sock) instead of the legacy/tmp/hyperterm.sock—htconnects from terminals the app didn’t spawn, noHT_SOCKET_PATHexport needed (it still overrides;ht doctordiagnoses drift).- Ten new shareBin utilities (0.3.186).
show_logs(live log viewer),show_csv_profile(CSV/TSV profiling),show_http(HTTP response inspector),show_mermaid(Mermaid diagrams — first version renders via a CDN bundle),show_env(environment diagnostics),show_sqlite(read-only SQLite browser),show_ports(live listening-port dashboard),show_proc(live process tree),show_image_diff(image comparison),show_openapi(OpenAPI/Swagger explorer). See shareBin.
0.3.183 — Workspace screenshots
Section titled “0.3.183 — Workspace screenshots”ht screenshot workspace(0.3.183). The screenshot CLI now captures a whole workspace — the bounding box of every visible pane — alongside the existing single-pane (default) and whole-window (window/--full-window) targets.ht screenshot workspace [id]or--workspace [id]targets the active workspace (or a specific one). A hidden/background target now falls back to the full-window grab instead of an empty crop. See Surfaces & I/O.
0.3.182 — Reliability, performance & CLI hardening
Section titled “0.3.182 — Reliability, performance & CLI hardening”A second pass over the code review (doc/full_app_review_2026-05.md) closed the remaining critical/high findings — graceful-shutdown data loss, idle CPU, agent-cost runaways, a native sideband-XSS gap — plus internal cleanups. Newest first.
Security
Section titled “Security”- Native sideband HTML/SVG is now sandboxed (0.3.181). Display-only
html/svgpanel content (inlinemeta.dataor fd 4 frames) renders inside a strict-CSP<iframe sandbox>on the native app too — not just the web mirror — so a careless or compromised sideband producer can’t run script with the app’s full IPC privilege. A panel that needs to forward DOM events still opts into the direct path by settinginteractive. See Binary data (fd 4).
Performance & reliability
Section titled “Performance & reliability”- No more silent data loss on quit (0.3.174). Closing the window, ⌘Q, Dock-quit, or the last surface exiting now reliably persists your layout, settings, cookies, and browser history. (macOS GUI quits bypass the Unix signals the previous save path relied on, so those saves were being skipped on the common exit paths.)
- Idle CPU drops sharply (0.3.179). The 1 Hz process-metadata poller now backs off (1s → 2s → 4s, capped 5s) while a terminal is idle and unchanging, snapping back to 1s the instant output, a pane open/close, or window focus changes. An idle-but-focused terminal goes from ~6–9% of a core to a trickle; an active one is unchanged.
- Effects stop burning idle cycles (0.3.173). WebGL bloom no longer re-renders on every cursor blink and pauses for background (non-visible) workspaces; the Process Manager’s CPU / RSS columns also stopped freezing.
- Auto-continue can’t run up a bill (0.3.175). The agent auto-continue engine now applies its cooldown and runaway gates before consulting the model, so a chatty or looping agent no longer triggers a paid round-trip per turn-end notification; the “agent looped” notice is logged once per episode instead of every time.
- Crashed agent panes are recoverable (0.3.176). When a pi agent subprocess exits, its pane now disables input, shows “Agent process exited (code N)”, and offers a one-click Restart agent — previously the input stayed live and silently swallowed everything you typed, with no way to recover.
- Web-mirror sidebar parity fix (0.3.180). The workspace card’s shortened cwd and RAM figure now match the native sidebar exactly (the mirror used to show a different path form and rendered any sub-1 MB process as a bogus
0M).
Architecture & tooling
Section titled “Architecture & tooling”htCLI internals split (0.3.182). The 2,361-line CLI entry was broken into a thinbin/htplus testablesrc/cli/modules (flags, RPC transport, command mapping). No command, flag, or output change.- pi-agent manager dead-code removal (0.3.177). Dropped ~200 lines of an unused Promise-based agent IPC path; the live fire-and-forget path is unchanged.
- Metadata poller test coverage (0.3.178). The previously-untested 1 Hz orchestration gained 11 tests via injectable subprocess runners. Pure internal hardening.
0.3.172 — Security review & architecture hardening
Section titled “0.3.172 — Security review & architecture hardening”A full code review (doc/full_app_review_2026-05.md) drove a wave of security ship-stoppers, dependency/tooling cleanups, and an architecture decomposition. Newest first.
Security (0.3.161 → 0.3.167)
Section titled “Security (0.3.161 → 0.3.167)”- Web mirror honours your bind + auth token on auto-start (0.3.161). Previously the auth token (
webMirrorAuthToken) and127.0.0.1bind were applied only via the manual Settings toggle; when the mirror auto-started at launch (or its port changed) it silently bound0.0.0.0with no auth. Fixed — your configured token / loopback bind now take effect on auto-start. - Telegram allow-list defaults to empty and fails closed (0.3.161).
telegramAllowedUserIdsno longer ships a hardcoded id, and an empty allow-list now rejects all inbound messages + notification-button taps (was: accept-anyone). Enter your numeric Telegram id in Settings → Telegram to enable remote control. - Sideband HTML/SVG sandboxed in the web mirror (0.3.161). Inline panel
meta.datamarkup now renders in the same sandboxed<iframe>(CSPscript-src 'none') as binary frames, closing a LAN-XSS hole. - Live auth-token rotation (0.3.162). Changing the mirror token / bind in Settings now takes effect without a restart; rotating the token also clears the brute-force cooldown.
ht browser navigaterejectsfile://(0.3.162). Prevents reading arbitrary local files via a pane over the socket;http(s)://,about:,data:,chrome-extension://still work.browser.eval/addscript/addstylenow share a 256 KiB payload cap.- More hardening (0.3.162). Brute-force throttle keyed on the real peer IP (not a spoofable header); settings + cookie files are created
0600from the first byte (+fsyncfor power-loss durability); the on-disk log redacts Telegram / auth tokens. - Opt-in RPC socket token (0.3.163, default off). Settings → Network → “Require RPC socket token” gates state-mutating
htcommands (typing into panes, killing processes) behind a per-boot token; read-only diagnostics stay open. Defense-in-depth against opportunistic same-user processes — not a hard boundary. New env overrideHT_RPC_TOKEN_PATH. - All dependency-audit vulnerabilities cleared (0.3.167).
bun auditwent 7 → 0 via targetedoverrides(ws, ip-address, brace-expansion, basic-ftp).
Architecture & tooling (0.3.164 → 0.3.172)
Section titled “Architecture & tooling (0.3.164 → 0.3.172)”- xterm migrated to
@xterm/xterm@6(0.3.164). The webview core is now aligned with the v6 addons + headless it already used (the deprecated unscopedxterm@5.3.0is gone). No user-facing change. - ask-user modal gets High-Contrast styling (0.3.164). The “This will execute on your machine” confirm prompt previously fell through to default styling in Windows High Contrast /
prefers-contrast(the CSS targeted a class the modal never emits). Fixed. - Settings schema versioning (0.3.165).
settings.jsonnow carries a__schemaVersion+ an ordered migration runner, so a future field rename/removal won’t silently drop your data. - Supply-chain hygiene (0.3.166). A Renovate config + a non-blocking dependency-vulnerability scan (
bun audit) in CI. - eslint fixed + wired into CI (0.3.168). A flat eslint config scoped to the project’s authored TypeScript (the previous config crawled
.claude/git worktrees → ~22k bogus errors and was never run). SurfaceManagerdecomposition (0.3.169 → 0.3.171). The browser / Telegram / editor / agent surface concerns were extracted into dedicated controllers — ~285 net lines out of the 2,700-line module. Pure internal refactor; no behavior change.- Brand consolidation (0.3.172). Brand identifiers centralized in one module; the
htCLI now prints “τ-mux” rather than “HyperTerm Canvas”. (The config dir, bundle id, and socket name stay for back-compat.) - CI / release gates. The release workflow now runs typecheck + tests before uploading binaries; CI re-runs the functional (non-pixel) web-mirror security e2e specs and lints on every push.
Earlier 0.3.x (0.3.150 → 0.3.160)
Section titled “Earlier 0.3.x (0.3.150 → 0.3.160)”- Command palette completeness (0.3.150). ~30 more verbs reachable via ⌘⇧P — workspace, pane, browser, theme, and editor operations.
- CLI rename verbs auto-detect (0.3.151).
ht rename-workspace NAME/ht rename-surface NAMEresolve the target fromHT_SURFACEwhen run inside a pane (no--workspaceneeded). - IME candidate positioning (0.3.153). Native + web mirror no longer force xterm’s helper textarea off-screen, so IME candidate windows appear at the cursor.
- Ask/plan prompt opacity + top-layer reliability (0.3.154 → 0.3.158). Prompts render as global blocking overlays with an opaque sheet + scrim; root-caused to the native webview not loading the
--ht-*design-token stylesheet (now linked intoindex.html). - Web mirror terminal-sizing parity (0.3.160). Web/native cell math aligned (pane padding, resize plumbing, a sub-pixel epsilon) so
clearno longer surfaces a stray%and long lines don’t wrap a column short.
0.3.x — Triple-A polish (Phases 6 → 9)
Section titled “0.3.x — Triple-A polish (Phases 6 → 9)”The 0.3 series ran a multi-phase polish push grading every feature against an S/A/B/C rubric and lifting concrete gaps to a higher grade in each session. The work is tracked in doc/feature_grades.json + doc/feature_grades.md + per-phase doc/tracking_feature_upgrade_to_AAA_phase*.md files in the repo.
0.3.146 → 0.3.148 — Phase 9 follow-up (B-grade gap closures)
Section titled “0.3.146 → 0.3.148 — Phase 9 follow-up (B-grade gap closures)”- Workspaces — strict
layout.jsonvalidator.src/shared/layout-persistence.tsexportsvalidatePersistedLayout+parsePersistedLayout(pure functions). Walks the full shape: top-levelactiveWorkspaceIndex(integer in[-1, len]),sidebarVisible(boolean),workspaces(non-empty array), each workspace’s required + optional record fields, everyPaneNodesubtree (leafwith validsurfaceId+ optionalSurfaceKind, orsplitwith validdirection+ratioin[0,1]+ exactly 2 valid children).loadLayoutinsrc/bun/index.tsnow callsparsePersistedLayout. A truncatedlayout.json(fsync interrupted, disk full, kernel panic mid-write, rsync of a partial backup) now boots to a clean slate rather than throwing downstream incollectLeafIds/remapPaneNode. 26 new tests cover happy paths + every parse-failure mode + every shape-mismatch mode. - Panel-registry — per-surface 256-cap with oldest-eviction.
PanelRegistryctor takes an optionalmaxPanelsPerSurface(default 256, exported asDEFAULT_MAX_PANELS_PER_SURFACE). When a new id arrives and the per-surface map is at the cap, the oldest entry (smallestcreatedAt) is evicted before insertion. Updates to existing ids never trip the cap. Cap clamped to ≥ 1 so a bogus0/ negative arg degrades gracefully. A runaway script that emits a fresh panel id every tick can no longer leak the registry. 13 new tests. - Sidebar file explorer — symlink-cycle protection.
SidebarFileExplorerEntrygains two optional fields:linkTarget: string | null(resolved realpath of a symlink, null for dangling links) andcycle: true(set when the realpath equals the listed directory or any ancestor). The newisAncestorOrSelf(candidate, root)helper correctly anchors on the path separator so/foois NOT mistakenly treated as an ancestor of/foobar. The webview can now refuse navigation into a loop with a clear “this would loop” affordance instead of letting the user walk into the cycle. 9 new tests.
0.3.145 — Phase 9 first push (observability)
Section titled “0.3.145 — Phase 9 first push (observability)”- CI coverage gate.
.github/workflows/ci.ymlgains acoverage-gatejob runningbun run test:coveragethenbun run report:coverage:checkon macOS-14 in parallel with the existing typecheck-and-unit job. A per-file lines-hit-ratio regression beyond the 0.5pp slack baseline attests/baselines/coverage-baseline.lcovfails the build. To lower the floor:bun run baseline:coveragelocally and commit the new baseline (review-gated). 4 source-grep tests intests/ci-coverage-gate.test.tslock in the job declaration. - Logger size-based rotation (logging A → S).
src/bun/logger.tsnow rotates by size in addition to date. When the active file exceedsHT_LOG_MAX_BYTES(50 MiB default, ≤ 0 disables) it’s renamed toapp-DATE.<n>.logand a freshapp-DATE.logopens.tail -f app-DATE.logalways follows the newest chunk; numbered chunks form the archive.bytesInActiveis seeded fromfstatSyncon open so a same-day restart picks up where it left off. The 14-day prune pattern matches the numbered variants too. - Project
CHANGELOG.mdpopulated.bun scripts/bump-version.ts patch --changelogwas run against the real repo to seedCHANGELOG.mdat the repo root with 312 commits since v0.2.30, grouped by conventional-commit type. Future bumps via--changelogprepend new sections.
0.3.143 → 0.3.144 — Phase 8 (release engineering)
Section titled “0.3.143 → 0.3.144 — Phase 8 (release engineering)”scripts/bump-version.tsC → A. Five new flags + two-tier rollback:--commit— creates achore(release): vX.Y.Zcommit staging only the seven version-tracked files. Refuses on dirty trees unless--allow-dirty.--tag— annotatedvX.Y.Zat HEAD (implies--commit); refuses to overwrite existing tags.--changelog— generates / extendsCHANGELOG.mdwith a conventional-commit-grouped section (feat / fix / perf / refactor / docs / test / chore / other). Empty sections skipped. Range =$(prev-tag)..HEAD.--allow-dirty— bypass the working-tree-clean check.--dry-run— print everything without writing or git-touching.- Two-tier rollback. File-phase snapshots restored on any update throw (CHANGELOG.md deleted if it didn’t pre-exist). Git-phase LIFO undo-stack resets the commit if
--tagfails afterwards. BUMP_VERSION_ROOTenv override lets tests sandbox the script against a tmpdir without mocking. 12 new tests.
scripts/post-package.tscross-platform. Previously hard-exited on non-macOS. Now branches three ways:macos(full pipeline — Info.plist patch via PlistBuddy,.tar.zstrebuild, DMG rebuild via hdiutil),linux(skip Info.plist + DMG; reuse the sametar | zstdshell pipeline with the Linux-flatAPP_DIR_NAMEtau-mux/instead oftau-mux.app/),other(Windows, BSD, … keep the old skip-with-message behaviour). 9 new tests.tau-focus-auditC → A. Wired intobun testvia a happy-dom fixture suite (tests/tau-focus-audit.test.ts, 10 tests). A chromatic-glow leak in chrome CSS now fails the build instead of waiting for someone to open DevTools.
0.3.4 → 0.3.142 — Phase 7 close (Cluster H + F.10)
Section titled “0.3.4 → 0.3.142 — Phase 7 close (Cluster H + F.10)”The Phase 7 push ran 43 sessions plus a finish run, closing the two long-standing structural items the masterplan was tracking.
- Cluster H —
audit:themingclean for the first time across both CSS files. ~1013 hard-coded colour literals acrosssrc/views/terminal/index.cssandsrc/web-client/client.cssare now zero — every literal sits behind an--ht-*CSS custom property. The vocabulary totals 200+ tokens grouped by family:--ht-vnext-*(post-Phase-6 redesign palette — 20+ tokens covering text scale, surface chrome, status colours, sheet shells).--ht-agent-*(pi-agent panel — 35 tokens covering toolbar, badges, dropdowns, code/think/tool-call states, msg bubbles, slash menu, confirm dialog, input bar, status chips).--ht-window-*(window-theme shell — titlebar / sidebar / surface / overlay / toast).--ht-sidebar-v2-*(sidebar v2 log / stat / script-pulse / server-dot status palette).--ht-telegram-*(telegram pane chrome).--ht-web-*(web-mirror exclusive — 39 tokens for status glows, sidebar drawer, WM overlays, telegram extras, tau-meter glow trio).--ht-contrast-*(@media (prefers-contrast: more)border bumps).
- Cluster F.10 — webview-handler extraction. The 82-method / 671-line
bunMessageHandlersinline block insrc/bun/index.tsis extracted into 13 per-domain modules undersrc/bun/webview-handlers/(clipboard, viewport, surface, reply, workspace, notification, system, browser, agent, telegram, editor, ask-user +types.ts+index.tsaggregator).satisfies BunMessageHandlersexhaustiveness is preserved viaBunMessageHandlerSlice<K> = Pick<BunMessageHandlers, K>plus a getter-backed late-binding pattern.src/bun/index.tsshrinks 3471 → 2860 lines. Zero behaviour change. 2823 / 2823 tests pass. - Theme-token test suite. Grew 0 → 619 source-grep tests asserting per-region migrations to the token vocabulary.
0.3.0 → 0.3.3 — Web mirror parity (M11 → M18, minor bump at M17)
Section titled “0.3.0 → 0.3.3 — Web mirror parity (M11 → M18, minor bump at M17)”The M11 → M17 plan brought the web mirror to feature parity with the native sidebar; the M18 series chased the multi-pane terminal sizing tail down to zero drift.
- M17 (0.3.0) — plan panel + logs polish (parity feature complete). Plan panel is now a fourth persistent sidebar zone (ordered first:
[plan, notif, main, log]) owned bycreateSidebarView. The dispatcher routesplansSnapshot+autoContinueAuditenvelopes throughsidebarView.setPlans/setAutoContinueAudit. Auto-continue audit hides when the user disables auto-continue natively. Logs zone gains per-row level badge (info / warning / error / success) +HH:MM:SStimestamp + source label + body, with click-to-copy. - M16 (0.2.90) — pane chrome chips +
paneGapfrom settings. Web mirror’s pane DOM renamed from.pane-bar*/.pane-chip*to.surface-bar*/.surface-chip*, mirroring native. SharedrenderSurfaceChipsextracted tosrc/shared/pane-chips.ts.paneGapflows from settings on every layout pass. Focus ring follows--ht-border-focustoken. - M15 (0.2.89) — floating notification overlay. When a notification arrives carrying a
surfaceId, the browser mirror anchors a card stack inside that pane (top-right) — same DOM + auto-dismiss + hover-pause + +N overflow pill semantics as native. Up to 3 cards visible per surface; older cards collapse into an overflow pill. Driven by the M11 settings broadcast (notificationOverlayEnabled,notificationOverlayMs). - M14 (0.2.88) — manifest cards (npm + Cargo).
package.jsonandCargo.tomlcards render in the web mirror’s workspace card via sharedrenderManifestCard. Header with icon + name + version + type chip; expanded body shows description +binchips + per-script action rows with state dots. Cargo card auto-derives default subcommands (build/run/test/check/clippy/fmt). Per-manifest expand/collapse persists in localStorage.runScriptdeferred for web mirror v1 (clicks fire a Web Notification + dispatch the sameht-run-scriptwindow event the native sidebar uses; real surface spawning tracked as M14-1). - M13 (0.2.87) — rich sidebar workspace cards. Each workspace card renders the same content shape as native: 3 px coloured stripe, header with dot + name + pane-count badge, focused command + listening-port chips (+N overflow past 3), aggregated CPU + RAM with rolling sparkline, pinned-CWD chip row, collapsible pane list, status pills via shared
renderStatusEntry, OSC 9;4 progress bar. SharedbuildSidebarWorkspacesprojection insrc/shared/sidebar-state.ts. NewselectWorkspaceCwdenvelope (client → server) when the user pins a CWD; v1 stores in localStorage and the server hook is null-safe so bun-side wiring is deferred without breaking the protocol contract. - M12 (0.2.86) — bottom status bar. 26 px fixed bar at the foot of the browser mirror runs the same data-driven
renderStatusKeyregistry the native bottom bar uses — workspace identity, CPU/mem meters, focused fg / cwd / branch, plusht set-statusbridge keys. Three zones (identity / meters / focus) match the native split.src/views/terminal/status-renderers.ts+status-keys.tsmoved tosrc/shared/;Meterextracted tosrc/shared/tau-meter.ts.tau-primitives.tsre-exports for back-compat. - M11 (0.2.85) — theme + settings broadcast. New
settingsSnapshotandhtKeysSeenenvelopes on the v2 protocol carry theme preset + ANSI palette + font + density + status-bar key order +ht set-statusdiscovery list to every connected web client. Sensitive fields (auth token, telegram bot token, allowed user ids) are intentionally dropped bypickWebSettingsand never reach the wire. The browser mirror switches palette without reload when the user picks a different theme natively. - M18 (0.3.3) — multi-pane terminal sizing. New
src/shared/xterm-fit.tsports the native webview’sfitSurfaceTerminal: bails on zero parent dimensions, reads cell metrics from the render service, calls_renderService.clear()beforeterm.resizeso fresh metrics replace cached ones, subtracts.xtermCSS padding from the cell-count math.applyLayoutwrites inline rects → forces a CSS layout flush viavoid termEl.offsetHeight→ callsfitTerminalper pane in the same tick. The deferred-rAF fit pass is gone.applySettingsre-fits onfontSize/fontFamily/lineHeightchange. - 0.3.1 sizing fix. Per-pane
fit()now refits each pane to its own container (pre-fix, all panes were forced to the SERVER’s authoritative size). Status bar no longer clips the last terminal row.
Native sidebar CWD file explorer
Section titled “Native sidebar CWD file explorer”- Workspace cards always show CWD. The webview sidebar renders a CWD row for every workspace card, including single-CWD cards and metadata-unavailable states.
- Native-only file explorer. Collapsible explorer rooted at the selected workspace CWD, with lazy per-directory listing, refresh, dotfile + max-entry Settings controls. Native-only — the HTTP mirror is not wired for it (yet).
- AAA polish. Filtered-count summaries (shown / hidden / ignored), root path header, richer file metadata (size + modified time), accessible
role="tree"/role="treeitem"semantics, stronger focus states, “New File” action that opens a create-enabled CodeMirror editor split.
CodeMirror editor pane
Section titled “CodeMirror editor pane”- Editor surface (
editor:*). A native webview-only editor pane backed by CodeMirror 6. Files open from the sidebar file explorer or viaht edit/ht editor .... Edits in a split pane, save with⌘S, reload, close, restore across layout persistence. - Editor file RPC. Bun performs local text-file reads and atomic saves with binary / large-file guardrails and mtime conflict detection. HTTP mirror not wired for editor panes in this iteration.
ht run-in-split pane readiness
Section titled “ht run-in-split pane readiness”- Wait for the new pane before typing. The pi
ht-bridgeextension snapshotssurface.list, handles legacysurface.splitresponses that only return"OK", polls until the new surface appears, and only sends the command aftersurface.wait_readyconfirms the new terminal metadata is observable. Times out cleanly without losing input. surface.splitreturns the created surface id when available. Internal split dispatch passes the requested source surface / CWD through and returns{ id }for synchronous split creation, keeping"OK"as a compatibility fallback.
0.2.82
Section titled “0.2.82”- pi-extensions/ht-bridge: active-label and
agent_endsummaries now follow the live pi session model (auth + base URL match too). Switching pi from Haiku to Sonnet retargets the summariser without a config edit. NewuseSessionModelflag (defaulttrue) +PI_HT_BRIDGE_USE_SESSION_MODELenv override; the existingprovider/modelIdpair is now the fallback path. - claude-integration: new
tau-muxClaude Code skill atclaude-integration/skills/tau-mux/SKILL.md. Mirrors the active / LLM-callable side ofpi-extensions/ht-bridge(plans →.claude/plans/<name>.mdreview-gated viaht ask choicethenht plan set,ht ask {yesno|choice|text|confirm-command}for structured questions, milestoneht notify,ht new-split+ht sendfor long-running processes,ht browserfor verification,ht screenshotfor evidence,ht set-status/ht set-progressfor in-progress signals, bash-safety gating). The runtime hook bridge keeps owning the passive pills (active label, cost ticker, idle/permission).install.shnow installs both pieces;SKIP_HOOKS=1/SKIP_SKILL=1for partial installs.
- Telegram bridge: chat pane, long-poll bot service, SQLite log,
ht telegramCLI, optional notification forwarding. - Sharebin: drop-and-share files served from the web mirror.
- Browser pane improvements: 40+
ht browsercommands, address bar with smart URL detection, force dark mode, terminal link interception. - Process Manager: collapse/expand per surface, port chips inside rows, summary header.
- Live process metadata: git state (branch, ahead/behind, dirty counts) added to the per-surface payload, TTL-cached.
- Web mirror: protocol v2 envelopes, resume-on-reconnect via 2 MB ring buffer,
@xterm/headlesssnapshot replay, constant-time token comparison. - Workspace package.json card with one-click script run + green/red/grey state dots.
- Initial public preview.
- Workspaces, tiling splits, draggable dividers.
- xterm.js +
Bun.spawnPTYs. - Sideband protocol (fd 3/4/5) with Python + TypeScript clients.
- Floating canvas panels.
htCLI for socket-driven control.- Web mirror v1.