read-only confinement cannot write /tmp, so the per-terminal history path
did nothing there; read-only shells now get an empty HISTFILE, which also
keeps them from loading the person's history. workspace-write shells keep
their per-terminal history file, and the controller now deletes it
through the same execution environment after the shell ends, including a
failed allocation; a failed deletion is logged.
The sidebar only opened tabs for Agent terminals that appeared in the
roster, so a terminal the Agent closed left a "Terminal closed." tab
behind. Each window now remembers which listed terminals the Agent
controls and closes the tabs bound to one that leaves the roster; a
terminal the person took over keeps its tab. The client terminal list
also adopts each whole roster instead of only merging it, so terminals
the Host no longer retains leave the cached list. ClientTerminals gains
boundTerminal to read a tab's terminal binding.
A click that focuses a program with focus reporting (DECSET 1004), such as
vim or neovim, made xterm send ESC [ I within the gesture window, so the
click took the terminal over. Scrolling a pager on the alternate screen
did the same through wheel-generated arrow keys. Focus reports are never
typed, and the wheel no longer opens the typed-input window.
A roster stream that failed while watched used to drop its watchers, so
Agent terminals stopped opening tabs until the Session header remounted.
The stream now reopens in place on the next watch of that Session or when
the connection resets.
maxAgentTerminals now counts only running Agent-controlled terminals and
pending Agent allocations, each id once; exited terminals and failed
allocations awaiting cleanup still count toward maxTerminals. openForAgent
applies rename's 1-120 character title rule and trims the title.
readScreen reports a terminal closed while its read drains as
RemoteError terminal/unavailable, like a missing terminal.
Only workspace-write can write the confined history file, so read-only
shells do not save history; unconfined shells keep the inherited HISTFILE
unless env sets one. AgentTerminal.close checks control once when called,
and a takeover during cleanup does not stop the close. The sidebar README
now says only a confined terminal's badge names its sandbox mode on hover.
Delete @deepseek-ai/dsh-invariants, every package's ./invariant companion
and its tests, the invariant gates and Vitest setup, the scoped-event
resolver generator, and the INVARIANT-coded listener rethrow paths.
Update docs, READMEs, Agent Notes, and add an upgrade guide.
Shell discovery keyed entries by resolved path. On merged-/usr hosts the
login shell /bin/bash from $SHELL and the PATH hit /usr/bin/bash for the
`bash` candidate are one file with two spellings, so the menu showed
"bash" twice. Key discovered shells by executable name, compared without
letter case or `.exe`, and keep the earliest entry so the default shell
stays first.