* Fix remote Node.js detection for nvm, mise, asdf, and volta
Remote Node resolution failed when node was installed via a version
manager (nvm with custom NVM_DIR, mise, asdf, volta) or when the user's
login shell was zsh/fish rather than bash.
Root cause: the SSH exec transport runs every command under /bin/sh,
which never sources shell init files. The only init-aware path was a
hardcoded `bash -lc` fallback that missed zsh/fish users and was never
reached for the newer version managers. nvm was handled by guessing
~/.nvm (breaking custom NVM_DIR), and mise/asdf/volta had no probes at
all. There was also no version gate, so nvm's highest-version glob
could return Node 8/10/12 and crash the relay on launch.
Fix: resolve via the user's own $SHELL as a login shell first (the only
path that runs nvm.sh / mise activate / asdf.sh init hooks), then fall
back to direct path probes for all major managers (nvm respecting
$NVM_DIR, fnm, mise, asdf, volta, n) plus system locations. Every
candidate is version-checked against the relay's Node 18+ requirement
before being accepted.
* Address CodeRabbit review: probes-first, no || short-circuit
- Reorder to path-probes first (deterministic, doesn't depend on shell
rc-file semantics where bash -lc skips .bashrc and zsh -lc skips
.zshrc — exactly where nvm/mise/asdf hooks live).
- Join probes with newlines instead of || so an empty
`ls | sort -V | tail -1` (exit 0) doesn't mask later probes.
- Deduplicate candidate paths before version-checking.
- Drop unreachable mock and fix misleading $SHELL-unset test name.
- Login shell is now a fallback for custom ~/.profile PATH setups.
* Fix remote Node path probing portability
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
Co-authored-by: Orca <help@stably.ai>
* Fix blank/unclosable mobile emulator tab in floating workspace
FloatingTerminalPanel only handled terminal/browser/editor content types, so simulator tabs rendered no pane and routed close through closeFile (a no-op for simulator tabs). Treat simulator as its own content type: render EmulatorPane for the active simulator tab, wire activeSimulatorTabId into TabBar, and close via closeUnifiedTab.
* Add floating Mobile Emulator tab E2E smoke test
Adds a deterministic Electron/Playwright spec that seeds a simulator unified tab in the floating workspace, asserts the emulator pane renders, and closes it through the real tab-strip X. Adds stable data-emulator-pane selectors and a data-tab-close-button hook on the simulator tab chrome, plus a targeted package script. No live iOS Simulator or Orca Computer/AX dependency.
* Exclude simulator tabs from floating Close All Files
Close All Files filtered out only terminal/browser tabs, so after the simulator render/close fix it would also close the Mobile Emulator. Simulator tabs are not files; exclude contentType 'simulator' to match terminal/browser behavior, and add a regression test asserting Close All Files closes the editor tab but leaves the simulator open.
* Keep floating simulator tabs mounted
---------
Co-authored-by: Wolfgang Schoenberger <221313372+wolfiesch@users.noreply.github.com>
Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
* fix(codex): wrap Windows hook command in cmd.exe to survive spaces in profile path (#6078)
Windows splits raw hook commands on whitespace, so a user profile path
like `C:\Users\Jane Doe` made Codex hooks exit with code 1. Add a
wrapWindowsHookCommand helper that invokes the .cmd through
`cmd.exe /d /c call "..."` and use it in getManagedCommand.
* fix(agent-hooks): wrap Windows hook command in cmd.exe for all agents with raw .cmd path (#6078)
Apply the wrapWindowsHookCommand helper to cursor, command-code, gemini,
grok, and droid, which shared the same raw-scriptPath-on-Windows pattern
as codex. A user profile path with a space (e.g. `C:\Users\Jane Doe`)
used to split at the space and fail with exit code 1.
Agents that already handle spaces correctly are left untouched:
- claude/openclaude (Git Bash + forward slashes)
- copilot (PowerShell with quoted path)
- kimi (Git Bash + forward slashes)
- antigravity (event-specific wrapper .cmd files)
- devin (already wraps via `cmd /d /s /c ""...""`)
Each fixed agent gets a Windows-only test asserting the cmd.exe wrapping
survives spaces in the profile path.
* fix(claude): wrap Windows hook command in cmd.exe to survive spaces in profile path (#6078)
Claude Code runs hooks through Git Bash on Windows. The previous
forward-slash trick only works when the path has no spaces — Git Bash
splits `C:/Users/Jane Doe/...` at the space and tries to execute
`C:/Users/Jane` as a command. Use wrapWindowsHookCommand so the .cmd is
invoked through `cmd.exe /d /c call "..."`, which Git Bash treats as one
argument. Applies to both Claude and OpenClaude (shared getManagedCommand).
* Harden Windows agent hook launcher
---------
Co-authored-by: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com>
Fixes#5906
The divider line on the workspace tab strip used full opacity
border-border, while other divider lines in the UI use varied
opacities. Changed to border-border/70 to match the standard
divider opacity used throughout the application.
Signed-off-by: Noah Khomer <108771853+noahkhomer18@users.noreply.github.com>
* feat(mobile): surface "Link an existing PR" in PR sidebar empty state
The mobile link-PR building blocks (MobileLinkPrForm, linkMobilePr,
parseGitHubPrReference) existed and were tested, but had no entry point —
only unlink was wired up. Add a "Link an existing PR" action to the no-PR
empty state that opens MobileLinkPrForm and refetches the sidebar on
success, mirroring desktop's link flow (GitHub-scoped via worktree.set).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Polish mobile PR link empty state
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
* 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>
* fix: darken Builtin Tango Light ANSI colors for readability
Nine ANSI colors had contrast ratios below 3:1 against the white
background, making CLI output unreadable in light mode. Darkened
to readable Tango shades while keeping the warm palette identity.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: enable xterm minimumContrastRatio and add light theme tests
xterm.js minimumContrastRatio (4.5) auto-adjusts low-contrast ANSI
foreground colors at render time — a safety net for all themes,
including custom imports. Added test coverage for this option and
for the Tango Light ANSI color contrast assertions.
Co-Authored-By: Claude <noreply@anthropic.com>
* chore: trigger CI re-run
---------
Co-authored-by: zhangqinzhong <vhudsongit@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
* fix(terminal): attach dropped image files via bracketed paste
Dragging an image file into a terminal pane wrote the shell-escaped path
as raw PTY input. Terminal TUIs (Claude Code, Codex, etc.) only turn a
path into an image attachment when it arrives as a *bracketed paste*, so
dropped images showed up as a literal path instead of an `[Image]`
placeholder — unlike iTerm2/Warp, which wrap dropped paths in bracketed
paste, and unlike Orca's own clipboard screenshot flow (#2842).
Route dropped image files (by extension, mirroring IMAGE_MIME_TYPES)
through `wrapTerminalBracketedPasteText` with the raw, un-escaped path so
the file-existence check those tools run on the pasted path succeeds.
Non-image drops keep the original shell-escaped, space-separated
behaviour for use in shell commands.
* fix(terminal): separate image paste from following non-image path
A mixed drop where an image precedes a non-image path concatenated the
two: the image bracketed-paste payload has no trailing space, so the
next shell-escaped path was appended directly after it. Add a single
separating space after an image payload only when the next path is a
non-image — back-to-back image pastes are self-delimiting and a stray
space between them would land in the TUI input.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Review terminal image drop path handling
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
Co-authored-by: Orca <help@stably.ai>
* fix: close mirrored editor tabs on the host so they stop reopening
On web and mobile companions, editor file tabs are mirrored from the
host's runtime-session snapshot, which the host derives from its
authoritative `openFiles`. Closing a mirrored tab only removed it
locally, so the next snapshot re-mirrored the still-open host file and
the tab immediately reopened.
Route the close to the host from the single chokepoint every editor
close funnels through (`store.closeFile`): when the file is mirrored and
a web runtime session is active, send `session.tabs.close` for the host
tab id. The RPC's recorded close-intent suppresses re-mirroring until the
host snapshot catches up, so the local removal is not undone. No-op for
the host's own (non-mirrored) files.
Also fix the desktop `ui:closeSessionTab` handler so a companion-driven
editor close goes through `closeFile` (clears `openFiles`) instead of
`closeUnifiedTab` (tab strip only), which had the same re-mirror bug for
mobile-originated closes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Fix mirrored editor close import cycle
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>
* Fix idle remote runtime session work
* fix: stamp remote/SSH worktrees with their repo execution host
Remote-runtime and SSH worktrees are fetched/created/reparented through the
owning host, which reports them from its own perspective (hostId defaults to
"local"). After the per-worktree hostId override in #2, that bogus "local"
overrode the repo's runtime owner, so terminals/sessions for a remote worktree
resolved to the local machine instead of the remote (clicking an omarchy-office
worktree opened a shell on the local omarchy-thinkpad).
Re-stamp every runtime worktree payload with the repo's execution host via a
shared withRepoHostId helper, applied at all worktreesByRepo ingress points:
toVisibleWorktrees (fetch), createWorktree, and applyWorktreeLineageUpdate.
Local-owned repos are left untouched, so an explicit local worktree still
overrides a runtime repo owner. Mirrors how repos already get repoWithFetchedOwner.
* fix: auto-discover remote runtime projects on connect
PR #2 gated the global session-tab sync to web clients only, removing the
desktop path that eagerly populated remote projects. With no on-connect repo
fetch left, remote projects only appeared after the user opened the
Add-Project dropdown (which calls fetchRuntimeEnvironmentRepos directly).
Seed an initial repo/worktree/lineage refresh for every runtime environment
that is already connected when useIpcEvents mounts, and for each one that
becomes connected afterward. Reuses the debounced/throttled refresh scheduler,
and works regardless of whether the remote server emits client events.
* fix: tighten idle runtime refresh behavior
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
Co-authored-by: Orca <help@stably.ai>
* Don't show local Windows shells for serve-runtime worktrees
The Windows shell menu (PowerShell/CMD/Git Bash) was offered for a
serve/remote-runtime worktree whose host is not Windows (e.g. a Linux
orca serve), where those local shell choices are meaningless and the
plain New Terminal already opens the runtime's default shell. AND a new
runtimeHostIsNonWindows exclusion into the existing shouldShowWindowsShellMenu
gate, keyed on the probed runtime host platform so a LOCAL Windows-WSL
project runtime (hostPlatform === win32) keeps its shell menu. Adds
regression tests.
Rebased onto current main (was 75 commits stale); net delta unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Hide runtime Windows shells until host is known
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>
* Fix floating workspace terminal selector_not_found on a remote runtime
resolveWorktreeSelector threw selector_not_found for the floating-terminal
sentinel id (global-floating-terminal) because it is a repo-less synthetic
session with no entry in the worktree catalog. A remote client paired to this
serve sends that sentinel, so the normal id: lookup failed and the floating
pane rendered black. Resolve the sentinel (bare and id:-prefixed) to a virtual
ResolvedWorktree rooted at the serve user's home so the PTY spawns in a real
existing dir; unknown id selectors still throw selector_not_found. Adds two
regression tests.
Rebased onto current main (was 75 commits stale); net delta unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Scope floating terminal selector to terminal launches
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>
Harden mobile session-tab snapshot reconciliation and terminal creation. Rejects stale mobile snapshots, tombstones locally closed tabs until the publisher catches up, rolls back half-created terminal tabs when their surface never appears, and scopes terminal-create idempotency by worktree.
* fix(mobile): keep iOS terminal inputs on the default keyboard
iOS treated keyboardType="ascii-capable" as an ASCII-only input surface,
which hides non-Latin keyboards (Zhuyin, Japanese, Korean) from the iOS
keyboard switcher, so terminal users could not switch away from English.
Use the system default keyboard for terminal inputs on every platform so
IMEs stay selectable, while keeping autoCorrect/spellCheck off so commands,
flags, and paths are not rewritten by the OS keyboard. The keyboard-type
helpers now return a single 'default' value; their dead per-platform
branching was removed.
Fixes#5525.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(mobile): tighten terminal keyboard comment
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>
* Resolve push targets for linked hosted reviews
Automatically resolve and persist push targets for linked GitHub PRs
and GitLab MRs to align source control actions with the review branch.
- Add `ensureHostedReviewPushTarget` to hydrate missing push targets
- Implement GitLab MR push target resolution alongside GitHub PRs
- Block actions from fallback to unrelated upstreams while resolving
- Prevent duplicate concurrent API lookups with in-flight tracking
* Clear stale review push targets when unlinking or replacing reviews
- Ensure unlinking a hosted review or replacing it with another provider
properly clears the previously associated push target, preventing pushes
from being steered to outdated review heads.
- Encode explicit push target clears as null over JSON-RPC to bypass the
automatic omission of undefined properties during transit, then map them
back to undefined in the main runtime database.
- Add stricter validation to ensure pull request and merge request IDs are
positive integers, ignoring invalid or dummy numbers like zero.
Long branch names and workspace titles were getting truncated, hiding
important identity context from developers.
* Add a dynamic `TruncatedSidebarLabel` component that measures text
overflow and renders a tooltip only when the text is actually truncated.
* Use the new label for branch names in `WorktreeCard`, disabling its
nested tooltip when parent hover details are active to prevent conflict.
* Update `WorktreeCardDetailsHover` to use `break-words` instead of
truncating, ensuring full branch and workspace identities wrap and remain
fully readable in the details panel.
* Enhance mobile emulator script with --port option and robust IP lookup
Introduce support for configuring the Metro bundler port via --port, and
allow overriding the CLI command name using the ORCA_CLI environment variable.
Additionally, improve LAN IP detection and verification so that Metro URLs
are correctly resolved and tested for reachability. Finally, fix the
worktree argument passed during the emulator attach step.
* Improve mobile emulator script shutdown
Track the specific key and modifier identities from the initial keydown
to construct a robust release matcher. This ensures hold dictation stops
reliably on keyup, even if modifier state flags have already dropped
or if keys are released in a different order.
- Delays input focus by 220ms to ensure animating bottom drawers settle before the soft keyboard is requested, improving focus reliability on mobile.
- Replaces standard TextInputs with this component in tasks workspace creation and the NewWorktreeModal.
Enable `experimentalNewWorktreeCardStyle` by default on new profiles where
onboarding is open, or when creating a brand-new config file.
- Avoids visual disruption for existing users who already completed
onboarding by keeping the old style active for them.
- Preserves explicit user opt-outs during migrations and CLI updates.
- Ensures the offline CLI handler mirrors this logic when creating the
initial configuration file before the desktop client loads.