Commit Graph
454 Commits
Author SHA1 Message Date
Jinwoo HongandOrca b665708c7f fix(ssh): make fs.listFiles cancellable and single-flight per relay client (#7769)
Co-authored-by: Orca <help@stably.ai>
2026-07-08 12:01:06 -07:00
Jinwoo HongandOrca 98bee1b419 Fix un-removable SSH ghost worktrees after host remove/re-add (#7753)
Co-authored-by: Orca <help@stably.ai>
2026-07-07 19:01:42 -07:00
Jinwoo HongandOrca 79d0de9c33 fix(terminal): remote query-reply corruption (#7329) + snapshot grid-width repaint (#7279) (#7736)
Co-authored-by: Orca <help@stably.ai>
2026-07-07 16:42:43 -07:00
Brennan BensonandOrca fed545d330 Stop wiping the shared WebGL glyph atlas on plain window refocus (#7604)
Co-authored-by: Orca <help@stably.ai>
2026-07-07 12:43:53 -07:00
NeilandOrca f057c9d664 perf(pty): cap the unsent main→renderer PTY backlog (Win/Linux GB-scale leak) (#7630)
* perf(pty): cap the unsent main→renderer PTY backlog (Win/Linux GB leak)

The per-pty `pendingData` string in ipc/pty.ts (main's unsent
main→renderer output queue) had no size cap. The 512 KB/pty + 8 MB
in-flight caps bound only SENT-but-unacked bytes; `onData` appends to
`pendingData` unconditionally and `flushPendingData` merely `continue`s
(no drain) while ack-gated. On Windows/Linux the main renderer is
background-throttled — `setBackgroundThrottling(false)` is set only for
`process.platform === 'darwin'` — and Chromium freezes hidden pages
after ~5 min, so a backgrounded Orca with an active/verbose agent stops
ACKing: in-flight pins at 8 MB, the flush gates, and `pendingData` grows
at raw PTY throughput → MB→GB in the main process. macOS is unaffected.

Fix: cap the backlog to the most-recent PENDING_DATA_MAX_CHARS (2 MB,
matching the daemon pendingOutput and renderer scheduler caps),
advancing startSeq by the dropped-char count (the same arithmetic
flushPendingData uses when slicing), and set a `droppedBacklog` flag on
the next payload. The renderer's dataCallback sees the flag and calls
the existing markHiddenOutputRestoreNeeded(), so on hidden→visible it
rebuilds the dropped span from the main headless snapshot — no output is
lost within scrollback depth. Background agents keep writing full-speed
into the runtime buffer; only the redundant delivery copy is trimmed.
The flag threads through the same layers as the existing `background`
field: main payload → preload types → dispatcher PtyDataMeta → renderer.

Tests (red→green): a daemon pty emits 5 MB while the renderer never
ACKs. Without the cap the flag is never set (fail); with it the first
emitted chunk carries droppedBacklog exactly once and delivery stays
bounded. Ordinary small output never sets the flag.

Note: leak + fix are Win/Linux-only (macOS is throttling-exempt), so not
reproducible at runtime on macOS; evidence is the code trace + tests.

* test(pty): clarify that the sent-total assertion is a sanity bound, not the cap proof

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-06 20:58:53 -07:00
PP 0b4196fc17 fix(ssh): stop deleted ~/.ssh/config hosts from reappearing on sync (#7302)
Deleting a config-sourced SSH target had no lasting effect: the Manage-SSH pane
re-imports ~/.ssh/config on open, and the import was a pure upsert with no record
of deletions, so the just-deleted host was re-inserted verbatim from the config
that still exists on disk.

Persist a `deletedSshConfigAliases` tombstone set:

- Deleting a config-managed target (source 'ssh-config', or an adopted legacy
  import) records its alias; manual targets are never tombstoned.
- The passive on-open sync skips tombstoned aliases, so a deleted host stays
  deleted.
- Re-adding or editing a target reclaims its alias, and the explicit Import
  action (`reAdopt`) clears all tombstones to deliberately re-adopt config.

This also fixes the edit-then-reappear case: editing a config host to `manual`
already reserved its current alias, and reclaim covers alias changes.
2026-07-06 18:35:42 -07:00
Jinwoo HongandOrca 9981c3b827 Check for Updates: cmd/ctrl-click fetches latest perf-tagged prerelease (#7278)
Co-authored-by: Orca <help@stably.ai>
2026-07-05 02:53:17 -07:00
Jinwoo Hong 812f24bd19 fix(terminal): clear PTY-side buffers on Ctrl+K so the prompt stops repainting at a stale row (#7413) 2026-07-05 04:36:24 -04:00
guihirsch f11a2109c8 feat(rate-limits): add MiniMax token control (#7387) 2026-07-04 23:33:25 -07:00
Brennan BensonandOrca b099e27703 fix(checks): keep a merged PR visible when the worktree sits behind its own PR head (#7277)
Co-authored-by: Orca <help@stably.ai>
2026-07-03 19:30:10 -07:00
2789a67604 feat(browser): add Copy to context menu when text is selected (#7159)
Co-authored-by: Orca <help@stably.ai>
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
2026-07-03 16:52:08 -07:00
42de074152 fix(browser): keep isolated profile storage on its partition (#6958)
* fix(browser): keep isolated profile storage on its partition

* fix(browser): keep cloned isolated tabs on their resolved partition

* fix(browser): thread resolved partition through UI profile switches

Keep the isolated-storage invariant universal: UI-initiated profile
switch/create now persist the resolved partition alongside the profile
id, matching the runtime path, so a tab stays on its partition even if
the renderer profile mirror is later stale (issue #6923).

Co-authored-by: Orca <help@stably.ai>

* fix(browser): persist resolved partition through session restore

Add sessionPartition to the workspace-session zod schema so the resolved
partition survives persist->load; without it zod strips the field and a
restored isolated tab whose profile mirror is stale at startup falls back
to the shared default partition, reopening the storage leak (#6923).

Fold the webview teardown branches (parent drift or partition mismatch)
into one and re-resolve the viewport container once, matching the
pre-refactor null-guard behavior.

Split the sleeping-agent parse tests into their own file to keep both
under the 800-line cap.

Co-authored-by: Orca <help@stably.ai>

* fix(browser): keep CLI-created tabs inheriting the default profile

browserTabCreate without an explicit profile was sending sessionProfileId:
null, which the renderer store treats as 'no inheritance' (its guard is
!== undefined). That forced CLI-created tabs onto the shared default
partition even when the user had configured a default browser session
profile, silently changing behavior a #6923 fix should not touch.

Leave sessionProfileId/sessionPartition undefined when no profile is named
so the renderer applies default-profile inheritance; thread the resolved
partition only when a profile is explicitly chosen.

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
2026-07-03 13:58:40 -07:00
gatsby74 5da41d4ed6 [codex] Add file explorer open in terminal
Add an Open in Terminal action for file-explorer directories and preserve terminal startup cwd through restore/session paths.
2026-07-02 16:47:38 -07:00
JinjingandOrca 6142ec1a06 Refresh AI Vault session list on window refocus (#7075)
* Refresh AI Vault session list when the window regains focus

Sessions started after the panel mounted never appeared until a manual
refresh, since the hook only scanned on mount and scope changes. Listen
for window focus and visibilitychange (to visible) while the panel is
mounted and trigger a non-force re-scan, letting the main process's 15s
scan cache rate-limit rapid focus flips. The manual refresh button keeps
its force (cache-bypassing) behavior.

Co-authored-by: Orca <help@stably.ai>

* Keep AI Vault refocus refresh render-free on cache hits

Refocus refreshes now run as background: the loading flag stays down (no
spinner flash on every alt-tab back), and when the main process replays
the cached snapshot (same scope key + scannedAt) the state updates are
skipped entirely so nothing re-renders. Fresh scans and the manual force
refresh apply results exactly as before.

Co-authored-by: Orca <help@stably.ai>

* Bypass AI Vault scan cache on panel entry and refocus, throttled

Non-force refreshes were served the 15s-old cached snapshot, so a
session started right before re-entering the panel or refocusing the
window still didn't appear — only the manual force refresh showed it.
Panel entry and refocus now request a force scan, throttled in module
scope to one forced scan per 5s (surviving panel remounts), so rapid
tab/focus flips still resolve from the main-process cache. Manual force
refreshes count against the throttle to avoid back-to-back full scans.

Co-authored-by: Orca <help@stably.ai>

* Deliver AI Vault refocus via main-process signal; calm in-app triggers

Renderer DOM focus/visibility events never fire on macOS app activation
(verified live: document.hasFocus() stays true and no focus/blur event
lands when the window loses/gains OS focus), so the refocus refresh was
inert. Main now broadcasts browser-window-focus to the renderer as
aiVault:windowFocused and the hook subscribes to that instead.

Sessions started inside Orca (window never blurs) get their own trigger
from agent hooks: re-scan only when an unseen provider session id
appears in agent status. State transitions and message pings on known
sessions are deliberately ignored — keying on them made the panel churn
on every AI message. Event-driven rescans that land inside the 5s
throttle window defer to one trailing scan instead of being dropped.

Verified end-to-end against a dev instance: a headless claude session
appeared in the panel on OS-level refocus and another on panel
re-entry, without the manual refresh button.

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-02 01:45:03 -07:00
Jinwoo HongandOrca 8afa84af82 Fix terminal switch input lag from daemon session listing (#7002)
Co-authored-by: Orca <help@stably.ai>
2026-07-01 13:19:50 -07:00
Jinwoo HongandOrca 8fa2c4a203 Fix terminal resume width flicker (#6989)
Co-authored-by: Orca <help@stably.ai>
2026-06-30 22:27:08 -07:00
cbd06a7671 feat(tab-bar): add shortcut to open commands for active tab group (#6325)
* Add keyboard shortcut to toggle the Quick Commands menu

- New `tab.openQuickCommandsMenu` keybinding action (no default binding)
- TabBarQuickCommandsMenu listens for the binding and toggles open/closed
- Scoped to the active tab group naturally since the component only mounts when its group is focused

* Show keyboard shortcut in Quick Commands menu trigger tooltip

* Add tests

* expand tests

* Expand keyboard toggle to call handleOpenChange and skip repeated keys

- Replace `setMenuOpen` toggle with `handleOpenChange(!menuOpen)` so closing
  via keyboard runs the same reset logic (query, focus frame, value override)
- Guard against key-repeat events to prevent rapid toggling on held key
- Wrap `handleOpenChange` in `useCallback` so it's stable enough to include
  in the `useEffect` dependency array without causing spurious re-registrations
- Update tests to reflect that re-running the effect between presses is
  required for the close path, and add a repeat-event test

* Add docstring to withShortcutHint func

* review: harden quick commands menu shortcut

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
Co-authored-by: Orca <help@stably.ai>
2026-06-30 18:21:37 -07:00
Wolfie 62ab2d470e fix: keep floating tabs local with active runtime
Keep the synthetic floating workspace local while a remote runtime is active, including terminal/browser creation, activation, close, and remote snapshot handling.

Maintainer follow-ups:
- require worktreeId for runtime-session terminal create payloads
- add renderer-backed terminal create reply sender regression coverage
- merge current main and keep the WSL readDir breadcrumb test aligned with main's Windows-only handler coverage
2026-06-30 12:48:26 -07:00
Jinwoo HongandOrca 39964149c8 Per-Workspace Environments (on-demand disposable runtimes) + Add Project remote host setup (#6320)
Co-authored-by: Orca <help@stably.ai>
2026-06-30 11:31:55 -07:00
Jinwoo HongandOrca 6cdd2ed946 Harden daemon degraded-spawn handling (follow-up to #6830) for #6814 (#6866)
* Harden merged #6830 daemon degraded-mode handling

- shutdownFallbackSessions is now best-effort: a single un-killable local PTY
  no longer throws and aborts the daemon restart (which would leave the user's
  recovery path unusable, recreating the original lockup). Logs and continues.
- checkPtySpawnHealth retries once (timeout raised 2s->4s) so a transient stall
  on a busy machine right after an upgrade does not mis-classify a healthy
  daemon as unable to spawn PTYs and silently drop new terminals to the local
  provider without daemon persistence.
- Surface degraded mode: DegradedDaemonPtyProvider exposes isDegraded, and
  pty:management:listSessions returns { degraded } so the session UI can warn
  instead of it being a silent console.warn. Clearer actionable warn message.

Refs #6814.

Co-authored-by: Orca <help@stably.ai>

* Add #6814 daemon failure-mode classification test

Drives the real DaemonServer + checkDaemonHealth over a real socket to lock in
the healthy / degraded(pty-spawn-unhealthy) / wedged(unreachable) / unreachable
classification. Documents the load-bearing boundary that the degraded-daemon
fallback rescues a degraded daemon but NOT a fully wedged one.

Co-authored-by: Orca <help@stably.ai>

* Add degraded flag to web preload listSessions stub (typecheck parity)

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Orca <help@stably.ai>
2026-06-30 01:29:45 -07:00
Neil 6130340229 fix: answer startup terminal color queries (#6824) 2026-06-29 21:37:49 -07:00
Neil 380db44d77 Measure and batch restored terminal startup (#6836) 2026-06-29 20:37:26 -07:00
a56da57bb8 Add worktree labels for localhost ports (#6424)
* Add worktree labels for localhost ports

* Fix localhost label test fixtures

* Extend localhost worktree labels to agents

* Address localhost label review feedback

* Remove localhost label page injection; stream responses untouched

Drop the title/favicon HTML injection from the localhost label proxy.
The proxy now only relabels the hostname and pipes responses straight
through, so app CSP/cookies/bodies are preserved and large/streamed
responses are no longer buffered in memory.

- Normalize wildcard bind hosts (0.0.0.0 -> 127.0.0.1, :: -> ::1) before
  using them as a proxy connect target.
- Delete the favicon SVG generator and dead repoIcon/badgeColor plumbing.
- Remove orphaned LocalhostLabelMock i18n keys.

Co-authored-by: Orca <help@stably.ai>

* Harden localhost label proxy from review

- IPC register: restrict proxy target to loopback or a matching scanned
  workspace port (close open-proxy/SSRF surface from untrusted renderer).
- Proxy: guard against ERR_HTTP_HEADERS_SENT on mid-stream upstream error;
  add error/cleanup listeners on client request/response and upgrade socket.
- labelLocalhostUrl: fall back to the raw URL when the proxy rejects a
  target (e.g. https) instead of throwing.
- Port label route: include worktreePath so button-open and terminal/CLI
  paths produce the same label.
- Terminal OSC link hover: discard stale async tooltip results via a hover
  token, matching the WebLinks path.

Co-authored-by: Orca <help@stably.ai>

* Match default-port advertised hosts in localhost label target check

Co-authored-by: Orca <help@stably.ai>

* Consolidate duplicated localhost label helpers

- Move the loopback host set, loopback-URL parser, and wildcard
  connect-host normalizer into shared/localhost-worktree-labels.ts; proxy,
  runtime, terminal link routing, and the IPC guard now share one copy.
- Extract the port -> repo -> worktree -> project label-route lookup into
  workspace-port-localhost-label-selector.ts (a hook plus an imperative
  resolver), replacing the block triplicated across the ports surfaces.

Co-authored-by: Orca <help@stably.ai>

* Extract command-code prompt-status seed to its own module

Keeps launch-agent-in-new-tab.ts under the max-lines limit after the
localhost-hint additions, without a lint disable.

Co-authored-by: Orca <help@stably.ai>

* Remove agent-facing localhost mechanism

Orca does not mutate user prompts or inject prompt snippets, so drop the
localhost-open agent hint entirely:

- Remove appendLocalhostOpeningHint / includeLocalhostOpeningHint and the
  hint constant from tui-agent-startup; agent prompts are no longer rewritten.
- Remove the ORCA_LOCALHOST_OPEN env var from local agent terminals (it was
  only discoverable via the now-removed hint).
- Remove the orca localhost label|open CLI commands, their workspacePorts RPC
  methods, and the runtime labelLocalhostUrl/openLocalhostUrl methods.

The feature is now purely structural: the loopback label proxy plus the
ports-panel and terminal-link 'Open in Browser' paths, which surface a
clickable labeled URL without touching agent prompts.

Co-authored-by: Orca <help@stably.ai>

* Make localhost worktree labels opt-in (default off)

Serving a dev app under a different host than localhost:<port> can break
apps that bind cookies/sessions to localhost, so the feature should not
change existing users' Open-in-Browser behavior on upgrade.

- Default localhostWorktreeLabelsEnabled to false.
- Flip the gates to enable only when explicitly true (=== true / !== true)
  instead of treating undefined as enabled.
- Update the setting switch to checked only when explicitly enabled.
- Drop the now-unused runtime store settings field.

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
Co-authored-by: Orca <help@stably.ai>
2026-06-29 12:27:30 -07:00
2739310839 fix(terminal): converge post-spawn PTY size reconcile to fix split-mount column desync (#6725)
* fix(terminal): converge post-spawn PTY size reconcile to fix split-mount column desync

Follow-up to #6644/#6649. Those added a post-spawn PTY reconcile but capped
it at a FIXED 12 requestAnimationFrames whose counter advanced even on frames
where the pane was unmeasurable or the split layout had not yet equalized. When
a tab MOUNTS with a split layout already present (a new worktree opened with the
side split panel on), the real narrow split width settles AFTER frame 12, so the
reconcile gave up while xterm had reflowed narrow and the PTY stayed pinned at
the wide spawn width. The corrective xterm onResize is dropped during the hidden
mount window (isRendererPtyResizeAuthoritative() is false), so process.stdout.columns
stayed wide and interactive TUIs (Claude Code) rendered garbled until a manual resize.

Extract the reconcile into pty-size-reconcile.ts with an authoritative-gated
convergence loop instead of a fixed frame budget:
- While the pane is hidden (onResize dropped), the reconcile is the SOLE corrector:
  it keeps polling and forwarding every grid change (its transport.resize bypasses
  the visibility gate). Hidden frames never advance the settle counter.
- Once visible AND stable for SETTLE_FRAMES, it hands off to the live
  onResize/ResizeObserver path, which reliably catches any later reflow.
- Hard cap (MAX_FRAMES) guarantees termination; mobile-fit/locked frames are
  skipped; the reconcile handle is cancelled on dispose.

Harness: pty-size-reconcile.test.ts (14 tests) drives the loop with a deterministic
frame scheduler; the desync-repro tests fail against the old 12-frame logic and
pass on the fix. Adds an e2e "MOUNTS with a split layout present" test.

Caveat: headless Electron does not reproduce this layout-settle-after-rAF race
(the existing golden e2e passes even against the old buggy logic), which is why
#6644/#6649 merged with green e2e yet the bug persisted. The unit test is the
real regression harness; the e2e tests are integration smoke.

Made with [Orca](https://github.com/stablyai/orca)

Co-authored-by: Orca <help@stably.ai>

* fix(terminal): re-assert PTY size on visibility resume to heal stubborn column desync

Follow-up within the same fix: the user reported "sometimes even resizing
doesn't fix it." Root cause beyond the mount-timing race — the renderer forwards
resizes fire-and-forget and dedupes on the size it THINKS it sent, but never
learns the PTY's actual size. A resize dropped main-side (the pane was hidden,
a mobile take-back resize-suppression window, or a provider no-op) leaves xterm
and the PTY silently diverged; a later same-cols layout fires no onResize, so it
never self-corrects.

Expose the PTY's last-APPLIED size to the renderer and re-assert on show:
- New read-only IPC pty:getSize -> ptySizes.get(id) (the size written only when
  a resize actually lands or at spawn — the authoritative "what the PTY believes
  it is"). Wired through preload (window.api.pty.getSize) + api-types.
- On visibility resume (noteVisibilityResume), the pane re-fits, reads the PTY's
  real size, and re-asserts via forwardPtyResize ONLY on genuine drift — so no
  spurious SIGWINCH on an already-synced resume (which would jar alt-screen TUIs).
  Routed through forwardPtyResize so the authoritative/mobile gates are
  re-checked at send time; remote-runtime PTYs (separate viewport channel) are
  skipped; overlapping resumes coalesce to one query.

Also register pty:getSize in the registerPtyHandlers removeHandler cleanup block
so re-registration (macOS re-activate / new window) doesn't throw on a duplicate
ipcMain.handle, and make the pty IPC test mock throw on duplicate channels like
real Electron so this class of omission is caught going forward.

Tests: 7 resume-reassert cases (drift / match / null / remote-skip /
mobile-fit-skip / hide-during-hop / overlap-coalesce), all non-vacuous. Full
terminal-pane + pty IPC suites green (1494 tests); typecheck (web+node) + oxlint
clean; e2e desync specs pass against a fresh build.

Made with [Orca](https://github.com/stablyai/orca)

Co-authored-by: Orca <help@stably.ai>

* Stub PTY getSize API and skip redundant Wayland GPU sandbox tests

- Implement PTY `getSize` stub in `web-preload-api.ts` to satisfy API
  requirements for the web-preload environment.
- Skip the unfixed Wayland GPU sandbox negative control test if the
  target base branch already contains the sandbox workaround.

---------

Co-authored-by: Orca <help@stably.ai>
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
2026-06-29 12:12:35 -07:00
Brennan BensonandNeil 5fd104a17b Fix runtime GitHub work item details (#6473)
* Fix runtime GitHub work item details

* Update web GitHub API parity test

* Address runtime GitHub action guards

* Fix runtime details cache invalidation

* test: add #6429 regression guard for runtime details routing

* Extract GitHub runtime source helpers

---------

Co-authored-by: Neil <neil@stably.ai>
2026-06-29 01:11:43 -07:00
lvfenandJinjing b776506809 Support submodule diffs and upstream-base compares in Source Control (#6350)
* feat(source-control): show submodule diffs with lazy expansion

Dirty submodules now expand inline in Source Control to reveal their
inner changes, with file-level diffs that are read-only from the parent
worktree. Inner status is fetched lazily only when a submodule is
expanded, so status polling never recurses into (possibly nested)
submodules. Adds a submodule-status path across local and SSH runtimes
and git providers.

* feat(source-control): add compare-against-current-branch setting

Adds a global setting (default off) that defaults the Source Control
compare base to the current branch's upstream so the panel prioritizes
local changes instead of the full delta versus the repository default
branch. When the branch has no upstream, the compare view falls back to
working-tree-only. This affects only the compare/diff view; the Pull
Request and rebase merge target are unchanged.

* refactor(source-control): extract submodule status hook and entry-action gates

Moves the lazy submodule-expansion state into a useSourceControlSubmoduleStatus
hook and centralizes per-row stage/unstage/discard eligibility into
source-control-entry-actions, shrinking SourceControl.tsx and keeping the
read-only submodule rules consistent across the row UI, bulk actions, and tests.
The hook adds a generation guard so a slow submodule-status response from a
previous worktree (common over SSH) can't write stale status into the current
panel. On the relay side, configured submodule paths are read through a
short-TTL per-instance cache so a burst of diff clicks does not re-read
.gitmodules over the SSH link. Adds tests for the new modules.

* fix(source-control): address submodule/compare review feedback

- Degrade git.submoduleStatus to an actionable reconnect hint when an older
  SSH relay lacks the RPC, mirroring clone()/worktreeIsClean fallbacks.
- Keep the branch-compare summary while upstream status is still loading so
  it no longer flickers when switching worktrees with prefer-upstream on.
- Mark the compare-base switch as type="button" to avoid form submission.
- Add diff base / source control keywords to the Git settings search catalog.
- Assert the compare-base toggle's own switch state and updateSettings call.

* fix(source-control): address second-round submodule/compare review feedback

- Route submodule inner diffs through resolveSubmoduleWorktreePath so a
  crafted .gitmodules path can't escape the selected worktree
- Clear statusReadsInFlight alongside the diff dedupe on git mutations so a
  post-mutation getStatus() can't join a stale in-flight read
- Clear the SSH diff dedupe in getSubmoduleStatus to mirror getStatus
- Derive list-view selection from the submodule-injected rows so expanded
  submodule children are selectable
- Refresh commit history when the upstream compare base changes

* Support staged submodule expansion and refine default compare base

- Support expanding and diffing staged submodule changes (HEAD vs index) independently of unstaged changes (index vs worktree).
- Track submodule expansion states using a compound key of area and path to prevent conflicts between staged and unstaged listings.
- Update the compare-against-upstream setting to a segmented control for the "Default Compare Base" policy.
- Fall back to the repository default branch when comparing a branch with no upstream, preventing comparison views from unexpectedly disappearing.

* Fix submodule staging behavior, WSL caching, and double-click toggles

- Namespace submodule path cache per WSL distro to prevent cross-distro
  collisions.
- Preserve the staged area of child entries when expanding unstaged
  submodules so staged inner changes do not open empty diffs.
- Prefix oldPath with the submodule path for renamed inner entries.
- Ignore click events where detail > 1 to prevent double-clicks from
  instantly collapsing newly expanded submodules.

* Secure submodule path resolution and prevent stale status updates

* Extract and centralize submodule path validation into a new
  `resolveSubmoduleWorktreePath` helper to prevent path traversal
  exploits when resolving paths from untrusted `.gitmodules` files.
* Invalidate submodule expansion state and increment the query
  generation whenever the active runtime environment or connection
  route changes, preventing out-of-order responses from writing
  stale data.

* Set git identity via CLI config options in test commits

- Extract test email and name into constants.
- Use `-c` config flags to pass user identity to `git commit` dynamically.
- This ensures commits succeed in submodule checkouts or CI environments
  where a local or global identity is not configured.

---------

Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
2026-06-28 23:43:33 -07:00
Jinwoo HongandOrca 41e5c3918b Add cancel flow for Claude account login (#6702)
Co-authored-by: Orca <help@stably.ai>
2026-06-28 22:33:07 -07:00
Jinjing 61bb2f9df1 Fix macOS IME input mode detection (#6699)
* Gate punctuation forwarding on input source feature

Ensure ASCII and CJK direct punctuation keys are only treated as keydown
candidates if `forwardAsciiPunctuation` is enabled in the current input
source features. This avoids forwarding punctuation when the feature is
disabled, satisfying requirements where certain input methods (such as
Vietnamese) should not have punctuation forwarding enabled.

* Fix macOS IME input mode detection
2026-06-28 21:32:13 -07:00
42b2ecc5c6 feat(emulator): Android emulation via scrcpy (cross-platform, iOS parity) (#6434)
* docs: add Android emulation design spec

Adds the design for first-class Android emulator support as a cross-platform
peer of the iOS simulator feature: an extracted EmulatorBackend interface
(iOS + Android), full AVD lifecycle management via the Android SDK, a live
scrcpy H.264 pane decoded in-renderer with WebCodecs, the full control surface
(tap/gesture/type/buttons/rotate), accessibility tree, app install/launch,
runtime permissions, logcat, and a dedicated orca-emulator-android skill.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(emulator): add EmulatorBackend interface + backend/codec session tags

First step of multi-backend emulator support: introduce the EmulatorBackend
type and tag each session with its backend kind + stream codec, defaulting to
ios/mjpeg so existing serve-sim behavior is unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(emulator): extract IosEmulatorBackend and make the bridge a router

Move the serve-sim/simctl device + helper + input mechanics out of
EmulatorBridge into IosEmulatorBackend (implementing EmulatorBackend). The
bridge now owns the session registry and lifecycle orchestration and routes
each command to the backend that owns the target device. iOS behavior is
unchanged; the existing bridge tests pass untouched and the backend gains its
own input-op coverage.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(emulator): add pure Android leaf modules (sdk/adb/avd/scrcpy/input/ax)

Dependency-injected building blocks for the Android emulator backend, each unit-
tested in isolation: SDK + tool discovery, adb device/output parsing, AVD list +
boot arg building, scrcpy control-socket byte encoders, normalized<->pixel +
keycode mapping, and a uiautomator XML accessibility-tree parser. Not yet wired;
AndroidEmulatorBackend composes these in the next phase.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(emulator): gate availability on the iOS backend + lock it with tests

inspectEmulatorAvailability now decides iOS host support via the registered iOS
backend instead of a bare platform literal, routing the decision through the
multi-backend seam. Output shape and all messages are unchanged (the settings
pane still reads simctl/serveSim). Adds the previously-missing regression tests
covering the unsupported, ready, no-devices, and tool-failure paths.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(emulator): add Android app/permissions/logcat arg builders

Pure adb arg-builders + a logcat line parser for app install/launch, runtime
permission grant/revoke/reset, and logcat capture. Unit-tested in isolation;
wired into AndroidEmulatorBackend's capability verbs in a later phase.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(emulator): AndroidEmulatorBackend device management + unified device list

Adds the Android backend (registered alongside iOS in the bridge): SDK-gated
host support, device/AVD discovery and merge, AVD boot + boot-completion wait,
shutdown, and tap/swipe/type/button/rotate/exec via `adb shell input` so control
works without the scrcpy server (the live H.264 stream lands in the streaming
phase). Surfaces everything through a new cross-platform `orca emulator devices`
command (RPC emulator.listDevices -> bridge.listAllDevices) with a platform
column. Device inventory is split into its own module to keep files focused.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(emulator): Android capability verbs (install/launch/permissions/ax/logcat)

Wires the Android capability operations into AndroidEmulatorBackend and exposes
them through a capability-gated bridge router (runCapability), RPC, and CLI:
- orca emulator install/launch/permissions/ax/logcat
Capabilities are advertised per backend; calling one on a backend that lacks it
(e.g. iOS) fails with emulator_unsupported instead of a silent no-op. Input ops
and capability ops are split into focused modules to keep files under the
line cap; the runtime shares one target-param type.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(skill): add orca-emulator-android skill + cross-ref from iOS skill

Documents the cross-platform Android emulator control surface (devices, input,
hardware buttons, rotate, install/launch, permissions, ax, logcat) driveable via
the orca CLI today, and notes the live visual pane is in development. Points the
iOS skill's "when not to use" at the new Android skill.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(emulator): Android live-pane streaming scaffolding (scrcpy + WebCodecs)

Builds the H.264 video path as scaffolding: scrcpy frame/codec-meta parsing,
server-deploy arg builders, control-protocol encoders (committed earlier), the
stream session (server + sockets), a video pub/sub registry, the
emulator:videoStream* IPC channel, and a renderer WebCodecs->canvas hook. Pure
framing/deploy/registry are unit-tested; the socket/WebCodecs/jar integration is
clearly flagged UNVERIFIED and the remaining wiring (startSession, preload,
pane codec branch, packaging the jar) is documented in
docs/android-emulation-streaming.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: fix streaming notes doc path in video-stream hook comment

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(emulator): add diagnostic probes for Android testing

Adds an emulator-probe logger (console + temp file at
os.tmpdir()/orca-android-emu-probe.log) and wires probes at four layers so
errors surface during manual testing: every emulator.* RPC call + error (RPC
dispatcher), every adb/emulator command + non-zero exit (command runner), and
the scrcpy session + video-stream IPC lifecycle. Temporary diagnostics; remove
or gate behind a flag once the Android pane is validated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(emulator): show Mobile Emulator settings cross-platform + aggregate Android availability

The Mobile Emulator settings section is no longer macOS-gated (Android works on
Windows/Linux), and inspectEmulatorAvailability now aggregates the iOS and
Android backends: Android devices/AVDs appear in the device list and a host
without iOS gets the Android setup message instead of "requires macOS".

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(emulator): show Mobile Emulator sidebar nav entry on non-mac desktops

The settings sidebar nav registered the Mobile Emulator entry behind isMac, so
it stayed hidden on Windows/Linux even after the section content was ungated.
Widen it to showDesktopOnlySettings to match the section.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(emulator): wire Android startSession to scrcpy + client-downloaded jar

AndroidEmulatorBackend.startSession now boots the device, ensures the scrcpy
server jar (downloaded by the client into the per-user cache on first use, not
bundled), starts a ScrcpyStreamSession, and feeds its H.264 frames to the video
registry; stopHelperForDevice tears it down. Sessions carry their backend kind
so worktree-active routing picks the right backend. Boot, host SDK discovery,
and the stream starter are split into focused modules to stay under the line cap.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(emulator): stop the iOS backend from claiming Android devices off-mac

iOS ownsDevice now returns false unless the host supports it, so on Windows an
Android serial routes to the Android backend instead of erroring with
"requires macOS". Backend-for-device fallback prefers a host-supported backend.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(emulator): scrcpy scid 31-bit + retry video socket until server delivers

Two fixes validated against a real emulator: scrcpy parses scid as a signed
32-bit hex int, so mask to 31 bits + pad to 8 digits (8-byte values overflowed
and the server exited). And adb accepts the forwarded TCP connection before the
server's abstract socket exists then resets it, so retry the video socket until
it actually delivers the dummy byte before connecting control. H.264 meta now
arrives (576x1280). Adds socket/server-exit diagnostics probes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(emulator): render the Android H.264 pane via WebCodecs

Wires the live Android pane end-to-end: preload exposes emulator video stream
APIs; the pane's device list uses the unified emulator.listDevices (Android +
iOS); and emulator-screen-stream-content renders a WebCodecs <canvas> for
scrcpy:// sessions (H.264, SPS/PPS prepended to the first keyframe) instead of
the MJPEG <img>. The video hook reports the stream size for the device frame.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(emulator): buffer the current GOP for late video subscribers

The renderer subscribes after attach already started the scrcpy stream, so the
registry now caches the current GOP (keyframe + following deltas) alongside the
codec meta and config, and replays it on subscribe. A pane opened mid-stream
decodes from the keyframe immediately instead of showing black until scrcpy's
next periodic keyframe (~10s). Refreshes the now-validated session doc comment.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(emulator): show New Mobile Emulator tab action off macOS

The tab create menu and its dropdown item gated the New Mobile Emulator action
on isMacOs, hiding it on Windows/Linux where Android emulation is now supported.
Gate on mobileEmulatorEnabled + onNewSimulatorTab (already cross-platform) so
the action appears wherever a mobile emulator backend is available.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(emulator): open the Mobile Emulator tab off macOS

openMobileEmulatorTab and ensureSimulatorTab both returned null unless the host
was macOS, so the New Mobile Emulator action no-opped on Windows/Linux even
though the menu entry showed. Drop the isMacOsHost early-returns; the
mobileEmulatorEnabled setting and backend availability already gate the feature.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(emulator): resolve a default attach device across backends

emulatorAttach with no device fell back only to the iOS listSimulators picker
(empty on Windows/Linux), so the pane's no-device launch flow errored. Extract
resolveDefaultAttachDevice: iOS default first, else the first booted (else
first) device across host backends, so Android attaches without an explicit
device. Split into its own module to stay under the line cap.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(emulator): render the pane off macOS instead of an unavailable wall

EmulatorPane short-circuited to the "macOS only" EmulatorUnavailablePane on any
non-Mac host, blocking the now-working Android pane. Always render the pane
content; its device discovery and error surface handle a missing backend.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(emulator): let attach boot a shut-down AVD with a stale active session

getReusableActiveForWorktree called resolveDeviceId on the requested device,
which throws for a not-yet-booted Android AVD, aborting the attach. Guard it so
a resolve failure means "not the active device" and the attach falls through to
a fresh boot — so picking a shut-down AVD in the pane and hitting Connect boots
it via ensureBooted instead of erroring.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(emulator): launch the AVD detached instead of via execFile

bootAndroidDevice started the emulator through the command runner (execFile with
a timeout + 1MB stdout maxBuffer), which kills the long-running, verbose emulator
process — so booting an AVD from the pane never actually came up. Spawn it
detached with no stdio and unref it so it outlives the call, mirroring how the
scrcpy server is launched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(emulator): dedupe concurrent attaches into one scrcpy stream

Extract AndroidStreamController to own the per-serial scrcpy lifecycle and
dedupe starts: concurrent attaches (e.g. the pane's auto-attach racing the tab
launch) now share one in-flight start and reuse the live stream instead of
spawning a second scrcpy server that fights for the port and kills the first.
Also initialize the registry GOP buffer in register() (latent type error).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(emulator): boot the AVD headless without a console window

The detached spawn opened a Windows console (showing the emulator's verbose
qemu/netsim logs) and a redundant native emulator window. Pass windowsHide and
run the emulator with -no-window so it boots headless — the scrcpy pane is the
view, matching how iOS hides Simulator.app.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(emulator): boot the AVD with a hidden console, not detached

detached: true sets DETACHED_PROCESS, which gives the console-subsystem emulator
no console — so it and its qemu/netsim children pop their own visible cmd window
that windowsHide can't suppress. Drop detached and rely on windowsHide
(CREATE_NO_WINDOW = hidden console) + unref; spawn already keeps it alive past
the launch call, and managed emulators are shut down on app quit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(emulator): keep Android emulators alive when switching devices

Attaching a different device shut down the active one (shutdownDevice: true),
which for Android meant killing the running emulator and cold-booting the target
(~60s) on every switch — and switching back. Add bridge.stopActiveForSwitch:
Android emulators stay running for instant switch-back, while iOS simulators are
still replaced. Switching to an already-running emulator is now immediate.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* perf(emulator): only resize the video canvas when dimensions change

The decoder output handler set canvas.width/height on every frame, which
reallocates the canvas backing store and forces an object-contain reflow each
frame — a needless per-frame cost. Resize only when the frame dimensions
actually change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(emulator): genericize copy + add Android Studio setup link

Replace iOS-only wording (Xcode/Simulator/iPhone) in the pane and settings with
backend-neutral copy so Android reads correctly on every platform. When no
emulator is available, the Mobile Emulator settings now show a "Download Android
Studio" link plus setup guidance (ANDROID_HOME / default install path). Removes
the now-unused, macOS-only EmulatorUnavailablePane.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(emulator): show emulator SDK status in settings

The backend availability now reports the resolved Android SDK path, aggregated
into emulator.availability as an `android` block. The Mobile Emulator settings
render an "Emulator SDKs" card showing Android SDK (detected at <path> / not
found, with a Download Android Studio link) and, on macOS, iOS Simulator (Xcode)
status — mirroring the agent-control card.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(emulator): locate a custom Android SDK folder from settings

Add an androidSdkPath setting and a "Locate SDK folder…" / Clear action in the
emulator SDK status card. The path is applied as the highest-priority discovery
candidate (falls back if invalid), and the backend's SDK is re-resolved on use
via a new AndroidSdkState — so locating or installing the SDK takes effect on
Refresh without restarting Orca. Guards the status card against older runtimes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(emulator): settle the scrcpy video socket once to stop retry storms

A failed TCP connect emits both 'error' and 'close', so retry ran twice and
scheduled openVideoSocket(attempt+1) twice — fanning out into an exponential
connection storm while waiting for the server to start listening. A runaway
chain could then hit attempt 100 and fail/close a stream that had already
connected. Replace the delivered flag with a single settled latch so each
socket retries (or delivers) exactly once.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(emulator): address CodeRabbit review findings

- avd-boot: handle spawn 'error' (an unhandled ChildProcess error crashed the
  main process); validate the target is a known AVD before launching.
- capability-ops: propagate adb non-zero exits for launch/permission/logcat and
  check the uiautomator dump before reading (avoids stale XML).
- scrcpy-video-registry: actually replay the buffered GOP on subscribe so late
  subscribers decode immediately.
- android-sdk-state: re-resolve host discovery every call so a changed SDK path
  takes effect live (no restart).
- android-sdk-discovery: require both adb and the emulator binary.
- emulator-bridge: fall back to the platform-primary backend (Android off-mac)
  so setup errors aren't iOS/CoreSimulator on Windows/Linux.
- scrcpy-server-download: dedupe concurrent first-use downloads + add a timeout.
- scrcpy-stream-session: idle-socket connect timeout; surface control-socket
  errors instead of swallowing them.
- android-exec: pass the whole command so the device shell parses quotes/pipes.
- avd-manager: match emulator log prefixes exactly (keep AVD names like
  PixelWARNINGTest).
- permissions: `pm reset-permissions` is global and takes no package argument.
- stream controller/starter: drop stale handles for dead streams; idempotent
  teardown. use-emulator-video-stream: stopVideoStream returns Promise.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(emulator): populate the GOP buffer and reuse live scrcpy streams

Self-audit follow-ups in the same class as the CodeRabbit GOP-replay finding:

- scrcpy-video-registry: pushFrame never wrote to entry.gop, so the replay
  loop added for late subscribers iterated an empty array — a no-op. Build the
  GOP on ingest (start at each keyframe, append following deltas; don't buffer
  deltas before the first keyframe). Adds tests for population, reset, and the
  pre-keyframe guard.
- android backend: isSessionReusable was stubbed to always return false with a
  "no persistent stream yet" note, but scrcpy streams are persistent now — so
  every renderer remount tore down and respawned the server. Reuse a live
  stream (scrcpyVideoRegistry.has) so remounts reconnect, matching iOS. The
  device-mismatch check still runs first, so device switching is unaffected.
- Refresh stale comments that implied unfinished/unverified work.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* WIP: Changes before auto-review fixes

Co-authored-by: Orca <help@stably.ai>

* Refine mobile emulator availability settings

Co-authored-by: Orca <help@stably.ai>

* Address emulator review follow-ups

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
Co-authored-by: Orca <help@stably.ai>
2026-06-28 21:01:09 -07:00
Jinjing 49c0917dbe Prevent background terminal sessions from auto-activating (#6686)
* Support background and focused terminal presentation modes

* Add `presentation` field ('background' | 'focused') to terminal
  creation to control focus behavior.
* Prevent terminal creation from stealing UI focus by default.
* Return discoverability warnings when default terminal presentation
  fails, unless explicit background mode is selected.
* Update orchestration SKILL.md to clarify review-only worker
  completion rules and named owner handoffs.

* Prevent background terminal sessions from auto-activating

Ensure that terminal tabs created with 'background' presentation (such
as background agent sessions or locally backed renderer transports) do
not automatically activate or get selected as the active tab on remote
or mobile sessions.

- Add a selectIfNoActiveTab option to control auto-activation on mobile
- Set presentation to 'background' for remote runtime transports and
  agent background sessions
- Skip tab auto-selection when the presentation is background

* Keep background terminal create payloads focused
2026-06-28 19:52:04 -07:00
Neil 0976f9427c fix(linux): disable GPU sandbox to stop terminal input freeze on Wayland (#5319) 2026-06-28 19:43:18 -07:00
NeilandOrca 31bfeff01d fix(gitlab): thread MR search query and surface MR base failures (#6263) (#6591)
Defect 1: the typed GitLab MR search query was dropped before reaching
the API. Thread query?: string end-to-end through the renderer effect,
the source-lookup, the preload/RPC args, and the desktop IPC handlers
(which previously passed a hardcoded undefined), and honor it on both the
glab REST path (&search=) and the cwd-inferred 'glab mr list' fallback.

Defect 2: when MR base resolution failed the renderer silently returned,
leaving baseBranch undefined so the worktree was created off the repo
default branch (origin/master) with no feedback. Surface the failure via
toast and clear stale base state, mirroring the GitHub PR path. Also make
resolveManagedMrBase resilient to an optional compare-base (target branch)
fetch failure: degrade gracefully by dropping compareBaseRef instead of
aborting, so a merged MR with a deleted target ref still resolves to its
valid source-branch base.

Fixes #6263

Co-authored-by: Orca <help@stably.ai>
2026-06-28 18:05:29 -07:00
Neil 2e59e066b0 fix: bound preload runtime subscription IPC listeners to O(1)
Fixes #6288. Bounds preload runtime subscription IPC listeners to one active dispatcher per renderer and releases subscription state on terminal cleanup paths.
2026-06-28 16:53:03 -07:00
b916248294 Polish desktop native chat view (#6641)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Orca <help@stably.ai>
Co-authored-by: gsxdsm <gsxdsm@users.noreply.github.com>
2026-06-28 15:43:07 -07:00
Rod BoevandJinwoo Hong 5e3d8a12bf fix(terminal): support Windows shell selection for SSH hosts (#6466)
* fix(terminal): support Windows shell selection for SSH hosts

* fix(runtime): register SSH Windows capability preflight RPC

* fix(terminal): honor remote Git Bash and split preflight IPC seams

* fix(terminal): keep SSH Windows shell state tied to the selected host

* fix(terminal): preserve Windows SSH shell selection

---------

Co-authored-by: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com>
2026-06-28 15:08:13 -07:00
Brennan BensonandOrca 8ed890374a Refresh project worktrees created outside Orca (#6241)
Co-authored-by: Orca <help@stably.ai>
2026-06-28 13:09:05 -07:00
Brennan BensonandOrca 5c3ba7be91 Improve inactive workspace cleanup UX (#3600)
Co-authored-by: Orca <help@stably.ai>
2026-06-28 11:38:23 -07:00
Brennan Bensonandbrennanb2025 e47b3364d9 Preserve terminal scrollback when restoring a hidden alternate-screen pane (#6532)
* Preserve terminal scrollback when restoring a hidden alternate-screen pane

On Windows, an alternate-screen TUI pane (Claude Code) could lose scroll-up
after being hidden (tab/worktree switch or app backgrounded) and shown again.

Root cause: on hidden->visible restore, applyMainBufferSnapshot
unconditionally wrote a destructive clear (\x1b[2J\x1b[3J\x1b[H) before
replaying the snapshot, wiping xterm's scrollback. The attach/reattach path
already guards this same clear behind !isAlternateScreen (pty-transport.ts);
the snapshot path did not.

Thread an alternateScreen flag from the backend headless serialization
through the pty:getMainBufferSnapshot IPC + preload types into
applyMainBufferSnapshot, and skip the destructive clear when restoring an
alternate-screen buffer. Add a regression test asserting the clear is NOT
emitted for an alt-screen restore (and the existing normal-buffer test still
asserts it IS).

Fixes #5723

* test: account for alternateScreen flag in empty headless snapshot assertions

The alt-screen scrollback fix threads an alternateScreen flag through the
headless snapshot return type. Two sibling empty-buffer tests used exact
toEqual() assertions that did not include the new (legitimately false) key.
Update them to reflect the correct snapshot shape.

---------

Co-authored-by: brennanb2025 <brennanb2025@users.noreply.github.com>
2026-06-28 00:57:56 -07:00
Jinwoo HongandOrca 4904ffda9a Fix Remote Host downloads and agent status parity (#6436)
* Fix remote host downloads and agent status parity

Co-authored-by: Orca <help@stably.ai>

* Address remote download review comments

Co-authored-by: Orca <help@stably.ai>

* Avoid inefficient SSH chunk fallback

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Orca <help@stably.ai>
2026-06-26 14:28:16 -07:00
Jinwoo Hong 59def7474a Revert Windows terminal clear repaint fix
Manual Windows testing still reproduces displaced input after Ctrl+K.
2026-06-26 14:05:09 -07:00
Jinwoo Hong daab519c5c Fix Windows terminal clear repaint (#6382) 2026-06-26 16:59:00 -04:00
Jinwoo HongandOrca 5f8b04e0f1 Fix image paste in Remote Host terminals (#6380)
Co-authored-by: Orca <help@stably.ai>
2026-06-25 13:16:08 -07:00
Brennan BensonandOrca a2fd8c11c2 Reduce source-control background load during refreshes (#6189)
Co-authored-by: Orca <help@stably.ai>
2026-06-23 15:06:38 -07:00
Ricardo Sawir 4df2b800b6 Support SSH file copy to OS clipboard (#6110)
* Support SSH file copy to OS clipboard

* Address SSH clipboard staging feedback
2026-06-22 18:36:37 -07:00
b98fd2ff8b feat: copy a file from the explorer to the OS clipboard (#5990)
* feat: copy a file from the explorer to the OS clipboard

Add a "Copy" action to the file explorer context menu that puts the
actual file on the system clipboard, so pasting in Finder/Explorer/a
file manager drops the file itself instead of its path as text.

- macOS: write a public.file-url buffer; Finder synthesizes the legacy
  file types it needs for paste.
- Windows: Set-Clipboard -LiteralPath populates the CF_HDROP file drop
  list that Explorer pastes as a file.
- Linux: best-effort, picked by desktop — text/uri-list on KDE,
  x-special/gnome-copied-files on GNOME-family — via wl-copy or xclip.
- Local files only; the action is hidden for remote/SSH files and the
  web client, where no OS clipboard reference is possible.

The platform logic never throws: failures resolve to a structured
result and the renderer surfaces an error toast.

* Review copy-file clipboard safety

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
Co-authored-by: Orca <help@stably.ai>
2026-06-22 15:02:38 -07:00
Brennan Benson 3cc610bcc3 Keep closed tab agents from reappearing (#6095) 2026-06-22 13:36:05 -07:00
Brennan BensonandOrca fe8fe16c83 Preserve agent settings when sleeping sessions resume (#5916)
Co-authored-by: Orca <help@stably.ai>
2026-06-21 16:44:58 -07:00
Jinjing 404e45f022 Improve built-in browser downloads (#5940)
* Improve built-in browser downloads

Implements docs/browser-normal-download-behavior.md with automatic OS Downloads saving, collision-safe destinations, and renderer download controls.

* Clean up browser downloads on tab closure and guest destruction

- Cancel and drop pending download records when webview guests are destroyed or closed to prevent memory leaks and dangling state.
- Purge tracking entries for cancelled downloads lacking an active tab.
- Document filename normalization and reservation assumptions in download builder.
- Translate "Copy Terminal ID" context menu action in Spanish and Chinese locales.
2026-06-20 16:33:20 -07:00
Jinjing 97dc6d63e3 Accept merged fallback PRs during branch lookup (#5908)
Ensure that when a visible fallback PR has been merged (e.g., outside
Orca with a deleted head branch), it is still accepted and refreshed by
branch lookup instead of being discarded as an implicit merged PR.

* Add `acceptMergedFallbackPR` option to GitHub branch lookups
* Enable this option during manual and background refreshes of fallback PRs
* Plumb the new option through preload APIs, IPC handlers, and RPC protocols
2026-06-20 03:30:18 -07:00
Jinwoo Hong 972078f2c4 Fix paste ownership, input bounds, and IPC validation
Supersedes #5745, #5746, and #5747.
2026-06-19 17:14:55 -07:00