Commit Graph
980 Commits
Author SHA1 Message Date
Neil 26224196f5 perf(worktrees): avoid auto-maintenance in create fetches (#8039)
* perf(worktrees): avoid auto-maintenance in create fetches

Git's opportunistic maintenance can keep an already-complete exact-base fetch open for seconds. Disable it per command for create-base refreshes only, leaving ordinary fetch maintenance and exact-ref freshness unchanged.

* docs(worktrees): explain create fetch maintenance scope

* test(worktrees): account for fetch config prefixes

* fix(worktrees): cover Git 2.29 auto maintenance
2026-07-10 02:31:47 -07:00
Neil e4c7aab4a2 Normalize invalid terminal line height before xterm (#8050) 2026-07-10 01:28:22 -07:00
67447fe64f Add AI Vault subagent display (#7423)
Co-authored-by: Orca <help@stably.ai>
Co-authored-by: hmrserver <>
Co-authored-by: brennanb2025 <brennankbenson@gmail.com>
2026-07-10 01:13:16 -07:00
b7e84aea3c Fix automatic branch rename for non-English git locales (#8012)
* Fix automatic branch rename for non-English git locales

A gettext-enabled git (Homebrew git, most Linux distro gits) under a
non-English locale translates every diagnostic, including the `fatal:`
prefix, so Orca's stderr phrase parsers stop matching. The first-message
branch auto-rename was the headline casualty: isNoUpstreamError missed
the translated no-upstream error, branchHasUpstream failed closed to
"has upstream", and the rename settled silently and permanently.

- Force LC_ALL=C on all Orca-spawned machine-parsed git: the local
  prompt-guard env chokepoint, the three relay git spawn sites, and both
  local clone spawns (progress + failure-message parsing). User
  terminals are untouched.
- Replace the boolean upstream check with a tri-state probe: rename
  proceeds only on a proven missing upstream; an unreadable probe now
  raises the rename-failed badge and retries instead of settling.

Fixes #7808

🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>

* Consolidate untranslated-git-locale into runner and relay primitives

Replace the five per-site LC_ALL=C patches with one shared
UNTRANSLATED_GIT_OUTPUT_ENV (LANGUAGE=en LC_ALL=en_US.UTF-8
LANG=en_US.UTF-8) injected inside the git runner primitives
(promptGuardGitEnv, gitSpawn, gitExecFileSync, gitExecFileAsyncBuffer)
and a relay buildRelayGitEnv() helper, so every current and future
machine-parsed git spawn is covered by construction — including the
fs-handler-git-fallback sites the per-site approach missed. The UTF-8
English locale keeps a UTF-8 LC_CTYPE for hooks git spawns; LANGUAGE is
pinned because gettext consults it before LC_ALL.

WSL-routed git gets the same values as a shell assignment prefix built
in resolveCommand, since spawn env cannot cross the wsl.exe boundary —
closing the WSL gap the first pass accepted.

Also scrub credential-bearing remote URLs from the probe-failed message
surfaced on the worktree card.

🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>

* Scrub credential-bearing URLs from clone failure messages

---------

Co-authored-by: Brennan Benson <brennanbenson@Brennans-MacBook-Pro.local>
Co-authored-by: Claude <noreply@anthropic.com>
2026-07-10 01:03:16 -07:00
f238952be2 Agent status over WSL: guest-resident hook relay + WSL-side hook installers (STA-1515) (#7903)
* docs: full design + context for agent status over WSL (STA-1515)

Why hooks don't work on Windows+WSL (loopback transport gap + WSL-side
installation gap), per-client transport map, the OMP-only fixes that
shipped (7642/7641) and why they don't generalize, the recommended
guest-resident relay over wsl.exe stdio mirroring the SSH relay plus
WSL-side hook installers, alternatives considered, validation facts and
gotchas from the 2026-07-08 Windows rig run, and acceptance criteria.

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

* feat(agent-hooks): agent status over WSL — guest relay + WSL-side hook installers (STA-1515)

Agent hooks have never worked from inside WSL: under default NAT
networking, WSL's 127.0.0.1 is its own loopback, so every hook POST to
the Windows listener dies silently, and hook configs were only ever
written to the Windows home where WSL agents never see them.

Transport: a hooks-only guest relay (src/relay/wsl-agent-hook-relay.ts)
runs inside the distro, binds WSL loopback on the very port the clients
were already given (host-issued token; EADDRINUSE falls back to :0 with
endpoint-file re-coordination, which also covers mirrored networking),
and forwards parsed envelopes over its own wsl.exe stdio into
agentHookServer.ingestRemote — the same shape as the SSH relay. It exits
when stdin closes so a freed Windows port can never be forwarded into a
dead guest listener.

Installation: the unchanged SSH remote hook installers run against an
SFTP-shaped adapter whose primitives are home-scoped fs RPCs served by
the relay, so all 14 managed agents' hooks land in the WSL home over the
already-open channel with zero per-file wsl.exe spawns.

Lifecycle: per-distro manager ensured from buildPtyHostEnv on every WSL
PTY spawn (covers post-restart daemon reattach re-spawns), stale-bundle
reinstall via exit 42, no-node-43 cooldown, bounded retry for wsl.exe
'Catastrophic failure (E_UNEXPECTED)', breadcrumbed failures.

Zero per-client transport changes; listener stays Windows-loopback-only.

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

* fix(agent-hooks): WSL relay link-death recovery + Codex runtime-home hook install (STA-1515)

Follow-ups from the first Windows-rig validation of PR #7903:

Link death: a mux protocol error or keepalive timeout could kill the
host<->guest link while the guest relay stayed alive returning 204s —
the manager stayed 'running' and every later envelope blackholed
silently (the exact observed signature: Claude hooks POST 204, store
never populates). wsl-hook-relay-link.ts now guarantees exactly-once
death handling from either signal (mux dispose OR child exit); the
manager breadcrumbs it, kills the child, and self-restarts after a
short cooldown since a live agent session produces no new PTY spawns
to re-trigger ensure. ORCA_WSL_HOOK_RELAY_DEBUG=1 traces each received
envelope pre-ingest. A live integration test pins the full host chain:
the real esbuild bundle over real child stdio through the real manager
into a real AgentHookServer.ingestRemote, exact Claude POST shape.

Codex: Orca launches WSL Codex with CODEX_HOME redirected to the
managed runtime home (~/.local/share/orca/codex-runtime-home/home), so
hooks installed to ~/.codex were never read. installRemote now accepts
an explicit codex home (flat layout), threaded from the relay manager;
the config.toml trust write is deferred while the file doesn't exist
(the launch path seeds it only-if-absent — creating it first would
cancel the seed), and the manager re-runs the byte-equality-idempotent
installers on later ensures (30s throttle) to upsert trust once the
seed lands.

Also: WSL test suites now run on Windows dev hosts (fs-backed suites
skip with rig coverage noted; manager suite uses a fixed POSIX home).

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

* fix(agent-hooks): renderer ownership gate treats wsl:* connection ids as local (STA-1515)

Round-2 rig finding: with the link fixed, WSL hook envelopes reached
ingestRemote and the durable cache, but useIpcEvents.applyAgentStatus
drops any status whose stamped connectionId differs from the owning
repo's — 'wsl:<distro>' !== null for a local repo, so every WSL-relayed
status died before setAgentStatus and notifications.

wsl:* ids are transport provenance, not ownership: the gate now
normalizes them to local via isWslHookRelayConnectionId (shared
contract, also used by the relay link when stamping), while still
rejecting WSL-stamped events against SSH-owned repos. Provenance stays
stamped — it is what made this drop diagnosable.

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

* fix(agent-hooks): adversarial-review hardening for the WSL hook relay (STA-1515)

Four independent review lenses over the branch; all confirmed findings
fixed before the next rig round:

Endpoint identity (4/4 reviewers): the guest endpoint dir was keyed by
the EPHEMERAL Windows hook port, so a daemon-surviving agent kept
sourcing the dead port-P1 file after an Orca restart — breaking the
restart-resume acceptance criterion and regressing shipped OMP
recovery. Now keyed by a restart-stable instance key (hash of the
Windows endpoint file path, crossed via ORCA_WSL_HOOK_INSTANCE): the
restarted instance's relay rewrites the SAME file, which is exactly
what re-coordinates survivors.

Restart policy: every failure arms the restart timer (one failed
relaunch no longer ends self-recovery), and the timer probes
wsl --list --running first — wsl -d BOOTS a stopped distro, so
recovery must never resurrect a VM the user shut down; stopped-distro
state is dropped instead. Failure counters reset only after 2min of
stable uptime, so connect-then-die loops escalate to the 10-min cap
instead of cycling every 10s. Timer policy extracted to
wsl-hook-relay-recovery.ts with direct tests.

Also: version-namespaced guest install dir (dev+prod instances no
longer reinstall over each other; PID-suffixed tmp files), 30s install
timeout (a wedged wsl.exe could pin the state machine at 'starting'
forever), per-candidate node version probing (apt node 12 on PATH no
longer masks nvm node 20 into a false no-node cooldown), WSL_UTF8=1 +
NUL-stripped stderr (catastrophic-failure matcher survives UTF-16LE),
ordered post-sentinel chunk handoff, port-fallback breadcrumb via the
home handshake, bad home reply now fails the connect, missing-bundle
warn-once, case-normalized distro keys, disposeAll wired to will-quit,
one-shot 60s reinstall timer for single-spawn Codex trust catch-up,
escaped + contract-derived spawn command.

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

* docs: record round-3 rig validation status for agent status over WSL (STA-1515)

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

* fix(agent-hooks): round-4 adversarial-review fixes for the WSL hook relay (STA-1515)

- dropState identity race: recovery re-checks state identity after the
  distro-running probe await, and the manager's dropState only deletes the
  exact state it was armed for — an ensure() landing mid-probe can no longer
  have its fresh relay orphaned outside the map.
- Distro-running probe fails CLOSED: a probe error no longer reports
  'running', so recovery can never wsl-d-boot a distro the user shut down.
- Relay spawns use --exec: bypasses the distro's default login shell
  (fish/nushell chsh) and passes argv verbatim, dropping the $-escape shim;
  same form as the Codex WSL login spawn.
- Post-sentinel chunk handoff rides a microtask so an envelope in the
  trailing bytes can no longer dispatch before the link's notification
  handler is registered.
- Guest relay mirrors the SSH relay's uncaughtException/unhandledRejection
  posture.
- Replay cache capped at 256 panes with recency eviction (the WSL relay has
  no per-pane teardown signal); meta map kept in lockstep.
- Launch script derives the stale-exit code from the shared contract
  constant; one-shot reinstall timer refuses to arm after dispose.
- New oracles: sentinel unit suite, fs-bridge scoping suite, fixed-token
  403/204, EADDRINUSE endpoint-file rewrite, cache-cap eviction, and the
  recovery/manager race regressions (verified to fail with fixes reverted).
- Doc: round-4 review section + revised curl.exe stance (kept as the
  no-node fallback — Codex is a native binary; fresh distros ship no node).

* docs: record round-4 pinned rig validation for agent status over WSL (STA-1515)

---------

Co-authored-by: Orca <help@stably.ai>
Co-authored-by: Brennan Benson <brennanbenson@Brennans-MacBook-Pro.local>
2026-07-10 00:19:45 -07:00
Doan Bac TamandBrennan Benson fa2b228ad3 feat(rate-limits): Grok CLI OAuth weekly credit usage (#7869)
* feat(rate-limits): Grok CLI OAuth weekly credit usage in status bar

Read ~/.grok/auth.json (read-only), fetch billing credits via cli-chat-proxy, and surface Grok in Settings, status bar toggles, and rate-limit polling alongside other usage providers.

* fix(grok): clarify comments and address CodeRabbit review

- Shorten Why comments per AGENTS.md; fix billing period end fallback.

- Share GrokAccountStatus type; hash-based locale keys; reload why in Settings.

* docs(grok): plain-language comments and Settings copy

* feat(stats): subscription usage section with Grok in Stats & Usage

Surface rate-limit weekly credits in Settings > Stats & Usage and link to Accounts for setup.

* feat(stats): Grok tab in Usage Analytics dropdown

* refactor(stats): drop Subscription usage block; align Grok pane with Codex

* fix(grok): align settings copy and visibility tests

* fix(grok): add localization catalog entries

* fix(grok): avoid eager usage refresh fanout

* fix(grok): harden usage refresh visibility

* test(grok): cover account status privacy boundary

* fix(grok): target refreshes and redact auth errors

* fix(grok): hide usage UI for signed-out users and align empty states

- Treat a token-less auth.json (e.g. after grok logout) as signed out
  instead of surfacing a permanent status-bar error.
- Map billing responses without credit usage to 'unavailable' so plans
  with no weekly credits hide the bar like Claude API-key billing.
- Gate the grok status-bar item and toggle on CLI PATH detection,
  matching claude/codex/gemini/kimi.
- Guard an empty GROK_CLI_CHAT_PROXY_BASE_URL from producing a
  relative billing URL.
- Drop dead minimax/kimi Stats & Usage search keywords left from the
  removed subscription section.
- Add missing grok search keyword catalog entries and translate the
  English-stubbed stats keywords in es/ja/ko/zh.

* test(ipc): mock grok account registrar in register-core-handlers test

---------

Co-authored-by: Brennan Benson <brennanbenson@Brennans-MacBook-Pro.local>
2026-07-10 00:11:27 -07:00
JinjingandOrca f495b39a6a Fix untracked line-stat cache thrash and add source-control scale benchmark (#8022)
* Fix untracked line-stat cache thrash and add source-control scale benchmark (#8013)

The untracked line-stat cache capped at 2,048 entries while a git status
scan can carry up to DEFAULT_GIT_STATUS_LIMIT (10,000) untracked entries.
A sequential scan over more files than the cap FIFO-evicted every entry
before the next poll revisited it (~0% hit rate), so every 3s status poll
re-read every untracked file's full contents. Measured with 64KB files:
warm rescan cost per file was 17x higher just past the cap.

- Size the cache to 2x the status entry limit and make eviction LRU
  (delete-before-set on hit and refresh) so a hot worktree's entries
  survive another worktree's scan.
- Add tests/e2e/source-control-large-file-count.spec.ts: a 5-scenario
  Playwright benchmark (pnpm run test:e2e:source-control-scale) that
  reproduces #8013 deterministically — event-loop stall, DOM node count,
  JS heap, and OS-level renderer working set at 5k/9.5k/11k changed files,
  plus a clean-repo control and a cache-effectiveness gate. Scenarios
  asserting bounded row mounting go green with the SourceControl
  virtualization fix (#7619).

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

* Address review: historical cache comment + fixture cleanup on partial setup failure

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

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-09 22:33:55 -07:00
Jinjing 6166f6c47a feat(linear): add server-side issue filters for status and other properties (#8021)
Add attribute filters (status, priority, assignee, labels) to the Linear
issues list, applied in GraphQL before pagination so hasMore matches the
filtered set. Thread filters through IPC/RPC/store cache with invalidation
on issue mutations, and mirror GitHub-style filter chrome on TaskPage.
2026-07-09 22:23:56 -07:00
Jinjing 44d5ed0439 1.4.131 rc2 release prep (#8020)
* Support WSL Codex settings promotion and harden config write-back

- Enable settings promotion for WSL runtimes using per-distro baselines.
- Create parent directories if missing to prevent promotion ENOENTs.
- Keep restrictive permissions (0600) and follow symlinks on promote.
- Respect CRLF line endings when inserting keys into CRLF config files.
- Skip redundant baseline file writes when settings are unchanged.
- Include the release scan report for the 1.4.131-rc2 prep.

* Refactor sleeping agent wake flow and fetch rate limits via backend

- Background-mount only targeted terminal tabs during passive wake to
  prevent spawning unnecessary PTYs for unvisited tabs.
- Latch edge-triggered wake requests that arrive mid-hibernation and
  track active claims to prevent double-resuming a provider session.
- Query the ChatGPT wham usage backend API directly with fetch for
  rate limits, avoiding launching Codex or WSL login shells.
- Asynchronously probe and serialize WSL auth files with timeouts to
  prevent synchronous I/O from stalling Electron's main process.
- Fix config promotion edge cases such as missing parent directories,
  dangling symlinks, and atomic write permission widening.

* Support WSL dotfile-symlink write-back and lengthen redeem timeout

- Preserve symlinked Codex config on WSL by writing through the
  existing file instead of atomic-rename, since \\wsl$ symlink
  metadata isn't reliably detected and rename would clobber the link.
- Tighten new ~/.codex directory creation to 0700 (holds auth.json).
- Give explicit reset-credit redemption a 30s backend timeout instead
  of the 10s background-poll default, since it's user-triggered.
- Read sleeping-agent session state from the worktree's actual
  execution-host partition instead of always the local one, so the
  headless-wake check works correctly for SSH-hosted worktrees.
- Isolate serve-sim watcher tests from the real $TMPDIR/serve-sim
  state file to avoid leaking unrelated events.
2026-07-09 21:54:22 -07:00
BingZandJinwoo Hong 5b1b8cc61f fix(preflight): resolve WSL/SSH agent paths past shell aliases (#7867)
* fix(preflight): resolve WSL/SSH agent paths past shell aliases

LeanCTX and similar tools wrap claude/codex as interactive shell aliases.
command -v then returns alias text, which fails absolute-path detection and
hides installed agents (#7816).

Prefer bash type -P, then zsh type -p, then command -v for dash/sh fallback
in WSL agent discovery, WSL isCommandOnPath, and remote relay probes.

* fix(preflight): harden alias-safe PATH lookup chain

Require non-empty results between type -P, type -p, and command -v so bash
type -p empty success cannot skip later lookups.

* fix(preflight): resolve agent executables directly from PATH

---------

Co-authored-by: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com>
2026-07-09 17:08:35 -07:00
Brennan Benson 1534edc073 Separate pane identity from liveness in the tab-agent resolver (fixes OMP tab flicker) (#7860) 2026-07-09 11:38:26 -07:00
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
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 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
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
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 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
Jinwoo Hong 20832ef36f ci(daemon): fail builds, packaging, and CI when the terminal daemon cannot start — and stop failing silently (#7849) 2026-07-08 19:46:14 -04: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
Jinwoo HongandOrca 5a390cd60e Fix Claude Agent Teams detection without Claude CLI (#7834)
Co-authored-by: Orca <help@stably.ai>
2026-07-08 14:34:16 -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
a09a00f066 fix(pty): load omp status extension in wsl shells (#7642)
Co-authored-by: Orca <help@stably.ai>
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
2026-07-07 21:24:08 -07:00
Jinwoo HongandOrca 8368e946df Fix orchestration agent prompt injection (#7758)
Co-authored-by: Orca <help@stably.ai>
2026-07-07 19:50:57 -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
Brennan BensonandOrca f311539f9c Split agent-detection into title status/display/identity domain modules (title evidence plan, PR 3) (#7612)
Co-authored-by: Orca <help@stably.ai>
2026-07-07 17:50:37 -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
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 d8df9c818f fix(terminal): allow terminals to spawn outside the worktree (#7685) (#7750)
Co-authored-by: Orca <help@stably.ai>
2026-07-07 15:34:29 -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
Jinjing 4cbf699360 fix: quote queued OMP resumes for Windows shells (#7628) 2026-07-06 18:41:32 -07:00
PP 0b4196fc17 fix(ssh): stop deleted ~/.ssh/config hosts from reappearing on sync (#7302)
Deleting a config-sourced SSH target had no lasting effect: the Manage-SSH pane
re-imports ~/.ssh/config on open, and the import was a pure upsert with no record
of deletions, so the just-deleted host was re-inserted verbatim from the config
that still exists on disk.

Persist a `deletedSshConfigAliases` tombstone set:

- Deleting a config-managed target (source 'ssh-config', or an adopted legacy
  import) records its alias; manual targets are never tombstoned.
- The passive on-open sync skips tombstoned aliases, so a deleted host stays
  deleted.
- Re-adding or editing a target reclaims its alias, and the explicit Import
  action (`reAdopt`) clears all tombstones to deliberately re-adopt config.

This also fixes the edit-then-reappear case: editing a config host to `manual`
already reserved its current alias, and reclaim covers alias changes.
2026-07-06 18:35:42 -07:00
f61500280b feat(ai-vault): add OMP sessions to the AI Vault session browser (#7618)
* feat(ai-vault): add OMP sessions to the AI Vault session browser

Adds OMP ("Oh My Pi") to the AI Vault/Agents catalog so historical
.omp/agent/sessions/**/*.jsonl transcripts are discovered, parsed, and
resumable from the right-sidebar session browser — locally and over SSH.

- Discovery mirrors Pi (OMP_CODING_AGENT_DIR env, WSL home roots, per-agent
  limit) in both the local scanner and the remote/SSH scanner.
- Parses OMP's message-graph JSONL via the shared graph parser (new
  MessageGraphAgent type), capturing the model from model_change.model (OMP's
  key, not Pi's modelId) or the assistant message, and tokens from usage.
- Routes OMP through the incremental parse cache so ~5s rescans resume from
  the last byte instead of re-reading whole transcripts.
- Resumes by absolute transcript path (`omp --resume <path>`) so it resolves
  regardless of which session-dir root (custom OMP_CODING_AGENT_DIR / WSL
  store) the file was discovered under; threaded through both the scanner and
  the renderer's local resume/copy rebuild.
- Renderer reuses the existing OmpIcon/catalog/grouping; adds overflow-x-hidden
  so long worktree chips never widen the sidebar.

Generalizes normalizePiSessionsDir -> normalizeAgentSessionsDir. Verified
end-to-end against 10 real ~/.omp transcripts and rendered in the app.

Co-authored-by: gatsby74 <166927047+gatsby74@users.noreply.github.com>

* fix: make AI Vault parse-cache agent switch explicit

---------

Co-authored-by: gatsby74 <166927047+gatsby74@users.noreply.github.com>
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
2026-07-06 18:03:36 -07:00
Jinwoo HongandOrca 417723411e perf(source-control): stop gh rate-limit storms and idle git-status spawn churn (#7595)
Co-authored-by: Orca <help@stably.ai>
2026-07-06 15:12:56 -07:00
NeilandOrca e33b2006f4 Remove stale max-lines lint disables from files under the limit (#7548)
110 files carried an eslint/oxlint-disable max-lines directive but are
already under the default max-lines budget (300 .ts / 400 .tsx / 600 .mjs
/ 800 test), so the suppression is dead. Removing it restores real
max-lines coverage on these files with zero behavior change.

Each removed directive had max-lines as its only rule; verified via a
full oxlint run (0 max-lines violations, 0 new errors). Diff is pure
deletions (200 lines, 0 additions) — no code touched.

Co-authored-by: Orca <help@stably.ai>
2026-07-06 02:12:32 -07:00
Brennan BensonandOrca eb8435950a Clear a worktree's merged pull request after it switches to a different branch (#7460)
Co-authored-by: Orca <help@stably.ai>
2026-07-06 01:26:10 -07:00
Brennan BensonandOrca 3826169c11 Keep Claude live-PTY refresh gate closed across restarts; recover wiped runtime credentials (STA-1246) (#7483)
Co-authored-by: Orca <help@stably.ai>
2026-07-06 01:16:23 -07:00
Jinjing 2b5f6af0e9 Respect terminalWindowsShell setting for local Windows agent launches (#7526)
Ensure agent CLI startup and draft launch commands use the correct quoting
format based on the user's configured local Windows shell (e.g., cmd.exe).
This avoids using host settings for remote/SSH targets where local shell
preferences do not apply.
2026-07-06 01:05:56 -07:00
NeilandOrca 88640ef4c6 docs(runtime): fix stale ps-snapshot factory comment after #7518 (#7529)
Co-authored-by: Orca <help@stably.ai>
2026-07-06 00:44:21 -07:00
Jinwoo HongandOrca d9103d08c1 Revert "fix(terminal): launch prior/default agent when a woken terminal can’t resume" (#7524)
Co-authored-by: Orca <help@stably.ai>
2026-07-06 00:27:32 -07:00
NeilandOrca 27ba95cf31 perf(runtime): cache parsed ps rows on POSIX so panes share one parse (#7518)
getProcessTableSnapshot deduped the ps fork (#6288/#6667) but cached only the
raw stdout string on POSIX, so every concurrent agent pane re-ran parsePsRows
over the identical output within each 500ms TTL window — O(M*P) redundant
tokenization + row allocation. The Windows reader already caches parsed rows;
this makes the POSIX default reader do the same by parsing inside the deduped
scan and returning ProcessTableRow[]. Collapses the duplicate parsePsRows in
the main and relay foreground resolvers into one shared parseProcessTableRows.

Co-authored-by: Orca <help@stably.ai>
2026-07-06 00:03:54 -07:00
Neil ce687221d3 lint(unicorn): enable prefer-number-properties, prefer-array-find, prefer-array-index-of (#7516)
Enable three unicorn rules — one correctness, two performance — and fix every
existing violation repo-wide so the rules pass as errors.

prefer-number-properties (76 sites)
- parseInt/parseFloat/NaN -> Number.* : safe aliases (autofixed).
- isNaN -> Number.isNaN (12 sites, hand-converted): global isNaN coerces its
  argument, Number.isNaN does not. Verified every call site already passes a
  number (Number.parseInt results, number-typed fields, Date.getTime()), so the
  conversion is behavior-preserving today and guards against a future non-numeric
  argument silently coercing.

prefer-array-find (26 sites)
- .filter(pred)[0] -> .find(pred); .filter(pred).at(-1) / .pop() -> .findLast(pred).
  Drops the intermediate array and short-circuits.

prefer-array-index-of (5 sites)
- .findIndex(x => x === v) -> .indexOf(v).

Verified: typecheck (node/cli/web) clean, 53 affected suites pass (1679 tests),
oxlint clean repo-wide. mobile/ uses findLast safely (already ships ES2023
.toReversed()); config scripts and e2e helpers run on Node 24.
2026-07-05 23:56:37 -07:00
Brennan Benson 7b7a21e3c7 Give agents access to inline Linear ticket screenshots and media (#7484) 2026-07-05 23:55:56 -07:00
Jinjing 2f52f0d665 fix(pty): deliver multiline agent-launch prompts via bracketed paste (#7487)
* fix(pty): deliver multiline agent-launch prompts via bracketed paste

Multiline agent-launch prompts (claude/codex/opencode argv injection) were
mangled when Orca typed the startup command into the interactive shell. The
command is single-quoted, but its literal embedded newlines survive quoting;
bash readline / zsh zle read every raw LF as accept-line (Enter), so the first
newline submits an unterminated single-quoted command, drops the shell into PS2
(>) continuation, and the rest executes piecemeal — backticks/$ evaluate, quotes
go unbalanced, and the agent never receives the intact prompt. Short single-line
prompts worked because they have no embedded newline.

Fix: when a startup command contains a newline, wrap the payload in
bracketed-paste markers (ESC[200~ … ESC[201~) before the trailing submit CR/LF
so the line editor inserts the multiline text literally and only the trailing
byte submits it. The single-line fast path is unchanged. Gated on the target
line editor having bracketed-paste mode active (Orca-wrapped bash/zsh) so shells
without it never echo the markers as garbage; Orca's bash rc wrappers now force
`enable-bracketed-paste on` (zsh has it on by default).

Applied consistently across every startup-command delivery path:
- src/main/providers/local-pty-shell-ready.ts (in-process / degraded local)
- src/main/daemon/terminal-host.ts (daemon host — primary local)
- src/relay/pty-handler.ts (SSH relay, remote host)
- src/renderer/src/lib/ssh-background-startup-delivery.ts (hidden SSH tab)
All share src/shared/startup-command-submission.ts. Windows cmd.exe and other
shells keep the current CR submit path (no regression); PSReadLine/POSIX
bash/zsh get the fix.

* Fix multiline detection for CRLF-terminated startup commands

Strip the entire CRLF terminator (or lone CR/LF) from startup commands
before checking if the body contains newline characters.

Previously, slicing off only the last character of a CRLF-terminated
command left a trailing CR in the body. This caused a single-line
command to be incorrectly categorized as multiline and wrapped in
bracketed paste.
2026-07-05 21:49:04 -07:00
Brennan BensonandOrca 72a23809fa Prevent OMP terminals from being mislabeled as Gemini (#7447)
Co-authored-by: Orca <help@stably.ai>
2026-07-05 13:39:38 -07:00
Jinjing e7d1fa4a21 Disable auto-merge for unstable GitHub PRs (#7415)
Prevent enabling auto-merge when a PR is in an UNSTABLE merge state.
GitHub auto-merge mutations reject UNSTABLE PRs directly instead of
allowing them to wait, so we should suppress the option.
2026-07-05 11:05:58 -07:00
Jinwoo HongandOrca 9981c3b827 Check for Updates: cmd/ctrl-click fetches latest perf-tagged prerelease (#7278)
Co-authored-by: Orca <help@stably.ai>
2026-07-05 02:53:17 -07:00