Commit Graph
5081 Commits
Author SHA1 Message Date
Jinwoo-HandOrca 1182bcdc5e test: keep relay cache suites client-offline
Co-authored-by: Orca <help@stably.ai>
2026-07-16 05:53:16 -04:00
Jinwoo-HandOrca 156ea35c0e feat: detect remote Linux libstdc++ ABI
Co-authored-by: Orca <help@stably.ai>
2026-07-16 05:35:48 -04:00
Jinwoo-HandOrca c8d4acb2cc feat: detect remote macOS translation
Co-authored-by: Orca <help@stably.ai>
2026-07-16 05:07:43 -04:00
Jinwoo-HandOrca bcda043895 feat: detect remote macOS version
Co-authored-by: Orca <help@stably.ai>
2026-07-16 04:41:43 -04:00
Jinwoo-HandOrca d1eb0eb63e feat: detect remote Linux kernel
Co-authored-by: Orca <help@stably.ai>
2026-07-16 04:12:20 -04:00
Jinwoo-HandOrca d8b17a354b feat: detect remote Linux libc
Co-authored-by: Orca <help@stably.ai>
2026-07-16 03:33:55 -04:00
Jinwoo-HandOrca c170ff92c1 feat: acquire verified relay artifacts
Co-authored-by: Orca <help@stably.ai>
2026-07-16 02:58:45 -04:00
Jinwoo-HandOrca bd240049ff fix: settle Windows relay cache locks
Co-authored-by: Orca <help@stably.ai>
2026-07-16 02:25:46 -04:00
Jinwoo-HandOrca 3018977c11 feat: populate verified relay cache
Co-authored-by: Orca <help@stably.ai>
2026-07-16 02:07:59 -04:00
Jinwoo-HandOrca 22031fa686 feat: resolve verified relay cache entries
Co-authored-by: Orca <help@stably.ai>
2026-07-16 01:32:29 -04:00
Jinwoo-HandOrca aefcaa9a93 feat: define relay artifact cache root
Co-authored-by: Orca <help@stably.ai>
2026-07-16 01:00:43 -04:00
Jinwoo-HandOrca 749f775f15 feat: compose official relay manifest trust
Co-authored-by: Orca <help@stably.ai>
2026-07-16 00:39:52 -04:00
Jinwoo-HandOrca 4c09902c04 feat: add compiled relay manifest trust boundary
Co-authored-by: Orca <help@stably.ai>
2026-07-16 00:00:26 -04:00
Jinwoo-HandOrca 11f367e661 feat: validate relay manifest accepted keys
Co-authored-by: Orca <help@stably.ai>
2026-07-15 22:44:36 -04:00
Jinwoo-HandOrca ffc7947a4a feat: load signed packaged relay manifest
Co-authored-by: Orca <help@stably.ai>
2026-07-15 22:20:20 -04:00
Jinwoo-HandOrca b79bcbd04e feat: add disconnected relay cache eviction
Co-authored-by: Orca <help@stably.ai>
2026-07-15 21:40:51 -04:00
Jinwoo-H 3c1ead8fb1 feat: add immutable relay artifact cache entries 2026-07-15 20:46:44 -04:00
Jinwoo-HandOrca 44ef150da3 fix: release relay cache locks on Windows
Co-authored-by: Orca <help@stably.ai>
2026-07-15 19:44:01 -04:00
Jinwoo-HandOrca 869603a3e8 feat: add disconnected relay cache lock
Co-authored-by: Orca <help@stably.ai>
2026-07-15 19:29:49 -04:00
Jinwoo-HandOrca 2d5785ef6f fix(ssh): bound relay artifact extraction memory
Co-authored-by: Orca <help@stably.ai>
2026-07-15 18:23:22 -04:00
Jinwoo-HandOrca 7580600e37 feat(ssh): add verified relay artifact extraction
Co-authored-by: Orca <help@stably.ai>
2026-07-15 17:51:26 -04:00
Jinwoo-HandOrca c9f6da55e4 build(ssh): use portable relay archives
Co-authored-by: Orca <help@stably.ai>
2026-07-15 16:30:48 -04:00
Jinwoo-HandOrca ef9681d325 feat(ssh): add verified relay artifact downloader
Co-authored-by: Orca <help@stably.ai>
2026-07-15 15:40:00 -04:00
Jinwoo-HandOrca 7b8c7669ac feat(ssh): bind relay selection to verified manifest
Co-authored-by: Orca <help@stably.ai>
2026-07-15 15:02:26 -04:00
Jinwoo-HandOrca cfea521c7c build(ssh): produce verified runtime manifest tuples
Co-authored-by: Orca <help@stably.ai>
2026-07-15 05:44:14 -04:00
Jinwoo-HandOrca 707a9da23e build(ssh): align Windows runtime manifest identity
Co-authored-by: Orca <help@stably.ai>
2026-07-15 02:00:46 -04:00
Jinwoo-HandOrca e3f76d3ba7 build(ssh): correct native runtime metadata contracts
Co-authored-by: Orca <help@stably.ai>
2026-07-14 17:21:26 -04:00
Jinwoo-HandOrca b9d80a4cbc feat(ssh): define relay runtime artifact contracts
Co-authored-by: Orca <help@stably.ai>
2026-07-14 07:43:28 -04:00
Jinwoo-HandOrca c4259d94fc fix(ssh): resolve complete remote node toolchains
Co-authored-by: Orca <help@stably.ai>
2026-07-14 06:28:26 -04:00
Brennan Benson 9d51732523 perf(terminal): defer cold worktree activation tab mounts until first reveal (#8597)
* perf(terminal): defer cold worktree activation tab mounts until first reveal

Activating a worktree mounted a TerminalPane for every saved terminal tab
in one render pass. Each mount replays scrollback through xterm, attaches
a WebGL renderer, and issues a sync-IPC snapshot read, so a worktree with
many agent-session tabs froze the renderer for tens of seconds (field
trace: 200+ replay-guard stall releases inside one activation window,
plus restore-marker feedback loops re-fetching snapshots at ~4/sec).

Cold activations now mount only the tabs the user can see (active tab,
each split group's active tab, activity-portal tabs, pending spawns) plus
tabs that are already live or that parked byte watchers cannot cover.
Every other tab defers like a cold-parked tab from birth: no view until
first reveal, with the parked byte watchers owning bell/title/completion
side effects meanwhile. The restriction reuses the targeted background
mount mechanism and lifts once every tab has been revealed.

Deferral only engages when more than four tabs would mount cold, and only
while hidden-view parking is enabled, so small worktrees and the parking
kill switch keep today's behavior.

* test(terminal): prove cold-activation deferral end-to-end; unblock it past hydration's blanket spawn flag

The new e2e (child worktree, 8 tabs, renderer reload against live daemon
sessions) caught the deferral never engaging: session hydration marks
every persisted tab pendingActivationSpawn, and treating that flag as
must-mount-now put all tabs in the immediate set. Only explicit queued
startups (pendingStartupByTabId) mount eagerly; a deferred tab's reveal
consumes the hydration flag exactly like an activation mount would.
Sleep/wake respawn flows go through targeted background mounts and are
unaffected (terminal-sleep-wake-restore passes).

The spec asserts the visible tab mounts, deferred tabs stay unmounted
with parked byte-watcher coverage, and a revealed tab mounts on demand.

* fix(terminal): harden deferred activation lifecycle

* docs(reliability): correct SSH startup assertion evidence
2026-07-14 02:58:34 -07:00
Brennan Benson 31f643ca42 Add version-matched skill guides to the CLI (#8624)
* Add version-matched bundled skill guides

* Clarify skill freshness rollout PRs

* Add canonical skills show alias

* fix(skills): address guide review feedback

* fix(skills): make guide commands cross-platform

* fix(skills): apply the ORCA convention to the emulator guides

Review follow-up: the emulator guides still instructed literal
`orca emulator ...` in sh fences with no Linux disambiguation, so on
unmanaged Linux they could launch the GNOME screen reader — the exact
failure the executable-selection preamble prevents. Both emulator
guides now carry the preamble and ORCA placeholder across fences,
tables, and prose, and the cross-platform safety test covers all four
converted guides. Also replaces computer-use's "unless a block names a
shell" carve-out, which contradicted its own POSIX example, with the
unconditional placeholder rule.
2026-07-14 02:17:55 -07:00
Rod BoevandOrcaWin 59460f7576 fix(terminal): derive zsh wrapper ZDOTDIR from %x so non-ASCII WSL logins load .zshrc (#8003) (#8209)
The shell-ready zsh wrapper restored ZDOTDIR from the env-imported
`${ZDOTDIR}`. On Windows+WSL the wrappers are generated with a Windows
path baked in but sourced via /mnt/c, and for non-ASCII Windows
usernames (e.g. a Korean login) zsh corrupts environment values whose
UTF-8 bytes fall in its 0x84-0x9D token range while processing startup
files. The corrupted `${ZDOTDIR}` failed the self-check, so the wrapper
fell back to the unusable baked Windows literal and the user's ~/.zshrc
never loaded — a bare `HOSTNAME%` prompt with no theme/aliases/PATH.

Derive the wrapper dir from `${${(%):-%x}:h}` instead — %x is zsh's
internal script name for the file being sourced and is not subject to
the env-import corruption. `${ZDOTDIR}` is kept only as a fallback when
%x expansion yields nothing; the existing final restore still validates
with -f before trusting the value. On native macOS/Linux the derived
value equals the old one, so behavior there is unchanged.

Covers local PTYs, the daemon, and the Windows→WSL launch path, which
all share getZshEnvTemplate. Adds a live-zsh regression test that sources
the wrappers from a non-ASCII (token-range) runtime path.

Supersedes the ORCA_ORIG_ZDOTDIR path-normalization approach with a wrapper self-location fix that also covers the non-ASCII (token-range) corruption trigger.

Co-authored-by: OrcaWin <alpha-eng@stably.ai>
2026-07-14 01:55:04 -07:00
NeilandOrca 840d3277d1 fix(daemon): replace a permanently wedged daemon instead of preserving it forever (#8689) (#8697)
* fix(daemon): replace a permanently wedged daemon instead of preserving it forever (#8689)

A daemon whose socket accepts connections but whose event loop never answers the
'hello' handshake was adopted by the launcher unconditionally and never re-evaluated,
so every terminal spawn failed with 'DaemonProtocolError: Hello response timed out'
with no recovery.

- daemon-init.ts: bound the launcher's 'preserve any unresponsive-but-connectable
  daemon' with a grace window. A transient wedge (Windows update-relaunch AV/disk
  pressure) drains within ~20s and is preserved WITH its live sessions; a permanent
  wedge exhausts the grace and is replaced. Stays well under the 60s local-PTY
  fail-open cap.
- daemon-pty-adapter.ts: isDaemonGoneError now treats 'Hello response timed out' as
  daemon-gone, so a runtime wedge triggers withDaemonRetry's respawn (re-entering the
  same grace-bounded launcher) instead of failing every spawn until app restart.

Tests pin the grace magnitude so it cannot be silently shrunk.

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

* fix(daemon): widen wedged-daemon grace window to ~60s

Bump WEDGED_DAEMON_GRACE_RETRIES 3 -> 11 (~20s -> ~60s) to keep live-session
loss on the transient-wedge (Windows update-relaunch) path as close to zero as
possible. A transient wedge drains early and stays under the 60s fail-open cap;
only a permanent wedge runs the full window. Export the constant and pin its
floor in tests so it can't be silently shrunk.

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

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-14 01:50:56 -07:00
fsdwen 2b8cec36d9 fix(i18n/zh): 修复3处翻译质量错误 (#8693) 2026-07-14 01:21:05 -07:00
Neil 8e0977d295 fix(mobile): resync worktree list + idempotent notification replay on reconnect (#8498 #8129)
Take-over of #8605 (issue #8591). Ships #8498 (worktree resync + pull-to-refresh + cache write-through) and #8129 (idempotent notification replay on reconnect). Fixes the original PR's field mismatch (seq vs notificationSeq) and adds the missing notifications.getMissedSince mobile RPC allowlist entry. #6784 and #4500 held back to avoid conflicting with the relay work (#8536). Co-authored-by: Brandon Bennett (@branben).
2026-07-14 00:23:53 -07:00
Brennan Benson 36cd8a3347 fix(terminal): retire sessions when tabs close (#8628)
* fix(terminal): retire sessions when tabs close

* fix(terminal): close remaining session lifecycle gaps

* fix(terminal): close review-discovered lifecycle gaps

* fix(terminal): revalidate bulk session retirement

* fix(terminal): harden retirement review edges

* fix(agent): reverify restored pane authority

* test: make terminal retirement gate portable on Windows

* test: use POSIX join in Linux PATH assertion

* test: keep simulated Linux PATH host-consistent
2026-07-14 00:15:34 -07:00
NeilandOrca a2b5431ca3 fix(markdown-preview): paint Find matches without mutating react DOM (crash 237acef1) (#8678)
Co-authored-by: Orca <help@stably.ai>
2026-07-13 23:34:13 -07:00
OrcaWin 3bb6f917e4 @ (#8683)
fix(sidebar): guard kanban worktree sort against undefined displayName (crash 99657ab1)

A worktree reached the sidebar with an undefined displayName, so the
Manual-sort comparator ran `a.displayName.localeCompare(...)` and threw
`TypeError: Cannot read properties of undefined (reading localeCompare)`,
unmounting the sidebar.worktrees error boundary.

Route both kanban comparators through a null-safe compareDisplayName so a
missing name only affects tie-break order instead of crashing. Behavior is
unchanged when displayName is present.

Adds regression tests for both the manual and recent sort paths.
@
2026-07-13 23:30:48 -07:00
NeilandOrca 9a324bf333 fix(agent-status): guard against re-entrant pending-status flush (crash 9fc89529) (#8671)
Co-authored-by: Orca <help@stably.ai>
2026-07-13 23:08:19 -07:00
moseohandOrcaWin 7d8c4fdca1 fix(github): pin work-item list ordering to updated-desc so cursor pagination reaches every page (#8658)
* fix(github): pin work-item list ordering to updated-desc so cursor pagination reaches every page

The Tasks page paginates work items with an updatedAt cursor
(updated:<oldest-item), but the underlying gh calls never pinned a sort:
'gh issue list' defaults to created-desc and '--search' defaults to
best-match. Items created long ago but updated recently therefore never
appeared on any page — page 0 (created order) skipped them and every
later page excluded them via the cursor — so the pager advertised pages
the fetch chain could never reach, clicks on them clamped to the last
real page, and cross-page ordering was scrambled.

Append sort:updated-desc to every list/search invocation so the fetch
order matches the cursor field on the first and all subsequent pages.

Verified against a live 588-issue repo: the cursor chain previously
died around page 5; it now traverses 585/588 unique issues (the
remainder is the pre-existing strict '<' boundary edge for items
sharing the cursor's exact timestamp).

Fixes #8649

* fix(github): make work-item cursor pagination lossless at updatedAt boundaries

Builds on the sort-pin fix: switch the pagination cursor from strict
'updated:<' to inclusive 'updated:<=' so items sharing the boundary row's
exact updatedAt are no longer skipped between pages (the residual 3/588 edge
in #8649).

The inclusive bound re-fetches the boundary rows, so dedupe them by repoId+id
(a bare item.id like 'issue:9' collides across repos). Extract the page
accumulation out of the 12k-line TaskPage component into a pure, unit-tested
helper (accumulateWorkItemPages) that dedupes and backfills: it accumulates
fresh rows across fetches and emits uniform pageSize pages, so deduped pages
never shrink below the size totalPages (count / effectivePageSize) assumes —
which would otherwise strand the tail items and break the no-count degraded
pager.

Also hoist the updated-desc ordering into a named WORK_ITEM_LIST_SORT_QUALIFIER
constant so the cursor's ordering contract has one home.

Tradeoff: when per-repo fetch size equals pageSize, the boundary dedupe costs
one extra fetch per page; acceptable for interactive pagination and bounded by
the gh rate-limit guard. Persisting the cursor/buffer across calls is a
possible follow-up.

---------

Co-authored-by: OrcaWin <alpha-eng@stably.ai>
2026-07-13 22:37:46 -07:00
Brennan Benson 8b86e7ead7 fix(terminal): let pane recovery reach input stuck behind a never-settling connect (#8673)
Follow-up to the pane self-heal: its input-undeliverable detector is
gated off while a transport connect is settling (correct — remounting
mid-reattach used to kill the resolving shell), but the gate had no
timeout. A connect that never settles (the SSH RPC-timeout class) left
the one state recovery still could not reach: no output flows, so no
other detector fires, and every keystroke is silently dropped forever.

Two changes that only make sense together:
- the gate becomes a 60s settle grace; past it, undeliverable input may
  request recovery again (still pty:hasPty-confirmed).
- the transport's destroyed-while-connecting check now kills only what
  the connect CREATED. A fresh spawn is ours to reap; a reattach
  (sessionId) resolves to a pre-existing session owned by the tab
  lifecycle — tab close kills it by id through its own path, so killing
  it here turned a remount racing a slow reattach into the loss of a
  live shell. This is what makes the grace safe: recovery firing at 60s
  against a connect that resolves at 61s now costs a wasted view
  rebuild, not the user's session.

Verified: transport unit test pins the reattach/fresh-spawn kill split;
the recovery e2e pair passes 6/6 consecutive iterations with this
change (an earlier flaky signal bisected to machine load: failures only
under parallel-build load, two unrelated failure modes, 4/4 then 6/6
green on quiet hardware with and without the change re-applied).
2026-07-13 22:33:00 -07:00
Brennan Benson 9924b07bcc fix(terminal): self-heal panes whose renderer dies while the PTY stays alive (#8630)
* fix(terminal): self-heal panes whose renderer dies while the PTY stays alive

A pane's xterm write pipeline can die while its shell keeps running: a
synchronous throw escaping an unguarded write callback wedges WriteBuffer
(issue #2836), and write() on a disposed terminal silently drops its
completion callback (verified against vendored xterm 6.1.0-beta.287 — it
does NOT throw, falsifying the output scheduler's disposed-race catch).
Every recovery path we have (dead-session reconcile #6514/#7002,
hibernation wake #7145, the allDead activation generation bump) gates on
the PTY being dead, so these panes stayed fossils: last frame painted,
every keystroke and byte of output silently dropped, delivery ack credits
leaking, until the user reloaded the window (issue #8104 class).

Detection is probe-certified, mirroring replay-guard.ts:
- a scheduler write whose completion stalls gets an empty probe write; a
  probe that never parses certifies the pipeline dead (catches both the
  wedged WriteBuffer and the disposed-terminal case) and credits the
  queued deliveries so main's in-flight window no longer leaks
- the replay guard's existing wedged release ("pane likely needs
  recovery") now actually hands the pane to recovery
- user input rejected by an unbound transport (detached during a
  remount/move and never rebound) arms recovery after confirming the PTY
  is alive via pty:hasPty

Recovery reuses the proven remount seam: bump the tab's generation so
TerminalPane unmounts, detach() preserves the live PTY, and the remounted
pane builds a fresh xterm that reattaches and replays the daemon
snapshot — no shell restart, capped per tab to prevent remount storms.

The new e2e spec pins both phenotypes end-to-end (wedge → recover,
dispose-under-live-bindings → recover); both fail on main and pass with
the fix, and the same arc was validated live in a pnpm dev instance.

* fix(terminal): make pane recovery strictly best-effort in timer contexts

Recovery fires from stall-watch timers, replay-guard releases, and onData —
contexts where a throw becomes an unhandled error (CI verify caught this:
pty-connection.test.ts mocks a partial store, other tests advance fake
timers past the stall window, and the certification path hit a missing
remountTerminalTabForRecovery). Guard the store action call and the
ptyIdsByTabId reads so a partial surface yields a false return, never a
throw, and pin it with a regression test.

* fix(terminal): guard pane recovery against in-flight reattach and remote liveness blind spots

Review findings on the self-heal (adversarial pass):

1. HIGH: typing during an in-flight connect/reattach (startup restore,
   app-SSH) hits sendInput while the transport is legitimately unbound; an
   input-undeliverable remount there destroys the unbound transport (no
   ptyId yet, so unmount cannot detach), and pty-transport's destroyed
   check then kills the PTY the resolving reattach returns — the live
   shell recovery exists to preserve. Gate the input detector on a
   transport-connect-in-flight flag (set around all three connect sites)
   and on disposed, so "not deliverable YET" never remounts. The fossil
   case (detached and never rebound) has no pending connect and still
   recovers.

2. pty:hasPty answers null for ids the local registry does not own, which
   made the liveness gate inert for remote panes: a disconnected remote
   runtime would remount-churn on every cooldown window while typing.
   Remote panes (connection-tagged or remote:-prefixed) now require an
   authoritative true; local panes keep the lenient null-proceeds gate.

Flagged for follow-up, not changed here: pty-transport's destroyed check
kills reattached sessions without discriminating isReattach — a
pre-existing hazard that tab-close covers by killing per id anyway.

* fix(terminal): keep certification throw-proof end to end

Guard the two remaining throw paths in the certification chain — the
entry-discard callback and the recovery handler — so nothing can escape a
timer as an unhandled error, and a throwing discard cannot suppress the
recovery notification it exists to precede. Pinned by two new tests.

* fix(terminal): breadcrumb swallowed recovery failures

A store-action throw in recovery returns false without consuming budget,
so the detector retries each cooldown — an invisible loop unless it
leaves a trace. Breadcrumb it (the recorder is self-guarded and cannot
throw where recovery runs). Also invoke transport.isConnected optionally
so partial test transports fail the gate quietly instead of logging a
contained TypeError.

* fix(terminal): end the zombie-pane replay loop at its root

Root-caused the production "wedged release drip" (1,302 breadcrumbs in one
day on one machine, 4-write bursts on a fixed timer phase, idle-required):
once a pane's xterm pipeline dies while its connection lives, the delivery
watchdog's heal (60s cooldown, fires only while idle because a dead xterm
never ACKs its in-flight bytes) re-delivers restore markers, the hidden
output restore replays 3-4 chunks into the dead parser, each write arms a
replay guard destined for another wedged release — and nothing ever
learns. The loop runs forever and re-forms after app restart.

Three fixes so the loop learns:
- replayIntoTerminal/Async short-circuit on a probe-certified dead
  pipeline: no more futile writes, so no more guard drips, and awaited
  restore chains resolve instead of hanging.
- requestHiddenOutputRestoreIfNeeded is gated the same way, so the
  watchdog heal stops refetching snapshots for a pane recovery owns.
- a window-cap recovery decline now schedules one retry for when the
  budget window reopens (deduped per tab, cancelled by any successful
  remount). Without it, the certified-dead latch plus the new write
  silence made a capped pane a permanent zombie: nothing would ever
  re-request recovery. Cooldown declines deliberately do not retry —
  remounts are tab-scoped, so the just-made remount already replaced
  every pane's xterm in the tab.

Still open (tracked separately): the deterministic wedge surface that
creates the dead pipeline on the release build in the first place — an
unguarded, unreported parse-path throw or silent disposed-write; zero
guard breadcrumbs fired all day, so the trigger predates the guards'
coverage.

* feat(terminal): name the silent zombie producer in breadcrumbs

The last unproven link in the zombie-pane chain is HOW a pane's xterm
dies on the release build. Field discriminators eliminated every
reporting channel: zero terminal guard breadcrumbs and zero xterm-stack
renderer_error/unhandled_rejection events across days of logs, while the
drip re-formed after a clean app restart. Every content-triggered wedge
surface would have reported; the only fully silent mechanism left is a
restore write into an already-disposed xterm instance (write() drops its
completion callback without a throw — verified against the vendored
6.1.0-beta.287).

Instrument that exact moment: a version-pinned disposal probe (its test
runs against the real vendored build so an upgrade that moves the private
field fails loudly), a terminal_restore_write_target_disposed breadcrumb
where startup scrollback restore would write into a disposed instance,
and a terminal_restore_write_failed breadcrumb replacing the fully silent
restore catch. The next zombie formation logs its own root cause.
2026-07-13 21:30:10 -07:00
Brennan Benson 6c4bb67415 fix(i18n): localize the terminal tab unread-completion aria-label (#8663)
The unread-activity bell landed with a hardcoded aria-label, and the
localization-coverage gate now fails on main itself (verify does not run
on pushes to main, so the break landed silently and blocks every PR's
CI). Route it through translate() with catalog entries for all five
locales.
2026-07-13 21:25:49 -07:00
Jinjing 9875b5528a Improve native chat style (#8654)
* Redesign native chat interactive UI to match assistant-turn styling

- Restyle approval and question cards as bordered cards docked in the
  composer region instead of top-border strips, and widen the chat
  column (max-w-3xl → max-w-4xl) to match the rest of the app
- Rework the question card into a numbered pick-list with an always-
  present free-text row and explicit Next/Skip/Send action, replacing
  the prior "Other…" toggle flow
- Hide the composer while a question card is active since it supplies
  its own answer input, and hold the card open until the paced answer
  send settles instead of dismissing on click
- Auto-repin the message list to the bottom during in-place streaming
  growth via a ResizeObserver on the content, not just the viewport
- Widen question-answer pacing (800ms→1000ms step, 300ms→500ms buffer)
  for reliability on slower machines / higher-latency SSH sessions
- Restyle tool-run/tool-line disclosure to a right-side hover chevron
  and add a formatted (pretty-printed) detail view for diff-less calls
- Add "Skip" i18n string across all locales

* Extend paste bridge and question-card fixes for native chat interactive

- Route pane-level Paste to the question card's free-text input when
  the composer is unmounted, so Cmd/Ctrl+V keeps working while a
  question prompt is showing.
- Fix a stale-timer bug where a new interactive prompt could inherit
  the previous card's dismiss/submitting state and swallow its first
  answer.
- Only render tool-run detail when there's actually content to show,
  and mark selected question options with aria-pressed for
  accessibility.
2026-07-13 21:24:33 -07:00
Brennan Benson 5a3e4b44f8 fix(lint): cover active/inactive in the terminal tab activity switch (#8660)
The WorktreeStatus union feeding activityDotState widened, and the
switch-exhaustiveness gate now fails on main itself (verify does not run
on pushes to main, so the break landed silently and blocks every PR's
CI). Cover the two glyph-less states explicitly — matching the
function's documented intent — and drop the now-unreachable default.
2026-07-13 21:10:01 -07:00
Jinwoo HongandOrca b379610cbe fix(browser): handle Cmd-click popups without adopted WebContents (#8659)
Co-authored-by: Orca <help@stably.ai>
2026-07-13 20:48:00 -07:00
JinjingandOrca 1ef0551bc1 Create pr not working for stacked worktree (#8651)
* Fix stacked-worktree PR creation targeting a local-only parent branch

- Resolve the eligibility default base to a remote-tracking ref instead
  of blindly trusting the submitted parent branch, since a stacked
  worktree's base is often a local-only branch the remote can't resolve
- Add a create-time hard block (base_not_on_remote) so a stale or
  unpushed submitted base fails with actionable copy instead of the
  provider's opaque error
- Update the dialog's default-base resolution and blocked-action/
  dropdown copy to match the new remote-validated default

* Split hosted-review-creation.test.ts to fix max-lines lint error

Moved getHostedReviewCreationEligibility tests to a separate file (hosted-review-creation-eligibility.test.ts) to reduce the original file size from 880 to 579 lines, satisfying the max-lines lint constraint.

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

* Fix Create PR intent flow to use remote-validated eligibility default fo

Prefer eligibilityDefaultBaseRef over the raw compare base when resolving
the review base for the one-click Create PR intent flow, since eligibility
is recomputed from the same compare base right before creation and already
corrects a local-only stacked parent to the repo default. Falls back to
the compare base only when eligibility supplies no default.

* Simplify base-ref remote existence check into a single for-each-ref call

Combine the wildcard and exact-tracking-ref lookups into one for-each-ref
invocation with multiple patterns instead of two sequential git calls,
removing the redundant rev-parse fallback path.

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-13 20:10:50 -07:00
NeilandOrca 8662e5a7ab perf(ssh): merge the two identical 5s keepalive timers into one per connection (#8653)
Co-authored-by: Orca <help@stably.ai>
2026-07-13 19:46:08 -07:00
Jinjingandbbingz 1d2aaf1bf5 Fix recipe serve desktop promotion (#8646)
* fix(runtime): preserve terminals during headless desktop activation

* rm design doc

* Fix desktop activation launch ordering and blocked-window status resolut

- Check desktopWindowStatus before spawning the Orca app so a blocked
  runtime no longer launches a doomed second instance.
- Reuse resolveDesktopWindowStatus for remote runtime status so it
  honors the same authoritativeWindowId fallback as local status.
- Re-check the authoritative window at spawn time instead of trusting
  a possibly-stale snapshot, since it can be destroyed mid-await.
- Harden the e2e activation spec against silent spawn failures.

---------

Co-authored-by: bbingz <zzb@gxsmjx.com>
2026-07-13 19:41:24 -07:00
NeilandOrca 6e1aa8503c perf(renderer): pause always-on idle timers while the window is hidden (#8528)
Co-authored-by: Orca <help@stably.ai>
2026-07-13 19:19:17 -07:00