* Prevent index churn from refreshing worktrees
* Cover IPC contract in worktree reliability gate
* Refresh background worktree heads without re-entering structural fanout
External commits, amends, and soft resets in non-active worktrees now reach
store rows through spawn-free Git metadata reads diffed in the watcher's
existing debounce, emitted only on real head moves. HEAD reflog appends become
status-only triggers, config.worktree becomes structural for sparse-flag
freshness, and the non-darwin poller gains a periodic ungated index re-stat
so in-place rewrites on coarse-mtime filesystems cannot be missed forever.
* Reject unsafe symref paths and validate object ids in the head reader
Ref content comes from repo files an attacker can craft. Backslash segments
traverse on Windows where join treats them as separators, and colons are
forbidden in Git ref names; both now fail isSafeRefName before any path is
built. Resolved values are additionally emitted only when they match a hex
SHA-1/SHA-256 object id, so no file content can leak through the identity
event even in principle.
---------
Co-authored-by: Brennan Benson <brennanbenson@Brennans-MacBook-Pro.local>
Co-authored-by: Brennan Benson <>
* Hide console window for Windows agent foreground-process scan
Agent foreground-process inspection re-forks powershell.exe (or the wmic fallback) to detect which agent runs in each terminal. Both spawns omitted windowsHide, so on Windows each fork popped a fresh conhost console window that flashed and stole keyboard focus from the foreground app — including Orca's own terminal — recurring roughly once every few tens of seconds while an agent session was open (and more often under continuous agent output).
Add windowsHide: true to both probes (matching the codebase-wide convention) plus a regression test asserting the spawn options.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* test(windows): include scan root in foreground fixtures
---------
Co-authored-by: xucongwei <xucongwei@bytedance.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com>
* fix(cli): migrate managed WSL launchers to native exe
* refactor(cli): reconcile managed WSL registrations
* fix(cli): harden WSL registration reconciliation
* fix(cli): serialize and harden WSL CLI registration lifecycle
- Cap per-distro concurrent repairs and add a filesystem lock so two
Orca installs can't race the same distro's launcher/bridge files
- Skip re-adopting a legacy managed launcher when its bridge is user-
owned, avoiding a doomed install retried on every startup
- Track per-distro reconciliation against launcher target + app
version so unchanged registrations aren't re-probed each boot
- Replace command install with a single atomic rename instead of a
move-based backup, closing a window where no orca-ide exists
- Remove the registry invalidate-on-persist-failure path now that
writes are atomic and repair is disk-authoritative
- Add brief negative caching to WSL distro discovery to bound
wsl.exe respawns on persistently broken hosts
- Extract keyed-promise-queue as a shared serialization primitive
* fix(orchestration): bind lifecycle sends to dispatched pane
* fix(orchestration): bind injected worker messaging
* Fix worker_done pane identity gating and rename SSH linear CLI files
- Lifecycle reconciliation now returns an explicit `rejected` action
(instead of `ignored`) for persisted sender_not_assignee markers, and
only treats a payload's `_orcaLifecycleRejection` as trusted state
when it exactly matches that reserved shape, preventing caller-
supplied markers from spoofing a rejected send as success.
- SSH legacy fallback CLI now fails closed with
`no_active_sender_terminal` when a worker_done/heartbeat send has no
resolvable sender identity, rejects mixed raw/structured payload
flags, and reconstructs structured lifecycle payload fields
(task/dispatch id, files-modified, report-path, phase) instead of
dropping them — extracted into ssh-remote-orchestration-send.ts.
- Renamed ssh-remote-linear-argument-error.ts to
ssh-remote-cli-argument-error.ts since RemoteCliArgumentError is now
shared beyond the Linear CLI.
* Add re-read logging for already-converted lifecycle rejections
Send-path reconcile converts worker_done/heartbeat rejections with a
no-op logger, so the coordinator's later re-read is the only chance
to surface the rejection message. Log it there instead of dropping it.
* fix(agent-status): surface Claude tool failures
* Fix compact sidebar hiding tool-failure errors behind stale tool name
Extract a shared clearActiveToolFieldsUpdate() helper and apply it to
Cursor's postToolUseFailure, Copilot's PostToolUseFailure/ErrorOccurred,
and Grok's post_tool_use_failure events, matching the existing Claude
behavior so the failure message surfaces instead of the last tool name.
* fix(grok): prefer auth.x.ai session and mention Grok in OSC52 toast
auth.json can hold multiple issuer keys; Object.values order could pick a
stale entry before the live auth.x.ai OIDC session. Prefer auth.x.ai keys
and fall back only when none exist. Also list Grok in the OSC52 blocked
clipboard toast so Grok TUI copy failures point at the same setting.
* fix(grok): harden preferred auth session selection
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
Co-authored-by: Orca <help@stably.ai>
* fix(ssh): isolate relay filesystem watchers
* Fix relay watcher fault-harness pid file and in-process fallback isolati
- Use exclusive ('wx') creation for the fault-harness pid file so a leaked
ORCA_WATCHER_CHILD_PID_FILE env var can't clobber an existing file, and
have the harness remove the file after reading a replacement pid.
- Force useInProcessVitestFallback to false in the relay watcher pool so a
leaked VITEST env var can never load the native watcher addon in-process
on the relay; fail closed instead when the isolated child is missing.
- Thread an injectable RelayWatcherProcessPool into FsHandler/
RelayFilesystemWatchRegistry for tests, and add coverage for both fixes.
* Fix crash-isolated file watcher process pool for orca-serve SIGSEGV afte
Replace the worker-thread runtime file watcher with a forked, crash-isolated
@parcel/watcher child process pool so a native FSEvents fault can no longer
take down the main/serve process, and add bounded event batching, delivery
backpressure, and quarantine-based recovery for faulty watch roots.
* Fix crash-isolated file watcher teardown and shutdown leaks
- Fault harness could throw before mkdtemp/realpath completed, skipping
cleanup; now tracks each temp path independently and races an async
watcher-callback error so it can't escape the try/finally unhandled.
- In-process fallback swallowed unsubscribe failures via a bare rejection
handler that could still throw; use .catch() instead.
- Watcher process entry's cancel-subscribe handler now reuses the async
unsubscribe path when a crawl already finished, releasing the native
handle instead of leaking it (blocks worktree unlock on Windows).
- Runtime watcher process pool exposed no real dispose(); shutdown now
kills pooled children so they don't outlive the main process.
* Fix disposeSlot double-iteration bug in file watcher pool teardown
Remove the unnecessary array snapshot in dispose(): disposeSlot mutates
allSlots by deleting the slot being visited, and deleting the
in-progress element during Set iteration is well-defined, so the spread
copy was dead weight left over from prior debugging.
* Fix pending file watcher installs not aborting on unsubscribe
- Local/WSL watcher installs and SSH fs.watch setup now honor the
in-flight AbortSignal, so the last unwatch cancels a slow native
subscribe or remote setup instead of waiting for it to finish.
- Thread signal through IFilesystemProvider.watch and SSH-backed
file explorer watches for the same early-cancel behavior.
* Fix crash-resubscribe hangs and SSH watch teardown races in file watcher
- Add a bounded deadline for post-crash resubscription crawls so one
stuck root quarantines instead of pinning its whole shard forever.
- Report FSEvents overflow as recoverable so delivery continues after
a dropped-events error instead of surfacing as terminal.
- Make WSL watcher abort errors real DOMException instances so
AbortSignal-based cancellation checks recognize them.
- Rework SSH watch registration so ownership of the shared setup
request (not just the first caller) decides teardown, preventing
one caller's abort from cancelling another's shared watch and
guaranteeing exactly one fs.unwatch per registration.
- Reformat reliability-gates.jsonc arrays and refresh WSL/SSH coverage
entries and evidence runs to match the above.
* Add CI gate to run the file-watcher SIGSEGV fault harness under Electron
- The reliability gate and release workflows (mac, Linux) previously only
exercised the crash-isolation harness under vanilla Node, which doesn't
catch runtime differences in the actual Electron binary that ships to
users.
- Adds an `ELECTRON_RUN_AS_NODE=1 pnpm exec electron ...` run of the same
harness alongside the existing Node run, so #8212's SIGSEGV-survival
contract is proven against both runtimes before packaging.
* Add CI gate blocking Linux/macOS release packaging on watcher fault reco
Adds a contract test asserting release-cut.yml and release-mac-build.yml
run the runtime-file-watcher-fault-harness after building and before
publishing artifacts, so a regression in watcher process fault recovery
fails release packaging instead of shipping silently.
* Fix use-after-clear crash in failAllWatcherSubscriptions
Snapshot the records map before iterating, since onTerminalError
hooks can dispose the supervisor and clear `records` mid-loop,
causing a crash. Also update the matching test to assert against
the shared buildParcelWatcherIgnoreOptions helper instead of a
loose arrayContaining match.
* Fix use-after-clear crash in failAllWatcherSubscriptions
Snapshot watcher records with Array.from instead of spread, since
spread syntax over an iterator that's mutated mid-loop by
onTerminalError hooks can produce inconsistent results.
#7598 pushed client-ui.ts to 301 lines, one over the max-lines limit, which
breaks repo lint for every PR that merges current main (verify does not run
on main pushes). Follow the existing browser-schemas.ts/computer-schemas.ts
pattern: zod schemas move to a sibling module, the method registry stays.
Co-authored-by: Brennan Benson <>
- Validate each remote path segment (name, symlink target, directory
entry) via assertSafeRemotePathSegment before it reaches the remote
filesystem, since Windows canonicalizes reserved device names and
NTFS streams in ways POSIX checks miss.
- Extract directory pre-scan/upload logic into
filesystem-import-ssh-directory.ts and add captureLocalUploadRoot to
detect a selected root swapped out from under an in-progress import.
- Add path-safety tests covering traversal, reserved Windows names,
and TOCTOU root replacement.
* fix(pr-comments): let users mark comment authors as bots for the Humans/Bots filter
Some review bots post from regular user accounts that defeat both provider
bot metadata and login heuristics, so their comments were misclassified as
human. Adds a persisted prBotAuthorOverrides setting with a "Mark author as
bot" comment action, applied consistently across desktop and mobile.
Fixes#7597
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix(pr-comments): address review feedback on bot-author overrides
- Cap sanitized prBotAuthorOverrides at 500 entries so malformed payloads
can't bloat GlobalSettings or slow comment classification
- Reuse the shared normalizePRCommentAuthorLogin in isBotPRComment on
desktop and mobile instead of duplicating the normalization inline
- Pass botAuthorOverrides from CommentRow to CommentMoreMenu instead of
re-subscribing per menu instance
- Re-fetch mobile bot-author overrides alongside each PR refetch so they
don't stay a stale one-shot snapshot for the whole session
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix(pr-comments): harden bot author override sync
* fix(pr-comments): bound and recover override updates
* fix(pr-comments): merge overrides from canonical settings
* fix(pr-comments): make bot override updates atomic
* fix(pr-comments): surface rejected bot overrides
* fix(i18n): translate bot override warning
* fix(i18n): translate bot author actions
---------
Co-authored-by: Dzmitry Bachko <dbachko@users.noreply.github.com>
Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Brennan Benson <brennanbenson@Brennans-MacBook-Pro.local>
* Surface the Pi CLI's real error when branch auto-naming and commit generation fail
Pi failures (missing provider credentials, HTTP 4xx/5xx, connection errors)
previously collapsed to the generic 'Pi CLI command failed with code 1.'
because extractAgentErrorMessage only recognized Error:-prefixed lines.
Add two stderr-only extraction passes for pi's failure formats and narrow
the unix-path redactor so pi's /login remedy token survives redaction.
Fixes the OP variant of STA-1492 (#7808).
* Replace per-CLI failure parsing with sanitized raw output excerpts
Every agent CLI formats errors differently, and the parsing passes only
ever covered the vendors someone had already debugged (the pi passes
fixed pi and nothing else). Show the output itself instead: a positional
excerpt (first two non-empty lines plus the last one) of stderr, falling
back to stdout when stderr is silent, path-redacted and capped as
before. Exit-0 runs with empty stdout now stay 'returned an empty
<result>' instead of misreporting a command failure. The sanitizer also
strips Cf characters (bidi overrides) now that provider-controlled
bodies flow through verbatim, and OSC sequences are stripped alongside
CSI.
* Keep the full CLI output of failed branch-name generation for on-demand viewing
The persisted rename-failed badge carries only a sanitized excerpt (it
syncs to paired clients), so the complete diagnosis was previously
buried in the main-process log. Failed generations now capture their
full stdout/stderr (bounded to 64 KiB per stream, head and tail) into a
main-memory-only store keyed by worktree — never persisted or synced.
The rename-failed dialog fetches it on demand and shows it in place of
the excerpt, ANSI/control/bidi-stripped; after a restart or on paired
web clients it falls back to the excerpt. Renderer-bound generation
results explicitly drop the capture so IPC payload shapes are
unchanged.
* Cover the rename-failed dialog's full-output fetch and excerpt fallback
* Split the folder-workspace title rename flow into its own module
first-work-branch-rename.ts sat at the max-lines ceiling; the folder
title flow is a self-contained concept and main's concurrent additions
to this file pushed the CI merge ref over the limit.
* Harden the failure-output capture and dialog against review findings
- Flatten bounded captures so V8 sliced strings no longer retain the
multi-megabyte parent stream in the capture map (128 MiB -> ~4 MiB in
a 32-entry probe).
- Require an OSC terminator and stop its char class at newlines so an
unterminated/boundary-truncated sequence can no longer swallow the
omission marker and diagnostic tail.
- Exclude stdout from the persisted branch-name failure detail (it can
echo the prompt into synced metadata); the full local-only capture
still keeps it for the dialog.
- Refetch and reset the dialog's full output when the persisted error
changes so an open dialog never shows or copies a stale run.
- Report signal-terminated generators (null exit code) as 'was
terminated before exiting' instead of 'code null'.
* Redact JSON-escaped Windows drive paths without breaking scheme URLs
Provider JSON bodies double backslashes (C:\\Users\\name), which slipped past the drive-letter redaction into the persisted, client-synced failure excerpt. Allow repeated backslashes only — a URL's :// must stay single so remedy links survive.
* Fix branch rename failure IPC re-registration
---------
Co-authored-by: Brennan Benson <brennanbenson@Brennans-MacBook-Pro.local>
* fix(orchestration): complete worker tasks and improve coordinator UX
* Fix orchestration lifecycle sender resolution and peek/check compat hand
- Lifecycle sends (worker_done/heartbeat) now use ORCA_TERMINAL_HANDLE
verbatim, skipping the liveness probe and pane remint that could
block delivery during restarts or mismatch stale-runtime assignee
handles.
- --peek now round-trips as {peek:true, unread:false} so older runtimes
that strip unknown params degrade to non-destructive "all" instead of
mark-read, with client-side filtering to restore peek semantics and a
clear error when --peek --wait can't be honored.
- Reject combined read-mode flags (--unread/--peek/--all) before calling
the runtime.
- Distinguish suppressed (already-consumed) lifecycle messages from
ignored ones so send doesn't wake --wait waiters for stale heartbeats.
- Fix task summary truncation to avoid splitting UTF-16 surrogate pairs
and to not misreport whitespace normalization as truncation.
* Add shared helper to abbreviate orchestration task specs for brief listi
- Normalizes whitespace and caps spec length at 160 chars, flagging
truncation separately from whitespace-only changes
- Truncates on UTF-16 code point boundaries to avoid splitting
surrogate pairs and emitting malformed strings
* Add pane-key identity to worker_done/heartbeat reconciliation and server
- Records the sender's pane key on messages and dispatch contexts so
worker_done/heartbeat ownership can be verified by the remint-stable
pane leaf instead of the terminal handle, which is reissued across
restarts.
- Rejects lifecycle messages from a genuinely foreign pane while still
tolerating handle remints, tab break-outs, and older CLIs that lack
pane identity.
- Moves task-spec abbreviation server-side (orchestration.taskList
--brief) so full specs no longer cross SSH/relay transports, with a
client-side fallback for older runtimes; consolidates the shared
abbreviation helper under src/shared.
- Adds a stderr warning when a pre-peek runtime's --peek response hits
the 100-row cap, since older unread messages may be missing.
* Isolate ORCA_PANE_KEY in CLI test beforeEach to fix leaked senderPaneKey
Co-authored-by: Orca <help@stably.ai>
* Fix pane-key remint bypassing dispatch mutual-exclusion lock
- Dispatch locking only matched on assignee_handle, so a reminted
terminal handle (tab break-out) could open a second concurrent
dispatch on the same pane.
- Add leaf-UUID-based pane key comparison (parsePaneKey) as a
secondary lock, falling back to exact handle match for legacy
rows without pane keys.
* Update orchestration skill docs for lifecycle authority and CLI flag add
- Clarify that dispatch lifecycle is tied to taskId+dispatchId verified against
the dispatched pane, not the terminal handle, since handles can be reminted
after restart
- Document new `check --peek`/`--all` and `task-list --brief` flags, with
fallback guidance for older CLIs that reject them
- Note that a valid worker_done auto-completes the task/dispatch, so workers
shouldn't also call task-update manually
---------
Co-authored-by: Orca <help@stably.ai>
* fix(settings): make WSL skill commands pasteable (#7795)
* Fix WSL skill commands so PowerShell 7 pastes match PowerShell 5.1 argv
- Encode the WSL login-shell script as base64 and decode/eval it inside
the sh -c invocation, avoiding raw nested quotes at the paste boundary
- Scope $PSNativeCommandArgumentPassing = 'Legacy' to the invocation so
PS 5.1 and PS 7 both hand wsl.exe the same escaped argv
- Extract powershell-native-argument.ts as the shared quoting module and
reuse it from ssh-remote-powershell.ts
* test(runtime): stub getRepo in mobile-tab startup cwd test
Main's #7892 made listMobileSessionTabs validate selectors via
this.store?.getRepo; the mock store here only defined
getWorkspaceSession, so the merged CI build threw 'getRepo is not a
function'. Return null (wt-1 is a worktree id, not a repo).
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
* fix(codex): share global AGENTS.md into managed runtime homes
Orca launches Codex with a managed CODEX_HOME, so host and WSL sessions otherwise lose the user's global instructions. Share AGENTS.md through the existing ownership-aware link-or-copy path, with a narrow WSL launch sync that avoids copying unrelated resource directories.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(codex): copy global AGENTS.md into WSL runtime homes
Symlinking across the \wsl.localhost 9P boundary stores a Windows UNC
target the distro cannot resolve, so the global AGENTS.md silently would
not load inside WSL when the host symlink succeeded. Copy it like the
config mirror already does across the same boundary.
Co-authored-by: Orca <help@stably.ai>
* Fix WSL global-instruction sync to dereference symlinks and skip redunda
- Copy AGENTS.md contents (not the symlink) into WSL runtime homes, since
a host-side symlink is unusable inside the distro
- Skip rewriting the fallback copy when source contents are unchanged,
avoiding needless writes across the UNC boundary on every Codex launch
- Handle non-regular-file sources and malformed/undeletable marker
directories without blocking sync or stranding stale instructions
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
* feat(ssh): support file transfers over system ssh
* fix(ssh): harden system file transfers
* test(runtime): stub getRepo in headless mobile tab cwd test
The mobile-session selector validator (getValidatedExplicitWorktreeIdSelector,
from main) calls this.store?.getRepo to reject repo ids passed as worktree ids.
The store stub only implemented getWorkspaceSession, so the guarded call threw
'getRepo is not a function' once main merged into this branch. Add a getRepo
that returns null (wt-1 is a worktree, not a repo).
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
On Linux the CLI installs as orca-ide so it never shadows /usr/bin/orca
(GNOME's screen reader), but agent-facing surfaces (orca-cli skill,
dispatch preambles, CLI hints) all invoke bare `orca` — so on stock
Ubuntu an agent inside an Orca terminal launched the screen reader,
which started speaking (#7904).
Fix: prepend a userData-scoped shim dir (bare `orca` -> bundled
orca-ide launcher, or the stable AppImage) to the PATH of every
packaged-Linux managed PTY, mirroring the existing dev-mode cli/bin
prepend. The user's own shells — and their real screen reader command —
stay untouched. Also flip the orca-cli skill probe to prefer orca-ide
so agents outside Orca terminals never execute the screen reader.
Fixes#7904
Co-authored-by: Orca <help@stably.ai>
* Fix Codex WSL project trust conflating case-distinct Linux paths
normalizeCodexProjectPathForLookup lowercased the entire Windows/UNC
path, including the case-sensitive Linux portion under \\wsl$\<distro>.
Two distinct WSL project dirs (.../Repo vs .../repo) collapsed onto one
trust key, and the mirror dedupe (codex-config-mirror) inherited it.
Preserve case for the Linux path after the case-insensitive
\\wsl$\<distro> / \\wsl.localhost\<distro> share prefix; true Windows
drive letters and normal UNC shares still case-fold as before.
Co-authored-by: Orca <help@stably.ai>
* Apply the same WSL case-fold fix to hook-trust key lookup
normalizeHookTrustKeyForLookup had the identical latent bug: on a win32
host it lowercased the whole Windows-shaped path, folding the
case-sensitive Linux tail of a \\wsl$\<distro> UNC hook path — contrary
to its own comment that WSL sources stay case-sensitive.
Extract foldWindowsCaseInsensitivePath (shared with the project-path
normalizer): fold only the case-insensitive drive/\\wsl$ share prefix,
preserve the Linux tail. Existing suffix (event:group:handler) is already
lowercase, so behavior is unchanged there.
Co-authored-by: Orca <help@stably.ai>
* Fix Codex WSL trust revocation and hook-key case folding
- Case-drifted or share-spelling-varied WSL revocations were being
ignored on merge, letting stale "trusted" entries survive; fold
revocation lookups fully so matching errs toward revoked.
- Hook-trust key folding was gated on process.platform === 'win32',
so WSL/SSH-remote hook sources weren't folded when Orca itself ran
on macOS/Linux; fold by path shape instead, via the new shared
foldWslUncPathCaseInsensitiveParts helper (also covers /mnt drvfs
automounts and wsl$/wsl.localhost share aliasing).
* Fix Codex WSL trust key case-folding regressions
- Don't fold case-variant `/MNT` dirs as if they were the drvfs
automount; only literal lowercase `/mnt/<drive>` folds.
- Preserve an exact-cased trusted project entry in ~/.codex during
runtime merge instead of letting a loosely-matched, case-drifted
revocation clobber a user's re-granted trust on every mirror pass.
- Minor cleanup: inline foldWindowsCaseInsensitivePath and hoist the
repeated normalizeHookTrustKeyForLookup call in findTrustBlockRanges.
* Refactor case-fold WSL trust test to assert real serializer output
Extract path variables and assert against escapeTomlString(incomingPath) instead of a hardcoded escaped string literal, so the fixture can't silently drift from the actual TOML header serialization.
---------
Co-authored-by: Orca <help@stably.ai>
* fix(runtime): explain full worktree id selectors (#7432)
* Fix full worktree id selectors for bare repo ids and doc guidance
- Reject bare repo-id selectors up front via a shared validator instead
of relying on worktree-list scanning, so RPC callers no longer trigger
an unnecessary rescan just to detect the mistake
- Propagate the structured worktree_id_requires_full_path code through
RPC error mapping so callers get a typed error, not just a message
- Update orca-cli, orca-emulator, and orchestration skill docs to show
the full `<repo-id>::<path>` id shape and stop implying a bare repo
id is a valid worktree selector
---------
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Pi awaits its extension event handlers, so an awaited loopback status
post that stalls (Orca restarting / receiver unavailable) blocked the
running Pi turn and disconnected it. Make post() fire-and-forget with a
latest-only pending slot drained by a single active request and a 1s
AbortController timeout, so a stalled receiver can never hold the turn.
Also stop treating session_shutdown as turn completion: Pi emits it on
reload/new/resume/fork while the PTY stays alive, so only agent_end
proves done (real exit is cleared by PTY teardown). Split the generated
handler registrations into agent-status-handler-source.ts.
Co-authored-by: Orca <help@stably.ai>
* docs: design Grok orchestration group
* docs: plan Grok orchestration group implementation
* fix: add Grok orchestration group
* test(orchestration): accept Windows skill newlines
* Fix @grok orchestration group matching and remove stale planning docs
- Reuse the shared buildAgentNameRe matcher in groups.ts instead of a
divergent local regex, so orchestration groups honor the same
Windows launcher-suffix rule (grok.exe/.cmd/.bat/.ps1) as the rest
of Orca's agent-title detection.
- Add test coverage for real Grok OSC title shapes (spinner-collapsed,
session titles) and Windows launcher-suffix titles.
- Delete the now-completed design and implementation-plan docs for
the Grok orchestration group work.
---------
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
* fix: host browser popups in an Orca origin-bar window instead of chrome-less child windows
A guest-opened popup previously became a default Electron child window
with no address bar, so users could not verify a popup's origin — a
phishing surface flagged in the prod-release scan of #7392. The reverted
#8332 tried gating on disposition, which is bypassable and breaks
featureless window.open() OAuth flows.
Instead, keep hosting popups in-app (preserving the shared session
partition and live window.opener handle OAuth depends on) but build the
child window ourselves via setWindowOpenHandler's createWindow callback:
a BaseWindow with an Orca-controlled origin-bar WebContentsView on top
and a content WebContentsView that adopts the pre-created popup
WebContents. The bar shows only the destination origin (never path or
query), updates on navigation, and flags plain http to remote hosts.
Also pins secure webPreferences (contextIsolation, no nodeIntegration,
sandbox, no webviewTag) on popup children via
SAFE_POPUP_WINDOW_OPTIONS, attaches guest policies to popup contents
directly (did-create-window does not fire for createWindow children),
emits the existing opened-in-orca renderer notice, and closes popups
with their opener guest.
Co-authored-by: Orca <help@stably.ai>
* fix: show page title in popup title bar instead of doubling the origin
The native title bar and the origin bar both showed the origin, reading
as a doubled header. Match Chrome popup behavior: title bar shows the
page title (reset to origin on navigation so a stale title cannot
outlive its origin); the origin bar below remains the unspoofable trust
surface.
Co-authored-by: Orca <help@stably.ai>
* fix: elide the start of long popup origins so the registrable domain stays visible
Adversarial review findings on the origin bar:
- The bar right-ellipsized long hostnames, hiding exactly the part that
matters: window.open('https://accounts.google.com.<...>.evil.com',
'', 'width=360') rendered as 'https://accounts.google.com.signin.s…'.
Clip from the left instead (rtl container + isolated ltr bdi so host
and port keep normal character order), matching how Chrome elides.
- Re-assert the origin on the popup's did-finish-load so a transiently
dropped executeJavaScript write cannot leave a stale origin up.
- Re-pin view layout on enter/leave-full-screen: verified at runtime
that HTML5 fullscreen keeps the bar visible on macOS (resize fires);
the explicit events make that hold on every platform.
- Test hardening: fake WebContents now flips isDestroyed after
'destroyed' so the double-close guard is actually exercised.
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Orca <help@stably.ai>
* fix(preflight): reject Windows paths from WSL lookup
WSL agent discovery previously treated path.win32 absolute results as
valid guest paths, so a Windows absolute path like C:\spoof could be
counted as a found agent. Only POSIX absolute paths are valid inside WSL.
* docs(preflight): explain WSL path boundary
* docs(preflight): correct WSL path rejection rationale
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
Once quitAndInstall commits, Squirrel's ShipIt (and the Win/Linux
installers) wait for the old app process to exit before replacing the
bundle and relaunching. The quit path defers exit behind unbounded async
teardown (daemon checkpoint RPCs at 30s each per session, SSH
disconnects, watcher/emulator shutdown); if any of it wedges, the app
looks closed but the process survives, ShipIt stalls, and the update
never applies — 'Check for updates closes the app but never relaunches'.
Arm a 20s unref'd exit watchdog at the exact install-commit point (where
recovery is already forbidden) and disarm it on pre-commit recovery, so
the old process is guaranteed to exit and the installer can relaunch.
Fixes#4438
Co-authored-by: Orca <help@stably.ai>
Rich editors reconcile browser editing transactions, while direct value/textContent writes can leave framework state stale. Classify explicit contenteditables through the requested agent-browser target, keep native fill and clear behavior for plain controls, and perform rich replacement or clear as one target-focused eval over stdin. Fail when the browser editing command is unavailable instead of presenting stale DOM state.
Preserves input/change behavior and spinbutton handling for standard fields. Verified against Draft.js and ProseMirror model state plus focused-target clear regressions.
Co-authored-by: Wolfgang Schoenberger <221313372+wolfiesch@users.noreply.github.com>
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
* Fix F3: use known-tag predicate for explicit-turn decisions
isHarnessInjectedUserTurnText matches any multi-word kebab tag, which is
safe for non-destructive UI classification but wrong for state-transition
decisions: a real prompt starting with a custom <my-element> paste looked
like machinery in resolvePrompt/hasExplicitUserPrompt. Combined with the
post-interrupt working-suppression in agent-hooks/server.ts, that left the
agent visibly done after Ctrl+C. Switch both explicit-turn callsites to the
known-tag isKnownHarnessInjectedUserTurnText predicate; known harness tags
and Grok <user_query> behave unchanged.
Co-authored-by: Orca <help@stably.ai>
* Retire broad harness-tag matcher; unify on known-tag predicate
The broad isHarnessInjectedUserTurnText had one remaining consumer: session
title selection in session-scanner-primary-parsers.ts. Switch it to the
known-tag predicate too — a real first turn that pastes a custom <my-element>
now titles the session instead of being demoted to the meta (fallback) title.
All observed first-turn machinery (system-reminder, caveat, command-name,
task-notification, …) is already in the known list, so the only behavior
change is that unknown, uncatalogued kebab tags stay user turns. Delete the
now-unused broad predicate and fold its coverage into the known-predicate
tests.
Co-authored-by: Orca <help@stably.ai>
* Fix bare <channel> tag being misclassified as harness machinery
Only the attributed `<channel source=…>` form is emitted by the harness;
a bare `<channel>` is legitimate user-pasted RSS/XML content. Remove
'channel' from the known-tag set (which matched any <channel ...>) and
rely solely on the existing '<channel source=' prefix rule.
---------
Co-authored-by: Orca <help@stably.ai>
- gh file-fetch failures (rate limit, auth, unresolved remote) previously
returned an empty array, which the Files tab rendered as "No files
changed." — indistinguishable from a real empty PR
- getPRFiles now returns null on failure; work-item-details surfaces this
as filesUnavailable so GitHubItemDialog and PullRequestPage can show a
retry action instead of a misleading empty state
* feat(status-bar): notify upgraded users usage meters show % used
Show a one-time status-bar callout when upgraded profiles still use the
new percent-used default. Brand-new profiles and users who already chose
remaining stay quiet; dismissing or changing the setting is permanent.
* Add settings deep-link to expand Appearance's Window accordion for one-s
- Replaces the searchQuery-based redirect (fragile, flashed filter UI) with a
dedicated appearanceAccordionDeepLink store field that force-opens the
correct accordion and scrolls to the target row
- Rebuilds the status-bar change notice as a plain elevated card instead of
a Popover, since PopoverContent's glass/backdrop-filter defaults fought
the opaque callout styling and needed heavy overrides
- Simplifies the light/dark card CSS tokens accordingly
* Reposition status-bar usage-change notice via fixed-position portal
Portal the one-shot callout to document.body with fixed positioning
anchored via getBoundingClientRect, since the status-bar's overflow-hidden
flex ancestors clipped or mispositioned the previous in-tree absolute
layout.
* Refine status-bar usage notice styling and test coverage
- Replace hand-tuned light/dark card colors with existing design
tokens (--popover, --border) and the documented floating elevation,
so the callout stays in sync with the design system instead of
duplicating its own palette
- Add tests covering dismiss via X button, "Got it", and Escape
- Scope the foreground-process confirm assertion to the pane's ptyId
so an unrelated pane's delayed confirm can't cause a false failure
* fix: authorize guarded sends to active ConPTY agents
* Fix stale-handle rejection using controller-less status checks and contr
- Distinguish `terminal_handle_stale` (PTY rebind mid-check) from `terminal_exited`
so callers get an accurate reason instead of a generic `terminal_not_writable`.
- Move the PTY binding assertion to always run after the fresh foreground read,
even when no controller is present, closing a gap where a rebind during a
controller-less check went undetected.
- Call `confirmForegroundProcess` through its owning controller instance so the
method keeps its `this` receiver instead of being invoked unbound.
- Update reliability gates and design doc to reflect the new test coverage and
corrected error semantics.
* rm design doc
* fix(naming): lead workspace and tab names with the work-item identifier
Auto-generated workspace names, branch-rename display names, and tab
titles now lead with the referenced PR/MR/issue/ticket — e.g.
`PR 1033 - Review` instead of a paraphrase like "Review community pr
1094" that buried or dropped the number. Identifiers are the highest-
signal, most searchable token, so leading with them makes the sidebar
and tabs scannable and disambiguates same-verb work items.
- New shared `work-item-reference.ts` extracts the identifier from the
raw prompt: URLs are validated by path structure (owner/repo/pull/N,
GitLab's `/-/` marker) so GitHub Enterprise / self-hosted GitLab still
resolve while stray `/pull/<n>` paths (CDN, docs) do not; a ticket-
prefix denylist keeps `SHA-256` / `UTF-8` / `ISO-8601` from being read
as Jira/Linear keys.
- Reconciles the existing create-from-work-item naming (was action-first
`Review PR 1033`) with the first-work auto-rename onto one identifier-
first format via a shared `formatIdentifierFirst`, so the two paths
can't drift.
- Fixes a pre-existing tab-title bug where markdown punctuation was
stripped before URLs, splitting a GitLab `merge_requests` URL at its
underscore and leaking "requests" into the title.
* fix(naming): keep emphasis-wrapped URLs intact in generated names
The URL/markdown strip reorder left the tab-title URL strip anchored on
\b, which fails when a URL is wrapped in markdown emphasis (_...pull/5_)
and leaked URL fragments into the title. Drop the \b anchor and trim
trailing markdown emphasis (*_~) in the URL identifier parser, keeping
interior underscores (merge_requests) intact. Adds regression tests.
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
Add a persisted Appearance setting that switches provider usage labels between percent used and percent remaining while keeping meter fill consumption-based.
Cover desktop and web persistence, current providers, settings search, localized copy, and regression tests.
Co-authored-by: gatsby74 <166927047+gatsby74@users.noreply.github.com>
* Surface a friendly error when Windows blocks the user PATH write
When HKCU\Environment is locked (Group Policy / restricted ACL / EDR-managed
environment), the PowerShell SetEnvironmentVariable write rejects with a raw
'Command failed: powershell ...' error (often mojibake-localized) that the CLI
Settings toast displayed verbatim. Detect the permission failure via the Latin
PowerShell error-envelope tokens and rethrow an actionable message naming the
exact folder to add to PATH manually, chaining the original error on cause.
Fixes#6412
Co-authored-by: Orca <help@stably.ai>
* Tighten Windows PATH permission detection
---------
Co-authored-by: Orca <help@stably.ai>
* fix: resolve daemon shell before spawning
* fix(daemon): reconcile shell-ready barrier with the resolved fallback shell
The adapter computes shellReadySupported from the preferred shell before
spawn. When the daemon falls back (e.g. to /bin/sh), the session would
queue startup commands for the full 15s ready-marker timeout and wrap
multiline prompts in bracketed-paste markers the fallback shell cannot
parse. Expose the spawned shell on SubprocessHandle and downgrade the
barrier and paste wrapping in TerminalHost when the actual shell cannot
emit the marker.
Also apply the Unix shell fallback after the relay env scrub so a
scrubbed SHELL cannot drop the corrected value, and cover the resolve-
before-launch-config ordering, the no-shell throw, and candidate dedup
with tests.
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Vitus Apollo <zhzvitus@gmail.com>
Co-authored-by: Orca <help@stably.ai>
* fix(rate-limits): debounce auto-refresh off last successful fetch
A failed post-update/relaunch fetch stamped the single debounce
timestamp the same as a success, so refreshIfWindowActive() no-oped
every focus/show/restore and the deferred-startup retry for the next
5 minutes — leaving the status bar stuck on "Refresh failed" until the
15-minute background poll (which on Windows also requires the relaunched
window to be focused).
Key the debounce off the last SUCCESSFUL read instead: only stamp
lastSuccessfulFetchAt when a provider returned fresh 'ok' data, so a
failed fetch no longer suppresses auto-recovery on the next window
activation. Removes the now-unused lastFetchAt field.
Fixes#5355
Co-authored-by: Orca <help@stably.ai>
* fix(rate-limits): narrow active refresh retries
* fix(rate-limits): refresh after live Claude credential handoff
* fix(rate-limits): cover antigravity provider in active refresh plan
Co-authored-by: Orca <help@stably.ai>
* refactor(rate-limits): compile-enforce active-refresh provider coverage
Co-authored-by: Orca <help@stably.ai>
* fix(rate-limits): keep durable non-Claude/Codex error retries on the 5-minute full-fetch cadence
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Orca <help@stably.ai>