From 22880d30e55597a8bd7d0484c5736b1c0659eeb4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 28 Oct 2026 11:11:47 +0000 Subject: [PATCH] chore: release package(s) --- .changeset/agent-helper-auto-retire.md | 7 -- .changeset/attach-cursor-honest.md | 6 -- .changeset/better-auth-refresh.md | 6 -- .changeset/calm-prompts-submit.md | 8 --- .../copy-mode-toast-and-node-enabled.md | 8 --- .changeset/display-helper-copy.md | 6 -- .changeset/fix-dir-picker-label-reopen.md | 5 -- .changeset/frozen-tail-recovery.md | 7 -- .changeset/grid-resync-on-settle.md | 5 -- .changeset/helper-review-hardening.md | 9 --- .changeset/pipe-writer-rearm.md | 5 -- .changeset/pr465-review-docs.md | 7 -- .changeset/pr465-review-hardening.md | 11 --- .changeset/preview-compatibility-docs.md | 5 -- .changeset/preview-proxy-compatibility.md | 8 --- .changeset/setup-command-credentials.md | 5 -- .changeset/ssh-bulletin-hardening.md | 8 --- .changeset/terminal-input-budgets.md | 7 -- .changeset/terminal-share-revocation.md | 7 -- .changeset/virtual-display-rail-entry.md | 28 -------- .changeset/website-virtual-displays.md | 12 ---- apps/client/desktop/CHANGELOG.md | 43 +++++++++++ apps/client/desktop/package.json | 2 +- apps/docs/CHANGELOG.md | 47 ++++++++++++ apps/docs/package.json | 2 +- apps/node/agent/CHANGELOG.md | 45 ++++++++++++ apps/node/agent/package.json | 2 +- apps/server/api/CHANGELOG.md | 71 +++++++++++++++++++ apps/server/api/package.json | 2 +- apps/server/desktop/CHANGELOG.md | 57 +++++++++++++++ apps/server/desktop/package.json | 2 +- apps/website/CHANGELOG.md | 10 +++ apps/website/package.json | 2 +- bun.lock | 12 ++-- 34 files changed, 285 insertions(+), 182 deletions(-) delete mode 100644 .changeset/agent-helper-auto-retire.md delete mode 100644 .changeset/attach-cursor-honest.md delete mode 100644 .changeset/better-auth-refresh.md delete mode 100644 .changeset/calm-prompts-submit.md delete mode 100644 .changeset/copy-mode-toast-and-node-enabled.md delete mode 100644 .changeset/display-helper-copy.md delete mode 100644 .changeset/fix-dir-picker-label-reopen.md delete mode 100644 .changeset/frozen-tail-recovery.md delete mode 100644 .changeset/grid-resync-on-settle.md delete mode 100644 .changeset/helper-review-hardening.md delete mode 100644 .changeset/pipe-writer-rearm.md delete mode 100644 .changeset/pr465-review-docs.md delete mode 100644 .changeset/pr465-review-hardening.md delete mode 100644 .changeset/preview-compatibility-docs.md delete mode 100644 .changeset/preview-proxy-compatibility.md delete mode 100644 .changeset/setup-command-credentials.md delete mode 100644 .changeset/ssh-bulletin-hardening.md delete mode 100644 .changeset/terminal-input-budgets.md delete mode 100644 .changeset/terminal-share-revocation.md delete mode 100644 .changeset/virtual-display-rail-entry.md delete mode 100644 .changeset/website-virtual-displays.md diff --git a/.changeset/agent-helper-auto-retire.md b/.changeset/agent-helper-auto-retire.md deleted file mode 100644 index 51b62f26f..000000000 --- a/.changeset/agent-helper-auto-retire.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@internal/server": patch -"@internal/node": patch -"@internal/docs": patch ---- - -A finished agent helper now leaves the sidebar on its own. A subshell an agent opened over MCP nests as a leaf beneath its creator, and until now that leaf stayed forever unless the agent deleted it, so a helper whose pane had exited cluttered the tree indefinitely. The server's 60 s housekeeping sweep now retires a cross-agent helper about half an hour after its pane died, or once a launch left a crashed-zombie row, through the ordinary delete act (token, log, and workspace cleanup included) so its leaf drops from every open dashboard at once. A helper an agent terminated on purpose to keep its history is left in place, as is any helper whose pane is still running, so collecting results and calling `delete_subshell` remains the immediate close; the MCP tool guidance and the helper-lifecycle guide say a forgotten helper is retired on that grace rather than lingering. diff --git a/.changeset/attach-cursor-honest.md b/.changeset/attach-cursor-honest.md deleted file mode 100644 index 074067ed2..000000000 --- a/.changeset/attach-cursor-honest.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@internal/server": patch -"@internal/node": patch ---- - -A pane viewer could open already garbled, and stay garbled for its whole life: the replay's cursor read accepts a blank answer as row zero. tmux occasionally prints `":"` for a pane caught mid-redraw, and `Number("")` is 0, so that moment became a confident "the cursor is top-left", the replay ended sixteen-odd rows off the pane, and every later frame that repositions relative to it (a TUI repainting its pinned spinner block) landed on the transcript instead. The read now answers the pane's real position or nothing at all, and "nothing" takes the known degrade: a replay without the restore rather than a wrong one. The attach journal line gained the matching witness, `cursor=x,y|none`, so the next "fixed elements overlap the output" report reads the base off the log instead of guessing. Also: the hand-run attach probes were repaired (stale dist paths, and `test-setup.ts` no longer loads outside the runner, so they get their own preload) and `probe-join.ts` joins a pane the way production does, overlap and all. diff --git a/.changeset/better-auth-refresh.md b/.changeset/better-auth-refresh.md deleted file mode 100644 index 979859450..000000000 --- a/.changeset/better-auth-refresh.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@internal/server": patch -"@internal/node": patch ---- - -Update Better Auth and its API-key and passkey plugins to 1.7.14, and JOSE to 6.2.13. The server migrates existing authentication accounts to Better Auth's provider-based identity keys while preserving issuer separation, credentials and sessions. Downgrading the server requires restoring a backup made before this upgrade. diff --git a/.changeset/calm-prompts-submit.md b/.changeset/calm-prompts-submit.md deleted file mode 100644 index 2c857783e..000000000 --- a/.changeset/calm-prompts-submit.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"@internal/server": patch -"@internal/node": patch -"@internal/desktop-server": patch -"@internal/desktop-client": patch ---- - -Fix long prompts remaining unsubmitted in Codex subshells. Automated input now waits for the end of the prompt to render and allows the paste parser to settle before pressing Enter, including prompts sent by another subshell. Concurrent automated sends stay separate, and repeated prompt text no longer triggers submission before the full burst renders. diff --git a/.changeset/copy-mode-toast-and-node-enabled.md b/.changeset/copy-mode-toast-and-node-enabled.md deleted file mode 100644 index c32caced3..000000000 --- a/.changeset/copy-mode-toast-and-node-enabled.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"@internal/server": patch -"@internal/node": patch ---- - -A pane's **Enable text copying** toggle now asks the preference what the device actually holds before deciding what the press did. The header menu and the touch bar share one per-device set, and each used to flip its own React copy of it, so a press that followed another window's change could "turn copying off" on a pane that was already off and say so out loud. In a private window, where the device stores nothing at all, the two surfaces disagreed the other way: the one you clicked remembered your click while the other re-read an empty store. Both now take the answer the preference module holds, which is also what a refused write leaves the page with. - -`get_node` also holds its answer about a display provider to the same rule the launch path applies, mirroring all four of the plane's gates: the node switch, the provider catalog, the owner's enabled list, and whether that provider runs on this node's operating system, with the protocol's own default rather than a provider name typed into the tool. The last one used to be inferred from `status`, which is the node's own report rather than the plane's verdict, so a node answering optimistically about a platform it cannot serve had the tool recommending a launch the server refuses. `enabled` is now the plane's answer and `status` stays the node's, and when the node cannot be asked the tool says so and promises nothing else. The other three were already enforced on the way in, so nothing a user could click read wrong before this; what changes is that a row arriving by some other route, or one a later release shrinks out of the catalog, can no longer be reported ready here and then refused by the launch it recommended. diff --git a/.changeset/display-helper-copy.md b/.changeset/display-helper-copy.md deleted file mode 100644 index ff72032ab..000000000 --- a/.changeset/display-helper-copy.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@internal/desktop-client": patch -"@internal/desktop-server": patch ---- - -Clarify virtual display setup and helper consent, permissions, sign-in, and quit messages. Keep shared-desktop warnings visible and explain when viewers disconnect while subshells keep running. Bump the bundled helper to 1.0.1 so its update is identified consistently across both desktop apps. diff --git a/.changeset/fix-dir-picker-label-reopen.md b/.changeset/fix-dir-picker-label-reopen.md deleted file mode 100644 index 571b281bb..000000000 --- a/.changeset/fix-dir-picker-label-reopen.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@internal/server": patch ---- - -Clicking a field's "Working directory" label now closes an open folder picker instead of bouncing it back open. One label click was two events: the click outside the panel closed it, and the browser then forwarded the label's activation click to the input, which reopened it. diff --git a/.changeset/frozen-tail-recovery.md b/.changeset/frozen-tail-recovery.md deleted file mode 100644 index 7957713e1..000000000 --- a/.changeset/frozen-tail-recovery.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@internal/server": patch ---- - -A pane terminal could freeze while the pane itself kept running: when the node's byte stream to the browser died, every device watching that subshell kept seeing the same stale screen, typing echoed nothing, and the state lasted until the server restarted. The dashboard builds one output pump per subshell and hands it to every later viewer, and a pump that went silent (a refused tail start, a relay that stopped delivering) never said so. Now it does: the server logs the failure, drops that pane's viewers with the retry close, and the re-attach that follows builds a fresh pump at the current end of the log. A shared pump also gets a watchdog: if the pane's log grows while the stream has delivered nothing for 25 seconds, the pump is declared dead the same way, and a genuinely quiet pane is left alone because the verdict compares the log's size against what the pump actually delivered. - -The workspace header's actions menu now offers the **Diagnostics** toggle for the focused pane, the same per-device HUD the subshell page has had since Wave C. It floats over the dock terminal the header stands above and reports the socket state, reconnects, viewers, input echo timings, and when the last pane byte arrived, which is what you want on screen while a terminal is misbehaving. diff --git a/.changeset/grid-resync-on-settle.md b/.changeset/grid-resync-on-settle.md deleted file mode 100644 index ab1d967d0..000000000 --- a/.changeset/grid-resync-on-settle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@internal/server": patch ---- - -A terminal no longer stays garbled after the pane it watches resizes. The reported shape: a TUI with a block that repaints constantly (a spinner, a pinned status line) would start drawing that block over the transcript, "where new output is mixed in", and keep doing it until the tab was closed. The window is the resize itself: bytes the pane emitted while changing its grid addressed a size the viewer never rendered, and every later frame the app positions relative to them lands a constant offset off. Only the attach can lay down a true base (absolute capture, cursor restore, confirmed geometry), so when a viewer adopts a grid other than the one its replay was painted into, and pane bytes arrived in between, it now silently re-attaches and heals. Once per observed change, debounced so a window drag settles before the replay, and gapped so a bouncing grid cannot turn the self-heal into a reconnect storm. The client never asks the server for a size, only for fresh truth. diff --git a/.changeset/helper-review-hardening.md b/.changeset/helper-review-hardening.md deleted file mode 100644 index b681b62fd..000000000 --- a/.changeset/helper-review-hardening.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"@internal/desktop-client": patch -"@internal/desktop-server": patch -"@internal/server": patch ---- - -Verify the installed display helper before opening it even when an update is blocked by another running helper. Preserve executable permissions when staging the helper, harden its bundled page's HTML escaping and native message boundary, and keep its state and menu updates consistent. - -Recover terminal grid changes that happen during the reconnect cooldown. Let native text selection reach the terminal rows in copy mode, and prevent input focus from clearing a new selection. diff --git a/.changeset/pipe-writer-rearm.md b/.changeset/pipe-writer-rearm.md deleted file mode 100644 index 00b8894dc..000000000 --- a/.changeset/pipe-writer-rearm.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@internal/node": patch ---- - -A node re-arms its own pane recorder now. Each pane's bytes reach the dashboard through a small writer process tmux pipes the pane into; if something kills just that writer (an OOM pass is the usual culprit) while the pane itself keeps running, the log stops growing, and every viewer sees a frozen terminal with a live pane behind it. tmux's own pipe flag cannot arbitrate (it has measured both on and off for a dead writer), so the exit watcher, which already touches every supervised pane, asks the honest question on that same beat: if the pane's screen moved and its log did not, repeatedly, for a little over a minute, the writer is dead, and the watcher re-arms the pipe on the same log file the long way: the form that cannot be talked out of it by tmux believing the pipe is already on. A quiet pane and a healthy writer both cost nothing beyond a periodic look, and the re-arm waits out three strikes so no single race spends one. diff --git a/.changeset/pr465-review-docs.md b/.changeset/pr465-review-docs.md deleted file mode 100644 index 7c0dcd1f6..000000000 --- a/.changeset/pr465-review-docs.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@internal/docs": patch ---- - -Explain display-helper updates and signature failures, touch-device text copying, and terminal display recovery after resizing. Add screenshots of the copy controls and selected text, and show where to open helper settings. - -Explain private setup-key delivery, the CLI capability check, coordinated server and node upgrades, and the remaining shell-history and local-account trust limits. diff --git a/.changeset/pr465-review-hardening.md b/.changeset/pr465-review-hardening.md deleted file mode 100644 index 1f468955a..000000000 --- a/.changeset/pr465-review-hardening.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"@internal/server": patch -"@internal/node": patch -"@internal/desktop-server": patch -"@internal/desktop-client": patch -"@internal/docs": patch ---- - -Keep preview redirects inside their proxy, preserve malformed app scripts for browser diagnostics, and bound concurrent rewriting by decoded bytes. Fix preview preferences on display-only nodes, reconnect grid tracking, launch-dialog cleanup, and node-settings links. Recover stalled capture writers between output bursts and rescue the Mac pointer after capture loss. Keep helper confirmations stable across state updates and clear them when settings reopen. Verify shared palette imports and refresh the preview documentation screenshots. - -Reserve terminal input capacity per node so an offline machine cannot consume the fleet's budget, give a keystroke on a live pane the capacity a stuck retry was holding before disconnecting anyone, and drop held input immediately during forced viewer recovery. Prune obsolete helper staging copies after a successful install while preserving recovery data on failure. Dismiss the helper's quit confirmation when Confirm is pressed, even where the native bridge is absent. diff --git a/.changeset/preview-compatibility-docs.md b/.changeset/preview-compatibility-docs.md deleted file mode 100644 index 76a38c5b0..000000000 --- a/.changeset/preview-compatibility-docs.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@internal/docs": patch ---- - -Document personal preview settings, common URL rewriting, and browser compatibility limits. Explain live sharing revocation, terminal input bounds, private installer credentials, and Display Helper verification. Give Web previews & screens its own sidebar section with shared prerequisites and dedicated app, browser, Linux, macOS, and agent guides. Consolidate platform setup and preserve existing links. Start the overview with a loaded website preview and consolidate repeated prerequisites, access rules, and provider lifecycle explanations. diff --git a/.changeset/preview-proxy-compatibility.md b/.changeset/preview-proxy-compatibility.md deleted file mode 100644 index 71037fe9b..000000000 --- a/.changeset/preview-proxy-compatibility.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"@internal/server": patch -"@internal/desktop-server": patch ---- - -Fix web preview asset paths and common development-server requests, including Next.js and Vite hot reload. Add personal proxy settings remembered for each app on a node, with controls for URL rewriting, the Host header, and redirects. - -Show the setup guide when either web previews or virtual displays is disabled, including when no views are discovered. diff --git a/.changeset/setup-command-credentials.md b/.changeset/setup-command-credentials.md deleted file mode 100644 index f0bc96a00..000000000 --- a/.changeset/setup-command-credentials.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@internal/server": patch ---- - -Keep setup keys out of process arguments in dashboard-generated install and setup commands. Explain the current CLI requirement and the remaining shell-history and local-account trust limits. diff --git a/.changeset/ssh-bulletin-hardening.md b/.changeset/ssh-bulletin-hardening.md deleted file mode 100644 index e52ac14ed..000000000 --- a/.changeset/ssh-bulletin-hardening.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"@internal/server": patch -"@internal/node": patch -"@internal/desktop-server": patch -"@internal/desktop-client": patch ---- - -Keep SSH setup keys out of subprocess arguments, require stdin-capable connecting nodes, and refuse older downloaded node binaries before passing credentials. Bound retained terminal resize work during sustained updates and document the SSH security bulletin review. diff --git a/.changeset/terminal-input-budgets.md b/.changeset/terminal-input-budgets.md deleted file mode 100644 index 69ea2fa53..000000000 --- a/.changeset/terminal-input-budgets.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@internal/server": patch -"@internal/desktop-server": patch -"@internal/docs": patch ---- - -Bound terminal WebSocket input by frame count and UTF-8 payload bytes per connection and across the server, including dispatched writes and retries. Close connections that exceed the limits and prevent late failures from recreating retry queues after disconnect. Document the limits and recovery behavior. diff --git a/.changeset/terminal-share-revocation.md b/.changeset/terminal-share-revocation.md deleted file mode 100644 index f18b86a11..000000000 --- a/.changeset/terminal-share-revocation.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@internal/server": patch -"@internal/desktop-server": patch -"@internal/docs": patch ---- - -Revoke cached terminal access when subshell shares change, including connections still attaching and queued input retries. Recipients reconnect through the current access checks; the owner's terminal stays connected. Document the cutoff and the limits for commands already dispatched. diff --git a/.changeset/virtual-display-rail-entry.md b/.changeset/virtual-display-rail-entry.md deleted file mode 100644 index 29a5f46d7..000000000 --- a/.changeset/virtual-display-rail-entry.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -"@internal/desktop-client": minor -"@internal/desktop-server": minor -"@internal/server": patch -"@internal/node": patch -"@internal/docs": patch ---- - -Add a **Virtual Displays** section to both desktop assistants' sidebar, shown only where a display helper exists, so the helper is reachable from the window a person is already looking at instead of only from the macOS menu bar. The screen carries the steps the menu item never showed (enable the helper, grant Screen Recording and Accessibility to Subshell Display Helper, and what to do when macOS asks for a restart) above one button that opens the helper's own settings panel and a link to the setup guide. Document the sidebar as the way in. - -In the web dashboard, a node that cannot serve previews now says so in gold when the reason is that both of its preview switches are off, which an owner can fix, with a link to the guide that says how; an offline or outdated node stays muted, because no setting fixes that. A launch form, a preset, and the provider list no longer offer a virtual display provider the selected machine's operating system cannot run, and once the server knows the machine's OS it refuses one before the subshell row, its token, or a launch command exists, so a Mac is not asked to start Xvfb. A machine whose OS has not answered is not refused on that basis. Both shapes of no-answer count (no value at all, and an agent on a platform it cannot classify reporting the protocol's own `unknown`), because neither is a verdict: the list keeps what it held and the saved choice stays. Filtering on a non-answer would empty the provider card of a node that enrolled and never reported, which is the owner's only way to enable anything there, and would hide what a preset is about to launch. It is not a promise that such a machine can run the provider: the node answers that itself and refuses Xvfb on any non-Linux platform with `preview-display-unsupported`, so the server's check is a fast no rather than the only no. A provider the node has not enabled now stays named in its picker instead of hiding behind a trigger reading "Node default", since the line beside that row tells you to choose another provider and a choice you cannot see is not one you can change. - -A launch refused over its virtual display now reports the problem beside the **Virtual display** checkbox it belongs to, not under Save as preset; switch the checkbox off and the line moves back to the form's own message, so the reason is never both invisible and orphaned. A refusal the server raises itself now carries the node's own word for the same failure, so it lands in the same place whoever noticed it. On the machine, the display allocation says which readiness gate stayed shut (the Xvfb socket, the VNC port, or the listener's owner), logs what Xvfb and x11vnc printed before cleaning up, and gives a cold box 10 seconds instead of 5. - -The preview icon in a pane's header now wears a count when the pane has things you could open but have not saved: discovered app ports with no Saved services row, plus the screen and the browser watch when the pane offers them. It is the picker's own data, so the number and the list cannot disagree, and "you could" is read as the reader's own access: someone holding view sees the port and screen rows but cannot act on them, each disabled control saying why, so their badge counts only the browser watch and never numbers a control that answers "needs edit access". - -The desktop apps' **Open Display Helper settings** control works: the helper's signature check handed `codesign` a bare requirement string, which it read as a file name, so the panel failed to open on every press. (The module is macOS-only, so no Linux lane even builds it, and the macOS lanes that do only fail at runtime on a machine with a real bundle: nothing in CI opens the helper's panel, so no lane ever pressed the path that checks a signature or reads a preference.) Pressing it then opened nothing either: the helper read its own preferences through a `UserDefaults` suite named after its bundle identifier, which Foundation refuses, so the process trapped before any window existed. Both are fixed, and a helper that cannot find its panel now says so instead of showing an empty window. - -The panel itself is now the same product as the rest of Subshell, built from the components a person already knows in the dashboard: a card plate with its border and radius, `Fact` rows (a strong muted label above a readable value) for the login item and the two macOS permissions, and a form-section heading with its trailing rule for each group. Gold marks what you can still fix and plain ink what is settled, red what macOS refused, a live line names how many people are watching this Mac's screens, help sits at the bottom left and the exits at the bottom right in one shape, and a disabled control's reason is drawn in gold rather than hidden where a person who cannot hover will never find it. It is a small local page bundled into the helper, so the design system's own checker guards it, and the palette it draws on moved to `packages/assistant/tokens.css`, which every CSS surface imports rather than copies: the dashboard, both assistants and the helper's panel now read one file, while the node's own dashboard and the mobile app still keep a hand-maintained copy of the same values. The menu bar's menu now says it belongs to the Subshell virtual display helper instead of leaving you to guess from an icon, its **Minimize helper** item follows whether the panel is actually on screen, and the consent item turns itself into **Enable helper** once you disable the helper, raising the panel so you can read what you agreed to. It used to offer only Disable, greyed out once consent was off, which left the settings window as the only way back to running. - -**Quit helper** asks first, from the panel and from the menu bar's menu alike, and the question answers the part the button never said: watching stops now, your consent and the macOS permissions stay, and the helper -says what it will actually do next: it returns at sign-in only if you registered -it there, and the panel's **Stop at sign-in** button, not disabling, is what -takes it off that list. Quitting also stopped revoking your consent, which it had been doing silently along with unregistering the login item: an app's Close button destroying the very permission the panel exists to collect, so every reopen started from Disabled. - -Enabling the helper no longer adds it to macOS's sign-in list, and disabling no longer takes it off. **Start at sign-in** is its own row with its own button, and the row says which of the three states macOS actually reports, registered, waiting for your approval, or not registered at all. Reading the last two as "approved" is what let a turned-off sign-in start keep claiming it starts at sign-in. The panel re-reads the machine every five seconds while it is open, in every run-loop mode and with App Nap held off, so a switch you flip in System Settings changes what you see without reopening anything. - -Discarding the last virtual display on a Mac also hands the pointer back. A viewer driving that screen leaves the real cursor on a display that no longer exists, which looked like a vanished cursor to whoever was sitting at the machine. A virtual screen can outlive the helper, so the rescue runs on any helper start as well as on teardown: whoever opens the panel next finds their cursor back where they can see it. diff --git a/.changeset/website-virtual-displays.md b/.changeset/website-virtual-displays.md deleted file mode 100644 index 11cc7ccdb..000000000 --- a/.changeset/website-virtual-displays.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -"@internal/website": patch -"@internal/docs": patch -"@internal/server": patch -"@internal/desktop-server": patch ---- - -Add a virtual-display section to the website with a screenshot and Linux and macOS setup links. Refresh the display screenshot in the guide, and show SSH owner-only restrictions in gold like other node settings. - -Refresh the website-preview recording with a neutral demo path. - -Keep the phone demo at the same size when switching to the wider website layout. diff --git a/apps/client/desktop/CHANGELOG.md b/apps/client/desktop/CHANGELOG.md index e7734f765..c629b706e 100644 --- a/apps/client/desktop/CHANGELOG.md +++ b/apps/client/desktop/CHANGELOG.md @@ -1,5 +1,48 @@ # @internal/desktop-client +## 1.5.0 + +### Minor Changes + +- [#465](https://github.com/subshell-ai/subshell/pull/465) [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d) Thanks [@theogravity](https://github.com/theogravity)! - Add a **Virtual Displays** section to both desktop assistants' sidebar, shown only where a display helper exists, so the helper is reachable from the window a person is already looking at instead of only from the macOS menu bar. The screen carries the steps the menu item never showed (enable the helper, grant Screen Recording and Accessibility to Subshell Display Helper, and what to do when macOS asks for a restart) above one button that opens the helper's own settings panel and a link to the setup guide. Document the sidebar as the way in. + + In the web dashboard, a node that cannot serve previews now says so in gold when the reason is that both of its preview switches are off, which an owner can fix, with a link to the guide that says how; an offline or outdated node stays muted, because no setting fixes that. A launch form, a preset, and the provider list no longer offer a virtual display provider the selected machine's operating system cannot run, and once the server knows the machine's OS it refuses one before the subshell row, its token, or a launch command exists, so a Mac is not asked to start Xvfb. A machine whose OS has not answered is not refused on that basis. Both shapes of no-answer count (no value at all, and an agent on a platform it cannot classify reporting the protocol's own `unknown`), because neither is a verdict: the list keeps what it held and the saved choice stays. Filtering on a non-answer would empty the provider card of a node that enrolled and never reported, which is the owner's only way to enable anything there, and would hide what a preset is about to launch. It is not a promise that such a machine can run the provider: the node answers that itself and refuses Xvfb on any non-Linux platform with `preview-display-unsupported`, so the server's check is a fast no rather than the only no. A provider the node has not enabled now stays named in its picker instead of hiding behind a trigger reading "Node default", since the line beside that row tells you to choose another provider and a choice you cannot see is not one you can change. + + A launch refused over its virtual display now reports the problem beside the **Virtual display** checkbox it belongs to, not under Save as preset; switch the checkbox off and the line moves back to the form's own message, so the reason is never both invisible and orphaned. A refusal the server raises itself now carries the node's own word for the same failure, so it lands in the same place whoever noticed it. On the machine, the display allocation says which readiness gate stayed shut (the Xvfb socket, the VNC port, or the listener's owner), logs what Xvfb and x11vnc printed before cleaning up, and gives a cold box 10 seconds instead of 5. + + The preview icon in a pane's header now wears a count when the pane has things you could open but have not saved: discovered app ports with no Saved services row, plus the screen and the browser watch when the pane offers them. It is the picker's own data, so the number and the list cannot disagree, and "you could" is read as the reader's own access: someone holding view sees the port and screen rows but cannot act on them, each disabled control saying why, so their badge counts only the browser watch and never numbers a control that answers "needs edit access". + + The desktop apps' **Open Display Helper settings** control works: the helper's signature check handed `codesign` a bare requirement string, which it read as a file name, so the panel failed to open on every press. (The module is macOS-only, so no Linux lane even builds it, and the macOS lanes that do only fail at runtime on a machine with a real bundle: nothing in CI opens the helper's panel, so no lane ever pressed the path that checks a signature or reads a preference.) Pressing it then opened nothing either: the helper read its own preferences through a `UserDefaults` suite named after its bundle identifier, which Foundation refuses, so the process trapped before any window existed. Both are fixed, and a helper that cannot find its panel now says so instead of showing an empty window. + + The panel itself is now the same product as the rest of Subshell, built from the components a person already knows in the dashboard: a card plate with its border and radius, `Fact` rows (a strong muted label above a readable value) for the login item and the two macOS permissions, and a form-section heading with its trailing rule for each group. Gold marks what you can still fix and plain ink what is settled, red what macOS refused, a live line names how many people are watching this Mac's screens, help sits at the bottom left and the exits at the bottom right in one shape, and a disabled control's reason is drawn in gold rather than hidden where a person who cannot hover will never find it. It is a small local page bundled into the helper, so the design system's own checker guards it, and the palette it draws on moved to `packages/assistant/tokens.css`, which every CSS surface imports rather than copies: the dashboard, both assistants and the helper's panel now read one file, while the node's own dashboard and the mobile app still keep a hand-maintained copy of the same values. The menu bar's menu now says it belongs to the Subshell virtual display helper instead of leaving you to guess from an icon, its **Minimize helper** item follows whether the panel is actually on screen, and the consent item turns itself into **Enable helper** once you disable the helper, raising the panel so you can read what you agreed to. It used to offer only Disable, greyed out once consent was off, which left the settings window as the only way back to running. + + **Quit helper** asks first, from the panel and from the menu bar's menu alike, and the question answers the part the button never said: watching stops now, your consent and the macOS permissions stay, and the helper + says what it will actually do next: it returns at sign-in only if you registered + it there, and the panel's **Stop at sign-in** button, not disabling, is what + takes it off that list. Quitting also stopped revoking your consent, which it had been doing silently along with unregistering the login item: an app's Close button destroying the very permission the panel exists to collect, so every reopen started from Disabled. + + Enabling the helper no longer adds it to macOS's sign-in list, and disabling no longer takes it off. **Start at sign-in** is its own row with its own button, and the row says which of the three states macOS actually reports, registered, waiting for your approval, or not registered at all. Reading the last two as "approved" is what let a turned-off sign-in start keep claiming it starts at sign-in. The panel re-reads the machine every five seconds while it is open, in every run-loop mode and with App Nap held off, so a switch you flip in System Settings changes what you see without reopening anything. + + Discarding the last virtual display on a Mac also hands the pointer back. A viewer driving that screen leaves the real cursor on a display that no longer exists, which looked like a vanished cursor to whoever was sitting at the machine. A virtual screen can outlive the helper, so the rescue runs on any helper start as well as on teardown: whoever opens the panel next finds their cursor back where they can see it. + +### Patch Changes + +- [#464](https://github.com/subshell-ai/subshell/pull/464) [`4fd0811`](https://github.com/subshell-ai/subshell/commit/4fd0811225e58ffce46678770d2ff32af920b6a3) Thanks [@theogravity](https://github.com/theogravity)! - Fix long prompts remaining unsubmitted in Codex subshells. Automated input now waits for the end of the prompt to render and allows the paste parser to settle before pressing Enter, including prompts sent by another subshell. Concurrent automated sends stay separate, and repeated prompt text no longer triggers submission before the full burst renders. + +- [#465](https://github.com/subshell-ai/subshell/pull/465) [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d) Thanks [@theogravity](https://github.com/theogravity)! - Clarify virtual display setup and helper consent, permissions, sign-in, and quit messages. Keep shared-desktop warnings visible and explain when viewers disconnect while subshells keep running. Bump the bundled helper to 1.0.1 so its update is identified consistently across both desktop apps. + +- [#465](https://github.com/subshell-ai/subshell/pull/465) [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d) Thanks [@theogravity](https://github.com/theogravity)! - Verify the installed display helper before opening it even when an update is blocked by another running helper. Preserve executable permissions when staging the helper, harden its bundled page's HTML escaping and native message boundary, and keep its state and menu updates consistent. + + Recover terminal grid changes that happen during the reconnect cooldown. Let native text selection reach the terminal rows in copy mode, and prevent input focus from clearing a new selection. + +- [#465](https://github.com/subshell-ai/subshell/pull/465) [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d) Thanks [@theogravity](https://github.com/theogravity)! - Keep preview redirects inside their proxy, preserve malformed app scripts for browser diagnostics, and bound concurrent rewriting by decoded bytes. Fix preview preferences on display-only nodes, reconnect grid tracking, launch-dialog cleanup, and node-settings links. Recover stalled capture writers between output bursts and rescue the Mac pointer after capture loss. Keep helper confirmations stable across state updates and clear them when settings reopen. Verify shared palette imports and refresh the preview documentation screenshots. + + Reserve terminal input capacity per node so an offline machine cannot consume the fleet's budget, give a keystroke on a live pane the capacity a stuck retry was holding before disconnecting anyone, and drop held input immediately during forced viewer recovery. Prune obsolete helper staging copies after a successful install while preserving recovery data on failure. Dismiss the helper's quit confirmation when Confirm is pressed, even where the native bridge is absent. + +- [#465](https://github.com/subshell-ai/subshell/pull/465) [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d) Thanks [@theogravity](https://github.com/theogravity)! - Keep SSH setup keys out of subprocess arguments, require stdin-capable connecting nodes, and refuse older downloaded node binaries before passing credentials. Bound retained terminal resize work during sustained updates and document the SSH security bulletin review. +- Updated dependencies [[`8a501b4`](https://github.com/subshell-ai/subshell/commit/8a501b45b22bca0b909e0d08c1b5339368f59d3f), [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d), [`69f8ff6`](https://github.com/subshell-ai/subshell/commit/69f8ff6ca67f9c916b509288bce3c5b081bb2d48), [`4fd0811`](https://github.com/subshell-ai/subshell/commit/4fd0811225e58ffce46678770d2ff32af920b6a3), [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d), [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d), [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d), [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d), [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d)]: + - @internal/node@3.0.1 + ## 1.4.0 ### Minor Changes diff --git a/apps/client/desktop/package.json b/apps/client/desktop/package.json index 4346491a4..3d1c4594f 100644 --- a/apps/client/desktop/package.json +++ b/apps/client/desktop/package.json @@ -1,7 +1,7 @@ { "name": "@internal/desktop-client", "description": "Subshell Client: a Tauri shell that registers this machine as a subshell node and manages its node daemon", - "version": "1.4.0", + "version": "1.5.0", "license": "Apache-2.0", "type": "module", "private": true, diff --git a/apps/docs/CHANGELOG.md b/apps/docs/CHANGELOG.md index da5f65189..4513d2b32 100644 --- a/apps/docs/CHANGELOG.md +++ b/apps/docs/CHANGELOG.md @@ -1,5 +1,52 @@ # @internal/docs +## 0.11.2 + +### Patch Changes + +- [#462](https://github.com/subshell-ai/subshell/pull/462) [`8a501b4`](https://github.com/subshell-ai/subshell/commit/8a501b45b22bca0b909e0d08c1b5339368f59d3f) Thanks [@theogravity](https://github.com/theogravity)! - A finished agent helper now leaves the sidebar on its own. A subshell an agent opened over MCP nests as a leaf beneath its creator, and until now that leaf stayed forever unless the agent deleted it, so a helper whose pane had exited cluttered the tree indefinitely. The server's 60 s housekeeping sweep now retires a cross-agent helper about half an hour after its pane died, or once a launch left a crashed-zombie row, through the ordinary delete act (token, log, and workspace cleanup included) so its leaf drops from every open dashboard at once. A helper an agent terminated on purpose to keep its history is left in place, as is any helper whose pane is still running, so collecting results and calling `delete_subshell` remains the immediate close; the MCP tool guidance and the helper-lifecycle guide say a forgotten helper is retired on that grace rather than lingering. + +- [#465](https://github.com/subshell-ai/subshell/pull/465) [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d) Thanks [@theogravity](https://github.com/theogravity)! - Explain display-helper updates and signature failures, touch-device text copying, and terminal display recovery after resizing. Add screenshots of the copy controls and selected text, and show where to open helper settings. + + Explain private setup-key delivery, the CLI capability check, coordinated server and node upgrades, and the remaining shell-history and local-account trust limits. + +- [#465](https://github.com/subshell-ai/subshell/pull/465) [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d) Thanks [@theogravity](https://github.com/theogravity)! - Keep preview redirects inside their proxy, preserve malformed app scripts for browser diagnostics, and bound concurrent rewriting by decoded bytes. Fix preview preferences on display-only nodes, reconnect grid tracking, launch-dialog cleanup, and node-settings links. Recover stalled capture writers between output bursts and rescue the Mac pointer after capture loss. Keep helper confirmations stable across state updates and clear them when settings reopen. Verify shared palette imports and refresh the preview documentation screenshots. + + Reserve terminal input capacity per node so an offline machine cannot consume the fleet's budget, give a keystroke on a live pane the capacity a stuck retry was holding before disconnecting anyone, and drop held input immediately during forced viewer recovery. Prune obsolete helper staging copies after a successful install while preserving recovery data on failure. Dismiss the helper's quit confirmation when Confirm is pressed, even where the native bridge is absent. + +- [#465](https://github.com/subshell-ai/subshell/pull/465) [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d) Thanks [@theogravity](https://github.com/theogravity)! - Document personal preview settings, common URL rewriting, and browser compatibility limits. Explain live sharing revocation, terminal input bounds, private installer credentials, and Display Helper verification. Give Web previews & screens its own sidebar section with shared prerequisites and dedicated app, browser, Linux, macOS, and agent guides. Consolidate platform setup and preserve existing links. Start the overview with a loaded website preview and consolidate repeated prerequisites, access rules, and provider lifecycle explanations. + +- [#465](https://github.com/subshell-ai/subshell/pull/465) [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d) Thanks [@theogravity](https://github.com/theogravity)! - Bound terminal WebSocket input by frame count and UTF-8 payload bytes per connection and across the server, including dispatched writes and retries. Close connections that exceed the limits and prevent late failures from recreating retry queues after disconnect. Document the limits and recovery behavior. + +- [#465](https://github.com/subshell-ai/subshell/pull/465) [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d) Thanks [@theogravity](https://github.com/theogravity)! - Revoke cached terminal access when subshell shares change, including connections still attaching and queued input retries. Recipients reconnect through the current access checks; the owner's terminal stays connected. Document the cutoff and the limits for commands already dispatched. + +- [#465](https://github.com/subshell-ai/subshell/pull/465) [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d) Thanks [@theogravity](https://github.com/theogravity)! - Add a **Virtual Displays** section to both desktop assistants' sidebar, shown only where a display helper exists, so the helper is reachable from the window a person is already looking at instead of only from the macOS menu bar. The screen carries the steps the menu item never showed (enable the helper, grant Screen Recording and Accessibility to Subshell Display Helper, and what to do when macOS asks for a restart) above one button that opens the helper's own settings panel and a link to the setup guide. Document the sidebar as the way in. + + In the web dashboard, a node that cannot serve previews now says so in gold when the reason is that both of its preview switches are off, which an owner can fix, with a link to the guide that says how; an offline or outdated node stays muted, because no setting fixes that. A launch form, a preset, and the provider list no longer offer a virtual display provider the selected machine's operating system cannot run, and once the server knows the machine's OS it refuses one before the subshell row, its token, or a launch command exists, so a Mac is not asked to start Xvfb. A machine whose OS has not answered is not refused on that basis. Both shapes of no-answer count (no value at all, and an agent on a platform it cannot classify reporting the protocol's own `unknown`), because neither is a verdict: the list keeps what it held and the saved choice stays. Filtering on a non-answer would empty the provider card of a node that enrolled and never reported, which is the owner's only way to enable anything there, and would hide what a preset is about to launch. It is not a promise that such a machine can run the provider: the node answers that itself and refuses Xvfb on any non-Linux platform with `preview-display-unsupported`, so the server's check is a fast no rather than the only no. A provider the node has not enabled now stays named in its picker instead of hiding behind a trigger reading "Node default", since the line beside that row tells you to choose another provider and a choice you cannot see is not one you can change. + + A launch refused over its virtual display now reports the problem beside the **Virtual display** checkbox it belongs to, not under Save as preset; switch the checkbox off and the line moves back to the form's own message, so the reason is never both invisible and orphaned. A refusal the server raises itself now carries the node's own word for the same failure, so it lands in the same place whoever noticed it. On the machine, the display allocation says which readiness gate stayed shut (the Xvfb socket, the VNC port, or the listener's owner), logs what Xvfb and x11vnc printed before cleaning up, and gives a cold box 10 seconds instead of 5. + + The preview icon in a pane's header now wears a count when the pane has things you could open but have not saved: discovered app ports with no Saved services row, plus the screen and the browser watch when the pane offers them. It is the picker's own data, so the number and the list cannot disagree, and "you could" is read as the reader's own access: someone holding view sees the port and screen rows but cannot act on them, each disabled control saying why, so their badge counts only the browser watch and never numbers a control that answers "needs edit access". + + The desktop apps' **Open Display Helper settings** control works: the helper's signature check handed `codesign` a bare requirement string, which it read as a file name, so the panel failed to open on every press. (The module is macOS-only, so no Linux lane even builds it, and the macOS lanes that do only fail at runtime on a machine with a real bundle: nothing in CI opens the helper's panel, so no lane ever pressed the path that checks a signature or reads a preference.) Pressing it then opened nothing either: the helper read its own preferences through a `UserDefaults` suite named after its bundle identifier, which Foundation refuses, so the process trapped before any window existed. Both are fixed, and a helper that cannot find its panel now says so instead of showing an empty window. + + The panel itself is now the same product as the rest of Subshell, built from the components a person already knows in the dashboard: a card plate with its border and radius, `Fact` rows (a strong muted label above a readable value) for the login item and the two macOS permissions, and a form-section heading with its trailing rule for each group. Gold marks what you can still fix and plain ink what is settled, red what macOS refused, a live line names how many people are watching this Mac's screens, help sits at the bottom left and the exits at the bottom right in one shape, and a disabled control's reason is drawn in gold rather than hidden where a person who cannot hover will never find it. It is a small local page bundled into the helper, so the design system's own checker guards it, and the palette it draws on moved to `packages/assistant/tokens.css`, which every CSS surface imports rather than copies: the dashboard, both assistants and the helper's panel now read one file, while the node's own dashboard and the mobile app still keep a hand-maintained copy of the same values. The menu bar's menu now says it belongs to the Subshell virtual display helper instead of leaving you to guess from an icon, its **Minimize helper** item follows whether the panel is actually on screen, and the consent item turns itself into **Enable helper** once you disable the helper, raising the panel so you can read what you agreed to. It used to offer only Disable, greyed out once consent was off, which left the settings window as the only way back to running. + + **Quit helper** asks first, from the panel and from the menu bar's menu alike, and the question answers the part the button never said: watching stops now, your consent and the macOS permissions stay, and the helper + says what it will actually do next: it returns at sign-in only if you registered + it there, and the panel's **Stop at sign-in** button, not disabling, is what + takes it off that list. Quitting also stopped revoking your consent, which it had been doing silently along with unregistering the login item: an app's Close button destroying the very permission the panel exists to collect, so every reopen started from Disabled. + + Enabling the helper no longer adds it to macOS's sign-in list, and disabling no longer takes it off. **Start at sign-in** is its own row with its own button, and the row says which of the three states macOS actually reports, registered, waiting for your approval, or not registered at all. Reading the last two as "approved" is what let a turned-off sign-in start keep claiming it starts at sign-in. The panel re-reads the machine every five seconds while it is open, in every run-loop mode and with App Nap held off, so a switch you flip in System Settings changes what you see without reopening anything. + + Discarding the last virtual display on a Mac also hands the pointer back. A viewer driving that screen leaves the real cursor on a display that no longer exists, which looked like a vanished cursor to whoever was sitting at the machine. A virtual screen can outlive the helper, so the rescue runs on any helper start as well as on teardown: whoever opens the panel next finds their cursor back where they can see it. + +- [#465](https://github.com/subshell-ai/subshell/pull/465) [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d) Thanks [@theogravity](https://github.com/theogravity)! - Add a virtual-display section to the website with a screenshot and Linux and macOS setup links. Refresh the display screenshot in the guide, and show SSH owner-only restrictions in gold like other node settings. + + Refresh the website-preview recording with a neutral demo path. + + Keep the phone demo at the same size when switching to the wider website layout. + ## 0.11.1 ### Patch Changes diff --git a/apps/docs/package.json b/apps/docs/package.json index 72a851160..9f17d505b 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -1,7 +1,7 @@ { "name": "@internal/docs", "description": "Public documentation site for Subshell — Fumadocs, statically exported", - "version": "0.11.1", + "version": "0.11.2", "license": "Apache-2.0", "type": "module", "private": true, diff --git a/apps/node/agent/CHANGELOG.md b/apps/node/agent/CHANGELOG.md index 990af0fb1..e862455ab 100644 --- a/apps/node/agent/CHANGELOG.md +++ b/apps/node/agent/CHANGELOG.md @@ -1,5 +1,50 @@ # @internal/node +## 3.0.1 + +### Patch Changes + +- [#462](https://github.com/subshell-ai/subshell/pull/462) [`8a501b4`](https://github.com/subshell-ai/subshell/commit/8a501b45b22bca0b909e0d08c1b5339368f59d3f) Thanks [@theogravity](https://github.com/theogravity)! - A finished agent helper now leaves the sidebar on its own. A subshell an agent opened over MCP nests as a leaf beneath its creator, and until now that leaf stayed forever unless the agent deleted it, so a helper whose pane had exited cluttered the tree indefinitely. The server's 60 s housekeeping sweep now retires a cross-agent helper about half an hour after its pane died, or once a launch left a crashed-zombie row, through the ordinary delete act (token, log, and workspace cleanup included) so its leaf drops from every open dashboard at once. A helper an agent terminated on purpose to keep its history is left in place, as is any helper whose pane is still running, so collecting results and calling `delete_subshell` remains the immediate close; the MCP tool guidance and the helper-lifecycle guide say a forgotten helper is retired on that grace rather than lingering. + +- [#465](https://github.com/subshell-ai/subshell/pull/465) [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d) Thanks [@theogravity](https://github.com/theogravity)! - A pane viewer could open already garbled, and stay garbled for its whole life: the replay's cursor read accepts a blank answer as row zero. tmux occasionally prints `":"` for a pane caught mid-redraw, and `Number("")` is 0, so that moment became a confident "the cursor is top-left", the replay ended sixteen-odd rows off the pane, and every later frame that repositions relative to it (a TUI repainting its pinned spinner block) landed on the transcript instead. The read now answers the pane's real position or nothing at all, and "nothing" takes the known degrade: a replay without the restore rather than a wrong one. The attach journal line gained the matching witness, `cursor=x,y|none`, so the next "fixed elements overlap the output" report reads the base off the log instead of guessing. Also: the hand-run attach probes were repaired (stale dist paths, and `test-setup.ts` no longer loads outside the runner, so they get their own preload) and `probe-join.ts` joins a pane the way production does, overlap and all. + +- [#466](https://github.com/subshell-ai/subshell/pull/466) [`69f8ff6`](https://github.com/subshell-ai/subshell/commit/69f8ff6ca67f9c916b509288bce3c5b081bb2d48) Thanks [@theogravity](https://github.com/theogravity)! - Update Better Auth and its API-key and passkey plugins to 1.7.14, and JOSE to 6.2.13. The server migrates existing authentication accounts to Better Auth's provider-based identity keys while preserving issuer separation, credentials and sessions. Downgrading the server requires restoring a backup made before this upgrade. + +- [#464](https://github.com/subshell-ai/subshell/pull/464) [`4fd0811`](https://github.com/subshell-ai/subshell/commit/4fd0811225e58ffce46678770d2ff32af920b6a3) Thanks [@theogravity](https://github.com/theogravity)! - Fix long prompts remaining unsubmitted in Codex subshells. Automated input now waits for the end of the prompt to render and allows the paste parser to settle before pressing Enter, including prompts sent by another subshell. Concurrent automated sends stay separate, and repeated prompt text no longer triggers submission before the full burst renders. + +- [#465](https://github.com/subshell-ai/subshell/pull/465) [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d) Thanks [@theogravity](https://github.com/theogravity)! - A pane's **Enable text copying** toggle now asks the preference what the device actually holds before deciding what the press did. The header menu and the touch bar share one per-device set, and each used to flip its own React copy of it, so a press that followed another window's change could "turn copying off" on a pane that was already off and say so out loud. In a private window, where the device stores nothing at all, the two surfaces disagreed the other way: the one you clicked remembered your click while the other re-read an empty store. Both now take the answer the preference module holds, which is also what a refused write leaves the page with. + + `get_node` also holds its answer about a display provider to the same rule the launch path applies, mirroring all four of the plane's gates: the node switch, the provider catalog, the owner's enabled list, and whether that provider runs on this node's operating system, with the protocol's own default rather than a provider name typed into the tool. The last one used to be inferred from `status`, which is the node's own report rather than the plane's verdict, so a node answering optimistically about a platform it cannot serve had the tool recommending a launch the server refuses. `enabled` is now the plane's answer and `status` stays the node's, and when the node cannot be asked the tool says so and promises nothing else. The other three were already enforced on the way in, so nothing a user could click read wrong before this; what changes is that a row arriving by some other route, or one a later release shrinks out of the catalog, can no longer be reported ready here and then refused by the launch it recommended. + +- [#465](https://github.com/subshell-ai/subshell/pull/465) [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d) Thanks [@theogravity](https://github.com/theogravity)! - A node re-arms its own pane recorder now. Each pane's bytes reach the dashboard through a small writer process tmux pipes the pane into; if something kills just that writer (an OOM pass is the usual culprit) while the pane itself keeps running, the log stops growing, and every viewer sees a frozen terminal with a live pane behind it. tmux's own pipe flag cannot arbitrate (it has measured both on and off for a dead writer), so the exit watcher, which already touches every supervised pane, asks the honest question on that same beat: if the pane's screen moved and its log did not, repeatedly, for a little over a minute, the writer is dead, and the watcher re-arms the pipe on the same log file the long way: the form that cannot be talked out of it by tmux believing the pipe is already on. A quiet pane and a healthy writer both cost nothing beyond a periodic look, and the re-arm waits out three strikes so no single race spends one. + +- [#465](https://github.com/subshell-ai/subshell/pull/465) [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d) Thanks [@theogravity](https://github.com/theogravity)! - Keep preview redirects inside their proxy, preserve malformed app scripts for browser diagnostics, and bound concurrent rewriting by decoded bytes. Fix preview preferences on display-only nodes, reconnect grid tracking, launch-dialog cleanup, and node-settings links. Recover stalled capture writers between output bursts and rescue the Mac pointer after capture loss. Keep helper confirmations stable across state updates and clear them when settings reopen. Verify shared palette imports and refresh the preview documentation screenshots. + + Reserve terminal input capacity per node so an offline machine cannot consume the fleet's budget, give a keystroke on a live pane the capacity a stuck retry was holding before disconnecting anyone, and drop held input immediately during forced viewer recovery. Prune obsolete helper staging copies after a successful install while preserving recovery data on failure. Dismiss the helper's quit confirmation when Confirm is pressed, even where the native bridge is absent. + +- [#465](https://github.com/subshell-ai/subshell/pull/465) [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d) Thanks [@theogravity](https://github.com/theogravity)! - Keep SSH setup keys out of subprocess arguments, require stdin-capable connecting nodes, and refuse older downloaded node binaries before passing credentials. Bound retained terminal resize work during sustained updates and document the SSH security bulletin review. + +- [#465](https://github.com/subshell-ai/subshell/pull/465) [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d) Thanks [@theogravity](https://github.com/theogravity)! - Add a **Virtual Displays** section to both desktop assistants' sidebar, shown only where a display helper exists, so the helper is reachable from the window a person is already looking at instead of only from the macOS menu bar. The screen carries the steps the menu item never showed (enable the helper, grant Screen Recording and Accessibility to Subshell Display Helper, and what to do when macOS asks for a restart) above one button that opens the helper's own settings panel and a link to the setup guide. Document the sidebar as the way in. + + In the web dashboard, a node that cannot serve previews now says so in gold when the reason is that both of its preview switches are off, which an owner can fix, with a link to the guide that says how; an offline or outdated node stays muted, because no setting fixes that. A launch form, a preset, and the provider list no longer offer a virtual display provider the selected machine's operating system cannot run, and once the server knows the machine's OS it refuses one before the subshell row, its token, or a launch command exists, so a Mac is not asked to start Xvfb. A machine whose OS has not answered is not refused on that basis. Both shapes of no-answer count (no value at all, and an agent on a platform it cannot classify reporting the protocol's own `unknown`), because neither is a verdict: the list keeps what it held and the saved choice stays. Filtering on a non-answer would empty the provider card of a node that enrolled and never reported, which is the owner's only way to enable anything there, and would hide what a preset is about to launch. It is not a promise that such a machine can run the provider: the node answers that itself and refuses Xvfb on any non-Linux platform with `preview-display-unsupported`, so the server's check is a fast no rather than the only no. A provider the node has not enabled now stays named in its picker instead of hiding behind a trigger reading "Node default", since the line beside that row tells you to choose another provider and a choice you cannot see is not one you can change. + + A launch refused over its virtual display now reports the problem beside the **Virtual display** checkbox it belongs to, not under Save as preset; switch the checkbox off and the line moves back to the form's own message, so the reason is never both invisible and orphaned. A refusal the server raises itself now carries the node's own word for the same failure, so it lands in the same place whoever noticed it. On the machine, the display allocation says which readiness gate stayed shut (the Xvfb socket, the VNC port, or the listener's owner), logs what Xvfb and x11vnc printed before cleaning up, and gives a cold box 10 seconds instead of 5. + + The preview icon in a pane's header now wears a count when the pane has things you could open but have not saved: discovered app ports with no Saved services row, plus the screen and the browser watch when the pane offers them. It is the picker's own data, so the number and the list cannot disagree, and "you could" is read as the reader's own access: someone holding view sees the port and screen rows but cannot act on them, each disabled control saying why, so their badge counts only the browser watch and never numbers a control that answers "needs edit access". + + The desktop apps' **Open Display Helper settings** control works: the helper's signature check handed `codesign` a bare requirement string, which it read as a file name, so the panel failed to open on every press. (The module is macOS-only, so no Linux lane even builds it, and the macOS lanes that do only fail at runtime on a machine with a real bundle: nothing in CI opens the helper's panel, so no lane ever pressed the path that checks a signature or reads a preference.) Pressing it then opened nothing either: the helper read its own preferences through a `UserDefaults` suite named after its bundle identifier, which Foundation refuses, so the process trapped before any window existed. Both are fixed, and a helper that cannot find its panel now says so instead of showing an empty window. + + The panel itself is now the same product as the rest of Subshell, built from the components a person already knows in the dashboard: a card plate with its border and radius, `Fact` rows (a strong muted label above a readable value) for the login item and the two macOS permissions, and a form-section heading with its trailing rule for each group. Gold marks what you can still fix and plain ink what is settled, red what macOS refused, a live line names how many people are watching this Mac's screens, help sits at the bottom left and the exits at the bottom right in one shape, and a disabled control's reason is drawn in gold rather than hidden where a person who cannot hover will never find it. It is a small local page bundled into the helper, so the design system's own checker guards it, and the palette it draws on moved to `packages/assistant/tokens.css`, which every CSS surface imports rather than copies: the dashboard, both assistants and the helper's panel now read one file, while the node's own dashboard and the mobile app still keep a hand-maintained copy of the same values. The menu bar's menu now says it belongs to the Subshell virtual display helper instead of leaving you to guess from an icon, its **Minimize helper** item follows whether the panel is actually on screen, and the consent item turns itself into **Enable helper** once you disable the helper, raising the panel so you can read what you agreed to. It used to offer only Disable, greyed out once consent was off, which left the settings window as the only way back to running. + + **Quit helper** asks first, from the panel and from the menu bar's menu alike, and the question answers the part the button never said: watching stops now, your consent and the macOS permissions stay, and the helper + says what it will actually do next: it returns at sign-in only if you registered + it there, and the panel's **Stop at sign-in** button, not disabling, is what + takes it off that list. Quitting also stopped revoking your consent, which it had been doing silently along with unregistering the login item: an app's Close button destroying the very permission the panel exists to collect, so every reopen started from Disabled. + + Enabling the helper no longer adds it to macOS's sign-in list, and disabling no longer takes it off. **Start at sign-in** is its own row with its own button, and the row says which of the three states macOS actually reports, registered, waiting for your approval, or not registered at all. Reading the last two as "approved" is what let a turned-off sign-in start keep claiming it starts at sign-in. The panel re-reads the machine every five seconds while it is open, in every run-loop mode and with App Nap held off, so a switch you flip in System Settings changes what you see without reopening anything. + + Discarding the last virtual display on a Mac also hands the pointer back. A viewer driving that screen leaves the real cursor on a display that no longer exists, which looked like a vanished cursor to whoever was sitting at the machine. A virtual screen can outlive the helper, so the rescue runs on any helper start as well as on teardown: whoever opens the panel next finds their cursor back where they can see it. + ## 3.0.0 ### Major Changes diff --git a/apps/node/agent/package.json b/apps/node/agent/package.json index 093bdade0..189ca9d5e 100644 --- a/apps/node/agent/package.json +++ b/apps/node/agent/package.json @@ -1,7 +1,7 @@ { "name": "@internal/node", "description": "subshell — node daemon: enrolls with the control plane and executes signed commands", - "version": "3.0.0", + "version": "3.0.1", "license": "Apache-2.0", "type": "module", "private": true, diff --git a/apps/server/api/CHANGELOG.md b/apps/server/api/CHANGELOG.md index 4a7212590..dd8533fc0 100644 --- a/apps/server/api/CHANGELOG.md +++ b/apps/server/api/CHANGELOG.md @@ -1,5 +1,76 @@ # @internal/server +## 3.0.3 + +### Patch Changes + +- [#462](https://github.com/subshell-ai/subshell/pull/462) [`8a501b4`](https://github.com/subshell-ai/subshell/commit/8a501b45b22bca0b909e0d08c1b5339368f59d3f) Thanks [@theogravity](https://github.com/theogravity)! - A finished agent helper now leaves the sidebar on its own. A subshell an agent opened over MCP nests as a leaf beneath its creator, and until now that leaf stayed forever unless the agent deleted it, so a helper whose pane had exited cluttered the tree indefinitely. The server's 60 s housekeeping sweep now retires a cross-agent helper about half an hour after its pane died, or once a launch left a crashed-zombie row, through the ordinary delete act (token, log, and workspace cleanup included) so its leaf drops from every open dashboard at once. A helper an agent terminated on purpose to keep its history is left in place, as is any helper whose pane is still running, so collecting results and calling `delete_subshell` remains the immediate close; the MCP tool guidance and the helper-lifecycle guide say a forgotten helper is retired on that grace rather than lingering. + +- [#465](https://github.com/subshell-ai/subshell/pull/465) [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d) Thanks [@theogravity](https://github.com/theogravity)! - A pane viewer could open already garbled, and stay garbled for its whole life: the replay's cursor read accepts a blank answer as row zero. tmux occasionally prints `":"` for a pane caught mid-redraw, and `Number("")` is 0, so that moment became a confident "the cursor is top-left", the replay ended sixteen-odd rows off the pane, and every later frame that repositions relative to it (a TUI repainting its pinned spinner block) landed on the transcript instead. The read now answers the pane's real position or nothing at all, and "nothing" takes the known degrade: a replay without the restore rather than a wrong one. The attach journal line gained the matching witness, `cursor=x,y|none`, so the next "fixed elements overlap the output" report reads the base off the log instead of guessing. Also: the hand-run attach probes were repaired (stale dist paths, and `test-setup.ts` no longer loads outside the runner, so they get their own preload) and `probe-join.ts` joins a pane the way production does, overlap and all. + +- [#466](https://github.com/subshell-ai/subshell/pull/466) [`69f8ff6`](https://github.com/subshell-ai/subshell/commit/69f8ff6ca67f9c916b509288bce3c5b081bb2d48) Thanks [@theogravity](https://github.com/theogravity)! - Update Better Auth and its API-key and passkey plugins to 1.7.14, and JOSE to 6.2.13. The server migrates existing authentication accounts to Better Auth's provider-based identity keys while preserving issuer separation, credentials and sessions. Downgrading the server requires restoring a backup made before this upgrade. + +- [#464](https://github.com/subshell-ai/subshell/pull/464) [`4fd0811`](https://github.com/subshell-ai/subshell/commit/4fd0811225e58ffce46678770d2ff32af920b6a3) Thanks [@theogravity](https://github.com/theogravity)! - Fix long prompts remaining unsubmitted in Codex subshells. Automated input now waits for the end of the prompt to render and allows the paste parser to settle before pressing Enter, including prompts sent by another subshell. Concurrent automated sends stay separate, and repeated prompt text no longer triggers submission before the full burst renders. + +- [#465](https://github.com/subshell-ai/subshell/pull/465) [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d) Thanks [@theogravity](https://github.com/theogravity)! - A pane's **Enable text copying** toggle now asks the preference what the device actually holds before deciding what the press did. The header menu and the touch bar share one per-device set, and each used to flip its own React copy of it, so a press that followed another window's change could "turn copying off" on a pane that was already off and say so out loud. In a private window, where the device stores nothing at all, the two surfaces disagreed the other way: the one you clicked remembered your click while the other re-read an empty store. Both now take the answer the preference module holds, which is also what a refused write leaves the page with. + + `get_node` also holds its answer about a display provider to the same rule the launch path applies, mirroring all four of the plane's gates: the node switch, the provider catalog, the owner's enabled list, and whether that provider runs on this node's operating system, with the protocol's own default rather than a provider name typed into the tool. The last one used to be inferred from `status`, which is the node's own report rather than the plane's verdict, so a node answering optimistically about a platform it cannot serve had the tool recommending a launch the server refuses. `enabled` is now the plane's answer and `status` stays the node's, and when the node cannot be asked the tool says so and promises nothing else. The other three were already enforced on the way in, so nothing a user could click read wrong before this; what changes is that a row arriving by some other route, or one a later release shrinks out of the catalog, can no longer be reported ready here and then refused by the launch it recommended. + +- [#460](https://github.com/subshell-ai/subshell/pull/460) [`bfb4e72`](https://github.com/subshell-ai/subshell/commit/bfb4e7228bf4f2eab774dbb6dc7f6a6179113366) Thanks [@theogravity](https://github.com/theogravity)! - Clicking a field's "Working directory" label now closes an open folder picker instead of bouncing it back open. One label click was two events: the click outside the panel closed it, and the browser then forwarded the label's activation click to the input, which reopened it. + +- [#465](https://github.com/subshell-ai/subshell/pull/465) [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d) Thanks [@theogravity](https://github.com/theogravity)! - A pane terminal could freeze while the pane itself kept running: when the node's byte stream to the browser died, every device watching that subshell kept seeing the same stale screen, typing echoed nothing, and the state lasted until the server restarted. The dashboard builds one output pump per subshell and hands it to every later viewer, and a pump that went silent (a refused tail start, a relay that stopped delivering) never said so. Now it does: the server logs the failure, drops that pane's viewers with the retry close, and the re-attach that follows builds a fresh pump at the current end of the log. A shared pump also gets a watchdog: if the pane's log grows while the stream has delivered nothing for 25 seconds, the pump is declared dead the same way, and a genuinely quiet pane is left alone because the verdict compares the log's size against what the pump actually delivered. + + The workspace header's actions menu now offers the **Diagnostics** toggle for the focused pane, the same per-device HUD the subshell page has had since Wave C. It floats over the dock terminal the header stands above and reports the socket state, reconnects, viewers, input echo timings, and when the last pane byte arrived, which is what you want on screen while a terminal is misbehaving. + +- [#465](https://github.com/subshell-ai/subshell/pull/465) [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d) Thanks [@theogravity](https://github.com/theogravity)! - A terminal no longer stays garbled after the pane it watches resizes. The reported shape: a TUI with a block that repaints constantly (a spinner, a pinned status line) would start drawing that block over the transcript, "where new output is mixed in", and keep doing it until the tab was closed. The window is the resize itself: bytes the pane emitted while changing its grid addressed a size the viewer never rendered, and every later frame the app positions relative to them lands a constant offset off. Only the attach can lay down a true base (absolute capture, cursor restore, confirmed geometry), so when a viewer adopts a grid other than the one its replay was painted into, and pane bytes arrived in between, it now silently re-attaches and heals. Once per observed change, debounced so a window drag settles before the replay, and gapped so a bouncing grid cannot turn the self-heal into a reconnect storm. The client never asks the server for a size, only for fresh truth. + +- [#465](https://github.com/subshell-ai/subshell/pull/465) [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d) Thanks [@theogravity](https://github.com/theogravity)! - Verify the installed display helper before opening it even when an update is blocked by another running helper. Preserve executable permissions when staging the helper, harden its bundled page's HTML escaping and native message boundary, and keep its state and menu updates consistent. + + Recover terminal grid changes that happen during the reconnect cooldown. Let native text selection reach the terminal rows in copy mode, and prevent input focus from clearing a new selection. + +- [#465](https://github.com/subshell-ai/subshell/pull/465) [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d) Thanks [@theogravity](https://github.com/theogravity)! - Keep preview redirects inside their proxy, preserve malformed app scripts for browser diagnostics, and bound concurrent rewriting by decoded bytes. Fix preview preferences on display-only nodes, reconnect grid tracking, launch-dialog cleanup, and node-settings links. Recover stalled capture writers between output bursts and rescue the Mac pointer after capture loss. Keep helper confirmations stable across state updates and clear them when settings reopen. Verify shared palette imports and refresh the preview documentation screenshots. + + Reserve terminal input capacity per node so an offline machine cannot consume the fleet's budget, give a keystroke on a live pane the capacity a stuck retry was holding before disconnecting anyone, and drop held input immediately during forced viewer recovery. Prune obsolete helper staging copies after a successful install while preserving recovery data on failure. Dismiss the helper's quit confirmation when Confirm is pressed, even where the native bridge is absent. + +- [#465](https://github.com/subshell-ai/subshell/pull/465) [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d) Thanks [@theogravity](https://github.com/theogravity)! - Fix web preview asset paths and common development-server requests, including Next.js and Vite hot reload. Add personal proxy settings remembered for each app on a node, with controls for URL rewriting, the Host header, and redirects. + + Show the setup guide when either web previews or virtual displays is disabled, including when no views are discovered. + +- [#465](https://github.com/subshell-ai/subshell/pull/465) [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d) Thanks [@theogravity](https://github.com/theogravity)! - Keep setup keys out of process arguments in dashboard-generated install and setup commands. Explain the current CLI requirement and the remaining shell-history and local-account trust limits. + +- [#465](https://github.com/subshell-ai/subshell/pull/465) [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d) Thanks [@theogravity](https://github.com/theogravity)! - Keep SSH setup keys out of subprocess arguments, require stdin-capable connecting nodes, and refuse older downloaded node binaries before passing credentials. Bound retained terminal resize work during sustained updates and document the SSH security bulletin review. + +- [#465](https://github.com/subshell-ai/subshell/pull/465) [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d) Thanks [@theogravity](https://github.com/theogravity)! - Bound terminal WebSocket input by frame count and UTF-8 payload bytes per connection and across the server, including dispatched writes and retries. Close connections that exceed the limits and prevent late failures from recreating retry queues after disconnect. Document the limits and recovery behavior. + +- [#465](https://github.com/subshell-ai/subshell/pull/465) [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d) Thanks [@theogravity](https://github.com/theogravity)! - Revoke cached terminal access when subshell shares change, including connections still attaching and queued input retries. Recipients reconnect through the current access checks; the owner's terminal stays connected. Document the cutoff and the limits for commands already dispatched. + +- [#465](https://github.com/subshell-ai/subshell/pull/465) [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d) Thanks [@theogravity](https://github.com/theogravity)! - Add a **Virtual Displays** section to both desktop assistants' sidebar, shown only where a display helper exists, so the helper is reachable from the window a person is already looking at instead of only from the macOS menu bar. The screen carries the steps the menu item never showed (enable the helper, grant Screen Recording and Accessibility to Subshell Display Helper, and what to do when macOS asks for a restart) above one button that opens the helper's own settings panel and a link to the setup guide. Document the sidebar as the way in. + + In the web dashboard, a node that cannot serve previews now says so in gold when the reason is that both of its preview switches are off, which an owner can fix, with a link to the guide that says how; an offline or outdated node stays muted, because no setting fixes that. A launch form, a preset, and the provider list no longer offer a virtual display provider the selected machine's operating system cannot run, and once the server knows the machine's OS it refuses one before the subshell row, its token, or a launch command exists, so a Mac is not asked to start Xvfb. A machine whose OS has not answered is not refused on that basis. Both shapes of no-answer count (no value at all, and an agent on a platform it cannot classify reporting the protocol's own `unknown`), because neither is a verdict: the list keeps what it held and the saved choice stays. Filtering on a non-answer would empty the provider card of a node that enrolled and never reported, which is the owner's only way to enable anything there, and would hide what a preset is about to launch. It is not a promise that such a machine can run the provider: the node answers that itself and refuses Xvfb on any non-Linux platform with `preview-display-unsupported`, so the server's check is a fast no rather than the only no. A provider the node has not enabled now stays named in its picker instead of hiding behind a trigger reading "Node default", since the line beside that row tells you to choose another provider and a choice you cannot see is not one you can change. + + A launch refused over its virtual display now reports the problem beside the **Virtual display** checkbox it belongs to, not under Save as preset; switch the checkbox off and the line moves back to the form's own message, so the reason is never both invisible and orphaned. A refusal the server raises itself now carries the node's own word for the same failure, so it lands in the same place whoever noticed it. On the machine, the display allocation says which readiness gate stayed shut (the Xvfb socket, the VNC port, or the listener's owner), logs what Xvfb and x11vnc printed before cleaning up, and gives a cold box 10 seconds instead of 5. + + The preview icon in a pane's header now wears a count when the pane has things you could open but have not saved: discovered app ports with no Saved services row, plus the screen and the browser watch when the pane offers them. It is the picker's own data, so the number and the list cannot disagree, and "you could" is read as the reader's own access: someone holding view sees the port and screen rows but cannot act on them, each disabled control saying why, so their badge counts only the browser watch and never numbers a control that answers "needs edit access". + + The desktop apps' **Open Display Helper settings** control works: the helper's signature check handed `codesign` a bare requirement string, which it read as a file name, so the panel failed to open on every press. (The module is macOS-only, so no Linux lane even builds it, and the macOS lanes that do only fail at runtime on a machine with a real bundle: nothing in CI opens the helper's panel, so no lane ever pressed the path that checks a signature or reads a preference.) Pressing it then opened nothing either: the helper read its own preferences through a `UserDefaults` suite named after its bundle identifier, which Foundation refuses, so the process trapped before any window existed. Both are fixed, and a helper that cannot find its panel now says so instead of showing an empty window. + + The panel itself is now the same product as the rest of Subshell, built from the components a person already knows in the dashboard: a card plate with its border and radius, `Fact` rows (a strong muted label above a readable value) for the login item and the two macOS permissions, and a form-section heading with its trailing rule for each group. Gold marks what you can still fix and plain ink what is settled, red what macOS refused, a live line names how many people are watching this Mac's screens, help sits at the bottom left and the exits at the bottom right in one shape, and a disabled control's reason is drawn in gold rather than hidden where a person who cannot hover will never find it. It is a small local page bundled into the helper, so the design system's own checker guards it, and the palette it draws on moved to `packages/assistant/tokens.css`, which every CSS surface imports rather than copies: the dashboard, both assistants and the helper's panel now read one file, while the node's own dashboard and the mobile app still keep a hand-maintained copy of the same values. The menu bar's menu now says it belongs to the Subshell virtual display helper instead of leaving you to guess from an icon, its **Minimize helper** item follows whether the panel is actually on screen, and the consent item turns itself into **Enable helper** once you disable the helper, raising the panel so you can read what you agreed to. It used to offer only Disable, greyed out once consent was off, which left the settings window as the only way back to running. + + **Quit helper** asks first, from the panel and from the menu bar's menu alike, and the question answers the part the button never said: watching stops now, your consent and the macOS permissions stay, and the helper + says what it will actually do next: it returns at sign-in only if you registered + it there, and the panel's **Stop at sign-in** button, not disabling, is what + takes it off that list. Quitting also stopped revoking your consent, which it had been doing silently along with unregistering the login item: an app's Close button destroying the very permission the panel exists to collect, so every reopen started from Disabled. + + Enabling the helper no longer adds it to macOS's sign-in list, and disabling no longer takes it off. **Start at sign-in** is its own row with its own button, and the row says which of the three states macOS actually reports, registered, waiting for your approval, or not registered at all. Reading the last two as "approved" is what let a turned-off sign-in start keep claiming it starts at sign-in. The panel re-reads the machine every five seconds while it is open, in every run-loop mode and with App Nap held off, so a switch you flip in System Settings changes what you see without reopening anything. + + Discarding the last virtual display on a Mac also hands the pointer back. A viewer driving that screen leaves the real cursor on a display that no longer exists, which looked like a vanished cursor to whoever was sitting at the machine. A virtual screen can outlive the helper, so the rescue runs on any helper start as well as on teardown: whoever opens the panel next finds their cursor back where they can see it. + +- [#465](https://github.com/subshell-ai/subshell/pull/465) [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d) Thanks [@theogravity](https://github.com/theogravity)! - Add a virtual-display section to the website with a screenshot and Linux and macOS setup links. Refresh the display screenshot in the guide, and show SSH owner-only restrictions in gold like other node settings. + + Refresh the website-preview recording with a neutral demo path. + + Keep the phone demo at the same size when switching to the wider website layout. + ## 3.0.2 ### Patch Changes diff --git a/apps/server/api/package.json b/apps/server/api/package.json index 011b937b3..b8ac7c5d8 100644 --- a/apps/server/api/package.json +++ b/apps/server/api/package.json @@ -1,7 +1,7 @@ { "name": "@internal/server", "description": "", - "version": "3.0.2", + "version": "3.0.3", "license": "AGPL-3.0-only", "type": "module", "private": true, diff --git a/apps/server/desktop/CHANGELOG.md b/apps/server/desktop/CHANGELOG.md index 3ecd2e46e..f5ac6b0fe 100644 --- a/apps/server/desktop/CHANGELOG.md +++ b/apps/server/desktop/CHANGELOG.md @@ -1,5 +1,62 @@ # @internal/desktop-server +## 1.7.0 + +### Minor Changes + +- [#465](https://github.com/subshell-ai/subshell/pull/465) [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d) Thanks [@theogravity](https://github.com/theogravity)! - Add a **Virtual Displays** section to both desktop assistants' sidebar, shown only where a display helper exists, so the helper is reachable from the window a person is already looking at instead of only from the macOS menu bar. The screen carries the steps the menu item never showed (enable the helper, grant Screen Recording and Accessibility to Subshell Display Helper, and what to do when macOS asks for a restart) above one button that opens the helper's own settings panel and a link to the setup guide. Document the sidebar as the way in. + + In the web dashboard, a node that cannot serve previews now says so in gold when the reason is that both of its preview switches are off, which an owner can fix, with a link to the guide that says how; an offline or outdated node stays muted, because no setting fixes that. A launch form, a preset, and the provider list no longer offer a virtual display provider the selected machine's operating system cannot run, and once the server knows the machine's OS it refuses one before the subshell row, its token, or a launch command exists, so a Mac is not asked to start Xvfb. A machine whose OS has not answered is not refused on that basis. Both shapes of no-answer count (no value at all, and an agent on a platform it cannot classify reporting the protocol's own `unknown`), because neither is a verdict: the list keeps what it held and the saved choice stays. Filtering on a non-answer would empty the provider card of a node that enrolled and never reported, which is the owner's only way to enable anything there, and would hide what a preset is about to launch. It is not a promise that such a machine can run the provider: the node answers that itself and refuses Xvfb on any non-Linux platform with `preview-display-unsupported`, so the server's check is a fast no rather than the only no. A provider the node has not enabled now stays named in its picker instead of hiding behind a trigger reading "Node default", since the line beside that row tells you to choose another provider and a choice you cannot see is not one you can change. + + A launch refused over its virtual display now reports the problem beside the **Virtual display** checkbox it belongs to, not under Save as preset; switch the checkbox off and the line moves back to the form's own message, so the reason is never both invisible and orphaned. A refusal the server raises itself now carries the node's own word for the same failure, so it lands in the same place whoever noticed it. On the machine, the display allocation says which readiness gate stayed shut (the Xvfb socket, the VNC port, or the listener's owner), logs what Xvfb and x11vnc printed before cleaning up, and gives a cold box 10 seconds instead of 5. + + The preview icon in a pane's header now wears a count when the pane has things you could open but have not saved: discovered app ports with no Saved services row, plus the screen and the browser watch when the pane offers them. It is the picker's own data, so the number and the list cannot disagree, and "you could" is read as the reader's own access: someone holding view sees the port and screen rows but cannot act on them, each disabled control saying why, so their badge counts only the browser watch and never numbers a control that answers "needs edit access". + + The desktop apps' **Open Display Helper settings** control works: the helper's signature check handed `codesign` a bare requirement string, which it read as a file name, so the panel failed to open on every press. (The module is macOS-only, so no Linux lane even builds it, and the macOS lanes that do only fail at runtime on a machine with a real bundle: nothing in CI opens the helper's panel, so no lane ever pressed the path that checks a signature or reads a preference.) Pressing it then opened nothing either: the helper read its own preferences through a `UserDefaults` suite named after its bundle identifier, which Foundation refuses, so the process trapped before any window existed. Both are fixed, and a helper that cannot find its panel now says so instead of showing an empty window. + + The panel itself is now the same product as the rest of Subshell, built from the components a person already knows in the dashboard: a card plate with its border and radius, `Fact` rows (a strong muted label above a readable value) for the login item and the two macOS permissions, and a form-section heading with its trailing rule for each group. Gold marks what you can still fix and plain ink what is settled, red what macOS refused, a live line names how many people are watching this Mac's screens, help sits at the bottom left and the exits at the bottom right in one shape, and a disabled control's reason is drawn in gold rather than hidden where a person who cannot hover will never find it. It is a small local page bundled into the helper, so the design system's own checker guards it, and the palette it draws on moved to `packages/assistant/tokens.css`, which every CSS surface imports rather than copies: the dashboard, both assistants and the helper's panel now read one file, while the node's own dashboard and the mobile app still keep a hand-maintained copy of the same values. The menu bar's menu now says it belongs to the Subshell virtual display helper instead of leaving you to guess from an icon, its **Minimize helper** item follows whether the panel is actually on screen, and the consent item turns itself into **Enable helper** once you disable the helper, raising the panel so you can read what you agreed to. It used to offer only Disable, greyed out once consent was off, which left the settings window as the only way back to running. + + **Quit helper** asks first, from the panel and from the menu bar's menu alike, and the question answers the part the button never said: watching stops now, your consent and the macOS permissions stay, and the helper + says what it will actually do next: it returns at sign-in only if you registered + it there, and the panel's **Stop at sign-in** button, not disabling, is what + takes it off that list. Quitting also stopped revoking your consent, which it had been doing silently along with unregistering the login item: an app's Close button destroying the very permission the panel exists to collect, so every reopen started from Disabled. + + Enabling the helper no longer adds it to macOS's sign-in list, and disabling no longer takes it off. **Start at sign-in** is its own row with its own button, and the row says which of the three states macOS actually reports, registered, waiting for your approval, or not registered at all. Reading the last two as "approved" is what let a turned-off sign-in start keep claiming it starts at sign-in. The panel re-reads the machine every five seconds while it is open, in every run-loop mode and with App Nap held off, so a switch you flip in System Settings changes what you see without reopening anything. + + Discarding the last virtual display on a Mac also hands the pointer back. A viewer driving that screen leaves the real cursor on a display that no longer exists, which looked like a vanished cursor to whoever was sitting at the machine. A virtual screen can outlive the helper, so the rescue runs on any helper start as well as on teardown: whoever opens the panel next finds their cursor back where they can see it. + +### Patch Changes + +- [#464](https://github.com/subshell-ai/subshell/pull/464) [`4fd0811`](https://github.com/subshell-ai/subshell/commit/4fd0811225e58ffce46678770d2ff32af920b6a3) Thanks [@theogravity](https://github.com/theogravity)! - Fix long prompts remaining unsubmitted in Codex subshells. Automated input now waits for the end of the prompt to render and allows the paste parser to settle before pressing Enter, including prompts sent by another subshell. Concurrent automated sends stay separate, and repeated prompt text no longer triggers submission before the full burst renders. + +- [#465](https://github.com/subshell-ai/subshell/pull/465) [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d) Thanks [@theogravity](https://github.com/theogravity)! - Clarify virtual display setup and helper consent, permissions, sign-in, and quit messages. Keep shared-desktop warnings visible and explain when viewers disconnect while subshells keep running. Bump the bundled helper to 1.0.1 so its update is identified consistently across both desktop apps. + +- [#465](https://github.com/subshell-ai/subshell/pull/465) [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d) Thanks [@theogravity](https://github.com/theogravity)! - Verify the installed display helper before opening it even when an update is blocked by another running helper. Preserve executable permissions when staging the helper, harden its bundled page's HTML escaping and native message boundary, and keep its state and menu updates consistent. + + Recover terminal grid changes that happen during the reconnect cooldown. Let native text selection reach the terminal rows in copy mode, and prevent input focus from clearing a new selection. + +- [#465](https://github.com/subshell-ai/subshell/pull/465) [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d) Thanks [@theogravity](https://github.com/theogravity)! - Keep preview redirects inside their proxy, preserve malformed app scripts for browser diagnostics, and bound concurrent rewriting by decoded bytes. Fix preview preferences on display-only nodes, reconnect grid tracking, launch-dialog cleanup, and node-settings links. Recover stalled capture writers between output bursts and rescue the Mac pointer after capture loss. Keep helper confirmations stable across state updates and clear them when settings reopen. Verify shared palette imports and refresh the preview documentation screenshots. + + Reserve terminal input capacity per node so an offline machine cannot consume the fleet's budget, give a keystroke on a live pane the capacity a stuck retry was holding before disconnecting anyone, and drop held input immediately during forced viewer recovery. Prune obsolete helper staging copies after a successful install while preserving recovery data on failure. Dismiss the helper's quit confirmation when Confirm is pressed, even where the native bridge is absent. + +- [#465](https://github.com/subshell-ai/subshell/pull/465) [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d) Thanks [@theogravity](https://github.com/theogravity)! - Fix web preview asset paths and common development-server requests, including Next.js and Vite hot reload. Add personal proxy settings remembered for each app on a node, with controls for URL rewriting, the Host header, and redirects. + + Show the setup guide when either web previews or virtual displays is disabled, including when no views are discovered. + +- [#465](https://github.com/subshell-ai/subshell/pull/465) [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d) Thanks [@theogravity](https://github.com/theogravity)! - Keep SSH setup keys out of subprocess arguments, require stdin-capable connecting nodes, and refuse older downloaded node binaries before passing credentials. Bound retained terminal resize work during sustained updates and document the SSH security bulletin review. + +- [#465](https://github.com/subshell-ai/subshell/pull/465) [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d) Thanks [@theogravity](https://github.com/theogravity)! - Bound terminal WebSocket input by frame count and UTF-8 payload bytes per connection and across the server, including dispatched writes and retries. Close connections that exceed the limits and prevent late failures from recreating retry queues after disconnect. Document the limits and recovery behavior. + +- [#465](https://github.com/subshell-ai/subshell/pull/465) [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d) Thanks [@theogravity](https://github.com/theogravity)! - Revoke cached terminal access when subshell shares change, including connections still attaching and queued input retries. Recipients reconnect through the current access checks; the owner's terminal stays connected. Document the cutoff and the limits for commands already dispatched. + +- [#465](https://github.com/subshell-ai/subshell/pull/465) [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d) Thanks [@theogravity](https://github.com/theogravity)! - Add a virtual-display section to the website with a screenshot and Linux and macOS setup links. Refresh the display screenshot in the guide, and show SSH owner-only restrictions in gold like other node settings. + + Refresh the website-preview recording with a neutral demo path. + + Keep the phone demo at the same size when switching to the wider website layout. +- Updated dependencies [[`8a501b4`](https://github.com/subshell-ai/subshell/commit/8a501b45b22bca0b909e0d08c1b5339368f59d3f), [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d), [`69f8ff6`](https://github.com/subshell-ai/subshell/commit/69f8ff6ca67f9c916b509288bce3c5b081bb2d48), [`4fd0811`](https://github.com/subshell-ai/subshell/commit/4fd0811225e58ffce46678770d2ff32af920b6a3), [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d), [`bfb4e72`](https://github.com/subshell-ai/subshell/commit/bfb4e7228bf4f2eab774dbb6dc7f6a6179113366), [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d), [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d), [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d), [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d), [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d), [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d), [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d), [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d), [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d), [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d), [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d)]: + - @internal/server@3.0.3 + ## 1.6.2 ### Patch Changes diff --git a/apps/server/desktop/package.json b/apps/server/desktop/package.json index dff2e2f35..2923e13e6 100644 --- a/apps/server/desktop/package.json +++ b/apps/server/desktop/package.json @@ -1,7 +1,7 @@ { "name": "@internal/desktop-server", "description": "Subshell Server — a Tauri shell that installs, runs and manages a local subshell-server", - "version": "1.6.2", + "version": "1.7.0", "license": "AGPL-3.0-only", "type": "module", "private": true, diff --git a/apps/website/CHANGELOG.md b/apps/website/CHANGELOG.md index 20ca3a38f..a3fda1c0e 100644 --- a/apps/website/CHANGELOG.md +++ b/apps/website/CHANGELOG.md @@ -1,5 +1,15 @@ # @internal/website +## 0.2.20 + +### Patch Changes + +- [#465](https://github.com/subshell-ai/subshell/pull/465) [`dbfb32e`](https://github.com/subshell-ai/subshell/commit/dbfb32eff2e2ae6b5233d8cfe6b1cfd68a58443d) Thanks [@theogravity](https://github.com/theogravity)! - Add a virtual-display section to the website with a screenshot and Linux and macOS setup links. Refresh the display screenshot in the guide, and show SSH owner-only restrictions in gold like other node settings. + + Refresh the website-preview recording with a neutral demo path. + + Keep the phone demo at the same size when switching to the wider website layout. + ## 0.2.19 ### Patch Changes diff --git a/apps/website/package.json b/apps/website/package.json index 0071b735f..839f90413 100644 --- a/apps/website/package.json +++ b/apps/website/package.json @@ -1,7 +1,7 @@ { "name": "@internal/website", "description": "Public marketing site for Subshell: static export at subshell.sh", - "version": "0.2.19", + "version": "0.2.20", "license": "Apache-2.0", "type": "module", "private": true, diff --git a/bun.lock b/bun.lock index e4fdddb45..e214c4710 100644 --- a/bun.lock +++ b/bun.lock @@ -31,7 +31,7 @@ }, "apps/client/desktop": { "name": "@internal/desktop-client", - "version": "1.4.0", + "version": "1.5.0", "dependencies": { "@base-ui/react": "1.7.0", "@internal/assistant": "workspace:*", @@ -105,7 +105,7 @@ }, "apps/docs": { "name": "@internal/docs", - "version": "0.11.1", + "version": "0.11.2", "dependencies": { "beautiful-mermaid": "1.1.3", "fumadocs-core": "16.15.11", @@ -128,7 +128,7 @@ }, "apps/node/agent": { "name": "@internal/node", - "version": "3.0.0", + "version": "3.0.1", "dependencies": { "@clack/prompts": "1.8.1", "@internal/backend-errors": "workspace:*", @@ -181,7 +181,7 @@ }, "apps/server/api": { "name": "@internal/server", - "version": "3.0.2", + "version": "3.0.3", "dependencies": { "@better-auth/api-key": "1.7.14", "@better-auth/passkey": "1.7.14", @@ -231,7 +231,7 @@ }, "apps/server/desktop": { "name": "@internal/desktop-server", - "version": "1.6.2", + "version": "1.7.0", "dependencies": { "@base-ui/react": "1.7.0", "@internal/assistant": "workspace:*", @@ -322,7 +322,7 @@ }, "apps/website": { "name": "@internal/website", - "version": "0.2.19", + "version": "0.2.20", "dependencies": { "@internal/subshell-protocol": "workspace:*", "class-variance-authority": "0.7.1",