mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 00:57:45 +00:00
8f83f6b076e2355096440407d404c3640d608f09
1256
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
8f83f6b076 |
feat(wsl-mover): pause a moving project's terminals through Sleep and prove they exited (D1b-5d) (#28820)
* feat(wsl-mover): pause a moving project's terminals through Sleep and prove they exited (D1b-5d) * fix(wsl-mover): gate again under the fence, swap journal phases atomically, keep sessions frozen through commit (D1b-5d) * test(wsl-mover): the fence e2e walks the journal one phase at a time, as a move writes it (D1b-5d) |
||
|
|
b0308f24ef |
feat(wsl-mover): refuse edits to a project a WSL move is taking (D1b-5c-2) (#28761)
* feat(wsl-mover): refuse edits to a project a WSL move is taking, and drop its background bookkeeping quietly (D1b-5c-2) * fix(wsl-mover): hide only local rows of a moved project; the host's own row keeps its id (D1b-5c-2) * fix(wsl-mover): refuse a user's unread toggle, fence branch deletes, host setups and issue commands; settings unfreeze once the host owns a repo (D1b-5c-2) * test(wsl-mover): the fence e2e refuses edits to moving projects and drops their bookkeeping quietly (D1b-5c-2) * refactor(wsl-mover): name the update type the fence inspects (D1b-5c-2) * test(wsl-mover): run the fence tests that open a real store in the Node runtime (D1b-5c-2) * fix(wsl-mover): a host setup's own repo stays frozen once the host owns it; e2e pins the quiet drop (D1b-5c-2) |
||
|
|
dfafde6a7f |
test(cross-version): pin the ephemeral-key and source-fence downgrade tests to v1.4.224 (#28803)
v1.4.225 ships both capabilities, so the newest release no longer models an old host. Pin each to the last release without its capability, as the other downgrade tests in the suite already do. |
||
|
|
b85597cbfa |
fix(serve): restore a remote server's browser tabs after it restarts (#28166)
* feat(serve): keep browser tabs across an orca serve restart A host with no window now saves the browser tabs it opens in the same shape a desktop window saves its own, and restores them the same way: listed at once with their last address, each page started when a client first opens or drives it. A closed tab is forgotten; a page that ends with the process is not a close. * test(e2e): the server's published strip now holds its saved browser tab * fix(serve): save browser addresses cheaply, open once, restore by page id Review fixes: a page's address is written onto its saved records without reloading the layout, and only on committed navigations; reopening a saved page writes nothing; a command naming only the page finds the workspace that saved it; a refused save no longer throws from a page event or a close. The browser-tab map is renamed to say it lists saved tabs too. * fix(serve): list, switch and close saved browser tabs from commands that name no page or worktree * fix(serve): a tab list does not start saved pages; only a command acting on a page does * fix(serve): a pageless command starts the browser tab the layout shows, not the first saved one A host now records the browser tab a window would show: the one it just opened, and the one a tab switch picks. With nothing running, a command that names no page starts that tab; when the layout shows none it starts nothing and reports no browser tab open. * fix(serve): report the saved active browser tab as current Claude-Session: p2-headless * chore(ratchet): count the serve browser-tab save as a main-process layout writer Claude-Session: p2-headless * fix(serve): use the window's active browser tab rule, one scope, one active row Claude-Session: p2-headless * fix(browser): pick the active tab among tabs this host can start A desktop document page or a remote page recorded as active no longer leaves a pageless command with no tab. Adds a test for the first-saved-worktree scope. Claude-Session: p2-headless * refactor(browser): restore saved serve tabs through the page wake point Saved serve browser tabs now start where a window would mount them: the two existing wake helpers ask one host hook before messaging a window. The offscreen backend reports opened and moved pages, so the command layer no longer threads save, list, select or close hooks. No worktree named and no page running reports no tab, as a window does. Claude-Session: p2-headless * fix(browser): only page creation opens a saved serve tab A navigation now only updates an existing saved tab, so a late load after a close cannot re-add it; a restored page sends no open, so its saved title stays. Claude-Session: p2-headless * refactor(browser): drop the restore mark the open/navigate split made redundant Claude-Session: p2-headless * refactor(browser): save serve tabs from the create command, list unstarted ones as tab rows The create command saves the tab (it holds the group), so the backend reports only navigations. Unstarted saved tabs are tab-info rows the existing listings take as-is, the restore dedupe map goes (a created page registers before createTab returns), and one lookup gives a page's unified tab id. Claude-Session: p2-headless * fix(browser): opening a serve browser tab no longer records it as active Which tab shows is a view's choice; after a restart the active tab is the one a window recorded, else the first. Claude-Session: p2-headless * fix(browser): give the composed browser command owners the saved-tab host member Completes the forward merge of main's browser command split (#27576). Claude-Session: p2-headless |
||
|
|
3e25fe70f6 |
fix(unread): a pane is read when it is on screen; closing a waiting tab clears its dot (#28637)
* test(unread): characterize today's workspace and pane unread transitions Pins, against the real store, what every unread trigger does today: the workspace flag per host row, the three tab/pane marker maps, persistence calls and payloads, lastActivityAt and sortEpoch bumps, the Dock count and the floating launcher dot. Events are replayed through one driver file so a later rewiring of who writes the state changes only the driver, never these expectations. * refactor(unread): one workspace-attention module owns every unread transition Every write to the workspace unread flag and the tab/pane/completion markers now goes through src/renderer/src/store/slices/workspace-attention: store operations named by the triggering event (bell, agent attention, pane input, viewed-agent auto-ack, manual mark read/unread) and pure transitions folded into other slices' set() calls (activation, tab activation, close, sleep, hibernate, retire, rekey, reconcile, mirror removal, purge, acknowledge). Behaviour, storage, readers, persistence and wire format are unchanged; the characterization suites pass without edits to their expectations. * refactor(unread): review fixes for the workspace-attention module - Ratchets: moved terminal-tab-attention baseline entries to terminal-tab-appearance; split the characterization setup/observables into an exempt fixture so the driver holds only events. - Removed the test-only writer seam; mocked-store tests assert the event they fire. - The slice owns the experimental pane gate for completions too; groupAttentionEnabled is gone from the attention request and write. - Collapsed marker removals to removeTabsAttention / removePanesAttention / removeCompletionAttention / removeTabMarkers / movePaneAttention; tab activation uses omitRecordKeys. - Mark read/unread is passed in like other store actions; readers of the workspace dot go through isWorkspaceUnread. * feat(unread): on screen means read; the workspace flag follows its markers While Orca's window is visible and focused, the tabs on screen (active tab of the active workspace, plus an open floating panel) are read: every tab, pane and completion marker of each. One scan (the auto-ack hook, now startViewedAttentionScan) replaces the keydown, pointerdown, tab activation, group focus, workspace activation and auto-ack marker clears. After a read, close, sleep, hibernate, retire or purge that removed a marker, a workspace's flag is cleared once no tab or completion marker remains on a live tab; a marker-less flag (Mark as unread, restart, another client) is read when its workspace comes on screen. Mark as read removes every marker in the workspace. Reads and removals dismiss the affected panes' OS notifications in one IPC call. * fix(unread): a bell or completion already on screen leaves no marker or flag write Marking now asks the scan's own on-screen rule (isTabOnScreen: the window is visible and focused and the tab is a scan target), so an on-screen bell no longer persists the flag true then false. A completion in any split pane of the tab on screen is viewed, matching the read. A superseded turn's completion-marker removal settles the flag and dismisses like other removals. * fix(unread): read the screen across hosts, groups and non-agent views; mark only for an absent user - Arriving at a workspace in its own view settles its flag even when it shows an editor, browser or nothing; Settings and other views read nothing. - A bell or completion on screen skips marking only when the user is confirmed present under the scan's own presence gate (input, or main reporting the desktop active within the shared away threshold); otherwise it marks and the gated scan reads it when the user returns. - The active workspace's host is part of the screen, so switching same-id folder rows between hosts is an arrival on the new host. - Removal settles lower every same-id folder row and persist each host. - Opening and closing the floating panel both rescan. - Every visible tab group's active tab is on screen, not only the focused one. - One pane-key parser, one marker-tab enumeration, one removal wrapper (settleAttentionRemovedBy), one remove-and-dismiss; the hook is now useReadOnScreenAttention over on-screen-attention-scan.ts, and terminal keystrokes reach presence through a capture-phase window keydown. * fix(unread): presence only from renderer input; host-exact folder flags; subjectless completions follow the screen - Main's 'not away' answers only the scan that asked; presence is stamped only by trusted input (wheel included), so it can't outlive the threshold. - isWorkspaceViewed uses the on-screen rule (workspace view showing, focused window, user present), so a subjectless completion marks under Settings or while away. - Folder flag writes resolve the row's host like updateFolderWorkspace does (project-group fallback), so the row flipped and the owner persisted match. - A runtime host purge dismisses but doesn't settle flags of surviving rows. - DESKTOP_AWAY_SECONDS is the shared threshold's neutral name. * test(unread): folder flag persists name the row's host * test(unread): drop casts from the folder persistence seeds * fix(unread): point the reliability gate and e2e fixture at the renamed on-screen hook * refactor(unread): drop reshapes the follow-up deletes; keep the flag functions module-internal Reverts the reconcile/mirror/activation/focus/group call sites and the isWorkspaceUnread selector to main, removes removeTabMarkers, removeCompletionAttention and the activation read helpers, and drives tests through public attention actions. * test(unread): read the Dock badge flag down by putting its tab on screen * fix(unread): keep the on-input read action removed after merging main |
||
|
|
93baa828be |
refactor(unread): one workspace-attention module owns every unread transition (#28538)
* test(unread): characterize today's workspace and pane unread transitions Pins, against the real store, what every unread trigger does today: the workspace flag per host row, the three tab/pane marker maps, persistence calls and payloads, lastActivityAt and sortEpoch bumps, the Dock count and the floating launcher dot. Events are replayed through one driver file so a later rewiring of who writes the state changes only the driver, never these expectations. * refactor(unread): one workspace-attention module owns every unread transition Every write to the workspace unread flag and the tab/pane/completion markers now goes through src/renderer/src/store/slices/workspace-attention: store operations named by the triggering event (bell, agent attention, pane input, viewed-agent auto-ack, manual mark read/unread) and pure transitions folded into other slices' set() calls (activation, tab activation, close, sleep, hibernate, retire, rekey, reconcile, mirror removal, purge, acknowledge). Behaviour, storage, readers, persistence and wire format are unchanged; the characterization suites pass without edits to their expectations. * refactor(unread): review fixes for the workspace-attention module - Ratchets: moved terminal-tab-attention baseline entries to terminal-tab-appearance; split the characterization setup/observables into an exempt fixture so the driver holds only events. - Removed the test-only writer seam; mocked-store tests assert the event they fire. - The slice owns the experimental pane gate for completions too; groupAttentionEnabled is gone from the attention request and write. - Collapsed marker removals to removeTabsAttention / removePanesAttention / removeCompletionAttention / removeTabMarkers / movePaneAttention; tab activation uses omitRecordKeys. - Mark read/unread is passed in like other store actions; readers of the workspace dot go through isWorkspaceUnread. * refactor(unread): drop reshapes the follow-up deletes; keep the flag functions module-internal Reverts the reconcile/mirror/activation/focus/group call sites and the isWorkspaceUnread selector to main, removes removeTabMarkers, removeCompletionAttention and the activation read helpers, and drives tests through public attention actions. |
||
|
|
dc146c09e2 |
fix(ssh): the CLI, phone and paired clients no longer list a converted host's retained rows (#28754)
* fix(ssh): the runtime lists a converted host's retained rows as hidden, as the desktop does * fix(ssh): never name a converted host as uncovered in runtime worktree listings * fix(ssh): a scoped worktree listing never names a hidden host either |
||
|
|
3879c6d96f |
feat(wsl-mover): fence moving projects from new terminals and hide them once the host owns them (D1b-5c) (#28668)
* feat(wsl-mover): a WSL move fences its projects from new terminals and hides them once the host owns them (D1b-5c) * fix(wsl-mover): hide owned rows from every list, follow respelled profile paths, let a live pane reattach (D1b-5c) * test(wsl-mover): fake stores without type assertions (D1b-5c) |
||
|
|
4731172bd8 |
feat(jira): choose planning columns in the issue list (#28615)
Choose and sort Jira planning columns while bounding optional field discovery, preserving complete Cloud searches and aligning copy/workspace actions in responsive rows. Co-authored-by: Dagan <idan.d@imagen-ai.com> |
||
|
|
b3938e6f37 |
fix(jira): adapt issue list columns to panel width (#28583)
Adapt Jira rows and sort headers to their panel width so titles remain readable in narrow side panels. Refs #16627. Co-authored-by: Leopold Tripot <leopold.tripot@gmail.com> |
||
|
|
9c0420fa9a |
fix(browser): agent clicks keep the user's keyboard focus (#28454)
Any press on a <webview> guest moves Chromium focus into it, so an agent click took the user's terminal focus (#20595). Main now asks the hosting pane to guard focus for each agent press (Orca's click/mouse down and the CDP proxy) and the pane puts it back inside the webview's focus event, and again when the press ends. Offscreen pages keep their native focus. |
||
|
|
09681f02ba |
test(native-chat): replay recorded Linux Sogou commits into the chat composer (#28610)
* test(native-chat): replay recorded Linux Sogou commits into the chat composer Guards #26722: keyup-only pinyin, plain Space/digit selector picks (GTK IM and XIM shapes), Process/229 phrase picks, and a client-preedit commit all land in the composer. The recorded fcitx4 shapes do not reproduce the issue. * test(native-chat): read the composer event log through a typed dataset value |
||
|
|
4a0541aff1 |
Stop orphaned background browsers from blocking Orca updates (#28562)
* Recover orphan browser processes and clarify blocked updates * Use isolated Electron launch flags for sidecar lifetime tests * Keep update retries fenced while describing blockers * Keep update translations and context clock valid on current main * Use the option context clock restored on main * fix(updater): address status guidance and test launch reviews |
||
|
|
207b220d9c |
fix(browser): agent keys and text go straight to the page, never into the user's focus (#28453)
Main-session CDP key and text input on a <webview> guest is routed by the window's focus, so it landed in the user's terminal (#20595). Embedded guests now get widget key/char events; child-frame focus and untypeable text keep the CDP path after focusing the guest. |
||
|
|
b428def8f1 |
feat(cli): open files at a requested line and column (#28240)
Accept positive safe-integer line and column flags through the existing file-open path. Reject invalid or incomplete positions before changing the editor. Bind deferred reveals to the requested workspace and tab, including floating panels, and clamp oversized positions without moving another editor or stealing late focus. Co-authored-by: Nikita Maketryuk <n.maketryuk@lince.studio> |
||
|
|
75513668fb |
fix(mobile): check a server workspace's SSH state on that server (#26519)
* test(e2e): add a paired phone client helper and phone-mirror baseline specs Later phone-mirror slices need one oracle that drives a real mobile-scoped device with the phone's own capabilities against a serve host or a paired desktop. Pins #26022 across a serve restart and today's gap: a desktop's phone does not list the desktop's server workspaces. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * feat(mobile): show SSH host health on the workspace row host badge Rows on an SSH host the desktop cannot reach looked healthy on the phone. The badge now adds the desktop sidebar's own word ("devbox · Disconnected"), derived by the same shared function from the connection status ssh.listTargetSummaries already sends; old desktops keep today's badge. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(e2e): compose the phone-mirror topology and check the cold snapshot Slices need one composer for host, desktop and phone with a single teardown, so specs stop copying setup. The serve-restart spec now reads the first tab-stream snapshot after the cold start, which is what the phone builds its tab strip from. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * feat(mobile): name an unhealthy SSH host even in a single-host list The desktop card shows SSH status on any host count, so the phone now reads target summaries whenever a repo is on SSH and badges an unhealthy host alone too. Labels and health share one target-to-host keying, and a hook-level test pins the reply-to-row wiring. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(e2e): keep phone-mirror teardown errors from masking test failures Teardown failures become a test attachment instead of replacing the real failure, and the cold-snapshot poll reports stream errors together with the PTY list. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): clear stale SSH host badges and announce them without the dot A catalog that no longer spans hosts or holds an SSH repo now clears the health and label maps, so a host that left cannot keep badging rows. The badge gets a screen-reader label ("devbox, Disconnected") instead of reading the middle dot. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * feat(runtime): let a paired desktop register its phones as delegated mobile devices A desktop paired to this runtime can now call pairing.delegatedMobileDevice.sync to make each of its phones an ordinary mobile-scope device it owns, so the phone mirrors that host through the desktop. Children are never pending, inherit the parent's reach, and die with the parent's grant. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(runtime): move the mobile device revoke primitive below dispatch Dispatch binds the caller-bound delegated-device context, so the revoke path it needs now lives in a layer under it as a pure move; this drops the abstract method seam. No behaviour change. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(runtime): keep a runtime grant until its delegated phones are revoked A failed child cleanup now leaves the parent in place so a retry can still find the children, and revokeRuntimeAccess lives beside the revoke it cascades to. Adds restart, rename and bounds coverage, shares credential minting, and drops the timing-dependent census. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(runtime): fail a delegated sync whose dropped phone cannot be revoked Matches the parent revoke: a child whose cleanup could not be saved is kept and the sync errors before minting anything, so the desktop retries instead of believing the server matches its list. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * feat(mobile): relay a paired phone's server-workspace requests through the desktop A phone mirrors its desktop, including workspaces on the desktop's configured servers. The desktop now forwards a request that names `executionHost: runtime:<env>` to that server over one socket per (phone, server), signed in as the phone's delegated device; replies pass through untouched except stream ids. Hidden until the phone client ships: the relay capability is not advertised yet. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): type the single-host health fixtures inline so the test tsc passes Fixtures hoisted into consts widened their health maps to string, which the now-required hostHealthById rejects under the tests-typecheck ratchet; passing them inline keeps the union. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * docs(mobile): state the desktop relay contract in its entry module main dropped docs/reference, so the relay's contract lives beside the code. Also pins that a server whose delegated sync fails (e.g. delegated_device_revoke_failed) is unavailable until a later request syncs it again. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): pin the relay's configured-server adapter The production hosts adapter was only typechecked; this pins that it resolves configured servers by exact id only (never a name or a removed server), fences by pairing revision and runtime identity, and stops reporting retirements once unsubscribed. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): re-record the repo-metadata goldens for SSH host health The settled metadata state now carries hostHealthById, and an SSH-only catalog now reads the target summaries so a lone unhealthy host can be badged. Scoped re-record via rpc-recording.mts. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(mobile): tighten the desktop relay after elegance review Reuses the shared E2EE socket opener, folds relay state into fewer fields, never sends a request whose link ended while opening, passes failure codes through, relays only this desktop's own phones, and drops the unused host-state reader until S2 needs one. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): bound relay frames and stop sticky relay verdicts Restores the 8 MiB frame cap on every remote runtime socket, re-checks an update-needed server on each new link so an in-place upgrade relays, answers a phone the server just declined without syncing again, and ends a link whose mirrored capability update cannot be queued. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): run targeted desktop-owned calls on the desktop The phone tags every call on a workspace screen with its execution host, so a paired-desktop method carrying `runtime:<env>` now runs locally instead of failing 'forbidden'. The Mac stays the only reader of the routing table. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * feat(mobile): list the desktop's configured servers and their last workspace rows for the phone The phone needs the same host list the desktop shows, including servers that are offline or too old to relay to. mobileRelay.hosts.list/worktrees answer from the desktop's own status and its own worktree.ps fetches, cached per server and fenced by pairing, never from relayed replies. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(mobile): list server rows through one desktop fetch, with no side effect on listing The phone now always lists a server's rows through mobileRelay.hosts.worktrees, so the desktop's own fetch is both the live source and the cache, and stamping stays in main. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): fetch a server's full workspace list within a bound, and forget removed servers The desktop now asks for the phone's full-catalog limit, answers within 5s with the last rows when a server hangs, drops a server's rows when it is removed or re-paired, and checks the server's identity after the fetch so a reply from before a re-pair is never served. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * feat(mobile): name a server workspace's host on the phone's calls A phone reaches a server's workspace through its desktop only if each call names the host; the direct, cloud-relay and shell-bridge transports now carry `executionHost`, streams keep it for replay and unsubscribe, and an older shell (no `executionHost` grant) refuses rather than strips it. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): list managed servers as the desktop sidebar does, and hide rows mid-delete A server Orca manages on an SSH host now lists once under that host's name, or not at all while the host is reached over the SSH relay, using the sidebar's own folding. Rows the server is deleting are left out at reply time, as a phone asking the server itself would see. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): keep a disconnected server's last rows, and never join a re-paired server's fetch Retirement also fires on a manual disconnect or managed-server update, which wrongly emptied the phone's list; rows are now pruned only once a server is no longer configured. Fetches are shared per server identity, so a poll after a re-pair asks the new server right away. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * feat(mobile): list the paired desktop's server workspaces on the phone Fixes the phone missing a desktop's `orca serve` workspaces: rows come from the desktop's mobileRelay.hosts.* with each server's health ("Update needed", "Disconnected"), open through the relay, follow the desktop's hidden hosts, and the desktop now advertises the relay capability. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): pin that a desktop failing the hosts list leaves only its own rows The page asks a relaying desktop for its servers; a refusal such as method_not_found must leave today's list with no server rows and no catalog error. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): mirror the desktop's shown hosts exactly, one host included Jinwoo's call: the phone follows visibleWorkspaceHostIds as the desktop sidebar does, so a one-host list narrows the phone too instead of being read as a single-host focus. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): scope a server workspace at its route, never the desktop Every screen under a server workspace's route now reads one context and gets that server's client and a router that keeps pushes on it, refusing to fall back to the desktop; pins write to the row's owner like the Mac. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): never open an unreachable server workspace, and prove the relay in e2e A server workspace the phone can't reach now gets one explicit screen at its route and no row tap or action-sheet entry opens it; native routes read the route's host, a census pins every workspace route, and the e2e reads files and git on the server. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * feat(mobile): gate a server workspace on that server's own features A targeted status.get now relays to the server, so a server workspace's route provides that server's status to every screen under it and its action sheet, while whether the phone can reach the server stays the desktop's answer. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(ci): run the remote-runtime WebSocket opener test on Node Under the bun vitest project, `ws` resolves to Bun's built-in client, which ignores maxPayload, so the frame-cap test hung. The opener only ships on Node/Electron, so run its test in the Node runtime project like its siblings. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): run a server workspace's SSH calls on that server A phone in a server workspace asks for SSH state before a file mutation, but the Mac answered about its own targets, so server-owned SSH repos failed. The desktop asks the server, so the ssh.* family now routes by target; an e2e pins files, uploads, commits and review on the server. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): keep the server files/git e2e offline and baseline-git safe Review eligibility reached real gh/glab over the network and proved nothing beyond repoSlug; setup git now ignores the runner's config and avoids `init -b` (Git 2.25). The relay integration test is the routing oracle, so the census re-listing goes. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): give the file preview the workspace router, back to 128 page assets The preview was the one workspace screen on the plain router, so the scoped router was reached by a route set of its own and esbuild split it into an extra chunk (129 assets); every workspace screen now uses it and it shares the route's chunk. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(mobile-relay): derive the phone allowlist from the routing table Adding a phone method now takes one edit (its route). A server-targeted call on a legacy non-E2EE socket is refused instead of running locally, and only execution-host methods enter the relay. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(mobile-relay): drop unread freshness fields from server workspace lists The phone reads only the rows, so stale, fetchedAt, totalCount and truncated were dead wire weight. The desktop still serves a server's last rows while the server is unreachable. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(mobile): read only the rows of a server's workspace list The desktop no longer sends a freshness flag, so the phone's schema drops it. The relay capability list names the delegated-devices constant instead of repeating its literal, which also retires a now-circular type check. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): open server-workspace diffs and file taps on the phone's own screens A headless orca serve has no renderer, so files.openDiff / files.open answer renderer_unavailable and the Changes tap showed that raw code. On that refusal the phone now opens its review screen (git.diff) or file preview (files.read) instead. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): show a server workspace as disconnected while its server is down The phone read the desktop's connection state for a server workspace, so a stopped server kept a green dot and "Loading tabs" spun forever. The workspace route now narrows that state by the server's health from mobileRelay.hosts.list, reusing the existing disconnected session UI and refetching tabs when the server returns. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): drop Open in Session and preview new notes in server workspaces A server workspace's host has no editor tabs, so the review screen no longer offers Open in Session there, and a new markdown note whose files.open answers renderer_unavailable opens in the file preview instead of a raw error toast. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(mobile): share one desktop server-list feed between the host list and workspaces A server workspace polled mobileRelay.hosts.list beside the host list's own read, so the phone could ask the desktop twice per tick and the row badge and the screen could disagree. One feed per desktop client now owns that read; both readers join it. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(e2e): read a server's reachability from the hosts list, not a dropped stale flag |
||
|
|
6cdb4b1006 |
test(mobile): prove server browsers reach a paired phone through the desktop (#26521)
* test(e2e): add a paired phone client helper and phone-mirror baseline specs Later phone-mirror slices need one oracle that drives a real mobile-scoped device with the phone's own capabilities against a serve host or a paired desktop. Pins #26022 across a serve restart and today's gap: a desktop's phone does not list the desktop's server workspaces. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * feat(mobile): show SSH host health on the workspace row host badge Rows on an SSH host the desktop cannot reach looked healthy on the phone. The badge now adds the desktop sidebar's own word ("devbox · Disconnected"), derived by the same shared function from the connection status ssh.listTargetSummaries already sends; old desktops keep today's badge. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(e2e): compose the phone-mirror topology and check the cold snapshot Slices need one composer for host, desktop and phone with a single teardown, so specs stop copying setup. The serve-restart spec now reads the first tab-stream snapshot after the cold start, which is what the phone builds its tab strip from. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * feat(mobile): name an unhealthy SSH host even in a single-host list The desktop card shows SSH status on any host count, so the phone now reads target summaries whenever a repo is on SSH and badges an unhealthy host alone too. Labels and health share one target-to-host keying, and a hook-level test pins the reply-to-row wiring. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(e2e): keep phone-mirror teardown errors from masking test failures Teardown failures become a test attachment instead of replacing the real failure, and the cold-snapshot poll reports stream errors together with the PTY list. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): clear stale SSH host badges and announce them without the dot A catalog that no longer spans hosts or holds an SSH repo now clears the health and label maps, so a host that left cannot keep badging rows. The badge gets a screen-reader label ("devbox, Disconnected") instead of reading the middle dot. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * feat(runtime): let a paired desktop register its phones as delegated mobile devices A desktop paired to this runtime can now call pairing.delegatedMobileDevice.sync to make each of its phones an ordinary mobile-scope device it owns, so the phone mirrors that host through the desktop. Children are never pending, inherit the parent's reach, and die with the parent's grant. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(runtime): move the mobile device revoke primitive below dispatch Dispatch binds the caller-bound delegated-device context, so the revoke path it needs now lives in a layer under it as a pure move; this drops the abstract method seam. No behaviour change. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(runtime): keep a runtime grant until its delegated phones are revoked A failed child cleanup now leaves the parent in place so a retry can still find the children, and revokeRuntimeAccess lives beside the revoke it cascades to. Adds restart, rename and bounds coverage, shares credential minting, and drops the timing-dependent census. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(runtime): fail a delegated sync whose dropped phone cannot be revoked Matches the parent revoke: a child whose cleanup could not be saved is kept and the sync errors before minting anything, so the desktop retries instead of believing the server matches its list. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * feat(mobile): relay a paired phone's server-workspace requests through the desktop A phone mirrors its desktop, including workspaces on the desktop's configured servers. The desktop now forwards a request that names `executionHost: runtime:<env>` to that server over one socket per (phone, server), signed in as the phone's delegated device; replies pass through untouched except stream ids. Hidden until the phone client ships: the relay capability is not advertised yet. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): type the single-host health fixtures inline so the test tsc passes Fixtures hoisted into consts widened their health maps to string, which the now-required hostHealthById rejects under the tests-typecheck ratchet; passing them inline keeps the union. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * docs(mobile): state the desktop relay contract in its entry module main dropped docs/reference, so the relay's contract lives beside the code. Also pins that a server whose delegated sync fails (e.g. delegated_device_revoke_failed) is unavailable until a later request syncs it again. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): pin the relay's configured-server adapter The production hosts adapter was only typechecked; this pins that it resolves configured servers by exact id only (never a name or a removed server), fences by pairing revision and runtime identity, and stops reporting retirements once unsubscribed. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): re-record the repo-metadata goldens for SSH host health The settled metadata state now carries hostHealthById, and an SSH-only catalog now reads the target summaries so a lone unhealthy host can be badged. Scoped re-record via rpc-recording.mts. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(mobile): tighten the desktop relay after elegance review Reuses the shared E2EE socket opener, folds relay state into fewer fields, never sends a request whose link ended while opening, passes failure codes through, relays only this desktop's own phones, and drops the unused host-state reader until S2 needs one. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): bound relay frames and stop sticky relay verdicts Restores the 8 MiB frame cap on every remote runtime socket, re-checks an update-needed server on each new link so an in-place upgrade relays, answers a phone the server just declined without syncing again, and ends a link whose mirrored capability update cannot be queued. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): run targeted desktop-owned calls on the desktop The phone tags every call on a workspace screen with its execution host, so a paired-desktop method carrying `runtime:<env>` now runs locally instead of failing 'forbidden'. The Mac stays the only reader of the routing table. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * feat(mobile): list the desktop's configured servers and their last workspace rows for the phone The phone needs the same host list the desktop shows, including servers that are offline or too old to relay to. mobileRelay.hosts.list/worktrees answer from the desktop's own status and its own worktree.ps fetches, cached per server and fenced by pairing, never from relayed replies. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(mobile): list server rows through one desktop fetch, with no side effect on listing The phone now always lists a server's rows through mobileRelay.hosts.worktrees, so the desktop's own fetch is both the live source and the cache, and stamping stays in main. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): fetch a server's full workspace list within a bound, and forget removed servers The desktop now asks for the phone's full-catalog limit, answers within 5s with the last rows when a server hangs, drops a server's rows when it is removed or re-paired, and checks the server's identity after the fetch so a reply from before a re-pair is never served. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * feat(mobile): name a server workspace's host on the phone's calls A phone reaches a server's workspace through its desktop only if each call names the host; the direct, cloud-relay and shell-bridge transports now carry `executionHost`, streams keep it for replay and unsubscribe, and an older shell (no `executionHost` grant) refuses rather than strips it. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): list managed servers as the desktop sidebar does, and hide rows mid-delete A server Orca manages on an SSH host now lists once under that host's name, or not at all while the host is reached over the SSH relay, using the sidebar's own folding. Rows the server is deleting are left out at reply time, as a phone asking the server itself would see. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): keep a disconnected server's last rows, and never join a re-paired server's fetch Retirement also fires on a manual disconnect or managed-server update, which wrongly emptied the phone's list; rows are now pruned only once a server is no longer configured. Fetches are shared per server identity, so a poll after a re-pair asks the new server right away. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * feat(mobile): list the paired desktop's server workspaces on the phone Fixes the phone missing a desktop's `orca serve` workspaces: rows come from the desktop's mobileRelay.hosts.* with each server's health ("Update needed", "Disconnected"), open through the relay, follow the desktop's hidden hosts, and the desktop now advertises the relay capability. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): pin that a desktop failing the hosts list leaves only its own rows The page asks a relaying desktop for its servers; a refusal such as method_not_found must leave today's list with no server rows and no catalog error. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): mirror the desktop's shown hosts exactly, one host included Jinwoo's call: the phone follows visibleWorkspaceHostIds as the desktop sidebar does, so a one-host list narrows the phone too instead of being read as a single-host focus. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): scope a server workspace at its route, never the desktop Every screen under a server workspace's route now reads one context and gets that server's client and a router that keeps pushes on it, refusing to fall back to the desktop; pins write to the row's owner like the Mac. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): never open an unreachable server workspace, and prove the relay in e2e A server workspace the phone can't reach now gets one explicit screen at its route and no row tap or action-sheet entry opens it; native routes read the route's host, a census pins every workspace route, and the e2e reads files and git on the server. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * feat(mobile): gate a server workspace on that server's own features A targeted status.get now relays to the server, so a server workspace's route provides that server's status to every screen under it and its action sheet, while whether the phone can reach the server stays the desktop's answer. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(ci): run the remote-runtime WebSocket opener test on Node Under the bun vitest project, `ws` resolves to Bun's built-in client, which ignores maxPayload, so the frame-cap test hung. The opener only ships on Node/Electron, so run its test in the Node runtime project like its siblings. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): prove a server browser page streams to a phone through its desktop Server-placed browser tabs already reach a desktop-paired phone: browser.* and status.get route to the execution host, and the relay passes screencast binary frames through untouched. This pins it end to end: the tab lists, and real JPEG frames arrive on the phone socket. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): stable request ids and checked replies in the server browser e2e Date.now() ids could repeat within a millisecond and match a stale reply; a counter cannot. The reply helper now checks its frame instead of asserting it non-null. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): give the file preview the workspace router, back to 128 page assets The preview was the one workspace screen on the plain router, so the scoped router was reached by a route set of its own and esbuild split it into an extra chunk (129 assets); every workspace screen now uses it and it shares the route's chunk. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(mobile-relay): derive the phone allowlist from the routing table Adding a phone method now takes one edit (its route). A server-targeted call on a legacy non-E2EE socket is refused instead of running locally, and only execution-host methods enter the relay. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(mobile-relay): drop unread freshness fields from server workspace lists The phone reads only the rows, so stale, fetchedAt, totalCount and truncated were dead wire weight. The desktop still serves a server's last rows while the server is unreachable. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(mobile): read only the rows of a server's workspace list The desktop no longer sends a freshness flag, so the phone's schema drops it. The relay capability list names the delegated-devices constant instead of repeating its literal, which also retires a now-circular type check. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): show a server workspace as disconnected while its server is down The phone read the desktop's connection state for a server workspace, so a stopped server kept a green dot and "Loading tabs" spun forever. The workspace route now narrows that state by the server's health from mobileRelay.hosts.list, reusing the existing disconnected session UI and refetching tabs when the server returns. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(mobile): share one desktop server-list feed between the host list and workspaces A server workspace polled mobileRelay.hosts.list beside the host list's own read, so the phone could ask the desktop twice per tick and the row badge and the screen could disagree. One feed per desktop client now owns that read; both readers join it. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(e2e): read a server's reachability from the hosts list, not a dropped stale flag |
||
|
|
3eaba4e01b |
feat(mobile): create workspaces on the desktop's servers from the phone (#26533)
* test(e2e): add a paired phone client helper and phone-mirror baseline specs
Later phone-mirror slices need one oracle that drives a real mobile-scoped device with the
phone's own capabilities against a serve host or a paired desktop. Pins #26022 across a serve
restart and today's gap: a desktop's phone does not list the desktop's server workspaces.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* feat(mobile): show SSH host health on the workspace row host badge
Rows on an SSH host the desktop cannot reach looked healthy on the phone. The badge now
adds the desktop sidebar's own word ("devbox · Disconnected"), derived by the same shared
function from the connection status ssh.listTargetSummaries already sends; old desktops keep today's badge.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* test(e2e): compose the phone-mirror topology and check the cold snapshot
Slices need one composer for host, desktop and phone with a single teardown, so specs stop
copying setup. The serve-restart spec now reads the first tab-stream snapshot after the cold
start, which is what the phone builds its tab strip from.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* feat(mobile): name an unhealthy SSH host even in a single-host list
The desktop card shows SSH status on any host count, so the phone now reads target summaries
whenever a repo is on SSH and badges an unhealthy host alone too. Labels and health share one
target-to-host keying, and a hook-level test pins the reply-to-row wiring.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* test(e2e): keep phone-mirror teardown errors from masking test failures
Teardown failures become a test attachment instead of replacing the real failure, and the
cold-snapshot poll reports stream errors together with the PTY list.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(mobile): clear stale SSH host badges and announce them without the dot
A catalog that no longer spans hosts or holds an SSH repo now clears the health and label maps,
so a host that left cannot keep badging rows. The badge gets a screen-reader label ("devbox,
Disconnected") instead of reading the middle dot.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* feat(runtime): let a paired desktop register its phones as delegated mobile devices
A desktop paired to this runtime can now call pairing.delegatedMobileDevice.sync to make each of
its phones an ordinary mobile-scope device it owns, so the phone mirrors that host through the
desktop. Children are never pending, inherit the parent's reach, and die with the parent's grant.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* refactor(runtime): move the mobile device revoke primitive below dispatch
Dispatch binds the caller-bound delegated-device context, so the revoke path it needs now lives in
a layer under it as a pure move; this drops the abstract method seam. No behaviour change.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(runtime): keep a runtime grant until its delegated phones are revoked
A failed child cleanup now leaves the parent in place so a retry can still find the children, and
revokeRuntimeAccess lives beside the revoke it cascades to. Adds restart, rename and bounds coverage,
shares credential minting, and drops the timing-dependent census.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(runtime): fail a delegated sync whose dropped phone cannot be revoked
Matches the parent revoke: a child whose cleanup could not be saved is kept and the sync errors
before minting anything, so the desktop retries instead of believing the server matches its list.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* feat(mobile): relay a paired phone's server-workspace requests through the desktop
A phone mirrors its desktop, including workspaces on the desktop's configured servers. The desktop
now forwards a request that names `executionHost: runtime:<env>` to that server over one socket per
(phone, server), signed in as the phone's delegated device; replies pass through untouched except
stream ids. Hidden until the phone client ships: the relay capability is not advertised yet.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* test(mobile): type the single-host health fixtures inline so the test tsc passes
Fixtures hoisted into consts widened their health maps to string, which the now-required
hostHealthById rejects under the tests-typecheck ratchet; passing them inline keeps the union.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* docs(mobile): state the desktop relay contract in its entry module
main dropped docs/reference, so the relay's contract lives beside the code. Also pins that a
server whose delegated sync fails (e.g. delegated_device_revoke_failed) is unavailable until a
later request syncs it again.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* test(mobile): pin the relay's configured-server adapter
The production hosts adapter was only typechecked; this pins that it resolves configured servers
by exact id only (never a name or a removed server), fences by pairing revision and runtime
identity, and stops reporting retirements once unsubscribed.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* test(mobile): re-record the repo-metadata goldens for SSH host health
The settled metadata state now carries hostHealthById, and an SSH-only catalog now reads the
target summaries so a lone unhealthy host can be badged. Scoped re-record via rpc-recording.mts.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* refactor(mobile): tighten the desktop relay after elegance review
Reuses the shared E2EE socket opener, folds relay state into fewer fields, never sends a request
whose link ended while opening, passes failure codes through, relays only this desktop's own
phones, and drops the unused host-state reader until S2 needs one.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(mobile): bound relay frames and stop sticky relay verdicts
Restores the 8 MiB frame cap on every remote runtime socket, re-checks an update-needed server on
each new link so an in-place upgrade relays, answers a phone the server just declined without
syncing again, and ends a link whose mirrored capability update cannot be queued.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(mobile): run targeted desktop-owned calls on the desktop
The phone tags every call on a workspace screen with its execution host, so a
paired-desktop method carrying `runtime:<env>` now runs locally instead of
failing 'forbidden'. The Mac stays the only reader of the routing table.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* feat(mobile): list the desktop's configured servers and their last workspace rows for the phone
The phone needs the same host list the desktop shows, including servers that are offline or
too old to relay to. mobileRelay.hosts.list/worktrees answer from the desktop's own status and
its own worktree.ps fetches, cached per server and fenced by pairing, never from relayed replies.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* refactor(mobile): list server rows through one desktop fetch, with no side effect on listing
The phone now always lists a server's rows through mobileRelay.hosts.worktrees, so the
desktop's own fetch is both the live source and the cache, and stamping stays in main.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(mobile): fetch a server's full workspace list within a bound, and forget removed servers
The desktop now asks for the phone's full-catalog limit, answers within 5s with the last rows
when a server hangs, drops a server's rows when it is removed or re-paired, and checks the
server's identity after the fetch so a reply from before a re-pair is never served.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* feat(mobile): name a server workspace's host on the phone's calls
A phone reaches a server's workspace through its desktop only if each call names the host; the
direct, cloud-relay and shell-bridge transports now carry `executionHost`, streams keep it for
replay and unsubscribe, and an older shell (no `executionHost` grant) refuses rather than strips it.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(mobile): list managed servers as the desktop sidebar does, and hide rows mid-delete
A server Orca manages on an SSH host now lists once under that host's name, or not at all while
the host is reached over the SSH relay, using the sidebar's own folding. Rows the server is
deleting are left out at reply time, as a phone asking the server itself would see.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(mobile): keep a disconnected server's last rows, and never join a re-paired server's fetch
Retirement also fires on a manual disconnect or managed-server update, which wrongly emptied
the phone's list; rows are now pruned only once a server is no longer configured. Fetches are
shared per server identity, so a poll after a re-pair asks the new server right away.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* feat(mobile): list the paired desktop's server workspaces on the phone
Fixes the phone missing a desktop's `orca serve` workspaces: rows come from the desktop's
mobileRelay.hosts.* with each server's health ("Update needed", "Disconnected"), open through the
relay, follow the desktop's hidden hosts, and the desktop now advertises the relay capability.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* test(mobile): pin that a desktop failing the hosts list leaves only its own rows
The page asks a relaying desktop for its servers; a refusal such as method_not_found must leave
today's list with no server rows and no catalog error.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(mobile): mirror the desktop's shown hosts exactly, one host included
Jinwoo's call: the phone follows visibleWorkspaceHostIds as the desktop sidebar does, so a
one-host list narrows the phone too instead of being read as a single-host focus.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(mobile): scope a server workspace at its route, never the desktop
Every screen under a server workspace's route now reads one context and
gets that server's client and a router that keeps pushes on it, refusing
to fall back to the desktop; pins write to the row's owner like the Mac.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(mobile): never open an unreachable server workspace, and prove the relay in e2e
A server workspace the phone can't reach now gets one explicit screen at its route
and no row tap or action-sheet entry opens it; native routes read the route's host,
a census pins every workspace route, and the e2e reads files and git on the server.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* feat(mobile): gate a server workspace on that server's own features
A targeted status.get now relays to the server, so a server workspace's route
provides that server's status to every screen under it and its action sheet,
while whether the phone can reach the server stays the desktop's answer.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(ci): run the remote-runtime WebSocket opener test on Node
Under the bun vitest project, `ws` resolves to Bun's built-in client, which
ignores maxPayload, so the frame-cap test hung. The opener only ships on
Node/Electron, so run its test in the Node runtime project like its siblings.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* feat(mobile): sleep, wake and new workspaces on a desktop's servers from the phone
The desktop renderer alone sleeps a server's workspace and holds its slept agents, so the phone
now asks the desktop (two paired-desktop methods) instead of the headless server; the
new-workspace sheet lists and creates on reachable servers the way the desktop's composer does.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(mobile): sleep and wake only a server the desktop has configured
A phone's sleep or wake names a server by host id; the desktop now refuses one that is not in
its own configured servers, read from its settings rather than any fetched workspace list.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(mobile): keep server workspaces' repos, picks and sleeps on their own host
A hung server no longer holds back the desktop's repos, a repo id shared by two hosts no longer
swaps the pick, and a server row's optimistic sleep waits for the server's own list to confirm it.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* test(mobile): give the server-workspaces test rows every Worktree field
The tests-typecheck ratchet checks mobile test files; the fixture row was missing required
Worktree members, so the file no longer typechecked.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(mobile): give the file preview the workspace router, back to 128 page assets
The preview was the one workspace screen on the plain router, so the scoped router
was reached by a route set of its own and esbuild split it into an extra chunk
(129 assets); every workspace screen now uses it and it shares the route's chunk.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* revert(mobile): drop server sleep and wake from the phone for now
The workspace-layout refactor deletes the notifier path these used; sleep and
wake on a server come back later as server-side layout.sleep / layout.wake.
A server row's Sleep behaves exactly as on feat/mobile-remote-workspaces.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* refactor(mobile-relay): derive the phone allowlist from the routing table
Adding a phone method now takes one edit (its route). A server-targeted call on a
legacy non-E2EE socket is refused instead of running locally, and only
execution-host methods enter the relay.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* refactor(mobile-relay): drop unread freshness fields from server workspace lists
The phone reads only the rows, so stale, fetchedAt, totalCount and truncated
were dead wire weight. The desktop still serves a server's last rows while
the server is unreachable.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* refactor(mobile): read only the rows of a server's workspace list
The desktop no longer sends a freshness flag, so the phone's schema drops it.
The relay capability list names the delegated-devices constant instead of
repeating its literal, which also retires a now-circular type check.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(mobile): show a server workspace as disconnected while its server is down
The phone read the desktop's connection state for a server workspace, so a stopped
server kept a green dot and "Loading tabs" spun forever. The workspace route now
narrows that state by the server's health from mobileRelay.hosts.list, reusing the
existing disconnected session UI and refetching tabs when the server returns.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(mobile): name run targets and the desktop's own row as the host list does
The Run on picker showed a server's raw environment id behind "Remote ·" and
always a monitor glyph; it now takes the host list's badge labels and icon, as
the desktop's picker does. Server rows also made the desktop row read "This
computer" because its platform was only read for a mixed desktop catalog.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* refactor(mobile): share one desktop server-list feed between the host list and workspaces
A server workspace polled mobileRelay.hosts.list beside the host list's own read, so
the phone could ask the desktop twice per tick and the row badge and the screen could
disagree. One feed per desktop client now owns that read; both readers join it.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* test(e2e): read a server's reachability from the hosts list, not a dropped stale flag
|
||
|
|
6741f4f165 |
refactor(orcad): move the Node host adapters out of orcad-entry.ts (#28530)
Fixes main's lint: #28464 left orcad-entry.ts at 301 lines (max 300). No behaviour change. |
||
|
|
5f134d8f01 |
feat(mobile): show and use the desktop's server workspaces on the phone (#26501)
* test(e2e): add a paired phone client helper and phone-mirror baseline specs
Later phone-mirror slices need one oracle that drives a real mobile-scoped device with the
phone's own capabilities against a serve host or a paired desktop. Pins #26022 across a serve
restart and today's gap: a desktop's phone does not list the desktop's server workspaces.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* feat(mobile): show SSH host health on the workspace row host badge
Rows on an SSH host the desktop cannot reach looked healthy on the phone. The badge now
adds the desktop sidebar's own word ("devbox · Disconnected"), derived by the same shared
function from the connection status ssh.listTargetSummaries already sends; old desktops keep today's badge.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* test(e2e): compose the phone-mirror topology and check the cold snapshot
Slices need one composer for host, desktop and phone with a single teardown, so specs stop
copying setup. The serve-restart spec now reads the first tab-stream snapshot after the cold
start, which is what the phone builds its tab strip from.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* feat(mobile): name an unhealthy SSH host even in a single-host list
The desktop card shows SSH status on any host count, so the phone now reads target summaries
whenever a repo is on SSH and badges an unhealthy host alone too. Labels and health share one
target-to-host keying, and a hook-level test pins the reply-to-row wiring.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* test(e2e): keep phone-mirror teardown errors from masking test failures
Teardown failures become a test attachment instead of replacing the real failure, and the
cold-snapshot poll reports stream errors together with the PTY list.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(mobile): clear stale SSH host badges and announce them without the dot
A catalog that no longer spans hosts or holds an SSH repo now clears the health and label maps,
so a host that left cannot keep badging rows. The badge gets a screen-reader label ("devbox,
Disconnected") instead of reading the middle dot.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* feat(runtime): let a paired desktop register its phones as delegated mobile devices
A desktop paired to this runtime can now call pairing.delegatedMobileDevice.sync to make each of
its phones an ordinary mobile-scope device it owns, so the phone mirrors that host through the
desktop. Children are never pending, inherit the parent's reach, and die with the parent's grant.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* refactor(runtime): move the mobile device revoke primitive below dispatch
Dispatch binds the caller-bound delegated-device context, so the revoke path it needs now lives in
a layer under it as a pure move; this drops the abstract method seam. No behaviour change.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(runtime): keep a runtime grant until its delegated phones are revoked
A failed child cleanup now leaves the parent in place so a retry can still find the children, and
revokeRuntimeAccess lives beside the revoke it cascades to. Adds restart, rename and bounds coverage,
shares credential minting, and drops the timing-dependent census.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(runtime): fail a delegated sync whose dropped phone cannot be revoked
Matches the parent revoke: a child whose cleanup could not be saved is kept and the sync errors
before minting anything, so the desktop retries instead of believing the server matches its list.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* feat(mobile): relay a paired phone's server-workspace requests through the desktop
A phone mirrors its desktop, including workspaces on the desktop's configured servers. The desktop
now forwards a request that names `executionHost: runtime:<env>` to that server over one socket per
(phone, server), signed in as the phone's delegated device; replies pass through untouched except
stream ids. Hidden until the phone client ships: the relay capability is not advertised yet.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* test(mobile): type the single-host health fixtures inline so the test tsc passes
Fixtures hoisted into consts widened their health maps to string, which the now-required
hostHealthById rejects under the tests-typecheck ratchet; passing them inline keeps the union.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* docs(mobile): state the desktop relay contract in its entry module
main dropped docs/reference, so the relay's contract lives beside the code. Also pins that a
server whose delegated sync fails (e.g. delegated_device_revoke_failed) is unavailable until a
later request syncs it again.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* test(mobile): pin the relay's configured-server adapter
The production hosts adapter was only typechecked; this pins that it resolves configured servers
by exact id only (never a name or a removed server), fences by pairing revision and runtime
identity, and stops reporting retirements once unsubscribed.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* test(mobile): re-record the repo-metadata goldens for SSH host health
The settled metadata state now carries hostHealthById, and an SSH-only catalog now reads the
target summaries so a lone unhealthy host can be badged. Scoped re-record via rpc-recording.mts.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* refactor(mobile): tighten the desktop relay after elegance review
Reuses the shared E2EE socket opener, folds relay state into fewer fields, never sends a request
whose link ended while opening, passes failure codes through, relays only this desktop's own
phones, and drops the unused host-state reader until S2 needs one.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(mobile): bound relay frames and stop sticky relay verdicts
Restores the 8 MiB frame cap on every remote runtime socket, re-checks an update-needed server on
each new link so an in-place upgrade relays, answers a phone the server just declined without
syncing again, and ends a link whose mirrored capability update cannot be queued.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(mobile): run targeted desktop-owned calls on the desktop
The phone tags every call on a workspace screen with its execution host, so a
paired-desktop method carrying `runtime:<env>` now runs locally instead of
failing 'forbidden'. The Mac stays the only reader of the routing table.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* feat(mobile): list the desktop's configured servers and their last workspace rows for the phone
The phone needs the same host list the desktop shows, including servers that are offline or
too old to relay to. mobileRelay.hosts.list/worktrees answer from the desktop's own status and
its own worktree.ps fetches, cached per server and fenced by pairing, never from relayed replies.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* refactor(mobile): list server rows through one desktop fetch, with no side effect on listing
The phone now always lists a server's rows through mobileRelay.hosts.worktrees, so the
desktop's own fetch is both the live source and the cache, and stamping stays in main.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(mobile): fetch a server's full workspace list within a bound, and forget removed servers
The desktop now asks for the phone's full-catalog limit, answers within 5s with the last rows
when a server hangs, drops a server's rows when it is removed or re-paired, and checks the
server's identity after the fetch so a reply from before a re-pair is never served.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* feat(mobile): name a server workspace's host on the phone's calls
A phone reaches a server's workspace through its desktop only if each call names the host; the
direct, cloud-relay and shell-bridge transports now carry `executionHost`, streams keep it for
replay and unsubscribe, and an older shell (no `executionHost` grant) refuses rather than strips it.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(mobile): list managed servers as the desktop sidebar does, and hide rows mid-delete
A server Orca manages on an SSH host now lists once under that host's name, or not at all while
the host is reached over the SSH relay, using the sidebar's own folding. Rows the server is
deleting are left out at reply time, as a phone asking the server itself would see.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(mobile): keep a disconnected server's last rows, and never join a re-paired server's fetch
Retirement also fires on a manual disconnect or managed-server update, which wrongly emptied
the phone's list; rows are now pruned only once a server is no longer configured. Fetches are
shared per server identity, so a poll after a re-pair asks the new server right away.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* feat(mobile): list the paired desktop's server workspaces on the phone
Fixes the phone missing a desktop's `orca serve` workspaces: rows come from the desktop's
mobileRelay.hosts.* with each server's health ("Update needed", "Disconnected"), open through the
relay, follow the desktop's hidden hosts, and the desktop now advertises the relay capability.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* test(mobile): pin that a desktop failing the hosts list leaves only its own rows
The page asks a relaying desktop for its servers; a refusal such as method_not_found must leave
today's list with no server rows and no catalog error.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(mobile): mirror the desktop's shown hosts exactly, one host included
Jinwoo's call: the phone follows visibleWorkspaceHostIds as the desktop sidebar does, so a
one-host list narrows the phone too instead of being read as a single-host focus.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(mobile): scope a server workspace at its route, never the desktop
Every screen under a server workspace's route now reads one context and
gets that server's client and a router that keeps pushes on it, refusing
to fall back to the desktop; pins write to the row's owner like the Mac.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(mobile): never open an unreachable server workspace, and prove the relay in e2e
A server workspace the phone can't reach now gets one explicit screen at its route
and no row tap or action-sheet entry opens it; native routes read the route's host,
a census pins every workspace route, and the e2e reads files and git on the server.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* feat(mobile): gate a server workspace on that server's own features
A targeted status.get now relays to the server, so a server workspace's route
provides that server's status to every screen under it and its action sheet,
while whether the phone can reach the server stays the desktop's answer.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(ci): run the remote-runtime WebSocket opener test on Node
Under the bun vitest project, `ws` resolves to Bun's built-in client, which
ignores maxPayload, so the frame-cap test hung. The opener only ships on
Node/Electron, so run its test in the Node runtime project like its siblings.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(mobile): give the file preview the workspace router, back to 128 page assets
The preview was the one workspace screen on the plain router, so the scoped router
was reached by a route set of its own and esbuild split it into an extra chunk
(129 assets); every workspace screen now uses it and it shares the route's chunk.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* refactor(mobile-relay): derive the phone allowlist from the routing table
Adding a phone method now takes one edit (its route). A server-targeted call on a
legacy non-E2EE socket is refused instead of running locally, and only
execution-host methods enter the relay.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* refactor(mobile-relay): drop unread freshness fields from server workspace lists
The phone reads only the rows, so stale, fetchedAt, totalCount and truncated
were dead wire weight. The desktop still serves a server's last rows while
the server is unreachable.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* refactor(mobile): read only the rows of a server's workspace list
The desktop no longer sends a freshness flag, so the phone's schema drops it.
The relay capability list names the delegated-devices constant instead of
repeating its literal, which also retires a now-circular type check.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(mobile): show a server workspace as disconnected while its server is down
The phone read the desktop's connection state for a server workspace, so a stopped
server kept a green dot and "Loading tabs" spun forever. The workspace route now
narrows that state by the server's health from mobileRelay.hosts.list, reusing the
existing disconnected session UI and refetching tabs when the server returns.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* refactor(mobile): share one desktop server-list feed between the host list and workspaces
A server workspace polled mobileRelay.hosts.list beside the host list's own read, so
the phone could ask the desktop twice per tick and the row badge and the screen could
disagree. One feed per desktop client now owns that read; both readers join it.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* test(e2e): read a server's reachability from the hosts list, not a dropped stale flag
|
||
|
|
464bd5a54b |
feat(native-chat): combine model and options into one picker (#27972)
* feat(native-chat): combine model and options into one picker Setting a model and its effort took two menus that each closed on a pick. * feat(native-chat): close the model picker on an effort pick Effort is the last thing set before typing. * fix(native-chat): write the picker's kept rows after commit React Doctor rejects a ref written in render, and a discarded render could leave a row behind. * fix(native-chat): open the picker once per /model or /effort, tooltip only when informative The session kept a slash command's picker request, and the picker remounted open from it whenever a question or approval card gave the composer back. The request is now consumed when the picker sees it, and open state lives in the popover, which unmounts with the pill, so it comes back closed. The pill's hover tooltip only named the categories the pill already shows, and the picker covered it on click. It now shows only the disabled reason or the not-confirmed note. Also drops the removed Options key from the locale test and covers the new short effort labels. --------- Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> |
||
|
|
624a095993 |
fix(cli): warn when a workspace status has no board column (#28264)
Return optional warnings from the owning host when a requested workspace status does not match its configured columns. Preserve the requested write and older-peer behavior; show warnings separately from normal CLI output. Co-authored-by: Kalvin Mizzi <kalvin@mizzi.com> |
||
|
|
23bd828af1 |
fix(usage): usage requests answer authenticated-proxy challenges on a cold start (#28411)
* fix(usage): usage requests answer authenticated-proxy challenges on a cold start Claude, Cursor, Gemini, Grok, Kimi, OpenCode Go (API key) and MiniMax (API key) usage fetchers called Electron's net.fetch directly, which cancels every proxy login challenge. They now go through the shared desktop HTTP client, which answers the challenge with the configured proxy credentials (#26957), like Codex and ZCode already do. * test(cursor): route the wire fixture through the desktop HTTP client |
||
|
|
8a6d6ea8c8 |
fix(terminal): send only the text a Linux IME commits, not the candidate key (#28275)
* fix(terminal): send only the text a Linux IME commits, not the candidate key fcitx4 with Sogou reports a single-character candidate pick as a plain Space/digit keydown whose keypress and insertText carry the candidate. xterm encoded the keydown instead: under kitty keyboard mode the Space leaked and the candidate was lost, and the old Linux digit window cancelled the keydown that carried the commit. Install the native text forwarder on desktop Linux (terminal pane and dashboard preview) so printable bytes come only from committed text, and delete the Linux timer heuristics it replaces. Pair an XIM pick's keyup with its code-less press, and stop dropping unclaimed input while an xterm composition transaction settles. Replay tests use real fcitx4 recordings (Sogou via GTK module and XIM, Google Pinyin in-client preedit). Fixes #28213 Fixes #26352 * test(terminal): replay Chromium's text insertion for uncancelled keys in the Hangul sequence The observed IBus Hangul replay dispatched a bare `a` keydown with no text insertion, which only matched reality while xterm cancelled that keydown. Now that printable Linux text comes from the committed insertText, supply it when the keydown is not cancelled and open the next session at the caret that follows it. Point the IME reliability gate at the fcitx4 replay test that replaced the deleted Linux candidate-state test. |
||
|
|
962c338b67 |
feat(wsl): Windows path actions for WSL hosts through one owner-routed capability (WSL-via-orcad C8) (#27955)
* feat(wsl): open WSL host paths from Windows through one owner-routed capability (WSL-via-orcad C8) * test(wsl): typed shell handler lookup for the WSL owner tests * fix(wsl): read known WSL hosts through a registered accessor; e2e for the Windows folder picker * fix(wsl): open the host-path entry filled in after a Windows pick; e2e screenshots * test(wsl): read the e2e picker's default path through the main process env * refactor(wsl): read a WSL host's distro from its record instead of a new accessor * fix(wsl): skip the host browser only for a path Windows' picker or an Explorer drop filled in A path left filled from an earlier pick on an SSH or paired host opens the host browser again, as it did before. |
||
|
|
deb6a71b29 |
Show folder workspaces in the Cmd+J jump palette (#28175)
* Show folder workspaces in the Cmd+J jump palette Cmd+J only listed git worktrees, so folder workspaces could not be found or opened from it. The palette now includes folder workspaces alongside git worktrees and renames the section from "Worktrees" to "Workspaces" to match. - Folder rows are searchable by name, comment, and attached GitHub item, and follow the sidebar's host scope. - A blocked activation (for example, a folder path that no longer exists) keeps the palette open and shows only the path error, instead of closing or showing a second generic error. - Folder path-status caching and sidebar reveal are keyed by execution host, so folders with the same id on different hosts no longer share cache entries or reveal the wrong group. * Consolidate runtime target imports in folder-path-status `getRuntimeTargetHostId` now comes from the same module as `runtimeTargetForOwnerEnvironment`, so both are imported from `runtime/runtime-client-target` instead of splitting them across two modules. * Wake sleeping folder workspaces from Cmd+J Picking a sleeping folder workspace from the jump palette did not restart its terminal or restore its Files explorer view. Folder and git worktree activation now share one code path in workspace-activation.ts, so both wake the same way. - Wake a sleeping folder the same way as a worktree: bump the tab generation and tag the pending spawn when no PTY is live, including when the workspace is already selected. - Restore the remembered right-sidebar explorer view for folders. - Skip the GitHub PR refresh for folder workspaces, which have no PR. - Clear unread only on the folder entry whose execution host matches the activation. * Reveal folder workspaces on their catalog owner host Cmd+J activation revealed folder workspaces against the focused runtime server's host, so a folder owned by a local or SSH project could be revealed under an unrelated server. The reveal host now comes from the folder workspace's catalog owner, and an explicit caller host still wins. Add a regression test covering local and SSH owners while another runtime environment is focused. * Keep shared workspace activation in the existing layout writer |
||
|
|
9f2abd5e70 |
feat(wsl): Add WSL distro host UI and status (WSL-via-orcad C7b, experimental) (#27934)
* feat(wsl): Add WSL distro host UI, host row status and fake-transport e2e (WSL-via-orcad C7b) Experimental toggle (Windows only), an Add WSL distro choice beside SSH host and remote server, the WSL host's own row in host pickers with its liveness state, a Connect action that starts a stopped distro only on request, a WSL hosts list in Settings, and the renderer report of which WSL hosts an open project or terminal needs. * feat(wsl): show WSL hosts as turned off while the experiment is off, and confirm their removal With Experimental → WSL distros as hosts off, a WSL host keeps its row so its projects never seem to vanish, says it is turned off, and offers Turn on (opening that setting) instead of Connect; Settings lists it the same way with Connect disabled. Removing a WSL host now asks first in the dialog SSH hosts use, with WSL copy. The e2e covers both. * fix(wsl): skip the WSL host status bridge when the preload lacks it * fix(wsl): show when this build cannot run WSL hosts instead of failing an add The UI reads main's template capability once. Without it, the add dialog says this build can't run WSL hosts and disables Add, existing hosts' rows and Settings say the same with Connect disabled, and the Experimental toggle notes it. The e2e covers a fake build without the files. * fix(wsl): re-read the WSL distro list when the add dialog's window returns, and on Refresh The add dialog read the registered distros once, so one imported while it was open never appeared. It now re-reads when the window regains focus or becomes visible, and from an explicit Refresh button, never on a timer. The selection is derived from the current list, so a refresh cannot leave a stale pick. The e2e imports a distro mid-dialog. * fix(wsl): list WSL hosts only as WSL hosts, not among the generic remote servers Settings → Remote Orca Servers, the server-update checks and the status bar's server list now leave out environments whose deployment record is a WSL host (by kind, never by name). The WSL hosts section reads them from the store itself. * fix(wsl): say a WSL host's removal ends its terminals, and report what it left running The confirmation now says removing ends the host's open terminals as well as Orca's server, and a removal main could not complete in the distro (a terminal or the daemon kept running) is reported instead of passing silently. * fix(wsl): ship the removal copy that says a WSL host's terminals end The build resolves copy from the English catalog, which still held the old text under the reused key, so the new inline default never reached the dialog. The copy gets its own key, the stale entry is dropped from both catalogs, and a test reads the shipped catalog. * fix(wsl): leave a removed host's workspace, keep its removal warning up, and prove removal in e2e After removal the active workspace is left when it lived on that host, so nothing keeps calling a host that no longer exists, and a warning about what still runs in the distro stays until dismissed. The e2e now checks that removal ends the host's terminals on the server standing in for orcad, shows no warning, leaves the workspace, and starts nothing for the host afterwards. * test(wsl): read the shipped removal copy and the fake's connect log without reflection * fix(wsl): say a WSL host is turned off in its terminal instead of 'Reconnecting' With the experiment off (or without server files) nothing retries, so a WSL host's terminal now says so, with Turn on, rather than claiming Orca is reconnecting. * test(wsl): reproduce project-removal survivors and orphan terminals in e2e The headless server standing in for orcad now carries a project whose terminal must end when the project is removed (and leave the host's own catalog), and an orphan terminal whose project the host already forgot, which removing the host must still end. Both checks fail without the fixes. * fix(wsl): drop the IPC wrapper from file-explorer and editor load errors; offer Turn on when WSL hosts are off The file explorer showed "Error invoking remote method 'runtimeEnvironments:call': Error: ..." for a WSL workspace whose host was turned off. Explorer read errors now go through the shared IPC-error unwrapping, and when the workspace's WSL host is turned off the empty pane shows the same turned-off copy and Turn on button as the terminal banner instead of a read error and a Retry that cannot work. The editor's file-load error view drops the same wrapper at render time (stored text is unchanged for retry classifiers). * test(wsl): open the internal gate for the WSL host e2e, and prove a closed gate shows nothing The fake-transport e2e launches its desktop with ORCA_INTERNAL_WSL_HOSTS=1. A new case leaves the gate closed with a WSL host already saved and the Experimental setting on: no WSL host API, list entry, host row, Add WSL distro action, Experimental toggle or Settings section appears, the fake never starts a distro, and the saved host stays on disk. * test(wsl): count only live shells when proving a removed project's terminals ended On Linux CI the hub keeps a pty-less tab record for the removed worktree after terminal.stop ended its shell, so counting every terminal.list row never reached zero. |
||
|
|
f1d1a9b076 |
test(e2e): sample the delete dialog until it has enough frames (#28323)
* test(e2e): sample the delete dialog until it has a full frame floor * test(e2e): drop the frame-count assertion the sampling loop now guarantees |
||
|
|
aa7afabb1d |
test(e2e): wait for the paired-reveal decoy to settle at its new size (#28319)
* test(e2e): read the parked-reveal decoy grid only after it settles at the new fit * test(e2e): name the settle wait and give it the file's reveal timeout |
||
|
|
7cf6f0a281 | test(e2e): probe the delete dialog's confirm button, not the force-delete menu chevron (#28315) | ||
|
|
e9285f0f58 |
fix(floating-workspace): open the floating panel instead of blanking the main window on host reveals (#28184)
* fix(floating-workspace): reveal workspaces where they live instead of selecting the floating one Every renderer path that takes the user to a workspace hand-wrote the main-window selection sequence, so a floating workspace id (terminal switch, CLI focus, agent launch, notification and mobile reveals, terminal handle links) became the main window's selection and blanked it while the floating panel stayed closed. revealWorkspaceSurface is now the one way to show a workspace: the floating workspace opens its panel and leaves the main window alone, any other workspace becomes the main window's selection and a visit. selectWorkspaceSurfaceOwner is its non-navigating twin for tabs opening beside the user. The ad-hoc floating checks in worktree activation, http links, the editor close queue, Activity and open-terminal-beside-tab now go through the same module. Co-authored-by: Velm14 <10477408+Velm14@users.noreply.github.com> * fix(notifications): bind clicks for floating and folder workspace terminals A terminal notification only bound its click when the worktree id carried a repo ("repoId::path"), so floating-workspace and folder-workspace terminal notifications did nothing when clicked. ui:focusTerminal now reveals any workspace where it lives, so the click binds whenever there is a pane to focus and ui:activateWorktree is sent only when a repo exists. Co-authored-by: YuriNachos <19365375+YuriNachos@users.noreply.github.com> * refactor(floating-workspace): tighten the workspace surface reveal Move the floating-owned check into floating-workspace-panel-reveal as revealFloatingWorkspacePanelFor so worktree activation no longer depends on workspace-surface-reveal, rename selectWorkspaceSurfaceOwner to selectWorkspaceSurface with a named state type, reuse the panel visibility selector, and flatten the IPC bridges' surface-owner branches. Co-authored-by: Velm14 <10477408+Velm14@users.noreply.github.com> * fix(floating-workspace): leave the floating setting alone before settings hydrate Host reveals (CLI focus, mobile, notification clicks) can arrive before settings load; re-enabling the floating feature then would overwrite a saved `false`. Unhydrated settings now skip the reveal. Co-authored-by: Velm14 <10477408+Velm14@users.noreply.github.com> * refactor(floating-workspace): drop extra reveal concepts Co-authored-by: Velm14 <10477408+Velm14@users.noreply.github.com> * fix(floating-workspace): keep main's setActiveWorktree call shape for plain reveals Co-authored-by: Velm14 <10477408+Velm14@users.noreply.github.com> --------- Co-authored-by: Velm14 <10477408+Velm14@users.noreply.github.com> Co-authored-by: YuriNachos <19365375+YuriNachos@users.noreply.github.com> |
||
|
|
687190e9f1 |
fix(ssh): let existing profiles migrate without losing client history (#28141)
* fix(ssh): migrate released profiles with retained client state * fix(ssh): reuse the shared adoption rule when the local and host copies overlap The host copy replaces the older local one only where it holds tabs, the catalog places the workspace on that host, no other host claims it, no worker fence or close record disagrees, and local drafts are kept. Elsewhere both copies export and must agree, as on main. --------- Co-authored-by: m4air <m4air@Mac.localdomain> |
||
|
|
b5c901747b |
[CI] Reuse test compilation and reduce avoidable test waits (#28169)
* [CI] Reuse test compilation and reduce avoidable test waits * [CI] Keep startup observation polls from advancing deadlines |
||
|
|
4d1001fba6 |
fix(terminal): show a host-created setup split in the window that holds its tab (#28105)
* fix(terminal): show a host-created setup split in the window that holds its tab (STA-9417) Main only revealed a split of a saved tab when its copy of the window's graph listed the source pane. That copy lags the tab the create reveal just added, so the window never drew the setup split and its first-activation sweep left the setup terminal with no pane or minted a second tab for it. Reveal unless the window's graph shows the source pane running a different terminal, and reject a split into an unknown tab before the window switches workspace. * refactor(terminal): return only the split-source fields a caller reads * test(terminal): explain the internals cast in the projected split-source test |
||
|
|
0e4207400b |
fix(terminal): keep a named terminal's title across an orca serve restart (#28126)
* fix(terminal): keep a host-made terminal's title across a serve restart A title given to terminal.create was kept only on the live process record, so after an orca serve restart the tab read "Terminal N" and terminal.list listed no title. The runtime now writes it as the tab's saved name when it admits the pane, and terminal.list falls back to that saved name for a terminal that has no title of its own yet, as a window does. * test(e2e): record the kept terminal titles in the layout-compat baselines * fix(terminal): save a created terminal's name only when the window would show it A background create (a worker the window never reveals) keeps its live title: a window reads a saved name on hydration and shows it over the agent's own title. Also corrects two comments and drops a key count. |
||
|
|
44fabbda52 |
fix(runtime): stop a project's terminals before the host forgets the project (#28024)
* fix(runtime): stop a project's terminals before the host forgets the project Removing a project on a runtime host (an SSH-managed or paired server) sent repo.rm first; the host resolves terminal.stop through the project's workspace rows, which repo.rm deletes, so the stop failed and the shells kept running on the host. Terminals stop first now. The removal toast also drops the IPC wrapper from its message. * fix(runtime): only stop host terminals when the host will accept the project removal Probe with repo.show (same resolver as repo.rm) so a removal the host rejects, such as an ambiguous project id, no longer kills the shells first. Also poll through terminal_not_writable in the paired-client e2e. * test(e2e): read the host shell pid through sh so non-POSIX login shells work |
||
|
|
7154e9045f |
fix(terminal): keep the tab when a reattach finds its process gone (#28067)
* fix(terminal): keep the tab when a reattach finds its process gone Co-Authored-By: Claude <noreply@anthropic.com> * test(e2e): read the paired retention heap after two collections Co-Authored-By: Claude <noreply@anthropic.com> * fix(terminal): clear the absent owner through the replaced-pane binding writer Co-Authored-By: Claude <noreply@anthropic.com> * test(e2e): fail the paired retention check when the heap cannot be read Co-Authored-By: Claude <noreply@anthropic.com> * fix(terminal): refuse the respawn without a Store; tighten the binding fixture and stop docs Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
a1f8e6482b |
fix(layout): save terminal rows in the tab groups' order (#28066)
* fix(layout): save terminal rows in the tab groups' order Co-Authored-By: Claude <noreply@anthropic.com> * test(e2e): read the paired retention heap after two collections Co-Authored-By: Claude <noreply@anthropic.com> * test(e2e): fail the paired retention check when the heap cannot be read Co-Authored-By: Claude <noreply@anthropic.com> * fix(layout): name the headless flow behind the row-id group fallback Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
125007b680 |
chore: upgrade terminal dependencies to matching payload betas (#27935)
* chore: upgrade terminal dependencies to the matching payload betas * test: harden xterm payload contract, align mobile pins, fix serialize-at-ref across version bumps - Cover foreign-payload replacement and the shared blank-cell reset in the installed image payload contract (both mutants previously survived). - Bump mobile's xterm, webgl and unicode11 pins to the root's betas. - Resolve the ref's own serialize version and patch so the differential fuzz baseline builds across a version bump. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
2233fd3669 |
feat(native-chat): each Claude/Codex chat gets its own permission mode (#26116)
Each native Claude and Codex chat now has its own permission mode (Ask for approval, Accept edits for Claude, Approve for me, Full access), shown and changed from a button right after "+" in the composer. A change takes effect before the next message: the agent restarts quietly and resumes the same conversation, never interrupting a reply or background work. New chats start from Settings → Chat → Permissions, which is seeded once from Agent Permissions. The mode is stored as an ordinary chat option, and the restart reuses the existing "replace the agent before the next message" step. |
||
|
|
fa4ef2b961 |
ci: stop new main-process code from freezing Orca with synchronous launches (#27756)
* ci: ratchet synchronous child-process launches in main and shared code
The Electron main process freezes every window while a synchronous child
process runs, and on Windows each launch is slow because antivirus scans
it. Nothing stopped new synchronous launches from landing in main-process
code, and the spawn observer behind ORCA_MAIN_THREAD_DIAGNOSTICS only saw
async spawns, so the worst blocks were invisible to it.
Ratchet: check:main-sync-spawn-ratchet parses production src/main and
src/shared files (test-only paths excluded) and fails when a file not in
config/main-sync-spawn-baseline.txt reaches execFileSync, spawnSync or
execSync from child_process, runProcessSync from @orca/process-host, or
one of the synchronous secure-file writers (they run icacls on Windows).
Named, aliased, namespace, default, require() and import() forms count.
A baseline entry whose file no longer launches synchronously fails as
stale, so the list can only shrink. It runs in lint and in the PR
static-analysis job next to the process-host import check.
Observer: runProcessSync now reports to the spawn observer, timing the
whole call (that is how long the thread was stuck) and reporting from a
finally so failing, throwing and timed-out children are counted too.
Runtime behavior is unchanged except that the diagnostics observer now
sees synchronous launches; with no observer registered the sync path
pays one extra branch.
* ci: count sync launches per file and gate the repo's sync wrappers
The sync-spawn ratchet only tracked which files launched synchronously, so a
file already on the list (wsl.ts, win32-utils.ts, git-exec-file.ts) could add
more launches unnoticed, and new callers of the repo's own synchronous
wrappers passed CI.
The baseline now stores a per-file count of uses, in the same format as the
focus-read ratchet: a rise or a new file fails, a lower or zero count fails
until `pnpm check:main-sync-spawn-ratchet --prune` lowers it, and --prune
never raises an entry. Every value reference to a gated function counts
(call, hand-off, alias and its uses, re-export); imports alone count nothing.
Gated wrappers: gitExecFileSync (and the git/runner re-export),
restrictWindowsPathSync, hardenSecurePath when called with sync: true,
grantDirAcl, getWslHome, listWslDistros, wslUncDirectoryExists,
isWslAvailable (and the wsl.ts re-export), isPwshAvailable. Their 18
current caller files join the baseline.
Failure messages now give advice per family: the async process-host API for
child-process launches, the async twin for wrappers, and for the secure
writers a note that no async secure writer exists yet, so the sync one stays
and must never become a plain write (that drops the Windows ACL hardening).
Also refresh two stale blockMs descriptions and drop a no-op line from the
spawn-observer "stays silent" test.
* ci: count hardenSecurePath unless visibly async, and tighten ratchet tests
hardenSecurePath was counted only when `{ sync: true }` was written inline,
so moving the options into a variable or a spread dropped the count and the
check then asked for a prune. It now counts every use except a call with no
options or a spread-free object literal whose `sync` is absent or false.
Also: resolve backslash paths in the importing file as well as the
specifier; still visit heritage and instantiation expressions
(`extends mixin(fn)`, `fn<T>`); state the reference-only counting limit;
and point the secure-writer advice at raising this file's baseline count.
Tests cover the hardenSecurePath shapes, the alias chains that need the
fixpoint loop, and a prune that must not raise a grown row.
* test(ci): pin the main sync-spawn ratchet background step
* fix: count secure hardening calls with spread arguments
|
||
|
|
3fb4b06dbb |
perf(notifications): stop every Windows notification from freezing Orca (#27731)
* perf(notifications): save mobile dismissal history off the main thread Every agent notification recorded its id in mobile-notification-dismissals.json through the secure-file writer. On Windows that writer runs about five synchronous icacls launches per write, so each notification froze every Orca window (STA-9692). The file holds notification ids, sequence numbers and session ids, not credentials, so it now goes through the existing AtomicSnapshotWriter: async temp-plus-rename, mode 0600, bursts coalesced into one write of the latest entries. Memory stays authoritative, so delivery and retirement are unchanged. The pending write is flushed in the quit barrier after chat and RPC teardown, and in the pre-relaunch hook, since renderer relaunch exits without the quit pipeline. * perf(notifications): flush dismissal history when a headless host shuts down orcad never runs the desktop quit barrier, so its cleanup list awaits the pending dismissal write after RPC stops. * fix(persistence): drain a snapshot write requested just after a drain ends AtomicSnapshotWriter cleared its pending-write promise one microtask after the drain loop saw no more requests. A write() in that gap set writeRequested, got the settling promise back, and nothing drained it, so the newest bytes stayed unwritten and waitForPendingWrite() (the dismissal store's flush) resolved without them. The drain now clears the pending promise in the same tick its loop exits, on success and failure, so a later write() always starts a new drain. * test(notifications): flush dismissal writes before deleting temp state dirs Dismissal history now saves asynchronously, and the writer recreates its directory before each write. Tests that deleted their temp dir with a write still pending left recreated dirs behind in the system temp folder and could race the delete on Windows. They now await the controller's flush first. * perf(notifications): close dismissal history at committed exits The shutdown flush only waited for writes already queued. A notification recorded after it (orcad cleanups still running, or the 150 ms before a renderer relaunch calls app.exit) started an async write the exit dropped, so the phone could keep an alert the desktop could no longer withdraw. The store's flush() becomes close(): it waits for the pending write, and every later record saves synchronously through the snapshot writer's existing writeSync (plain fs, no icacls; a failure only warns). It is called only where the exit can no longer be vetoed: the will-quit teardown, a new exit-only hook that app:relaunch runs before app.exit(0) (restart and profile switches share onBeforeRelaunch but quit through will-quit), and orcad's cleanup after RPC stops. The quit test now holds chat teardown and RPC stop and proves the close starts only after both settle, in either order. The orcad push-startup runtime mock gains the method the entry now calls. * fix(notifications): bound the dismissal close before a renderer relaunch app:relaunch waited on the dismissal store's close with no limit, so a stalled userData disk left Restart stuck on "Restarting…" with a half-quit app saving synchronously. The close now races a 2 s cap through the existing settleWithinMs helper, the same cap the restart path's auth preservation uses, and logs when it gives up. * test(notifications): keep async dismissal writes under test between reads Mid-test disk reads used closeDismissals() as a barrier, which switched every later record to the exit-only synchronous path. The store gains a non-closing flush() (close() = mark closed + flush), exposed on the controller as flushDismissals for tests; mid-test barriers use it, and close stays only where a test simulates an exit or teardown. Also notes why the writer's pendingWrite assignment is safe, renames a store test to what it covers, and groups the orcad cleanup-order test under a describe. |
||
|
|
cef4d29096 |
refactor(renderer): name target converters by meaning; drop re-export shims (#27992)
Co-authored-by: m4air <m4air@Mac.localdomain> |
||
|
|
61aaa98eb3 |
refactor(renderer): remove the keyboard-handlers barrel and partial re-exports (#27986)
Co-authored-by: m4air <m4air@Mac.localdomain> |
||
|
|
f8daed0d98 |
refactor(renderer): remove the web-session-tabs-sync barrel (#27969)
Co-authored-by: m4air <m4air@Mac.localdomain> |
||
|
|
41fc9542b3 |
fix(ssh): never start a hidden orcad shell for a tab the legacy relay bridge serves (#27939)
* fix(ssh): never start a hidden orcad shell for a pane the legacy relay bridge serves * fix(ssh): a skipped bridged activation still supersedes an earlier tab's pending focus * fix(ssh): a click on a whole mirrored tab never activates its bridged pane on orcad * fix(ssh): load the bridge pane list lazily from tab activation --------- Co-authored-by: m4air <m4air@Mac.localdomain> |
||
|
|
bce860fec8 |
refactor(renderer): remove the web-runtime-session barrel (#27967)
Co-authored-by: m4air <m4air@Mac.localdomain> |
||
|
|
33ddc1ab7f |
fix(ssh): an old relay terminal's tab shows output again after a reconnect (#27951)
* fix(ssh): a bridged pane reattaches after a disconnect instead of going deaf * fix(ssh): a bridged pane asks again when it turns live during an earlier ask --------- Co-authored-by: m4air <m4air@Mac.localdomain> |
||
|
|
822bd9f972 |
fix(ssh): keep 1.4.224's running terminals working after the upgrade (legacy relay bridge) (#27787)
* wip: legacy relay bridge client
* Revert "fix(ssh): end an older Orca's relay terminals from the connect refusal (STA-10166) (#27748)"
This reverts commit
|
||
|
|
564693f176 |
fix(cli): honor Chat UI setting for worktree agent creation (#27796)
* fix(cli): honor Chat UI setting for worktree agent creation * fix(cli): preserve older host launch and worktree lineage warnings |