Commit Graph
8079 Commits
Author SHA1 Message Date
Brennan Benson fbb46a63e4 fix(ai-vault): tolerate unavailable Cursor CLI in session rows 2026-08-12 18:42:08 -07:00
Brennan Benson c9a4b9608a fix(ai-vault): wake Cursor read waits on cancel 2026-08-10 16:30:37 -07:00
Brennan Benson 97a3b74978 fix(ai-vault): wait for Cursor read reservations 2026-08-10 16:06:10 -07:00
Brennan Benson 565429c8e1 fix(ai-vault): preserve bounded Cursor read concurrency 2026-08-10 15:54:41 -07:00
Brennan Benson 8c2e4b7863 fix(ai-vault): make Cursor tie-breaks locale-independent 2026-08-10 15:45:22 -07:00
Brennan Benson b4265dfab2 fix(ai-vault): make Cursor retention locale-independent 2026-08-10 15:32:21 -07:00
Brennan Benson 5910328b42 fix(ai-vault): isolate invalid Cursor file stats 2026-08-10 15:20:59 -07:00
Brennan Benson 1318cdac80 fix(ai-vault): reject invalid Cursor sidecar UTF-8 2026-08-10 15:10:40 -07:00
Brennan Benson 70e9548c6c fix: harden Cursor sidecar discovery bounds 2026-08-10 14:55:15 -07:00
Brennan Benson a8e470359c fix(ai-vault): charge failed Cursor sidecar reads 2026-08-10 14:22:44 -07:00
Brennan Benson d386fa8262 fix(ai-vault): bound raced Cursor sidecar reads 2026-08-10 13:57:15 -07:00
Brennan Benson 7a9b388bce fix(ai-vault): harden Cursor scope and read bounds 2026-08-10 13:41:51 -07:00
Brennan Benson d117f938f6 fix(ai-vault): preserve WSL Cursor cwd semantics 2026-08-10 13:15:26 -07:00
Brennan Benson f788c39c0c fix(ai-vault): avoid false Cursor session truncation 2026-08-10 13:01:09 -07:00
Brennan Benson 912cf9a0b6 fix(ai-vault): normalize Cursor scopes before shared caps 2026-08-10 12:48:50 -07:00
Brennan Benson ecb08771fd fix(ai-vault): isolate Cursor read budgets by storage 2026-08-10 12:26:26 -07:00
Brennan Benson 29473cf340 fix(ai-vault): truthfully truncate Cursor dir examination at budget
Probe one overflow dirent so an exact 8,192 stop reports examinationTruncated
when more entries exist, and shrink the readdir fallback to the examination
window so adversarial directories cannot retain an unbounded listing.
2026-08-10 11:33:45 -07:00
Brennan Benson 99b68e4530 fix(ai-vault): harden Cursor sidecar discovery cancel, retention, and caps
Rethrow scan cancellation from every enumeration phase, rank newest-first
before the aggregate byte budget, enforce the 16 MiB cap on actual verified
read bytes, and hard-bound cold directory walks with cancel-aware streaming.
2026-08-10 10:57:19 -07:00
Brennan Benson 3b5de3b9e2 perf(ai-vault): bound local/WSL Cursor sidecar discovery at ingress
Share the owning-host chats discovery algorithm between local/WSL and remote
so ordinary unscoped scans cap bucket and session enumeration instead of
walking the full store. Carry explicit path flavor for WSL exact-scope
hashes after UNC→Linux conversion, stream directory selection for bounded
memory, restore relay listSessions scanCursorSidecars, and record path-free
aiVault.scan counters after verified reads with aggregate byte caps.
2026-08-10 10:19:42 -07:00
Jinjing 5dcd16edcd Merge branch 'main' of https://github.com/stablyai/orca into feature-extend-ssh-host-aware-agent-session-hist 2026-08-05 13:49:04 -07:00
Jinwoo Hong be2f9eddd3 fix(mobile): abandon pairing journals that can no longer reconcile (#12773) 2026-08-05 13:45:39 -07:00
Brennan Benson 7f4570c9a6 fix(mobile): activate Source Control diff tabs on phones (#12770)
* fix(mobile): activate source-control diff tabs on phones

* fix(mobile): reveal legacy source control file tabs
2026-08-05 13:36:08 -07:00
Brennan Benson 885afb55a9 fix(mobile): open host editor from root navigation (#12766)
* fix(mobile): open host editor from root navigation

* test(mobile): update task navigation router contract
2026-08-05 13:32:58 -07:00
KyouandOrcaWin 74ac7049ec fix(windows): make managed grok-hook.cmd safe when GROK_HOME is unset (#11782)
* fix(windows): make managed grok-hook.cmd safe when GROK_HOME is unset

Fixes #9358 and #9941.

cmd.exe expands %VAR:~n,m% at parse time. When GROK_HOME is unset (default
outside Orca terminals), the generated length/trailing-backslash guards
became a syntax error and every Grok hook event failed with exit 255.

- Skip substring work when GROK_HOME is undefined (if defined + goto)
- Replace if "%x:~-1%"=="\" (itself a quote-parser bug) with findstr
- Extract Windows script builder; add template + spawn tests

* fix(windows): harden grok-hook GROK_HOME guards and tests

Address review on #11782:
- Inject grokHome via buildWindowsAgentHookPostCommand extra form lines
  (no fragile string replace of the shared payload line)
- Spawn tests delete GROK_HOME and keep PORT/TOKEN/PANE_KEY set so the
  GROK_HOME path actually runs before curl

* fix(windows): cover Grok hook home boundaries

---------

Co-authored-by: OrcaWin <alpha-eng@stably.ai>
2026-08-05 13:31:26 -07:00
Brennan Benson d4dfc35ac4 fix(mobile): preserve multi-image chat attachments (#12639)
* fix(mobile): preserve multi-image chat attachments

* fix(mobile): use preferred array syntax

* fix(mobile): harden multi-image attachment flow

* fix(mobile): retain first-send image previews
2026-08-05 13:23:29 -07:00
Brennan Benson 23238aee0b fix(mobile): stop native-chat send button flicker (#12764)
* fix(mobile): stop native-chat send button flicker

* fix(mobile): keep composer lock rendering pure
2026-08-05 13:13:05 -07:00
Brennan Benson 4c49989c2e refactor(codex): delete the unreachable managed shared-mirror lane (#12614)
PR 9501 shipped real-home routing for the host system default, and the
env override that could turn it back off was never a shipped control. The
managed-account half of the shared runtime mirror has been unreachable
since: every host account routes to its own self-contained CODEX_HOME
before that code runs.

Delete the flag module and its env plumbing plus the managed branch of
syncForCurrentSelection and the six helpers only it called. The three
lanes that still use the shared mirror -- Windows, a custom CODEX_HOME,
and a hook-lane gate that reports unusable -- are untouched, as are every
legacy migration and the WSL read-back helpers.
2026-08-05 12:57:02 -07:00
Brennan Benson 38ba22ecd1 fix(browser): align cookie import safeguards (#12607)
* fix(browser): align cookie import safeguards

* fix(browser): preserve sessions on failed cookie imports

* fix(browser): bound single-label cookie replacement

* fix(browser): preserve host-only parent cookies

* fix(build): bundle cookie scope parser
2026-08-05 12:51:05 -07:00
Jinjing 8ab8b03f17 ci: retrigger PR checks 2026-08-05 12:48:21 -07:00
Jinjing f624d4c2a5 test(e2e): fix flaky PR check failures from recent main drift
- Seed Jira composer e2e with taskSourceContext required since #11296
- Assert new-tab outcome instead of Radix menu teardown timing
- Use durable WIDTH marker after shell prompt redraw clobbers RESTORE
- Re-probe discoverActivePtyId when ConPTY drops the first write
2026-08-05 12:48:21 -07:00
Jinjing 8cdfae055c test: add session resume controls and id building tests
Comprehensive test coverage for cursor session resume controls and
session ID building with host-aware context isolation, covering command
detection, bucket collision handling, and identity preservation.
2026-08-05 12:48:01 -07:00
Jinjing a07efd89fb feat(ai-vault): add cursor command cache with host isolation
Cursor command detection is expensive—spawning a shell process or making
an SSH RPC. Cache detected commands with a 30-second TTL and keep entries
separate per host (SSH connection, WSL distro, local) to avoid repeated
probes and ensure each host resolves correctly. Thread wslDistro through
mobile resume targets so session resumption identifies the right host.
2026-08-05 12:48:01 -07:00
Jinjing 1cff3be4e4 extend(ai-vault): support Cursor sessions with host-aware context isolat
- Hide previous client snapshot while next client loads; ignore stale responses
- Extend session-scanner cache keys with execution host ID and file identity (dev/ino/nlink) to prevent reusing host-stamped issues across different execution contexts
- Invalidate all-host cache when connected host set changes via topology snapshot
- Forward WSL distro context through preflight agent detection methods
- Preserve legacy Cursor command when paired with older runtimes via fallback detection
- Fix bounded file read to handle partial reads until EOF
- Add test coverage for snapshot isolation, cache invalidation, and backward compatibility
2026-08-05 12:48:01 -07:00
Jinjing d6a27423fc extend(ai-vault): support Cursor sessions with host-aware context isolat
Extends AI Vault session history to surface Cursor agent sessions with proper isolation across native, WSL, and SSH execution contexts. Implements cursor sidecar discovery and parsing (local and remote), host-matched command resolution, and metadata-only conversation tracking for sessions without readable transcripts. Adds mobile support for Cursor history resume and strengthens execution-context validation to prevent resuming sessions in incompatible target environments.
2026-08-05 12:44:29 -07:00
Maxon PhongandJinjing b1b291db08 feat(browser): add hard reload option and shortcut hints to reload button (#12483)
* feat(browser): add hard reload option and shortcut hints to reload button

Add a tooltip to the browser reload button showing the reload shortcut.
Add a right-click context menu with Reload and Hard Reload options.
Add localized labels for Hard Reload across EN, ZH, JA, KO, ES.

* fix(browser): add aria-labels to reload buttons

* feat(browser): make reload button contextual and extract action logic

- Button label now reflects actual action: Stop when loading, Retry on failure, Reload when idle
- Extract reload intent resolution into reusable browser-reload-action module with tests
- Add keyboard support (Enter/Space) for the reload button
- Simplify remote page reload to tooltip-only (no ignore-cache RPC for remote pages)
- Add "Stop" translations for all supported languages

* fix(browser): exhaust reload intent switch for type-aware lint

Replace the default branch with an explicit reload case so oxlint
switch-exhaustiveness-check accepts BrowserReloadIntent.

---------

Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
2026-08-05 12:25:21 -07:00
Jinjing fde816e4ee move folders (#12758) 2026-08-05 12:09:24 -07:00
Jinjing d021e1b711 Improve gh issue table (#9068)
* Refactor GitHub work-item mutations onto a shared optimistic coordinator

- Extract PR/issue status, assignee, reviewer, and merge/auto-merge mutations
  out of TaskPage cell components into a registry-backed
  begin/confirm/rollback pipeline (task-page-github-work-item-mutation-*),
  so soft-hide, sticky filter-membership, and quiet revalidation behave
  consistently across all mutation types instead of each cell re-implementing
  optimistic update/rollback/toast logic.
- Add quiet revalidation (no filter skeleton, no page blanking) and soft-hide
  handling so a row that exits the active filter (e.g. closing an issue under
  `is:open`) stays hidden without a jarring list reflow.
- Restyle the GitHub task table: opaque sticky ID/Title cells, distinct header
  fill, accent hover, and tighter row/toolbar chrome to fix background bleed
  and muddy contrast in the scrolled table.

* Fix quiet-revalidate cancellation and sticky-hide scoping in TaskPage

- Replace per-render `cancelled` flag with a ref that only flips on
  true unmount, so a nonce-triggered re-render no longer strands the
  shared quietState's trailing/backoff bookkeeping mid-flight.
- Fix backoff index to use max lag attempts instead of lagging-key
  count, matching processTaskPageQuietRevalidateSettle so several
  single-lag items can't jump the delay tier.
- Scope sticky-hide retention in materializeTaskPageItemList to the
  originating query key, preventing non-membership confirms (e.g.
  auto-merge) from lingering as stale rows across refetches.

* Fix is:draft filter to soft-hide non-draft PRs

Previously state was forced to 'open' for is:draft queries, so a PR
that stopped being a draft still passed the state check and stayed
visible. Add an explicit draft check to soft-hide it.

* Improve GitHub work-item mutations with scoped quiet revalidation

Prevent race conditions and stale data by tracking quiet run ownership,
validating scope changes with generations, and blocking overlapping mutations
with pre-flight checks. Extract quiet state management into a dedicated module
with improved authority clearing and network retry logic.
2026-08-05 11:48:50 -07:00
Jinjing 6c2d168f74 refactor(sidebar): restructure filters into consistent submenu layout (#12759)
Reorganize the host and project filters to share a unified single-row design
(label left, value right) with detailed selection moved to nested panels. Group
both filters under a "Show" section label to keep the parent menu flat. Extract
project-filter search logic into SidebarProjectFilterPanel with explicit focus
and keyboard-handling tests.
2026-08-05 11:36:14 -07:00
Brennan Benson 86b878cfd6 fix(mobile): parse classified PR lookup outcomes (#12659) 2026-08-05 11:31:11 -07:00
github-actions[bot] 2539889197 Update README downloads badge 2026-08-05 18:28:05 +00:00
Brennan Benson 5d2ad3597a fix(native-chat): add direct Codex model selection (#12657)
* fix(native-chat): select Codex models directly

* fix(native-chat): confirm agent exits before switching views

* fix(runtime): handle unavailable foreground probes
2026-08-05 11:27:12 -07:00
Brennan Benson de64337c26 fix(worktree-watcher): refresh status after external pushes (#12361)
* fix(worktree-watcher): surface external push -u through the git-common watch

An external-shell 'git push -u' writes only the common .git/config (plus
refs/remotes/<remote>/<branch>), both invisible to the git-common event
filter, so the Checks panel stayed on 'No upstream configured' until the
renderer safety poll. Classify the common config and remote-tracking refs
as status-tier signals, poll config alongside the other primary-checkout
metadata files, and keep FETCH_HEAD/reflog/ref-lock churn ignored.

* fix(worktree-watcher): refresh after subsequent pushes
2026-08-05 11:26:13 -07:00
6942871194 feat(editor): add Markdown table structure controls (#11985)
* feat(editor): add markdown table structure controls

* fix(editor): scope table context actions to cells

* Replace table toolbar with context-aware overlay controls

Replace the fixed table toolbar with context-sensitive overlay controls that position themselves around the active table, adding support for direct row/column insertion and full table deletion. This approach is less intrusive and supports click-targeted actions via coordinate-based cell resolution. Enhance structural safety by preventing header removal and ensuring tables never collapse below a single cell, deleting instead when the final row or column is removed. Harden the context-menu query with a 120ms timeout to keep the native menu responsive even if the renderer hangs.

* Replace markdown table context query with IPC coordination

Capture table cell targets on pointerdown and report via IPC channel
instead of executing JavaScript on context-menu events. Eliminates
120ms query timeout and unavailability race conditions. Header cells
now disable incompatible row-level actions.

* fix(editor): make table column rebalancing atomic with insertion

- Refactor rebalanceAddedColumn to mutate the caller's transaction, grouping
  insertion and rebalance into a single undo step
- Add validation for cached cell positions that may outlive the document
- Fix table detection to use isInTable() instead of isActive('table')
- Correct z-index layering to respect menu stacking context
- Fix cleanup of stale animation frames and pending pointer state

---------

Co-authored-by: rainL <WYK15@users.noreply.github.com>
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
2026-08-05 11:10:05 -07:00
Jinjing 7aba9b306b Validate auto-generated PR details; abort with clear errors (#12752)
* Validate auto-generated PR details; abort with clear errors

Add fail-closed validation for generated review fields to ensure empty
bodies and generation failures prevent unintended auto-submission, with
user-visible error messages. Preserves current base ref since intent
auto-submits without confirmation.

* Add translations for PR generation error messages
2026-08-05 11:09:46 -07:00
Jinjing d72daf8153 fix(release): admit PTY consumer in SSH watcher isolation gate (#12754)
#12746 only delivers pty.data after an authenticated openClient grant.
Update the macOS release harness to use a credentialed --connect client
and open a legacy session-owner so the gate still exercises watcher
isolation without timing out on the initial PTY echo.
2026-08-05 10:56:26 -07:00
Jinjing 9fb4dbe8eb fix(ssh): handle rejected PTY deliveries with targeted recovery (#12746)
Add targeted recovery for rejected PTY source frames instead of
terminating the relay channel. Classify rejection reasons (malformed,
generation mismatch, range invalid) and attempt recovery based on the
rejection type. Implement admission control at publication time to ensure
frames aren't delivered after ownership changes. Bound recovery attempts
and retry with backoff to prevent exhaustion. Diagnose and log rejection
reasons to aid debugging.
2026-08-05 10:30:04 -07:00
Jinwoo Hong eea0bb64db fix(ssh): make PTY owner admission explicit and non-destructive (#12673)
An owner-capable `pty.openClient` had two failure modes that presented as something else.

If the relay still held an owner record but the request carried no matching resume proof, admission fell through to a SUBSCRIBER grant — a success-shaped response the client cannot use, which it then rejected as "did not grant an authenticated PTY session owner". And if the relay had forgotten the record the client named, admission threw a stale-recovery error, which the client answered by deleting its own recovery row — `clientInstanceId` included — and reopening. Two round trips, and the identity that lets it resume that target at all went with the deletion.

Now every owner grant carries a required `resumed` flag, a forgotten record mints a fresh claim in one round trip, a held claim returns one of three coded refusals, duplicate opens on one connection are rejected even when identical, and an attached-holder refusal becomes a typed error routed through the terminal-relay-error callback instead of feeding redeploy backoff a link that is working fine.

Independent review caught two regressions in the first attempt, both now fixed and both with tests that fail without them:

**A backpressure teardown could take a live owner's session.** The safety argument was that a record only becomes `disconnected` from an observed peer close — but two of the six paths there are capacity paths, where the relay destroys the client's socket itself because its lane queue filled. That is the signature of a client that is ALIVE but not draining fast enough. Demonstrated: the real owner is torn down for backpressure, a rival is granted ownership 270ms into a nominal 30s grace, and the owner's later reconnect with a valid resume proof is refused permanently, backoff cleared, no retry. Closes now carry a cause (`peer-closed` | `local`, defaulting to `local`, which only ever widens a grace), and the floor applies only to closes the transport actually observed on the peer's side. Capacity teardowns, decode faults and sink failures keep the default.

**A client's own zombie connection blocked it permanently.** Only `SshRelaySession` ever requests owner, and every endpoint-credential client shares one principal — so in a normal single-app deployment an `active` incumbent refusing you is almost always your own half-open connection the relay never saw close. That was refused as terminal, where main recovered on bounded backoff once keepalive noticed. The refusal already held both client identities; a match is now a distinct transient refusal that falls through to relay-lost backoff, restoring that recovery. A genuinely different client is still blocked.

Also: each retry deadline now starts when its own phase begins, instead of both being computed at entry where a slow first phase could leave the second with zero attempts.

Fixes STA-3365.
2026-08-05 02:56:07 -07:00
Jinwoo Hong cc1859d61c fix(ssh): snapshot detached SSH leases on quit and bound the teardown (#12687)
Per-target SSH teardown awaited `removeAllForwards` BEFORE anything marked the lease detached, so a slow forward close let the final store flush snapshot while leases still said `attached` — and the later durable write was rejected because persistence had already finalized. On the next launch those leases described a state that never existed.

`beginSshShutdown()` now performs every in-memory transition synchronously before returning, and the quit path calls it immediately before `store.flushAsync()` with no await between. The whole drain shares one deadline that REPORTS unfinished `{targetId, phase}` rather than concluding anything about it, and `waitForSystemSshForwardStop` gained a post-SIGKILL bound.

Nothing here destroys a session. `detached` means this app let go of the lease, not that the shell died — the pre-pass exists precisely so still-running PTYs are recorded as detached-but-alive instead of being lost to an `attached` snapshot. Review confirmed every reader honors that: reattach enumeration and persistence restore filter only `terminated`/`expired`, lease normalization has no age-based expiry, and attempt exhaustion leaves a lease alone. The drain deadline's only consumers are a warning log and a join that discards the value — nothing reads it as "gone".

Review also caught a defect the refactor introduced, now fixed: making the pre-pass synchronous meant a throw from `beginShutdownDetach` — via `webContents.send` on a renderer that quit had already destroyed — escaped the non-async `will-quit` listener and skipped `killAllPty()`, the watchers, `store.flushAsync()`, the teardown barrier and `app.quit()`. That would have lost the exact snapshot this PR exists to make correct. Each call is now wrapped per session, collecting errors and continuing. Proven: the test throws from the first of two sessions and fails without the fix with "Object has been destroyed".

A second test could only fail via timeout rather than assertion; the ordering is corrected so removing the post-SIGKILL bound now fails in 5ms with a clean assertion instead of a 5s timeout.

Rebased onto main and verified independent of #12673 (zero references to its owner-admission changes), which is being reworked separately. Fixes STA-3366.
2026-08-05 02:54:29 -07:00
Jinjing ade4354ddd Open GitHub items in workspace composer with issue automation (#12653)
* Open TaskPage GitHub items directly in workspace composer

Remove background creation indirection. The composer prefills with
issue/pull-request metadata from the GitHub work item.

* Generalize workspace composer tests to support multiple sources

Rename test from task-page-github-composer-boundary to task-page-workspace-composer-boundary. Add test verifying Linear items open directly in the workspace composer, expanding beyond GitHub-specific routing.

* Support issue command automation in workspace composer

- Pass GitHub work items through composer for issue context
- Quick create resolves commands at workspace creation time
- Extract command building and trust logic to dedicated module

* Derive composer hooks by host context for React Doctor

Key loaded hooks and issue commands by execution-host context so
repo/host switches no longer reset derived state in effects (the
static-analysis gate). Also wire SSH-aware workspace targets and
cancel-safe submit settlement for workspace creation.

* Support duplicate repo IDs across hosts in workspace creation

When the same repo exists on multiple hosts (local and SSH), resolve the
workspace creation target to the ready setup on the preferred host instead of
failing closed. Also add resilience to hook checks by clearing the cache on
transient IPC failures.
2026-08-05 02:34:13 -07:00
Jinwoo Hong d15939c5fd fix(terminal): recover rejected paired-runtime input (STA-2830) (#12675)
With a desktop client paired to a remote Orca runtime, terminal panes could report connected, writable, and `terminal.send` returning accepted — yet keystrokes never reached the agent. No error, no banner, no recovery; input silently vanished.

The ticket was really two bugs. The attach half was already fixed by #12589 (subscriber-driven daemon attach), confirmed by reproducing against current main. This fixes the remaining half: a write the host refuses had no way to tell anyone.

A capability-negotiated `WriteUnavailable` opcode carries that refusal back to the client, where it feeds the pane's pre-existing recovery hook. Capability gating matters because decoders reject unknown opcodes on desktop — and, worse, silently drop them on mobile — so the signal is negotiated in the subscribe handshake. Verified per direction: an old host strips the unknown Subscribe key, an old client omits it so the host never emits, and capability cannot be inherited across resubscribe.

Independent review then found the signal was being delivered and discarded: recovery demanded an authoritative liveness answer, and `pty:hasPty` had no `remote:` guard, so a paired pane's id fell through to the LOCAL provider, which returned false, and recovery bailed before remounting. Every test stopped at the transport boundary, so all of them passed while the pane stayed just as stuck. `pty:kill` already had exactly that guard.

The fix makes main answer LESS rather than claim more: `pty:hasPty` now returns unknown for a `remote:` id instead of a fabricated false, because main cannot speak for another host's PTY. The remount is then authorized by positive evidence — the process that owns the PTY stating it refused this specific write over a live negotiated connection — not by inference from silence. Local and app-SSH ids keep the probe, where a false genuinely means the shell died. Nothing is destroyed on this path; the remount rebuilds the renderer over the session it already had.

An end-to-end test now carries a rejected write from the host through to an actual remount, which no prior test did. A surviving mutant was also killed: the legacy-binary capability gate could previously be deleted with nothing turning red.

The reliability gate stays experimental — live paired journeys and mixed installed-release evidence remain uncollected. Fixes STA-2830.
2026-08-05 01:41:27 -07:00