Commit Graph
2110 Commits
Author SHA1 Message Date
NeilandOrca b07ea4a1a5 Push-refresh git status on repo metadata changes and shell command completion (#7086)
Branch switches and commits made inside terminals now surface through push
signals instead of waiting for the 30s terminal-only fallback poll:

- The git-common watch now also covers the primary checkout's HEAD,
  packed-refs, and index (a few stat calls per tick; no new native watchers).
- worktrees:changed events nudge the active worktree's coalesced git status
  runner (visibility-gated, 3s floor).
- OSC 133;D command completion dispatches a window event that nudges the
  same runner for the active worktree.

Co-authored-by: Orca <help@stably.ai>
2026-07-02 01:32:05 -07:00
Jinwoo HongandOrca c29bf6f3da Raise WebGL terminal context budget and recover from context loss (#7064)
Co-authored-by: Orca <help@stably.ai>
2026-07-01 23:43:32 -07:00
Neil 7c66f37632 Fix AppImage env leakage into Linux terminal shells (#7076) 2026-07-01 23:09:20 -07:00
NeilandOrca 7f1e280436 Cut fseventsd load: fix macOS exclusion cap, drop wide always-on FSEvents streams (#7068)
* Cut fseventsd load: fix macOS exclusion cap, drop wide always-on FSEvents streams

- @parcel/watcher maps plain-path ignores to FSEventStreamSetExclusionPaths,
  but macOS caps exclusions at 8 per stream and fails closed. Orca passed 9,
  silently disabling ALL daemon-side exclusions: fseventsd delivered every
  node_modules/.git event to each per-worktree explorer stream (measured 16x
  client CPU). Cap plain paths at 8 (extras demoted to userspace globs) via a
  shared filesystem-watcher-ignore module, also unifying the runtime
  file-watcher host list so identical roots share one native stream.
- worktree-base-directory-watcher subscribed recursive FSEvents streams over
  every repo's ENTIRE workspace root (all worktrees) and whole common .git
  (objects included) with glob-only ignores (zero daemon-side exclusion),
  just to observe a few shallow paths. Replace with a 2s readdir/stat poller
  (zero fseventsd clients, measured 70x less delivery CPU); SSH targets keep
  provider.watch unchanged.
- serve-sim state watcher held an app-lifetime fs.watch on $TMPDIR (the
  noisiest dir on the system); its existing 250ms existence poll makes the
  parent watch redundant, so drop it.

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

* Eliminate base-poller idle cost: mtime-gated ticks + narrow macOS worktree-metadata stream

- base targets: tick is now one stat of each gate dir (root + nested
  containers); the readdir + marker fan-out runs only when a gate dir
  changed, with a 30s ungated backstop and a bounded pending-marker
  recheck list. Idle tick 600us -> 1.1us at 146 worktrees.
- git-common targets on macOS: replace the 2s entry sweep with one narrow
  native stream on <common>/.git/worktrees (tiny, rare-churn tree): zero
  idle syscalls and external worktree add/remove/HEAD detection back to
  sub-second. Re-arms via existence poll across git worktree prune
  deleting/recreating the dir. Win/Linux keep the listing poll (no
  fseventsd there; a Windows dir handle could block prune).
- split git-common logic into worktree-git-common-watch.ts (max-lines).

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

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-01 23:04:13 -07:00
4dbc9f3817 feat(ssh): add ControlMaster multiplexing for system SSH transport (#6922)
* feat(ssh): add ControlMaster multiplexing for system SSH transport

System SSH transport spawns a new OpenSSH process per exec command
(platform detect, relay install check, node resolution, relay launch,
socket probe). Each process pays the full SSH handshake cost — ~9s on
Uber devpods — making a typical relay connect take 54s+ and reliably
exceeding the 15s startup reconnect budget.

Add SSH ControlMaster multiplexing via a per-target socket in
$TMPDIR/orca-ssh-ctl/<hash>.sock. The first command establishes the
master; subsequent commands reuse it at ~100ms per exec instead of ~9s.
ControlPersist=300 keeps the master alive after commands exit so rapid
reconnects (e.g. on tab focus) also benefit. Windows is excluded since
OpenSSH's ControlMaster support there is limited.

* fix(ssh): address ControlMaster key collision and directory permission risks

- Use target.id in the socket key so distinct SSH targets can never
  collide even when configHost/port/user happen to match
- Switch from SHA1 to SHA256 and extend hash slice from 12 to 16 chars
- Stat the control-socket directory after mkdirSync to reject pre-existing
  dirs that are symlinks, foreign-owned, or have group/other write bits
  (mkdirSync mode is ignored on pre-existing dirs)
- Update two tests that used exact spawn-arg arrays; replace with
  ordering assertions (forward flags before --) that stay correct
  regardless of which extra ControlMaster options are injected

* fix(ssh): bind ControlPath identity to route and reject symlinked ctl dir

Fold proxyCommand/jumpHost/identity fields into the ControlPath hash so a
target whose route is edited no longer reuses a still-alive master built on
the old route. Switch the control-socket dir check from statSync to lstatSync
so a planted symlink fails the directory validation outright.

* test(ssh): drop tautological argv re-assertion in spawn checks

The toHaveBeenCalledWith re-passed the args array extracted from the same
mock call, making that argument position always pass. argv content is
already verified by the index-ordering assertions above; use expect.any(Array)
so the spawn check only claims what it actually verifies (binary path, stdio).

* fix(ssh): harden system ssh connection reuse

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

* test(ssh): isolate control socket runtime dir

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

---------

Co-authored-by: Test <test@example.com>
Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
Co-authored-by: Orca <help@stably.ai>
2026-07-01 21:29:48 -07:00
Jinwoo HongandOrca f8c1f0fdf8 Fix delayed TUI mouse wheel reports (#7060)
Co-authored-by: Orca <help@stably.ai>
2026-07-01 19:54:53 -07:00
91e7e1d91c fix: detect valid repos when git rev-parse can't confirm (#6173)
* fix: detect valid repos when git rev-parse can't confirm

isGitRepo() has depended entirely on a successful `git rev-parse`
since 18ed7b27d, with catch blocks that collapse every failure into
"not a git repository". When that subprocess fails for a reason
unrelated to repo-ness — a transient spawn / git-shim hiccup in the
packaged app, main-process resource pressure, or a config-level error —
a real repository is silently downgraded to a plain folder. The folder
scanner already tolerates this via a `.git` marker, so the scan reports
"git_repo" but the subsequent addRepo throws, producing the spurious
"Open as Folder" prompt for a valid repo.

Keep `git rev-parse` as the authoritative positive signal, but on any
non-positive result fall back to a validated `.git` marker instead of
returning false: `.git` dir must contain HEAD, a `.git` file must point
at a gitdir, and bare roots need HEAD + objects/ + refs/. A garbage
`.git` file and an empty `.git/` are still rejected, preserving the
validation 18ed7b27d added. Logs a warning when recovery via the marker
happens so the underlying probe failure stays visible.

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

* fix: address review feedback on repo detection

- isGitRepo: warn at most once per session when recovering a repo via
  the .git marker, so a broken-git scan can't flood main-process logs.
- repo-detection test: delete PATH instead of assigning "undefined" when
  it was originally unset, avoiding a corrupted PATH for later tests.

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

* fix: harden git repo marker fallback

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

---------

Co-authored-by: Omar Shahine <10343873+omarshahine@users.noreply.github.com>
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-07-01 15:10:35 -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
Eddie JaoudeandJinjing e0a7f0eadd fix: reordered default columns in kanban board (#6934)
* fix: reordered default columns in kanban board

* Introduce dedicated flag to repair reversed default workspace statuses

- Add `_workspaceStatusesReorderedDefaultRepaired` to decouple the
  one-shot repair from the initial status order migration.
- Ensure the repair runs for users who saved the reversed default
  payload (with "Done" on the left) during a short-lived broken build.
- Support both "Completed" and "Done" labels when identifying default
  status shapes to migrate or repair.

---------

Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
2026-07-01 13:17:52 -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
Jinwoo HongandOrca 8a805e256d Fix SSH relay lost backoff stabilization (#6979)
Co-authored-by: Orca <help@stably.ai>
2026-06-30 19:45:24 -07:00
Jinwoo Hong 582baf533e Fix WSL OMP live status env injection (#6974) 2026-06-30 22:14:54 -04:00
Jinwoo Hong 3eb5b247bb Fix WSL UI project branch discovery (#6973) 2026-06-30 21:51:19 -04: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
Jinwoo HongandOrca 782eb12688 Make remote SSH terminals persistent by default (#6955)
Co-authored-by: Orca <help@stably.ai>
2026-06-30 16:16:10 -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
Neil be3f30e2f8 Fix Windows CJK terminal repaint and ConPTY wrap markers
Fix native Windows CJK terminal repaint and ConPTY wrap metadata handling.\n\nNormalize filesystem realpath results before authorization so WSL UNC roots compare consistently in CI.
2026-06-30 03:50:30 -07:00
Brennan BensonandNeil 6cf52518e2 Fix explicit PowerShell 7 terminal selection on Windows (#6876)
* Fix explicit PowerShell 7 terminal selection

* Make WSL UNC readDir test portable

---------

Co-authored-by: Neil <neil@stably.ai>
2026-06-30 03:48:25 -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
Brennan BensonandNeil 48f0e54c03 fix(win): fall back to software rendering after repeated GPU crashes (#6857)
* fix(win): fall back to software rendering after repeated GPU crashes

On old/flaky GPU drivers the GPU child process crashes
(STATUS_BREAKPOINT / ANGLE-D3D init failure) within seconds of launch,
repeatedly — Windows crash clusters F0BDNADU79Q (Win11) and
F0BDNRZ5MDG (Win10). GPU child deaths are intentionally suppressed as
recoverable churn, so Orca never reacted and the GPU kept crashing every
launch with no self-healing path.

Track GPU child crashes in a post-launch window (3 / 30s). On the
threshold, record a gpu_fallback_engaged breadcrumb, persist a marker
file in userData, and relaunch once. The marker is read before
app.whenReady() resolves (the Store isn't available that early) so
app.disableHardwareAcceleration() + --disable-gpu take effect, then it's
consumed/cleared so the fallback applies to exactly one launch and can't
relaunch-loop. Records gpu_fallback_applied on the software-render boot.

Pure logic lives in GpuCrashFallbackTracker and the marker read/write
module, both unit-tested; the index.ts wiring is thin. Desktop only —
headless serve already uses software rendering where needed.

* fix(win): harden GPU crash fallback policy

---------

Co-authored-by: Neil <neil@stably.ai>
2026-06-30 01:21:07 -07:00
Brennan Benson 973360cfce Reliably deliver the issue prompt into opencode's composer (#6798) 2026-06-30 01:16:55 -07:00
Brennan BensonandOrca 7df09f2f4c Restore agent terminal screen on hibernation wake (#6833)
Co-authored-by: Orca <help@stably.ai>
2026-06-30 01:02:11 -07:00
Brennan BensonandNeil 5b7611ddcb fix(win): release PR-refresh aliases when a worktree is removed (#6861)
The OOM reports (F0BDMD16LJ2 and the taifunk many-worktree sessions)
show memory creeping over long sessions with churning worktrees. One
contributor: in pr-refresh-coordinator, many local worktrees that track
the same linked PR coalesce into a single queue entry whose 'aliases'
map keeps one entry per worktree. Aliases were only pruned when a
candidate was re-enqueued as invalid — never when a worktree was simply
removed/closed — so the maps grew unbounded across a session.

Add pruneWorktreePRRefreshAliases(worktreeId) and call it from
removeWorktreeMetadataAndTransientState (the existing central
worktree-removal cleanup, alongside removeWorktreeMeta /
forgetWorktree / deleteWorktreeHistoryDir). It drops the removed
worktree's aliases, deletes the queue entry when none remain, and
rebinds the representative candidate if the removed worktree owned it.

Covered by 3 new coordinator tests (accumulate-then-prune, keep-entry-
on-remaining-aliases with candidate rebind, no-op for unknown worktree)
plus two test-only inspection helpers.

Co-authored-by: Neil <neil@stably.ai>
2026-06-30 00:55:54 -07:00
Brennan BensonandNeil 9078ecaef5 fix(win): record a redacted breadcrumb when fs:readDir throws (#6862)
The user report F0BDXH978GJ surfaced 'Error invoking remote method
fs:readDir' — Electron's opaque message when the main-process handler
throws. On Windows this is typically a WSL \wsl$ / \wsl.localhostUNC path or network drive failing realpath/readdir after the distro or
share goes away, or a dropped SSH provider. Nothing recorded which throw
site fired or what kind of path was involved, so the crash report
carried no actionable cause.

Wrap the fs:readDir handler to record an 'fs_readdir_error' breadcrumb
on throw, tagging the throw site (ssh-provider | authorize | readdir),
the error name/code, and a REDACTED path shape (isUNC, isWsl,
driveLetter, hasConnectionId) — never the raw path. The error is
re-thrown unchanged, so renderer behavior is identical; only crash
reports gain structured context.

Pure classification lives in readdir-error-diagnostics.ts (8 unit tests
covering WSL UNC, \wsl$, network share, drive letter, SSH, and
no-path-leak); 4 handler tests assert the breadcrumb fires per throw
site and not on success.

Co-authored-by: Neil <neil@stably.ai>
2026-06-30 00:28:07 -07:00
Brennan BensonandNeil 60412362a1 fix(win): stop renderer crash-loop with an auto-recovery circuit breaker (#6856)
A deterministic per-load renderer fault (bad GPU driver, corrupt chunk,
AV interference) crashes on every load. Orca auto-reloads recoverable
renderer deaths after 250ms, so without a limit it reloads every
~0.25-1.3s forever — the Windows clusters F0BDRAZN55L (launch-failed
exit 18, bootstrap loop ~1.3s) and F0BDPCL93UM (ACCESS_VIOLATION on
Win10 1909, ~0.8s loop). shouldRecoverRendererAfterProcessGone only
gated integrity-failure/launch-failed, never 'crashed'/'killed'.

Add a rolling-window circuit breaker (3 recoveries / 60s) consulted at
the reload point in createMainWindow. When it opens, Orca stops
auto-reloading, records a renderer_recovery_circuit_breaker_open
breadcrumb, and shows a native Reload/Quit prompt (the renderer is dead,
so a main-process dialog is the only available surface). The window is
NOT reset on did-finish-load: a crash loop renders on every cycle before
dying, so stale attempts must age out by time instead.

Covered by renderer-recovery-circuit-breaker.test.ts (pure helper) and a
createMainWindow.test.ts integration test that drives the loop and
asserts reloads stop after the limit.

Co-authored-by: Neil <neil@stably.ai>
2026-06-30 00:26:29 -07:00
Neil f9e18910ae chore(lint): adopt unicorn/prefer-import-meta-properties (error) (#6847)
Migrate fileURLToPath(import.meta.url) / dirname(...) boilerplate to the
native import.meta.dirname / import.meta.filename, then enable the rule
at error so new code stays on the native form.

The oxlint autofix rewrites the expression but leaves the now-unused
node:url / node:path imports behind (which the already-enabled
no-unused-vars=error would then flag), so this commit also removes those
34 orphaned imports — trimming the named import where other names are
still used, deleting the line where it was the sole import.

Scope is build scripts + Node-env tests only (config/scripts, tools/
benchmarks, *.test.{ts,mjs}, vitest configs); zero shipped runtime code.
The native properties are exact equivalents (Node >= 20.11; repo is on
24), so behavior is unchanged.

Verified: oxlint 0 errors tree-wide (root + mobile), oxfmt clean,
typecheck (node+cli+web) + mobile tsc pass, root vitest 22825 passed /
0 failed, mobile vitest 1018 passed. Exercised the rewritten scripts
directly: build:relay (6 targets), ensure-native-runtime,
verify-macos-entitlements all run correctly with import.meta.dirname.
2026-06-29 23:37:30 -07:00
NeilandOrca 46646d7ff1 chore(lint): upgrade oxlint to 1.71 + enable 7 new rules (autofixed backlog) (#6841)
* chore(lint): upgrade oxlint to 1.71 and enable 7 new rules

Upgrade oxlint 1.67.0 -> 1.71.0 (1.72 was blocked by the repo's 3-day
minimum-release-age supply-chain guard; nothing here needs it). The
bump is a no-op on the existing config.

Enable 3 error rules (backlog autofixed to zero in this commit) and
4 warn rules (surface signal without gating CI):

error (autofixed, behavior-preserving):
- unicorn/prefer-node-protocol        (~1531 sites: bare builtin -> node:)
- typescript/no-import-type-side-effects (~36: all-inline-type -> import type)
- unicorn/no-array-reverse            (19: copy-then-reverse -> toReversed)

warn (real signal, current fires are test-only/correct):
- unicorn/no-array-fill-with-reference-type  (aliasing footgun guard)
- typescript/no-unsafe-function-type         (bans bare Function type)
- unicorn/prefer-array-flat-map              (map().flat() -> flatMap())
- unicorn/prefer-regexp-test                 (.match() in bool ctx -> .test())

mobile/.oxlintrc.json extends root, so it inherits all 7; the autofix
ran from root and covered mobile/ too.

Verification (all green): oxlint 0 errors (root+mobile+aux configs),
oxfmt clean, typecheck (node+cli+web), vitest 22795 passed / 0 failed,
builds (electron-vite + web + cli) succeed. node: rewrites confirmed to
skip embedded SSH/CLI string payloads (AST-only); all toReversed sites
verified to operate on fresh copies or write-once locals.

* chore(lint): bump mobile oxlint to 1.71 so inherited rules parse

mobile/ is a standalone pnpm project pinning its own oxlint@1.67, which
lacks unicorn/no-array-fill-with-reference-type (needs >=1.70). Since
mobile/.oxlintrc.json extends the root config, mobile CI's 'cd mobile &&
oxlint' failed to parse the new rule. Bump mobile to match root (1.71).

Verified in mobile/: oxlint 0 errors, oxfmt --check clean, tsc --noEmit
pass, vitest 978 passed / 0 failed.

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

---------

Co-authored-by: Orca <help@stably.ai>
2026-06-29 22:38:29 -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
Neil 220d5cb7fe Prevent and recover from degraded terminal daemon spawns
Adds daemon PTY-spawn health classification, POSIX daemon cwd repair before terminal spawn, and degraded-daemon fallback routing so fresh terminals keep opening without killing preserved sessions.
2026-06-29 20:17:46 -07:00
NeilandOrca 16ce1aa73d perf(startup): lazy-load locale catalogs + mermaid (−5.40MB / −31% eager cold-start JS) (#6804)
Co-authored-by: Orca <help@stably.ai>
2026-06-29 20:02:28 -07:00
Neil 30fc4859e2 fix: reject mobile-scope web pairing and surface forbidden runtime errors (#6581)
Fixes #5508.

Rejects mobile-scope phone-QR pairing in the full web client, keeps runtime browser access links on the instant startup path via advisory pairing scope metadata, and surfaces forbidden runtime scope errors instead of rendering empty workspaces or retry-looping setup checks.

Security note: pairing offer scope is UI metadata only. Runtime RPC authorization remains based on the server-side device token registry and mobile allowlist.
2026-06-29 19:06:59 -07:00
NeilandAlexander Kirilin 4776ac1fe5 fix: include legacy commit statuses in PR checks (#6556)
Co-authored-by: Alexander Kirilin <github@alexanderkirilin.com>
2026-06-29 19:04:09 -07:00
Neil 499b900f1b Remove unused dead-code exports (#6821) 2026-06-29 18:32:56 -07:00
Neil 61503b7666 refactor(command-code): split managed hook script (#6807)
* refactor(command-code): split managed hook script

* fix(command-code): include split script in cli typecheck
2026-06-29 18:13:45 -07:00
Neil e2b1386544 refactor(telemetry): split client lifecycle tests (#6813) 2026-06-29 18:09:33 -07:00
Neil bdc8b2a1ca refactor(pr-context): split guard tests (#6810) 2026-06-29 18:04:36 -07:00
Neil 669ff13467 fix: allow duplicate review worktrees (#6812) 2026-06-29 18:02:10 -07:00
Brennan BensonandOrca 90d3d7a0e5 Reduce CPU from source-control git polling on Windows (#6736)
Co-authored-by: Orca <help@stably.ai>
2026-06-29 15:25:13 -07:00
Jinjing a978222184 Clarify auto-merge UI and errors when direct merge is available (#6777)
GitHub rejects enabling auto-merge on a PR that is already mergeable
with a "Pull request is in clean status" error.

* Suppress "Enable auto-merge" option in the UI when direct merge
  is available, while retaining "Disable auto-merge".
* Translate the GitHub "clean status" GraphQL error into an actionable
  message recommending direct merge.
2026-06-29 14:40:53 -07:00
3f39d7548b Recommend Tailscale when the remote Orca runtime is unreachable (#6637)
* Recommend Tailscale when the remote Orca runtime is unreachable

When a remote-runtime connection fails (RemoteRuntimeClientError "Could not
connect to the remote Orca runtime."), append an actionable Tailscale hint to
the user-facing error, branched on whether the endpoint is already on a tailnet:

- Non-Tailscale endpoint: recommend connecting both devices over Tailscale and
  pairing with its Tailscale address, with a download link.
- Tailscale endpoint (*.ts.net or 100.64.0.0/10): point at the real causes —
  server offline on the tailnet, or Funnel reverted to tailnet-only — and note
  that already-paired devices reconnect without re-pairing.

Applied at the desktop transport chokepoint (status probe, in-use calls, and
subscriptions — connection failures reject, so the hint is applied to the thrown
error, not just ok:false responses) and at the web client's connect/timeout
sites. New pure shared helper mirrors withMacTailscaleDnsHint.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Address review: scope CGNAT hint to IPv4 literals, track re-paired endpoint

- isTailscaleEndpoint: gate the 100.64.0.0/10 check on a full IPv4 literal so
  DNS names like 100.64.0.1.example.com no longer get tailnet-specific advice.
- callRuntimeEnvironment: capture the endpoint the queued closure actually used,
  so a re-pair between enqueue and dispatch can't append the wrong hint.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Recognize Tailscale IPv6 endpoints and trailing-dot FQDNs in hint

The remote-runtime Tailscale hint classified IPv6 Tailscale nodes
(fd7a:115c:a1e0::/48) and trailing-dot FQDNs as non-Tailscale, so a
user already reaching their server over Tailscale by IPv6 literal was
wrongly told to 'connect both devices to Tailscale'. Pairing endpoints
can carry bracketed IPv6 literals (resolvePairingEndpoint), so this is
a reachable path. Normalize the extracted host (strip brackets and the
trailing FQDN dot) and add an IPv6 ULA-range check.

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

---------

Co-authored-by: s546126 <268420947+s546126@users.noreply.github.com>
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>
2026-06-29 13:41:13 -07:00
Jinwoo HongandOrca 22b00a7cd2 fix(terminal): report PTY's applied size so dropped resizes self-heal (split-mount desync) (#6785)
Co-authored-by: Orca <help@stably.ai>
2026-06-29 13:35:43 -07:00
Nicholas Zolton 4febefd454 feat(browser): add Helium as a Chromium cookie-import source (#6768)
Register Helium (imput, bundle id net.imput.helium) so it is auto-detected
and importable from the picker on macOS like Chrome/Edge/Arc/Brave/Comet,
instead of only via the "From File..." JSON path. Reuses the existing macOS
Chromium decryption path unchanged.

Helium quirks (verified on a real install): its Keychain entry is service
"Helium Storage Key" / account "Helium" (not the usual "<Browser> Safe
Storage"), it stores data under its bundle id, and it keeps cookies at the
legacy <Profile>/Cookies path. Mac-only for now; Windows/Linux data dirs are
unverified so winRoot/linuxRoot are omitted and the source defaults off there.

Claude-Session: https://claude.ai/code/session_01SZzX6ktZaaCUg5PWnzniuC
2026-06-29 13:21:20 -07:00
Adam ReedandClaude Opus 4.8 c80dc43ed2 Support respawn-pane in Claude Agent Teams tmux shim (#6771)
* Support respawn-pane in Claude Agent Teams tmux shim

Claude Code's pane backend spawns each teammate in two steps: it
`split-window`s a holding pane running `cat`, then `respawn-pane -k`s
that pane with the real teammate command. Orca's tmux dispatcher had no
`respawn-pane` case, so every teammate spawn failed with
`tmux: unsupported command: respawn-pane` and the real command was
dropped — agent teams were completely broken in Orca on current Claude
Code (2.1.x).

Honor respawn-pane by closing the placeholder terminal and re-splitting
from the pane's original origin/direction with the real command, keeping
the fake pane id stable so later send-keys/kill-pane/list-panes still
resolve. Panes now remember how they were split for faithful recreation.

Pure pane geometry/format helpers move to a sibling module to keep the
dispatcher within the 300-line cap.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* respawn-pane: create replacement before closing placeholder

Address review: closing the placeholder before the replacement split
succeeded could leave the fake pane id registered against a dead handle.
Split first; on cleanup failure, discard the new split and keep the
placeholder registered so send-keys/kill-pane/capture-pane still resolve.
Adds a regression test covering a failed respawn split.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 13:16:14 -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
Jinjing 1c30d28113 Surface GitHub check suites awaiting approval (#6717)
* Surface GitHub check suites awaiting approval to unblock merge

- Query the check-suites API endpoint to find suites with an
  "action_required" conclusion, which are often workflows awaiting
  "Approve and run" and do not have any associated check runs.
- Map the "action_required" status distinctly instead of treating it as
  a standard failure or omitting it entirely.
- Update the UI to render these suites with a warning icon, a dedicated
  "Action required" label, and a localized hint explaining that manual
  approval is required on GitHub.
- Count "action_required" checks as failed/blocking when deriving overall
  PR and task statuses so the UI does not report all checks passing.

* Enhance visibility and handling of action-required PR check suites

* Include check suite IDs in pending approval check names and URLs to
  allow navigating directly to the specific workflow run.
* Add an "action required" count badge to PR dialog and page checks tabs.
* Prioritize action-required checks in the checks preview summary.
* Use correct check run state for the action-required fallback hint in
  the right sidebar details panel.
* Add translations for the new status across all supported locales.
2026-06-29 12:05:57 -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
Brennan BensonandNeil 206a70964e Recover partially deleted local worktrees (#6657)
Co-authored-by: Neil <neil@stably.ai>
2026-06-29 00:53:07 -07:00