Commit Graph
7106 Commits
Author SHA1 Message Date
OrcaWin 84968dfd9e Fix duplicate paired hosts in worktree run target picker (#10218) 2026-07-23 13:01:28 -07:00
Jinjing 5dc6799c48 Rename 'local network' to 'LAN' across the UI (#10216)
Improves clarity and consistency throughout mobile pairing, settings,
and permission descriptions. Makes the distinction from Tailscale
more explicit where relevant. Updates all translated locales.
2026-07-23 12:48:26 -07:00
Brennan Benson eb2508e7f3 feat(diagnostics): name what grew in renderer OOM crash reports (#9984)
* feat(diagnostics): name what grew in renderer OOM crash reports

Renderer OOMs are the dominant crash class (heap pinned at the ~3.5GB V8
ceiling in crash-channel reports) but renderer_memory breadcrumbs carry only
heap totals, so reports say "it grew" without saying what.

Add a one-shot renderer_memory_highwater breadcrumb at 60%/80% of the heap
limit carrying leak-diagnosis counts: DOM node census, terminal element
count, and per-subsystem counts from a new contributor registry. The store
registers the first contributor, reporting its 20 largest top-level
collections. Counts only, capped per contributor; zero work on the 60s
sample path while below threshold.

* fix(diagnostics): retain renderer heap profiles

* test(agent-status): remove subagent row order race

* fix(diagnostics): bound aggregate heap profile work

* fix(diagnostics): bound heap profile contributor calls

* fix(types): avoid overloaded stat return inference

* fix(diagnostics): fully bound heap profile registry

* chore(skills): refresh release snapshot manifest

* fix(diagnostics): retain browser counts in heap profiles
2026-07-23 12:22:33 -07:00
Brennan Benson 7ba946e39e fix(checks): stop the transient "PR already exists" card before the PR loads (#10008)
* fix(checks): stop the transient "PR already exists" card before the PR loads

The Checks panel briefly showed the terminal "Pull request already exists"
card (Open Review only) on a cold mount, and only rendered the real PR panel
after a manual refresh.

Root cause (regression from #9428): the eligibility probe resolves
`existing_review` from cache faster than the renderable PR hydrates, and the
new exhaustive selector ranks `existing_review` as a terminal safety blocker
above every loading state. So during the fetch window the panel settled on the
terminal card instead of a self-updating loading state. The auto-fetch also
used a lazy SWR priority, so the PR landed slowly unless the user forced a
foreground refresh.

Fix:
- Selector: while a PR fetch is queued/in-flight and we have positive-but-not-
  yet-renderable review evidence (existing_review or positive_unresolved), show
  the self-updating "Checking status" state instead of the terminal card. It
  flips to the review once details land. A concurrent branch blocker still owns
  the copy.
- Fetch: when a review is known to exist but no renderable PR is cached, escalate
  the auto-fetch to a foreground `active`/80 request (matching manual refresh) so
  it resolves promptly.

Reuses existing i18n keys; adds unit coverage for both parts.

* fix(checks): bound unresolved review foreground refreshes

* fix(types): exclude absent stat overloads

* fix(checks): preserve refresh precedence and budget

* fix(checks): avoid redundant foreground PR refreshes

Fold unresolved-review promotion into the existing refresh effect so cached evidence produces one dispatch instead of an SWR plus active pair. Keep non-GitHub evidence on the budgeted path to avoid forced GitHub work for GitLab and other providers.

* fix(checks): scope foreground refreshes by provider
2026-07-23 12:11:36 -07:00
Neil 49d0fdadfa fix(terminal): stop the orphan sweep from deleting reconnecting terminals (#9911) (#10155) 2026-07-23 12:10:03 -07:00
Jinjing 46683f0f55 fix(mobile): keep name input continuous during source picker transition (#10145)
* fix(mobile): keep name input continuous during source picker transition

Refactor create-workspace flow to maintain input continuity: single TextInput
morphs from form slot to docked position above the keyboard while results
reflow above it. Prevents field unmount/remount and keeps user focus on the
input as it transitions. Add keyboard inset resolution utilities and fill-mode
bottom drawer support for stable frame heights during result reflowing.

* fix(mobile): keep name input continuous during source picker transition

Extract drawer navigation into a custom hook and add `interactive` prop to
BottomDrawer to pin the form sheet under the source picker. The form stays
visible and laid out but non-interactive during source selection, revealing
its original height when the picker dismisses. Fix bottom-drawer height
calculation to never exceed the space above the keyboard.

* fix(mobile): reset drawer state when create-workspace modal closes

Prevent a queued transition timer from landing after the modal closes and
leaving stale drawer/pin state for the next open.

* fix(mobile): disable source field focus during drawer transitions

Prevent the workspace-name field from reopening the source picker when
the drawer closes. The drawer's dismiss restores focus to the field,
which re-fires onFocus and reopens the drawer. Gate the field's
focusability with an interactive prop so it only accepts focus when
the form sheet is active.
2026-07-23 12:09:30 -07:00
github-actions[bot] e55e15d44b Update README downloads badge 2026-07-23 18:43:10 +00:00
Neil 8f40ddf328 fix(memory): bound OOM-prone accumulators (#10179) 2026-07-23 06:22:56 -07:00
github-actions[bot] 434f433287 Update README downloads badge 2026-07-23 12:48:59 +00:00
NeilandOrca 8a23618310 fix(terminal): route host-agnostic setup and floating terminals instead of failing them closed (#10151)
Inline setup/onboarding terminals (skill installs, feature tips) and the floating terminal use host-agnostic synthetic worktree ids with no worktree/repo row. Since #9994, connectPanePty gated their transport on resolveWorktreeOperationRouteResult, which fails closed ('missing') for unknown ids — so the Orca CLI skill Update button, every other inline setup terminal, and the floating terminal showed "Workspace identity is ambiguous across hosts" instead of running.

Route them through the shared resolveTerminalWorktreeRoute (which already exempts the floating terminal and folder workspaces), and add the missing ephemeral-setup exemption so setup terminals follow the single active runtime (remote skill installs land there) or run locally when none is focused. Genuinely unknown/stale repo-backed worktrees still fail closed, preserving #9994.

Adds terminal-worktree-route unit tests and connectPanePty regression tests (proven to fail without the fix).

Co-authored-by: Orca <help@stably.ai>
2026-07-23 02:33:27 -07:00
Mark XianandOrcaWin 7ab601487c fix(remote): don't classify a stale/gone remote handle as agent completion (#9263)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-07-23 01:45:24 -07:00
1648251fb8 fix(terminal): restore a dark-background contrast floor (#10108)
* fix(terminal): restore a dark-background contrast floor

Fully disabling xterm minimumContrastRatio on dark backgrounds (#9599)
left near-background body text unreadable — Antigravity paints #262b30
on #1e242a (~1.1:1). Keep light backgrounds at WCAG-AA 4.5 and use a
milder dark floor (3) so dark-on-dark body text is lifted without the
full light-bg correction strength.

Fixes #10104

* fix(terminal): extend dark-bg contrast floor to preview + mobile terminals

The dark-background minimumContrastRatio floor (#10104) is applied per
`new Terminal()` construction site. Beyond the live pane, agent output also
renders in the dashboard popout preview and the mobile WebView, which were
still at the floor-1 default, so Antigravity output stayed unreadable there.

- AgentTerminalPreview: gate via resolveTerminalMinimumContrastRatio
- mobile WebView: port the gate as resolveTerminalContrastFloor (Chrome-74 JS)
- tests: builtin-catalog guard + mobile vm-harness coverage

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

---------

Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
2026-07-23 01:04:45 -07:00
e8d5d50c35 fix(quick-open): guide rg install after fallback budget errors (#9627)
* fix(quick-open): guide rg install after fallback budget errors

* fix(quick-open): show local host wording for local install guidance

The install-rg guidance component hardcoded 'on the remote', so the new
local fallback path told local users to install ripgrep 'on the remote'
— wrong for the exact case #9627 targets. Parse the location out of the
message and render the matching wording; add the local locale string and
a render test that guards against the 'on the remote' regression. Also
harden the reason capture against a stray ')' in the error text.

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

---------

Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
2026-07-23 00:27:06 -07:00
OrcaWin 569e9d88f8 docs: update WeChat QR to group 5 (#10126)
All earlier WeChat groups are full; point the README QR and copy at group 5.
2026-07-23 00:19:06 -07:00
Jinjing 746a8f3e48 fix: retry crash reports on primary endpoint instead of fallback (#10023)
Replace the api.onorca.dev fallback pattern with same-host retries on
www.onorca.dev, the endpoint that owns crash delivery. This ensures
consistent behavior and clearer error reporting when failures occur.
2026-07-23 00:13:43 -07:00
github-actions[bot] 2b7aa0aead Update README downloads badge 2026-07-23 07:09:53 +00:00
NeilandOrca a29c487200 fix(new-workspace): polish Project and Run-on pickers in create dialog (#10120)
- Tighten combobox row padding (py-2 -> py-1.5) in the Project and Run-on
  lists so items aren't so vertically loose.
- Use a Monitor glyph (not a server) for the local machine in Run-on.
- Pin 'Add host' as a compact single-row footer with one clean divider,
  matching the Project picker's 'Add a new project' footer (removes the
  taller two-line in-list row and its extra separator).
- Give the smart name field a solid bg-background so it matches the other
  inputs on light mode instead of reading translucent over the dialog.

Co-authored-by: Orca <help@stably.ai>
2026-07-22 23:56:53 -07:00
NeilandOrca 0b344425eb test(worktree): align teardown/watch tests with verified-exit behavior (#10115)
- orca-runtime: 'stop cannot be proven' now requires the fresh PTY inventory
  to still show the pty live (empty inventory proves exit post-#10106).
- git-common-watch: swap worktrees dir->file synchronously so no poll tick
  observes the transient ENOENT window and fabricates a delete.


(cherry picked from commit e11736d7e170982eeebe09256eb549446a4f4f7b)

Co-authored-by: Orca <help@stably.ai>
2026-07-22 23:20:33 -07:00
NeilandOrca 56101422d2 fix(release): regenerate Windows blockmap via app-builder-lib JS (#10110)
electron-builder 26 dropped the app-builder-bin Go binary, so the
signed-installer staging step failed with 'node_modules/app-builder-bin/
win/x64/app-builder.exe is not recognized'. Blockmap generation now lives
in app-builder-lib's pure-JS buildBlockMap; call it through a small script
in both the release-cut and signing-rehearsal workflows.

Co-authored-by: Orca <help@stably.ai>
2026-07-22 23:10:19 -07:00
OrcaWin 72a2d7bc7d fix(terminal): bound parse-deferred remote flow (#10012)
Credits remote terminal output only after parse or intentional discard, with bounded adaptive windows, fair draining, recovery cleanup, and RTT/parser benchmarks.
2026-07-22 22:58:38 -07:00
OrcaWin 88b7e69ba1 fix(runtime): recover orphan terminals without changing Active Server (#10011)
Adds guarded host-authoritative orphan PTY adoption and keeps Active Server durable preference mutations exclusive to its explicit settings control.
2026-07-22 22:58:09 -07:00
Neil c3620f0954 fix(worktree): verify exited PTYs before blocking deletion (#10106) 2026-07-22 22:55:35 -07:00
Neil c445f26541 perf(renderer): share retained TabBar projections (#10094) mobile-android-v0.0.32 2026-07-22 22:17:53 -07:00
Jinjing 09756dfaff Revert "Preserve Codex [tui] settings across managed CODEX_HOME remirrors (#9475)" (#10085)
This reverts commit c8381f3ea7.
2026-07-22 22:14:57 -07:00
Jinjing 1c477f99dd Fix p1 remote terminal resync (#10089)
* fix(terminal): resume output after recovery resync

* fix(terminal): harden recovery resync boundaries

* fix(terminal): prevent double rendering on frame-drop resync

After an untagged resync snapshot, the client resets to the snapshot's output
high-water. Buffered output generated while serializing is already included in
the snapshot, so replaying the entire buffer afterward renders those bytes
twice. Trim to output after the snapshot seq.

Tagged snapshots feed side consumers and need all buffered output unchanged.
Add exponential backoff for truncated recovery retries to avoid stampeding
flooded servers.
2026-07-22 22:14:42 -07:00
Jinjing 23fc1ea59a fix(mobile): bind markdown creation to file owner (#10083) 2026-07-22 22:14:30 -07:00
OrcaWin 8685cdb3fb fix(runtime): stop a throwing client-event listener from wedging worktree sleep + harden fan-out (#10052) 2026-07-22 21:54:33 -07:00
OrcaWin 8cb0b8dcde test(runtime): cover remote terminal retirement across paired viewers (#10053) 2026-07-22 21:44:30 -07:00
Brennan Benson 1d8ce38a5f feat(dashboard-popout): size the terminal dialog's PTY to the dialog grid (#9997)
* feat(dashboard-popout): size the terminal dialog's PTY to the dialog grid

The popout agent-terminal dialog rendered the pane's serialized frame at its
original cols/rows and CSS-scaled it down to fit. The dialog now claims the
PTY grid for its own box through the remote-desktop viewer registry: the PTY
reflows to the dialog's dimensions (crisp, unscaled), the main-window pane
parks at the held grid like any remote viewer hold, and closing the dialog
releases the claim so the pane reclaims its geometry. A phone-driven PTY
keeps the floor; the dialog then falls back to the scale-to-fit rendering.

Any grid change under a live preview stream (fit landing, host reclaim,
phone takeover) now pushes a resync so the dialog repaints from a snapshot
at the new grid — this also fixes garbled dialogs when the pane resized
while a dialog was open.

* fix(dashboard-popout): harden terminal grid claims

* perf(dashboard-popout): bound terminal preview resize work
2026-07-22 21:31:55 -07:00
Jinjingand5Hyeons 4a9affd6e5 fix(emulator): iOS ax via plain-JSON serve-sim helper (supersedes #10007) (#10029)
* Revert "Enable accessibility tree (`ax`) command on iOS emulator sessions (#10007)"

This reverts commit 43ae014a64.

* fix(emulator): expose iOS accessibility tree

* fix(emulator): support device-only iOS AX

* fix(emulator): normalize iOS ax to 0..1 and heal missing axUrl

serve-sim's helper /ax reports element frames in absolute pixels, but
tap/gesture take normalized 0..1 coords. Normalize the raw AX node tree
into a compact nested shape whose frames are 0..1 over the device screen
(first root's frame), mirroring serve-sim's own normalizeAxTree, so agents
can feed ax output straight back into input commands.

Also heal sessions that were registered without an axUrl: #9924 only
derived /ax at parse time, so already-active sessions had no endpoint.
The bridge now derives it from the session's mjpeg stream URL, guarded to
the /stream.mjpeg suffix so a non-mjpeg URL never fabricates a bogus /ax.

* docs(emulator): mark ax working on iOS with correct raw-AX-tree shape

Both skill guides and the CLI summary described iOS ax as unsupported (or,
via the reverted #10007, as a normalized "screen + elements" shape that
never matched the endpoint). ax works on both backends: Android via
uiautomator, iOS via the serve-sim helper. Document the real iOS output —
a raw AX node tree (labels, roles, nested children) with frames normalized
to 0..1 — and regenerate the bundled skill guides.

* chore(skills): regenerate skill bundle manifests

CI verify failed because generated skill artifacts were stale after version/skill revision bumps.

* fix(emulator): read ax from explicit device without active session

Fall back to udid-keyed session lookup when a worktree has no active emulator,
allowing `--device` targeting to work the same way for ax as it does for tap/type.
Also clarify in docs that AX frames are normalized 0..1 with top-left origin,
and show how to tap an element at its frame center (x+width/2, y+height/2).

* fix(emulator): cap iOS AX tree at 500 nodes

Unbounded accessibility trees can flood agent output. Enforce a 500-node limit (matching serve-sim's snapshot cap) and mark truncated parents so consumers know the tree was cut.

---------

Co-authored-by: 5Hyeons <ohs2251@naver.com>
2026-07-22 21:30:53 -07:00
Brennan Benson ee6319ebe4 fix(agents): scope Settings agent list and quick-launch menu to the remote-server host (#9790)
* fix(agents): scope Settings agent list and quick-launch menu to the remote-server host

With a paired Remote Server as the Active Server, Settings → Agents and the
tab-bar + quick-launch items always ran agent detection on the local client's
PATH, so a Windows client showed its own agents while worktree-create
correctly listed the server's.

- Extract TabBar's ssh/runtime/local owner resolution into a shared
  useAgentDetectionTargetForWorktree hook and use it in QuickLaunch, which
  previously resolved only SSH connections and fell back to local for
  paired-runtime worktrees.
- Scope AgentsPane detection (and its Refresh button) to the Active Server,
  with an "on <server>" badge showing which host the list came from.
  Enable/disable/default toggles remain client-side settings.
- Split runtime detection into store/slices/runtime-detected-agents.ts and add
  refreshRuntimeDetectedAgents: preflight.refreshAgents over the relay
  (login-shell PATH re-read), falling back to preflight.detectAgents on
  servers that predate the refresh RPC, keeping the last known list when the
  runtime is unreachable.

* fix(agents): avoid redundant runtime refresh fallback

* fix(agents): dedupe SSH agent refreshes

* fix(agents): preserve remote host boundaries

* fix(agents): prevent remote detection refresh races

* fix(agents): harden remote detection failures

* fix(agents): keep unresolved detection off local host

* fix(agents): keep cold remote ownership unresolved
2026-07-22 21:22:18 -07:00
Jinjing fc05769edf fix(ssh): allow blank user in VS Code authority (#10072) 2026-07-22 21:10:05 -07:00
NeilandOrca 8448557be9 perf(startup): overlap catalog/session disk reads with the worktree scan (#9841)
* perf(startup): overlap catalog/session disk reads with the worktree scan

App.tsx hydrated repos, project-groups, folder-workspaces, worktrees, then
session-get strictly serially, even though only worktrees depends on repos.
Once repos is loaded, run fetch-worktrees, session-get, and the (internally
ordered) local project-group/folder-workspace catalog chain concurrently so
the two disk reads hide behind the O(repos) worktree git scan (the startup
long pole). list-runtime-session-hosts now overlaps the repo scan too.

Ordering preserved: repos before worktrees/session; project-groups before
folder-workspaces; hydrate-session-stores still runs only after all settle.
fetchAllWorktrees({hydrationPurge:'defer'}) returns before its folderWorkspaces
read, so it needs no catalog ordering at startup.

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

* perf(startup): don't serialize the worktree scan behind host discovery

Address CodeRabbit review: awaiting runtimeHostsPromise before the Promise.all
made fetch-worktrees + the catalog chain wait on list-runtime-session-hosts, even
though only session-get needs the host ids. Chain session-get off the host promise
inside the Promise.all instead, so the worktree scan and catalog reads start
immediately and the host-list IPC only gates session-get.

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

* test(startup): assert concurrent hydration graph in source-order guard

The #18 startup change runs worktrees/session-get/catalog concurrently in a
single Promise.all, so the guard's old serial folders<worktrees assertion (and
its session-get slice terminator) no longer describe the code. Assert the real
invariants: UI hydrates before any local read, the catalog chain stays ordered,
worktrees+session start after repos, and all three are joined in one Promise.all.

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

* fix(startup): join concurrent hydration with allSettled so recovery can't race in-flight tasks

The concurrent worktrees/session/catalog join used fail-fast Promise.all, so a fast
rejection from one branch dropped into the catch/recovery path (which reconnects
terminals and flips readiness) while a sibling hydration task was still in flight and
mutating catalog/worktree state — a race the old serial flow could not hit. Use
Promise.allSettled and surface the first rejection only after all three settle, so
recovery still triggers but nothing is left writing to the store. Guard test updated.

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

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-22 20:41:52 -07:00
NeilandOrca 414bdc9f86 perf(main): gate idle git-common polling + coarse-FS add/remove fix (re-open of #9882) (#10074)
* perf(main): gate idle git-common polling

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

* fix(main): enumerate git-common worktrees every tick to catch coarse-FS add/remove

The worktrees-dir readdir was gated on the dir's mtime:ctime:ino:size signature, so on
a coarse-mtime/FAT filesystem a same-granule add+remove (all four fields collide) went
undetected until the ~30s index backstop. A single readdir of a small dir is negligible
next to the per-entry structural stats that already run every tick, so always enumerate —
the listing is the authoritative add/remove signal. The expensive per-entry index read
stays gated on each entry's own dir signature; onFullScan now reflects the ungated
index-metadata backstop fan-out (the real periodic cost) rather than the readdir.

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

* fix(main): don't fabricate worktree deletions on a transient git-common readdir failure

Follow-up to always-enumerating the worktrees dir: the readdir catch-all treated ANY
error as an authoritative empty listing, so a transient failure (EIO/ESTALE/EMFILE,
network/SSH hiccup) emitted a false delete for every linked worktree (and a false create
next tick) — and enumerating every tick widened that exposure. Only ENOENT (dir truly
absent) now yields an empty listing; other errors retain the known entries so per-entry
stats still run and a real removal surfaces as that entry's own stat miss. Adds a
regression test (readdir → ENOTDIR) asserting no false delete.

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

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-22 20:41:30 -07:00
NeilandOrca 63bd3ff189 perf(main): park worktree metadata pollers while the window is hidden (#9849)
* perf(main): park worktree pollers while hidden

The per-repo worktree metadata pollers ran 24/7 with no window-visibility gate
(~50 fs.stat/sec at 20 repos on macOS while the window is hidden). Park the poll
timers while the window is hidden and resume losslessly (immediate fresh-vs-retained
snapshot diff) on reveal, via an injected WorktreePollerWindowVisibility mirroring
the ssh-port-scanner pattern.

Only the poll timers are gated; the darwin native worktrees/ fsevents watch and its
teardown/re-arm lifecycle stay always-on (push-based, ~0 idle cost; #8732 race).

A window is parked only once it has actually been shown and is now hidden: a live
never-shown window (ORCA_E2E_HEADLESS keeps one) and null/destroyed windows stay
always-visible, so a windowless/headless host never permanently parks the poller.

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

* fix(main): keep start-to-start poller cadence after the interval→timeout change

Parking the pollers moved them from setInterval to a one-shot setTimeout chain, but
scheduling the next tick a full pollIntervalMs AFTER each scan completed turned the
cadence into gap-after-completion — every visible refresh landed ~one scan-duration
late per tick. Measure from tick start and schedule max(0, interval - elapsed) so the
cadence is start-to-start like the original interval, while keeping the one-shot chain
that park/resume needs.

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

* fix(main): clamp poller reschedule delay to [0, pollIntervalMs]

Date.now() is not monotonic: a backward wall-clock jump (NTP correction) during a scan
makes (now - startedAt) negative, so the start-to-start delay pollIntervalMs - elapsed
would exceed one interval by the adjustment — suppressing visible metadata refreshes for
minutes/hours, unlike the former setInterval. Cap the computed delay at pollIntervalMs
(upper) as well as 0 (lower) in all three pollers.

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

* refactor(main): drop redundant notifyTimer guards to stay under max-lines

The visibility-parking change pushed worktree-base-directory-watcher.ts to 303 code
lines (limit 300). clearTimeout tolerates null/undefined, so the two truthy guards
around it are redundant — remove them (coalescing null→undefined for the type). No
behavior change; back under the limit without an eslint-disable.

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

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-22 20:38:35 -07:00
NeilandOrca 059a80b298 feat(review): add assignable "Send Review Notes to Agent" shortcut (#10027) (#10070)
Adds a keyboard command that opens the "Send notes to an agent" picker for
the active worktree's AI diff-review notes, enabling a fully keyboard-driven
review flow. Unbound by default; users assign it in Settings → Keyboard
Shortcuts.

- New `sourceControl.sendReviewNotes` command (scope global, unbound). Set
  `conflictGroup: 'editor'` so Settings warns on collisions with editor chords
  (e.g. Add Review Note), not just global ones.
- Dispatched from App.tsx's existing global capture handler so it respects the
  terminal-shortcut policy, the shortcut-recorder guard, and defaultPrevented.
- Store thunk `openDiffNotesSendMenuForActiveWorktree` reveals Source Control
  and requests the notes send menu open; no-op when there are no unsent notes.
- Menu opens via a nonce-based store request consumed on mount, TTL-bounded so
  a request the menu never consumed can't reopen it on a later remount.

Co-authored-by: Orca <help@stably.ai>
2026-07-22 20:38:10 -07:00
NeilandOrca d152039e9d fix(terminal): never sweep descendants of an already-exited PTY root (hardening, #9191 investigation) (#10069)
Co-authored-by: Orca <help@stably.ai>
2026-07-22 20:31:16 -07:00
Zuz666andYuris Auzins ffe54a59a3 fix(skills): make skippedReason switch exhaustive (#9594)
The type-aware switch-exhaustiveness lint rule requires explicit cases
for 'current', 'duplicate', and undefined; they fall through to the
existing generic skipped message, so behavior is unchanged.

Co-authored-by: Yuris Auzins <zuz666@users.noreply.github.com>
2026-07-22 20:04:41 -07:00
OrcaWinandOrcaWin a2e440b308 fix(sidebar): identify remote server workspaces (#10061)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-07-22 23:03:39 -04:00
Jinjing bae29fd1e2 fix(native-chat): handle reordering with timestampless transcripts (#10050)
Grok transcripts carry no timestamps. Previous logic excluded these rows
from matching, leaving pending sends and launch prompts unmatched and
causing the seeded bubble to appear rank-pinned at the list tail — which
reads as conversation reordering.

Now pending sends, launch prompts, and their pruning rules treat null
timestamps as matching-eligible, allowing echo suppression and proper
cleanup of delivered messages.
2026-07-22 19:48:55 -07:00
NeilandOrca 099003188d fix(file-explorer): stop italic ignored filenames clipping ".md" to ".ma" (#10047)
Co-authored-by: Orca <help@stably.ai>
2026-07-22 19:47:02 -07:00
Brennan Benson 4fce2de494 fix(mobile): keep native chat from resizing the covered terminal PTY (#9988)
* fix(mobile): keep native chat from resizing the covered terminal PTY

Native chat reads the agent transcript stream and never renders the
terminal grid, but two paths still pushed phone dimensions into the
covered PTY, reflowing the desktop terminal for no benefit:

- The covered lease-only subscribe carried the cached viewport, and
  handleMobileSubscribe phone-fits the PTY whenever a viewport is
  present. The lease now omits the viewport so the host keeps the
  desktop baseline and late-binds on return to the terminal tab.
- useTerminalViewportRefit measured the still-mounted WebView under
  the chat overlay and sent terminal.updateViewport on rotation,
  keyboard, text-scale, reconnect, and iOS-resume triggers. Refits
  are now suppressed while native chat covers the active terminal;
  the triggers already mark the viewport stale, and the
  return-to-terminal resubscribe re-measures.

* fix(mobile): harden native-chat resize suppression
2026-07-22 19:36:22 -07:00
Jinjing 5b7fdd7ef5 Capture daemon startup logs before window ready in e2e test (#10040)
The daemon health-check guard logs during main-process startup, which can
complete before the renderer window resolves. Moved stderr listening to the
launch options so early logs aren't missed. Also made the assertion regex
pattern-based instead of exact-string matching to tolerate benign log
rewording, and added a check that the replace path stayed off.
2026-07-22 19:34:10 -07:00
JinjingandOrca 3708c4f6ce fix(mobile): report interrupted native chat sends as delivery-unknown, not failed (#10021)
* fix(mobile): report interrupted native chat sends as delivery-unknown, not failed

A terminal.send interrupted mid-flight showed a definite "Message not sent"
even when the desktop may have already delivered the text. Three paths were
misclassified as definite failures:

- Logical relay/direct cutover: migrateTo rejects in-flight requests with
  LogicalClientCutoverError, which mapped to 'rejected'. Now maps to 'unknown'
  (held unconfirmed + transcript-echo verification; never retried since
  terminal.send is non-idempotent).
- Suspend/close of a half-open session: the stable logical client blanket-
  rejected in-flight pendings with plain 'Client suspended'/'Client closed',
  preempting the physical layer's delivery-unknown marking. It now lets the
  physical close settle them, so post-write failures stay marked and pre-write
  failures stay definite.
- Relay path: mobile-relay-rpc-session never marked delivery ambiguity at all
  (timeout, close, link failure). Post-write rejections are now marked;
  pending entries only exist after the frame reached the authenticated link.

Permission, ask-answer, and cancel-Escape surfaces now show "unconfirmed —
check chat before retrying" instead of a definite "not sent" on ambiguous
outcomes (still not-accepted, never retried). Also consolidates a private
copy of isLogicalClientCutoverError in worktree-create-retry.

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

* chore(skills): regenerate skill-bundle manifest artifacts

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-22 19:28:21 -07:00
Jinjing 6d55c7fa16 rename: rebrand user-facing Native chat to Chat UI (#10036)
Update desktop experimental settings, mobile settings/onboarding, i18n
(en/zh/ja/ko/es), and user-visible error strings. Keep internal APIs and
identifiers as nativeChat.
2026-07-22 19:14:44 -07:00
Neil a0944cc129 fix(linux): restore Ubuntu 20.04 launch — pin node-pty glibc symbols + add glibc/libstdc++ packaging gate (#9902) (#10019)
* fix(linux): restore Ubuntu 20.04 launch by pinning node-pty glibc symbols (#9902)

The bundled node-pty pty.node is compiled from source in release CI on
ubuntu-latest (glibc 2.39). glibc's 2.32-2.34 libpthread/libutil merge
relocated openpty/forkpty (GLIBC_2.34) and pthread_sigmask (GLIBC_2.32)
into libc under new symbol versions, so the from-source build bound to
versions absent on Ubuntu 20.04 (glibc 2.31). The main process imports
node-pty at startup, so the app crashed on launch. pty.node is the sole
blocker (Electron needs GLIBC_2.25; other native modules <= 2.17).

- Patch node-pty: a .symver shim pins the 3 symbols to their pre-merge
  version (GLIBC_2.2.5 x64 / GLIBC_2.17 arm64), and Linux-only ldflags
  force libutil.so.1/libpthread.so.0 back into DT_NEEDED. Guarded to
  Linux; macOS/Windows untouched.
- Add a packaging gate (verify-linux-glibc-floor.cjs, afterPack): reads
  each bundled native binary's objdump -p version needs and fails the
  Linux build if any strong GLIBC_/GLIBCXX_/CXXABI_ node exceeds stock
  Ubuntu 20.04 (glibc 2.31 / GLIBCXX_3.4.28 / CXXABI_1.3.12). Catches
  GLIBC_ABI_DT_RELR, rejects GLIBC_PRIVATE, skips weak needs, fail-closed.
- Docs + tests; the lazy sherpa-onnx speech prebuilt (GLIBCXX_3.4.29,
  never loaded at launch) is a documented libstdc++-floor exemption.

* fix(linux): assert DT_NEEDED provider deps in the glibc-floor gate

Harden the packaging gate (flagged in adversarial re-eval): the version-floor
check alone can false-pass if the patch's forced `-l:libutil.so.1` ever silently
drops — the pinned openpty@GLIBC_2.2.5 still resolves from libc's compat alias at
build time, but fails to load on Ubuntu 20.04 where openpty/forkpty live only in
libutil. The gate now also asserts that any binary importing openpty/forkpty
keeps libutil.so.1 in DT_NEEDED. Validated on a real symver-pinned .so with
libutil dropped (now fails) vs. present (passes). Documents the recommended
real-host smoke-test follow-up.
2026-07-22 19:11:44 -07:00
OrcaWin 0326594d52 Update paired Orca servers from the active client (#9839) 2026-07-22 18:52:37 -07:00
Neil 7a422712b1 fix(settings): stop React #185 in Voice speech-model dropdown (crashes 7f93d89c, 362b566d) (#10028) 2026-07-22 18:37:49 -07:00
OrcaWin 41751dd90d fix(runtime): route HUB-owned SSH worktrees through owning runtime (#9994) 2026-07-22 18:25:05 -07:00
OrcaWin 3b86405201 fix(terminal): stop overlay fallback measure-fit loop (#10026)
Coalesce identical and sub-pixel fallback overlay measurements so ResizeObserver and xterm fit cannot sustain a render feedback loop, while preserving precise committed geometry.

Adds regression coverage for stable measurements, sub-pixel jitter across integer boundaries, and genuine resizes.
2026-07-22 18:22:53 -07:00