Commit Graph
3741 Commits
Author SHA1 Message Date
NeilandOrca 06afbc4a4b Add optional Orca account sign-in (#7515)
* auth v1

* fable review

* lint

* Account menu with org membership management

Default UX is a compact account menu (sign in, organization selection, sign
out) that renders only when cloud auth is configured; adds an organization
members dialog (invite, role, remove) gated on server-side role checks. The
multi-profile switcher UI is preserved behind ORCA_MULTI_PROFILE_UI=1.

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

* Gate the optional account sign-in UI to dev builds

The account switcher stays hidden in packaged builds while the feature is
in progress. Dev builds still show it when the client env vars are set, and
a dev-only Settings > Dev Tools > Orca Cloud section mirrors it.

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

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-09 02:13:13 -07:00
Brennan BensonandBrennan Benson 24d7f6b790 Align per-workspace environment toggle (#7908)
Co-authored-by: Brennan Benson <brennanbenson@Brennans-MacBook-Pro.local>
2026-07-09 00:47:25 -07:00
Neil 7efbe10394 fix(onboarding): dismiss on notifications step + drop skipped steps from stepper (#7909)
Two onboarding-screen bugs:

- The final "notifications" step blocked click-off/Escape dismissal, unlike
  every other step. Remove the notifications-only guard so the skip
  confirmation opens on all steps; the footer "Skip to project setup" stays
  hidden there since the primary button already hands off to Add Project.
- A skipped "integrations" step (GitHub CLI already installed) still rendered
  as a dead, disabled stepper dot the user skipped past on Continue. The
  stepper now drops all skipped steps (integrations + Windows terminal)
  entirely instead of showing an unreachable dot.

Allowing dismissal on the last step let a click-off race the "Add your first
project" completion handoff (both call closeWith) and double-write onboarding
state / double-fire telemetry. Make closeWith idempotent with a first-wins
latch. Also map the displayed step index through resolveStepIndex so a
momentarily-skipped resume step can't flash "1 of N".

Verified: onboarding unit tests, full onboarding e2e spec (rewritten
notifications test locks in the new dismiss behavior), typecheck, lint, and
live Electron.
2026-07-09 00:46:03 -07:00
NeilandOrca 25ea2bbfd1 onboarding: seamless macOS notification permission step with live state detection (#7684)
* feat(onboarding): state-aware macOS notification permission step

The Set up notifications step showed a one-size-fits-all 'Open Mac
Settings' button that simultaneously fired the macOS permission prompt
and opened System Settings — two competing system UIs, with System
Settings unnecessary for the common fresh-install case.

Electron exposes no API to read macOS notification authorization, but
scheduling outcomes do reveal it: a silent probe notification's 'show'
event means permission is granted, 'failed' means delivery is blocked.
A new notifications:probeDelivery IPC runs that probe (cached via
passive delivery evidence and a persisted confirmation flag), and the
onboarding card now renders the real state:

- fresh install: the probe itself pops the native Allow dialog the
  moment the step opens; the card flips to 'Notifications are enabled'
  automatically when the user clicks Allow (silent 2.5s re-probes)
- blocked: amber card with an Open System Settings deep-link, which
  also self-heals once the user flips the toggle
- granted: green confirmation card

The test-notification button now feeds the same card instead of the
ambiguous 'if no banner appeared…' toast during onboarding.

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

* fix: don't log expected probe rejections while polling for permission

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

* fix: amber warning styling + single stable dev bundle id for notifications

- Blocked card now uses the app's shipped amber idiom (tinted surface with
  amber title/body) instead of white-on-amber-wash, which read muddy in
  dark mode; macOS permission card split into its own module to stay under
  the max-lines budget.
- Dev instances previously minted a unique macOS bundle id per
  branch x Electron version, registering a new Notification Settings entry
  every time ('Orca: <branch>' rows piling up forever) and pointing the
  settings deep-link at ids System Settings can't resolve. All dev
  instances now share com.stablyai.orca.dev: one Notification Center
  entry, one permission grant covering every dev build.

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

* fix: tighten macOS permission card copy

Body copy was one long sentence; now a single short instruction with
'Updates automatically.' as a separate dimmer line. Also repairs locale
catalog parity for keys introduced by commits rebased into this branch.

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

* fix: drop 'Updates automatically.' line; ad-hoc sign dev app copies

The extra line read as confusing filler — the cards now carry one short
instruction each.

Dev Electron copies had broken code signatures (the Info.plist identity
edits invalidate the ad-hoc seal), which macOS punishes by refusing
Notification Center registration outright: every dev notification failed
with UNErrorDomain error 1, the app never appeared in System Settings >
Notifications, and the settings deep-link had nothing to land on. The dev
runner now ad-hoc re-signs the copied bundle after the plist edits
(bundleLayoutVersion bumped so stale unsigned copies are recreated).
Verified end-to-end: runner-built copy passes codesign --verify --deep,
probe delivery returns delivered, the onboarding card flips green in dev,
and the deep link opens the dev app's own notifications pane.

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

* fix: drop confusing copy line; session-only permission evidence

Removes the 'Updates automatically.' line from both permission cards.

Also drops the persisted notificationDeliveryConfirmed flag: OS-level
permission changes between sessions, and a stale positive rendered a
false green card. Delivery evidence is now session-scoped only.

Documented detection ceiling (verified empirically on macOS 26): while
the permission dialog is unanswered — and when notifications are toggled
off in System Settings after being authorized — macOS accepts requests
and silently swallows them, with no public API (Notification Center
delivered-history and legacy ncprefs both included) able to distinguish
that from real delivery. 'failed' remains definitive for unsigned builds
and dialog-level denials.

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

* feat: real macOS notification permission readout via native helper

Electron has no API for UNUserNotificationCenter authorization, and every
observable fallback lies: scheduling succeeds (and getHistory lists the
notification) even while macOS silently swallows display because the
permission dialog is unanswered or notifications were toggled off in
System Settings. The onboarding card therefore showed 'enabled' after the
user disabled notifications.

Adds native/notification-status-macos: a tiny Swift binary that prints
the app's real authorization status. It runs from inside the app bundle
(NSBundle resolves the bundle by walking up from the executable) and
embeds the app's CFBundleIdentifier in a __TEXT,__info_plist section so
every codesign --force pass — electron-builder's signing or the dev
runner's ad-hoc deep sign — derives the identifier macOS keys
notification records to. Spawning it from the app returns authorized /
denied / not-determined exactly matching System Settings.

notifications:probeDelivery now prefers this readout (authoritative,
silent), firing at most one dialog-trigger probe per session while the
decision is pending, and falls back to the previous delivery-probe
heuristics when the helper is unavailable. The card polls the readout
silently in every state, so toggling Allow notifications in System
Settings flips the card within a poll — both directions, verified live.
Test notifications also consult the readout so 'delivered' is no longer
claimed for swallowed notifications.

Packaged builds ship the helper via extraResources and sign it in
afterPack like the computer-use helper; dev copies compile it on demand
(swiftc, non-fatal when missing) with the shared dev bundle id.

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

* feat: in-app fallback for swallowed notifications + permission card in Settings

- Dispatch now consults the authorization readout before creating a
  native notification: when macOS would silently swallow it (denied or
  prompt unanswered) it returns reason 'blocked-by-system' instead of
  piling invisible notifications into Notification Center. The terminal
  notification path surfaces that as a once-per-session in-app toast
  with an Open System Settings action. Mobile fan-out is unaffected.
- Settings > Notifications now shows the same live permission card as
  onboarding (moved to components/notifications/), polling the readout
  so System Settings changes reflect within seconds, and the test
  button updates it inline.
- Test sends that are blocked at the OS level now show the
  settings-pointing failure toast instead of a generic error.

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

* fix: hide macOS permission card while Orca notifications are disabled

A green 'Notifications are enabled' card next to a disabled Enable
Notifications toggle read as a contradiction — the card now renders (and
the readout polls) only while Orca's own notifications setting is on.
Also single-flights the authorization helper so simultaneous agent
completions share one readout process.

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

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-08 22:21:40 -07:00
Jinjing c170866180 fix(terminal): clear leaked mouse-reporting modes on pane reattach (#7893)
* fix(terminal): clear leaked mouse-reporting modes on pane reattach

A TUI that enables mouse tracking (?1000/1002/1003 + SGR 1006/1016) and
dies uncleanly never emits the disable sequence, so the daemon's snapshot
records the mode and buildRehydrateSequences re-arms it on every reattach.
POST_REPLAY_REATTACH_RESET cleared cursor/focus/kitty state but not mouse
modes, so a plain shell in the reattached pane echoed every pointer-motion
report (`<35;col;rowM`) as literal text.

Add RESET_MOUSE_REPORTING (?9l ?1000l ?1002l ?1003l ?1006l ?1016l) to both
POST_REPLAY_REATTACH_RESET and POST_REPLAY_MODE_RESET. Live agent panes keep
mouse modes via POST_REPLAY_LIVE_AGENT_REATTACH_RESET, so agent scroll is
unaffected.

Verified against the real daemon serializer + real xterm: the old reset
leaves mouseTrackingMode armed, the new reset returns it to 'none'.

* test: add regression tests for terminal mouse mode leak on reattach

- Add an E2E test to verify that a warm reattach disarms mouse modes
  left armed by an uncleanly exited TUI.
- Add a test fixture that writes the mouse tracking enable sequence
  without a matching disable sequence.
- Ensure the reattached pane disarms mouse tracking and that actual
  mouse movement produces no reports.

* Add types to isMouseReport in terminal reattach leak test

Explicitly annotate parameter and return types for the isMouseReport
helper function inside the page.evaluate block.

* Refactor mouse-mode leak E2E test to use shell printf and live pane

Eliminate the external Node.js fixture file and streamline the E2E test
by using a POSIX printf shell builtin to arm the mouse tracking modes.

Additionally, verify the leak precondition by inspecting the active pane's
live terminal state (mouseTrackingMode) rather than querying internal
daemon buffer snapshots via window.api.pty.getMainBufferSnapshot.
2026-07-08 22:14:08 -07:00
Jinjing 404804737d Change GitHub issue layout from sidebar to top columns (#7887)
Replace the sidebar layout in the GitHub issue details view with a
responsive grid of top columns placed above the description body. This
ensures the description content is not squeezed by a right rail and fits
the header's full content width.
2026-07-08 22:06:46 -07:00
Jinjing 4ded642a3a Detect and surface recoverable zero-turn sessions in AI Vault (#7889)
Identify Claude sessions with no saved conversation turns but possessing
recoverable signals such as queued prompts or subagent transcripts.
This displays them in the sidebar with a "Not saved" badge instead of
filtering them out as empty, and provides detailed notices to recover
them via logs while disabling standard resume actions.

Additionally, extract Gemini session parsing logic into a separate
module and support counting sibling subagent transcripts across local
and remote SSH session scans.
2026-07-08 21:47:02 -07:00
Neil 8dbcb2be3e feat(floating-terminal): amber attention dot for unacknowledged bells and completions (#7888)
Show a small amber dot on the floating-workspace launcher (both the
floating-button and status-bar triggers) whenever any floating-workspace tab
still has an unacknowledged terminal bell or agent completion, and a
composited amber dot on the Windows tray icon when the window is
minimized/hidden. Both clear through the existing show-until-interact paths —
engaging with or closing the offending tab drops the dot with no stale unread
state left behind.

The launcher dot derives from the existing per-tab/per-pane unread maps via a
new selectFloatingWorkspaceHasUnread selector (primitive boolean, empty-
workspace early return, no bespoke state). The tray dot rides the notification
dispatch and clears on window show/restore.
2026-07-08 21:39:49 -07:00
Jinjing 5c6f0ad52d Fix stale open pr after merge (#7886)
* docs: design fix for sticky OPEN PR after merge

Capture root cause and primary fix for Checks panel preserving open/draft
PR cache on authoritative no-pr after merge + HEAD diverge.

* Clear open and draft PR caches on fallback refresh misses

Avoids preserving non-terminal ("open" or "draft") PR states in both
the PR and hosted-review caches when a fallback refresh returns an
authoritative "no-pr" result. This resolves a sticky "OPEN" UI bug
where a merged PR continued to show as open.

- Removes fallback PR preservation from the main PR cache check.
- Gates the hosted-review cache fallback preservation to only accept
  terminal states ("closed" or "merged").
- Updates unit tests to assert cache clearing for open/draft states.

* Remove stale open PR refresh design document

Delete the design and diagnosis document for the stale open PR checks
panel refresh issue now that the investigation and planning phase is
complete.
2026-07-08 21:38:49 -07:00
Jinjing 040e61e13f Keep explicit push and force push actions available without upstream (#7864)
- Keep the explicit "Push" and "Force Push" dropdown options enabled when
  there is no upstream or the upstream status is loading, letting Git
  resolve the target at execution time.
- Stop auto-upgrading regular push operations to force push in the background,
  ensuring normal push actions stay non-force.
- Explicitly route the primary action and commit-and-push flows to the
  "force_push" action when a lease force push is required.
2026-07-08 21:22:49 -07:00
JinjingandOrca 6b4831d5ef Fix Grok and Pi terminal title normalization and status detection (#7880)
* fix(mobile): normalize Grok rotating OSC titles at the main observation boundary

Desktop already collapses Grok Build's rotating working frames via the
renderer's normalizeTerminalTitle, but the main process stored raw OSC
titles, so mobile session tabs (fed from pty.lastOscTitle) still saw a
distinct title every spinner frame and re-touched snapshots each time.

Apply normalizeTerminalTitle once where main records an observed OSC
title, before the prevTitle comparison that gates session-tab and
mobile-snapshot touches, and normalize hydration-seeded titles the same
way so the first live frame after a seed compares equal. Agent status
stays detected from the raw title, mirroring the renderer tracker.

Covers remoted/SSH PTYs too since they surface through the same main
observation path.

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

* Fix Grok and Pi terminal title normalization and status detection

- Require a strict "spinner - phrase - grok" pattern for Grok working
  titles to prevent false positives on other agent tasks ending in
  "- grok" (such as Claude or Codex).
- Treat collapsed "Pi" and "OMP" synthetic titles as idle by default
  to prevent them from reverting to neutral status after normalization.
- Prevent duplicate mobile session tab updates during Grok status
  frame rotations by ensuring they normalize to a stable title.

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-08 21:22:06 -07:00
JinjingandOrca 7e1a77728b fix(renderer): preserve live agent cursor visibility across replays (#7858)
Cursor Agent parks the real cursor on a blank row and hides it (?25l),
drawing its own caret. Orca's post-replay resets forced ?25h, painting a
stray cursor block below the agent's prompt after reattach and after
hidden-to-visible snapshot restores.

Root cause fix: stop re-showing a cursor the replayed bytes intentionally
hid, instead of compensating afterwards.

- Live-agent reattach reset now preserves the payload's final DECTCEM
  state (last ?25l/?25h wins); the post-parse viewport veto re-shows the
  cursor when the screen disproves a live parked agent, so a shell can
  never inherit a permanently hidden cursor from a dead TUI's leftovers.
- Hidden-output restore now uses a live-agent variant of the snapshot
  reset when status/title corroborates a live agent: forcing ?25h
  re-showed the parked cursor, and ?1004l permanently silenced the
  focus-in the agent needs to unpark (agents only enable focus reporting
  at startup, so nothing ever re-armed it).

Co-authored-by: Orca <help@stably.ai>
2026-07-08 19:08:00 -07:00
Jinjinganddalveytech-vincent 070a956a72 feat(source-control): add push failure AI recovery (#7826)
* feat(source-control): add Fix push failure with AI for pre-push hooks

Detect pre-push hook failures separately from auth/transport errors so
push toasts and inline messages no longer suggest checking repo access.
Mirror the commit-failure recovery flow with a fixPushFailure action,
summary panel, details dialog, and agent launch recipe in Settings.

Fixes #6497

* feat(source-control): add push failure AI recovery

Co-authored-by: dalveytech-vincent <vincent@dalveytech.com>

---------

Co-authored-by: dalveytech-vincent <vincent@dalveytech.com>
2026-07-08 18:59:28 -07:00
Jinjing 2af8fb886f Collapse Grok rotating working titles to stable label (#7863)
Grok Build's working OSC titles interpolate a rotating status or tool
phrase between the spinner and its name, causing tab and sidebar
titles to fluctuate rapidly. Collapse these working frames to a stable
"⠋ Grok" label, while leaving idle and session titles untouched.
2026-07-08 18:57:07 -07:00
Jinjing 29cbe2d8cb Show empty sessions by default in AI Vault Panel (#7853)
* Show empty sessions by default in AI Vault Panel

Change the default state of `hideEmptySessions` from true to false, and update the active filter count calculation to treat false as the new default.

* Centralize AI Vault view defaults and fix reset state alignment

Consolidate the default values for sorting, grouping, and hiding empty
sessions into a single place. This prevents inconsistencies between the
initial state, the badge count adjustment checks, and the "Reset" view
action, where the reset previously toggled hideEmptySessions to true
contrary to the initial false value.

* Count agent adjustments by membership instead of list length

Ensure that swapping one agent for another (which keeps the total
array length the same) is correctly identified as a deviation
from the default of having all agents enabled.
2026-07-08 18:44:54 -07:00
Jinjing 9e23b8963b fix(agent-status): show waiting for Claude AskUserQuestion, stop stale-title worktree spinners (#7852)
Claude's AskUserQuestion tool is auto-allowed, so it emits a PreToolUse (not
PermissionRequest) while blocked on a human answer. normalizeClaudeEvent mapped
that to `working`, so the sidebar showed a spinner that decayed to grey while
the question sat. Map the auto-allowed AskUserQuestion PreToolUse to `waiting`
instead, mirroring the Kimi/OpenCode handling, so the row and worktree dots read
amber "Waiting for input".

Separately, a frozen braille-spinner title left by an exited agent (e.g.
"⠐ Review branch for regressions" over a shell prompt) kept classifyTitleActivity
returning `working`, spinning the worktree dot forever with 0 agents — the row
builder rejects the same unattributable title, so no agent row is shown. Gate
tabHasStatus's title-derived working/permission on the same agent attribution the
row builder uses (resolveAgentTypeFromTerminalTitle), so a title only spins the
dot when it would also show a row. Hook-driven status (hasLiveWorking/
hasPermission) is untouched.
2026-07-08 18:44:43 -07:00
JinjingandOrca c2b074bd15 fix(terminal): shape Arabic/RTL text on the cell grid via xterm character joiner (#7665)
* fix(terminal): shape Arabic/RTL runs on the cell grid via xterm character joiner (#5262)

Register a character joiner that groups contiguous RTL runs (tunneling
through neutral spaces/digits/punctuation between RTL words) so both the
WebGL and DOM renderers draw each run as one string, applying native
contextual shaping and BiDi ordering inside the run's grid-aligned cell
box. Buffer and PTY stream are untouched; xterm un-joins ranges holding
the cursor or a partial selection, keeping cursor visibility and
selection cell-accurate. Non-RTL lines early-return via a single
charCodeAt sweep.

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

* Deregister Arabic shaping joiner when disposing pane

Since xterm.js does not automatically remove registered character joiners
upon terminal disposal, they must be explicitly deregistered to avoid
memory and registration leaks.

- Update `registerArabicShapingJoiner` to return a cleanup function.
- Store and call the cleanup handler when disposing a managed pane.
- Update tests to assert proper registration and deregistration.

* fix(terminal): gate RTL joining on live WebGL, tunnel ZWNJ/ZWJ/RLM, lock joiner lifecycle in tests

Review findings on the Arabic shaping branch:
- The DOM renderer sizes a joined span with one letter-spacing value applied
  per character, so joined RTL runs blew out row grid alignment whenever the
  WebGL renderer was unavailable (GPU off, auto policy, context loss). The
  joiner now only returns ranges while the pane's WebGL addon is live.
- ZWNJ/ZWJ/RLM (mandatory in Persian orthography) hard-broke RTL runs,
  rendering word halves in swapped visual order. They are now transparent
  inside a run without extending or counting toward it.
- openTerminal's existing unicode-ordering test failed at HEAD because the
  fake terminal lacked registerCharacterJoiner; the harness now implements
  the joiner API and a new test locks register-on-open/deregister-on-dispose.

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

* fix(terminal): keep zero-width RTL marks from opening joined runs, treat ALM as transparent

Round-2 review findings:
- A combining mark (tashkeel/niqqud) orphaned after an LTR base could open
  an RTL run mid-cell; xterm rounds that string range to an empty joined
  cell range and the WebGL renderer draws an empty glyph over the next
  character. Marks now extend and count only in runs opened by a spacing
  RTL letter.
- ALM (U+061C) sat inside the strong-RTL block scan, so it opened and
  counted toward runs unlike RLM. It now joins the transparent set.
- New test locks openTerminal wiring the joiner predicate to
  pane.webglAddon (previously () => true would pass the suite).

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

* fix(terminal): treat zero-width Cf controls in RTL blocks as run-transparent

Round-3 review finding: zero-width format controls that live inside the
strong-RTL scan ranges (Arabic number signs U+0600-0605, end of ayah U+06DD,
Syriac abbreviation mark U+070F, disputed end of ayah U+08E2) and the BOM/
ZWNBSP U+FEFF are width-0 in xterm. Like combining marks they could open a
run mid-cell, producing an empty joined cell range that blanks the following
glyph in the WebGL renderer. They are Cf not Mn, so canOpenRtlRun could not
catch them. Fold them into the run-transparent set (never open, count, or
break a run) and document the upstream standalone-width-0-cell join skew.

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

* Document WebGL decoration limitation for Arabic shaping joiner

Add comments explaining a known upstream limitation in xterm.js WebGL
renderer where search-match highlights inside a joined run render
all-or-nothing, similar to ligatures.

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-08 18:29:36 -07:00
Brennan BensonandOrca 95f3933ea2 feat(native-chat): upload composer attachments over SSH (#7832)
Co-authored-by: Orca <help@stably.ai>
2026-07-08 16:56:16 -07:00
Brennan BensonandOrca c8c5201145 perf(windows): relax whole-process-table scans for terminals with no agent evidence (#7607)
Co-authored-by: Orca <help@stably.ai>
2026-07-08 16:41:21 -07:00
NeilandOrca 4a6d12959f fix: recover fresh local terminal spawns from a deleted saved cwd (#7847)
After #7750 removed the containment guard, the residual #7239 failure mode
is a persisted/inherited startupCwd whose directory no longer exists: every
spawn dies with the provider's missing-directory error. Fresh local renderer
spawns now opt in (cwdFallback: 'worktree') to recover at the workspace root
with a generic in-terminal notice; reattach, SSH, remote-runtime, runtime/API
and mobile callers keep exact cwd semantics, and existing directories —
including outside the worktree (#7685) — spawn as requested.

Co-authored-by: Orca <help@stably.ai>
2026-07-08 16:33:58 -07:00
Brennan Benson e218bf88f6 Keep your New GitHub issue draft after an accidental dismissal (#7778) 2026-07-08 14:38:25 -07:00
Brennan Benson e3eec5c39f Let pane-scoped evidence beat stale tab icon identity (#7755) 2026-07-08 13:54:06 -07:00
c2468ab820 fix(source-control): keep unresolved PR comment prompts retryable (#7466) (#7638)
Co-authored-by: Orca <help@stably.ai>
Co-authored-by: brennanb2025 <brennankbenson@gmail.com>
2026-07-08 13:24:04 -07:00
Jinwoo HongandOrca 2b3eba46cb Per-environment SSH state so desktop clients get live SSH overlays for Remote Orca Server workspaces (STA-1468 desktop topology) (#7831)
Co-authored-by: Orca <help@stably.ai>
2026-07-08 13:17:19 -07:00
Jinwoo HongandOrca 6c988356e2 fix: Remote Orca Server reconnect liveness — recover mirrors, input routing, and creates after tunnel drops (#7827)
Co-authored-by: Orca <help@stably.ai>
2026-07-08 12:08:27 -07:00
Jinwoo HongandOrca b665708c7f fix(ssh): make fs.listFiles cancellable and single-flight per relay client (#7769)
Co-authored-by: Orca <help@stably.ai>
2026-07-08 12:01:06 -07:00
Jinwoo HongandOrca 864594ffdd Fix SSH state for paired remote clients + complete target re-adoption sweep (STA-1468) (#7767)
* Sweep all persisted carriers of a removed SSH target id on re-adoption

reassignSshTargetId re-pointed repos and worktree metas but left the old
target id embedded in persisted session pty ids (ssh:<id>@@pty-N in tabs,
layouts, remoteSessionIdsByTabId), the startup reconnect list
(activeConnectionIdsAtShutdown, replayed via ssh.connect at boot — the
exact 'SSH target not found' in STA-1468), sleeping-agent resume records,
provisioned project host setups, sidebar host-scope arrays, and relay pty
leases. Any survivor resurfaces later as a failing connect or reattach.

New ssh-target-id-migration module re-points every carrier in one pass,
wired into reassignSshTargetId with per-carrier unit and store-level
round-trip tests.

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

* Bridge SSH connection state to paired remote clients

The SSH surface was desktop-only: ssh:state-changed went to the host's
own BrowserWindow and the web client's ssh API was a no-op stub, so a
paired client's reconnect overlay never learned the host connected and
its target labels stayed empty (STA-1468 — overlay stuck on 'please
connect' over a live terminal).

- New sshStateChanged runtime client event, emitted from broadcastSshState
  through OrcaRuntimeService onto the existing clientEvents stream.
- New ssh.listTargets / ssh.listRemovedTargetLabels RPC methods next to
  the previously unused ssh.getState / ssh.connect.
- Web preload now routes listTargets / listRemovedTargetLabels / getState
  / connect to the paired host's runtime RPC instead of stubbing them.
- useIpcEvents applies sshStateChanged on paired web clients through the
  same guarded path as desktop ssh.onStateChanged; desktop clients ignore
  the event since a foreign runtime's targets would pollute their local
  SSH store.

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

* Harden the SSH reconnect overlay against stale or unknown target state

- Only present the destructive 'SSH host removed' state on positive
  evidence (a removal tombstone label, or a hydrated non-empty target
  list lacking the id). A client whose SSH state never hydrated has an
  empty labels map for every id and must not offer workspace removal.
- After a failed Connect, resync target metadata so a stale overlay
  converges to the ghost/re-adopted state instead of offering the same
  failing Connect forever (the repeated 'SSH target not found' toast
  loop in STA-1468).

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

* Address CodeRabbit review on #7767

- Re-key workspaceSessionsByHostId partitions stored under a removed SSH
  host id during re-adoption (no writer keys partitions by ssh host today,
  but the schema tolerates it — re-key instead of stranding; live partition
  wins when both keys exist).
- Track SSH target-list hydration explicitly (sshTargetsHydrated) instead
  of inferring it from a non-empty label map, so a legitimately empty
  target list still counts as removal evidence and a never-hydrated client
  still never offers destructive removal.
- Apply the refreshed target list before the best-effort removed-labels
  fetch in the overlay resync, so a labels failure can't discard it.

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

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-08 11:36:19 -07:00
38e82b4fff Fix AI Vault scanning for runtime hosts (#7631)
* Fix AI Vault runtime host scanning

* fix runtime ai vault response validation

* test runtime ai vault validation more loosely

* fix(ai-vault): harden runtime host scanning

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

* fix(ai-vault): resume runtime host sessions

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

---------

Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
Co-authored-by: Orca <help@stably.ai>
2026-07-08 11:20:26 -07:00
Ken FukuyamaandJinjing 45931a716e Allow browser annotations to target existing agents (#7347)
* Allow browser annotations to target existing agents

* Update browser annotation send tooltip

* test: guard browser annotation send menu wiring

---------

Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
2026-07-08 01:35:37 -07:00
Jinwoo HongandOrca 98bee1b419 Fix un-removable SSH ghost worktrees after host remove/re-add (#7753)
Co-authored-by: Orca <help@stably.ai>
2026-07-07 19:01:42 -07:00
Luis Sebastian Urrutia Fuentes 37ba94a228 fix(i18n): polish Spanish locale copy (#7422) 2026-07-07 17:10:53 -07:00
Brennan BensonandOrca 482bfbc9bb Keep editor tabs in sync with external file changes instead of silently dropping them (#7591)
Co-authored-by: Orca <help@stably.ai>
2026-07-07 17:10:40 -07:00
Jinjing 9cc177b8ea Update mobile Android APK links to 0.0.24 (#7756) 2026-07-07 17:05:24 -07:00
Jinwoo HongandOrca 79d0de9c33 fix(terminal): remote query-reply corruption (#7329) + snapshot grid-width repaint (#7279) (#7736)
Co-authored-by: Orca <help@stably.ai>
2026-07-07 16:42:43 -07:00
Brennan BensonandOrca 46dcfd33ea Fix offline runtime worktree refresh call fanout (#7660)
Co-authored-by: Orca <help@stably.ai>
2026-07-07 14:53:56 -07:00
Brennan BensonandOrca 9e03e28d37 Stop unreachable-runtime request storms (negative cache, stable hook deps, subscribe backoff) (#7743)
Co-authored-by: Orca <help@stably.ai>
2026-07-07 14:50:44 -07:00
Brennan BensonandOrca d9225c5ccd fix(github): route all PR mutations through the repo's owner host (#7739)
Co-authored-by: Orca <help@stably.ai>
2026-07-07 14:43:31 -07:00
bea9b38544 Fix OMP worktree title identity flicker
* Fix OMP worktree title identity flicker

* Add headless fresh pairing server script

* Harden fresh pairing server script startup

* Default fresh pairing server to reachable address

* review: harden OMP tab identity validation

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

---------

Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
Co-authored-by: Orca <help@stably.ai>
2026-07-07 13:51:25 -07:00
Brennan BensonandOrca 0fe20ddff9 Fix Monaco Peek References preview collapse (#7662)
Co-authored-by: Orca <help@stably.ai>
2026-07-07 12:55:41 -07:00
Brennan BensonandOrca fed545d330 Stop wiping the shared WebGL glyph atlas on plain window refocus (#7604)
Co-authored-by: Orca <help@stably.ai>
2026-07-07 12:43:53 -07:00
Jinjing 0f7658d77c Make center split divider more visible (#7701)
* fix: address review findings

* Improve split divider contrast and refactor tab split context menus

- Increase contrast of `--tab-group-split-divider` colors in light and
  dark modes to achieve at least 3:1 contrast against `--card`.
- Refactor `TerminalTabSplitMenuSection` to use the shared
  `TabWorkspaceLayoutMenuSection` for moving tabs between splits.
- Clarify terminal-specific split action labels in the context menu.
- Remove redundant icon margins in `EditorFileTabContextMenu`.
2026-07-07 12:12:00 -07:00
JinjingandOrca 544bca5202 fix: terminal IME candidate selection and text commit on Linux (#7634)
* fix terminal IME candidate selection and text commit on Linux

Sogou Pinyin and fcitx on Linux failed in Orca's terminal because bare
229 keydowns were swallowed, and empty composition updates prematurely
deactivated tracking. This led to dropped Chinese text or leaked Space/digit
candidate-selection keys reaching the PTY.

- Allow bare 229 keydowns to bypass suppression on Linux so xterm can diff
  and commit text.
- Prevent empty compositionupdate events from prematurely deactivating
  the composition tracker.
- Suppress and preventDefault candidate-selection keys (Space and digits)
  during active composition and a brief post-composition window.
- Add comprehensive unit tests and an Electron CDP-driven E2E repro.

* fix: register IME gate command as direct spec-file invocation

The reliability-gate checker rejects --grep title selectors and requires
every evidenceRun command to match a gate command. Drop the --grep from
the e2e gate command and its evidence run, and remove the stale 3-file
evidence run superseded by the full 7-file run.

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

* Guard overlapping and post-composition Linux IME candidate keys

- Track pending candidate key releases in a Map instead of a single
  slot to support overlapping selector key events without stranding.
- Apply the candidate selection guard to post-composition key releases
  that arrive after compositionend, preventing digits/Space from
  leaking into the PTY.
- Restrict the Linux/Sogou candidate selection guard to Linux to
  prevent interference on macOS and Windows.
- Exclude Shift+Space from candidate selection key checks.

* Guard held-key IME candidate repeats and scope policy to desktop Linux

- Keep auto-repeat keydowns for a candidate key suppressed past the
  250ms guard window until its corresponding keyup event is received.
- Clear stale pending releases on fresh non-repeat keydowns to avoid
  guarding the wrong key events.
- Exclude Android and ChromeOS user agents from desktop Linux-specific
  IME candidate key suppression behaviors.
- Ensure the composition tracker is activated unconditionally on
  compositionupdate events.

* Clean up IME reference and extract shared test event fixture

- Remove the obsolete Linux Sogou Pinyin IME reference document.
- Extract the fully-defaulted XtermBypassEvent helper into a shared
  fixture file to keep the policy test suites in sync.
- Add a test verifying that Shift+Space (fcitx full-/half-width toggle)
  is not suppressed as an IME candidate key.

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-06 22:03:39 -07:00
Jinwoo HongandOrca e33f31689b Make Codex session-history source home configurable (host + WSL) (#7629)
Co-authored-by: Orca <help@stably.ai>
2026-07-06 21:47:02 -07:00
Jinjing aa0993f546 Improve remote connection terminal error msg and session restore recovery (#7661)
* Robustify SSH terminal reconnect and session restore recovery

- Release the replay guard after a fallback timeout to prevent permanent
  keyboard input lockouts when an unmounted terminal never parses.
- Verify backing process liveness on PTY attach and reap stale entries
  so dead shells cleanly trigger a fresh pane spawn.
- Normalize connection IDs during restore to prevent spurious mismatch
  errors, and treat true mismatches as expired sessions instead of crashing.

* Route disconnected SSH terminal panes through deferred connection gate

Avoid spawning SSH terminal processes against disconnected targets,
which otherwise throws "No PTY provider" and leaves panes stranded.

- Intercept spawning via a new connect gate that triggers the deferred
  connection flow when the SSH target is disconnected.
- Fall back to composite worktree IDs during cold-start hydration to
  ensure deferred SSH session IDs are properly stashed.
- Retry spawning and remounting terminal panes upon SSH reconnect if
  they are stranded or failed to spawn.
2026-07-06 21:41:23 -07:00
Pablo AlbrechtandClaude Opus 4.8 1a4232fe96 fix(tabs): keep the worktree's remembered active tab on switch-back (#7385)
When returning to a worktree in the sidebar, the active tab reset to the
first tab instead of the one the user left on. Three fallback sites derived
the active tab from the first tab rather than the per-worktree remembered
selection (activeTabIdByWorktree):

- reconcileWorktreeTabModel: promoting legacy runtime terminals into a
  freshly-ensured group seeded activeTabId from restoredLegacyTabs[0].
- Terminal.tsx active-terminal repair: reset to tabs[0]; a repair firing on
  a transient worktree-switch render permanently clobbered the selection to
  Terminal 1.
- hydrateLegacyFormat: used the global session.activeTabId, so every
  worktree except the last-focused one lost its terminal on restart.

All three now honor activeTabIdByWorktree before falling back to the first
tab. Adds fails-old/passes-new regression tests.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 21:40:40 -07:00
Neil d0403d808e perf(renderer): FIFO-cap recentlyClosedAgentStatusTabIds (#7645)
recentlyClosedAgentStatusTabIds (agent-status store) suppresses late
hook/status events for a just-closed terminal tab. It was only ever
added to — one `true` entry per agent-tab close, keyed by the ephemeral
tabId, never deleted or capped — so it grew for the renderer's whole
session. It's the renderer twin of the main-process
closedAgentStatusTabIds set that #7561 already FIFO-capped.

Fix: bound it to the 1024 most-recent closed tabs via delete-then-set
LRU with oldest-key eviction (Record key order is insertion order),
mirroring #7561. A status event for a tab closed >1024 tabs ago cannot
still arrive, so suppression behavior is unchanged.

Test (red->green): closing 1029 tabs leaves exactly 1024 markers with
the oldest evicted and the most-recent retained; without the cap all
1029 persist.
2026-07-06 21:28:03 -07:00
Neil 8936225889 perf(native-chat): LRU-bound the pending-send and command-marker scope caches (#7643)
Two module-level caches in native-chat-pending.ts capped their per-key
arrays (8 entries) but never bounded the KEY count:

- commandMarkerCache — keyed by paneKey\0agent\0sessionId; sessionId
  changes on every /clear and paneKey embeds an ephemeral leafId, so a
  distinct key was stranded per (pane, session) for the renderer's whole
  life. Only test-only clear() ever removed keys.
- pendingSendCache — self-cleans on the normal empty-drain path, but a
  pane closed with an unconfirmed send (agent crash / early close) left
  its non-empty entry keyed forever.

Route both writes through the existing setBoundedScopeCacheEntry LRU
helper (cap 128) that #7566 already applied to the draft/attachment
caches in the same folder — same pattern, same file family, was just
missed here. Values are tiny so this is a slow leak, but real and
unbounded.

Tests (red->green): appending 133 distinct scopes evicts the oldest and
keeps the most-recent 128; without the cap all 133 survive.
2026-07-06 21:27:57 -07:00
Jinjing 7842a48090 Pin PR comments list header when scrolling checks panel (#7657)
Keep the filter and add-comment actions reachable while reading long
threads in the sidebar. Also add a test to verify the sticky classes
are applied.
2026-07-06 21:22:08 -07:00
NeilandOrca f057c9d664 perf(pty): cap the unsent main→renderer PTY backlog (Win/Linux GB-scale leak) (#7630)
* perf(pty): cap the unsent main→renderer PTY backlog (Win/Linux GB leak)

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

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

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

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

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

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

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-06 20:58:53 -07:00
gatsby74andJinjing 98d850aeba fix(settings): keep full installed font list (#7581)
* fix(settings): keep full installed font list

* fix(settings): bound font picker rendering

* Fix redundant font queries and handle partial state in worktree purge

- Latch the loaded state even when the returned font list is empty, preventing font-less systems from reissuing font listing calls on every picker interaction.
- Add null-tolerance for `ptyIdsByTabId` when building worktree purge state to support callers that pass partial AppState.

---------

Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
2026-07-06 20:21:58 -07:00