Commit Graph
8614 Commits
Author SHA1 Message Date
Brennan Benson 580f8eb492 feat(status-bar): consolidate agent usage into a single roster popover (#8761)
* feat(status-bar): consolidate agent usage into a single roster popover

The footer usage cluster is now one quiet, borderless control: each agent
shows its tightest window as neutral text (letter badges at icon-only
width), and clicking anywhere opens a consolidated Usage popover listing
every agent worst-first — icon, name, plan, soonest reset, and per-window
threshold-colored bars. Claude/Codex rows drill into their existing
account switcher, runtime toggle, and Codex reset credits via a submenu
variant of ProviderDetailsMenu; all other providers drill into their
detail panel. Row actions and the footer links route to Settings.

Also: barColor's <60% band goes green -> neutral so color is reserved for
approaching limits (threshold color now lives only in the popover, the
always-visible bar stays monochrome), Codex plan_type is surfaced as
ProviderRateLimits.planType for the "Codex · Plus" label, and the Fable
weekly window is labeled "Fable" so it no longer collides with "wk".

* fix(status-bar): single-line sign-in row + review dedup

Signed-out roster rows now read as one line (name · "not signed in" ·
right-aligned Sign in) instead of a floating button over an orphaned
second line. Review follow-ups: the non-submenu ProviderDetailsMenu
branch reuses the extracted panelBody, and the icon-only letter badge is
one shared component so its has-data dot condition can't drift.

* fix(status-bar): harden usage roster interactions

* chore: remove unrelated formatting change

* chore(skills): refresh bundle manifest for rc.2

* fix(status-bar): preserve usage roster semantics

* fix(status-bar): preserve roster menu interactions

* chore: remove unrelated formatting changes

* fix(status-bar): keep usage reset countdowns live

* feat(status-bar): toggle compact usage summary

* fix(status-bar): simplify usage mode toggle

* feat(status-bar): replace usage footer toggle with Compact/Detailed segmented control

Swap the bottom-of-popover on/off switch for a SettingsSegmentedControl at
the top of the Usage popover (view-switcher pattern), so both modes are
named and discoverable on first open. Reuses the repo's canonical
Compact/Detailed vocabulary from the Workspace card-layout control.
2026-07-20 17:46:09 -07:00
Brennan Benson 3468b434d6 feat(dashboard): add agent dashboard popout (#9604)
* feat(dashboard): add agent dashboard popout

* fix(dashboard): gate and harden agent popout

* test(ipc): isolate dashboard handler registration

* fix(dashboard): drop diff status from bucket counts

* fix(dashboard): address review feedback

* perf(dashboard): ignore unrelated store churn
2026-07-20 17:11:23 -07:00
Brennan Benson 0ed1d04b3b fix(codex): migrate legacy shared-home sessions before resume (#9624)
* fix(codex): migrate legacy shared-home resumes

* fix(mobile): allow legacy Codex resume preparation
2026-07-20 17:10:07 -07:00
Brennan Benson bf7fbdd57b fix(codex): auto-trust linked worktrees before launch (#9621)
* fix(codex): trust linked worktrees before launch

* fix(codex): validate linked worktree trust roots
2026-07-20 16:37:49 -07:00
Brennan Benson fec996a548 fix(codex): preserve active account during reauth (#9620)
* fix(codex): preserve active account during reauth

* fix(codex): keep reauth validation authoritative

* fix(codex): scope reauth selection restoration
2026-07-20 16:25:15 -07:00
Brennan Benson c45dc62eaf fix(codex): flag signed-out system default (#9619)
* fix(codex): flag signed-out system default

* fix(codex): distinguish missing sign-in warnings

* test(codex): preserve system OAuth auth warning
2026-07-20 16:18:43 -07:00
Brennan Benson f655ae3929 fix(pi): resolve OMP tab identity from the outer wrapper, not the wrapped pi (#6364) (#9600)
* fix(pi): resolve OMP tab identity from the outer wrapper, not the wrapped pi (#6364)

OMP runs as a `shell → omp → pi` process tree and Orca recognizes both `omp`
and `pi` as distinct agents. The foreground-process readers scored the deepest
`+` foreground descendant, so they returned the wrapped `pi` engine instead of
the `omp` the user actually launched — and because the sampled `+` frame moves
between omp and pi across command boundaries, the read oscillated and the tab
icon/label flickered OMP↔Pi. It surfaced on Remote Host / SSH (only in a
worktree, only while working) where a mirrored/restored pane has no client-side
launchAgent to anchor identity, so the (host-read) foreground was the only
pi/omp signal — and it was wrong.

Fix the detection at its source so foregroundAgent is trustworthy everywhere
(local tab icon, exit detection, and the remote mirror):

- Add `resolveOuterWrapperForegroundProcess` (src/shared/foreground-wrapper-agent.ts):
  when the recognized foreground winner shares a `titleIdentityGroup` with a
  shallower recognized ancestor, return the shallowest — the outer wrapper.
  Stable regardless of which of omp/pi holds the foreground at the sample; bare
  Pi and cross-group reads (Codex, etc.) are unchanged.
- Wire it into both readers: local `agent-foreground-process.ts` and the SSH
  relay `pty-shell-utils.ts`. Also harden the relay's fallback short-circuit so a
  pi-compatible process reported by node-pty rescans for the omp wrapper instead
  of trusting `pi` raw (never downgrading below the fallback).

Also unify the host-side publish path onto the shared owner resolver: the mobile
session snapshot builders now resolve pane ownership via `resolvePaneAgentOwner`
({launchAgent, hookAgent}) instead of a bespoke `launchAgent ?? foregroundAgent`,
so the host stops being a divergent fourth identity path and the hook's
host-stamped identity survives when launchAgent is dropped on a mirrored pane.

Tests: unit coverage for the wrapper resolver, `shell→omp→pi` ps trees for both
readers (outer omp, oscillating-frame, bare pi, node-pty pi fallback), and a
host-snapshot regression that keeps an OMP hook labeled OMP when the wrapped pi
child owns the foreground.

* fix(pi): harden wrapper foreground ownership

* fix(pi): preserve OMP ownership in daemon foreground reads

* fix(pi): retain OMP owner when process scans fail
2026-07-20 16:16:34 -07:00
Brennan Benson 58293445e1 perf(status-bar): guard usage-notice anchor against equal-geometry re-render churn (#9615)
The UsagePercentageDisplayChangeNotice callout re-measures its fixed anchor
on every ResizeObserver/resize delivery and calls setAnchorPosition with a
fresh object each time. Equal-geometry deliveries (the common case) still
churned a re-render apiece because the object identity changed.

Bail when bottom/left are unchanged so React short-circuits before commit.
Measured: 20 identical-geometry deliveries → 20 re-renders before, 1 after.

This is defensive robustness/perf hardening for the status-bar subtree that
surfaced in crash f75ed81b (React #185 in overlay.status-bar). It is NOT a
proven fix for that crash — the reproduced #185 loop lives in radix Popper
internals and is not unit-reproducible; this removes one churn source feeding
that subtree.
2026-07-20 15:57:48 -07:00
Brennan Benson d19cb8bc47 feat(codex): remove "Not now" from account-switch notice (#9613)
* feat(codex): drop "Not now" collapse from account-switch notice

The Codex account-switch overlay had a "Not now" button that collapsed
the loud dialog into a corner chip via component-local state. Remove the
button and all of its collapse state: the useState/useEffect, the
CollapsedRestartChip, and the now-dead collapse helpers.

buildCodexRestartNoticeKey still backs the focus effect, so the module is
renamed codex-restart-overlay-collapse.ts -> codex-restart-notice-key.ts
and its tests trimmed to match. The orphaned "Not now" i18n key is pruned
from all locale catalogs.

* test(codex): lock restart notice actions
2026-07-20 15:44:03 -07:00
e218dfbf8b fix:Keep Jira linked work items attached when the composer project changes (#7770)
* Keep Jira linked work items attached when the composer project changes

Repo/project switches in the new-workspace composer cleared every linked
work item except Linear, so starting a workspace from a Jira task and then
picking the actual implementation project silently dropped the ticket link.
Route all three switch paths through a shared isRepoScopedLinkedWorkItem
predicate: GitHub/GitLab sources stay repo-scoped and clear on a switch,
Linear/Jira issues stay attached.

* test(new-workspace): drop dead isLinearLinkedWorkItem, harden preserve-predicate coverage

Follow-up to the Jira-preservation fix (review findings):
- Remove isLinearLinkedWorkItem: no production consumer remains after all three
  composer switch paths route through shouldPreserveWorkspaceSourceOnRepoChange.
- Pin the clear cases in workspace-source.test.ts (GitLab explicit + inferred,
  null) that both delegating paths depend on, not just GitHub.

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

---------

Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
2026-07-20 15:05:23 -07:00
OrcaWinandBrennan Benson e109e78ebf fix(source-control): keep huge change sets responsive (#9477)
* fix(source-control): keep huge change sets responsive

* Fix cancellation and retry handling for capped status

* Harden capped status for conflict-heavy repositories

* Harden capped status recovery and cancellation

* fix(source-control): preserve capped status correctness

* fix(source-control): translate submodule status at render time

---------

Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
2026-07-20 14:50:12 -07:00
Brennan Benson 658532a1b0 Stop the mobile app from running hot during terminal streaming (#9489)
A busy PTY delivers up to ~200 terminal frames/s to the phone (the desktop
coalesces output at a 5ms window), and each frame paid a full RN-bridge +
WebView postMessage + WebKit IPC + xterm write + paint pipeline. Coalesce
stream writes in the RN layer: leading-edge immediate delivery keeps
keystroke echo instant, and sustained streams batch into at most ~21
WebView messages/s (48ms trailing window).

Measured (iOS Simulator A/B at ~200 lines/s, only this file flipped):
terminal WebContent CPU 8.0% -> 2.8%, app process 19.3% -> 15.2%,
combined continuous CPU -34%.

Ordering boundaries preserve today's semantics: resize/reflow flush
pending bytes first; init/clear drop superseded pre-snapshot bytes;
reload/content-process-termination/unmount clear the buffer. The
notification-dispatch extraction from TerminalWebView is a verbatim move
forced by the max-lines cap.
2026-07-20 14:45:58 -07:00
Brennan Benson 51aab1c793 fix(terminal): harden macOS login-preflight spawn path (#9301 follow-ups) (#9606)
* fix(terminal): harden macOS login-preflight spawn path (#9301 follow-ups)

Follow-ups to the #9301 two-pass review. Re-derived severities: none are
release blockers; these are low-likelihood robustness/observability fixes.

F1 macos-tcc-login-shell: only cache a conclusive PAM verdict. A probe our
own SIGKILL timeout / ETIMEDOUT / maxBuffer killed proves nothing about PAM,
so it no longer sticks the whole process into direct-spawn — it fails open
for that spawn, re-probes next time, and self-heals once PAM answers.

F2 daemon-entry: prepareMacosTccLoginShell returns the outcome; the daemon
emits a structured macos-login-preflight {ok, reason} log on degrade, since
detached daemons destroy stderr and lose the console.warn.

F3 local-pty-provider: an atomic guard before the tracking write coalesces a
concurrent same-session-id spawn onto the winner instead of orphaning an
untracked, unkillable PTY.

F4 daemon-server: preparations carry their clientId; a control-socket close
cancels only that client's pending preps, so a mid-preflight disconnect no
longer leaves a durable orphan daemon PTY.

F7 warm the PAM probe at daemon startup (mirrors warmPwshAvailabilityCache);
note the pipe-vs-PTY probe fidelity limit; test that kill with no pending
prep still surfaces SessionNotFoundError.

Deliberately skipped F5 (self-limits to next reload; a real fix needs
speculative in-flight-generation tracking) and F6 (inherent to legacy
v22 attach; self-heals once daemons are v23).

* fix(terminal): bound preflight recovery work

* fix(terminal): cancel preflight when daemon stream closes
2026-07-20 14:43:42 -07:00
Brennan BensonandOrcaWin e58de71f5e feat(codex): real-home routing + self-contained multi-account homes (#9501)
* feat(codex): backfill managed-home sessions into the real Codex home once per host

Orca-launched Codex sessions currently land only in the Orca-managed
runtime home, so the user's own `codex resume` picker and app history
never see them (#4444, #8612). Backfill the managed sessions tree into
the real ~/.codex/sessions/YYYY/MM/DD layout once per host:

- hardlink first (one physical rollout log), copy as the cross-volume
  fallback; existing target files are always skipped, nothing in either
  home is deleted or moved
- idempotent; per-file failures leave the completion marker unset so the
  next startup retries cheaply
- JSONL audit log of every link/copy/failure under
  <userData>/codex-session-backfill/
- honors the custom Codex session source home override, mirroring the
  existing system->managed bridge

WSL managed homes are distro-local and need an in-distro variant; that
is a follow-up.

* feat(codex): flag-gated system-default real-home routing scaffolding

Staged internal flag (default OFF, no settings UI): route the SYSTEM-DEFAULT
Codex account at the user's real ~/.codex instead of Orca's managed runtime
home. Flag OFF is byte-identical to today; managed (multi-account) selections
are unchanged in either state.

Routing (flag ON + host system default = no managed account):
- CodexRuntimeHomeService.prepareForCodexLaunch / prepareForRateLimitFetch
  return null so the PTY/env layer injects no managed CODEX_HOME and the
  rate-limit fetcher + auth-presence gate fall back to ~/.codex (the background
  poller stops spawning Codex against the managed home — the #5370 auth war).
- buildPtyHostEnv strips only a nested-Orca-inherited Orca-owned override
  (CODEX_HOME matching the private ORCA_CODEX_HOME marker), preserving a
  user-set CODEX_HOME. Shell-ready re-exports already no-op without the marker.
- The headless commit-message Codex path strips the same inherited override.

Hook install for the real-home lane (append-last into ~/.codex/hooks.json,
trust via the app-server client) lands with the trust plumbing; the managed
hook install is skipped for this lane meanwhile.

Credit @jellychoco (#8606) for the native-home routing direction.

Depends on the codex trust-rpc-grant plumbing for the real-home hook installer.

* fix(codex): strip the daemon-inherited Orca CODEX_HOME override for real-home routing

The daemon spawns PTYs from its own inherited environment and honors only
spawnOptions.envToDelete, so mutating the sparse env object was not enough to
strip an Orca-owned CODEX_HOME the daemon already carries. Add the strip to
envToDelete for both daemon host-spawn paths, preserving a user-set CODEX_HOME.

Verified live via CDP against a sandboxed dev instance (flag ON): an
Orca-spawned pane reports empty CODEX_HOME/ORCA_CODEX_HOME, so Codex resolves
its own ~/.codex. Adds daemon-path unit coverage (strip Orca-owned, preserve
user-owned, no-op when flag OFF).

* fix(codex): harden one-time session backfill

* test(codex): cover staged cross-volume install

* feat(codex): app-server trust-grant client, capability cache, and grant ledger

Short-lived codex app-server JSON-RPC client (hooks/list + config/batchWrite,
the same pair the Codex TUI 'Trust all' flow calls), run in a bundled
ELECTRON_RUN_AS_NODE entry so synchronous launch prep can block on it with a
hard deadline and guaranteed child reap. Capability cache modeled on
GitCapabilityCache, scoped per execution host (native vs each WSL distro),
with a narrow unknown-method/missing-subcommand unsupported predicate. The
grant ledger records verified grants so steady-state launches skip the RPC.

* fix(codex): grant managed hook trust via codex app-server RPCs in install/refresh

Host and WSL installs now grant trust for Orca's managed status hooks through
codex's own hooks/list -> config/batchWrite -> re-list verify, scoped to
exactly the managed entries; the previous computeTrustedHash lane is the
unchanged fallback for incapable/erroring CLIs. getStatus and the removal
paths recognize ledger-recorded codex hashes so drift between codex's real
algorithm and the replica no longer misreports or strands trust. SSH remote
install is untouched by design.

* test(codex): cover app-server trust grant client, cache, ledger, and lanes

* test(codex): cover commit-message real-home override strip/preserve

Adds the two cases for the headless commit-message Codex env under real-home
routing: a nested-Orca-inherited Orca-owned CODEX_HOME is stripped, and a
user-owned CODEX_HOME is preserved.

* test(codex): WSL grant-lane coverage — in-distro invocation and fallback parity

* feat(codex): real-home hook installer trusted via the codex app-server grant client

With the real-home flag ON and the system-default selection, install Orca's
status hook into the user's real ~/.codex before any pane spawns:

- entry APPENDED LAST per managed event: codex hook trust keys are positional
  (source:event:group:handler), so appending keeps every user entry's position
  and trust record intact; user entries and unknown top-level hooks.json fields
  are preserved verbatim
- trust is granted exclusively through the codex app-server client
  (hooks/list + config/batchWrite, verified by re-list); Orca never writes
  [hooks.state] into the user's real config.toml itself
- if the grant lane is unavailable (old binary, unsupported RPC, verify
  failure), the appended entry is rolled back byte-exactly and the host keeps
  the managed-home lane end to end (PTY env, rate limits, commit messages)
  via a lane gate on the runtime-home service
- one-time pristine backup of the user's hooks.json under Orca's userData;
  a rolling .bak sits next to the file (existing atomic writer)
- hook opt-out sweeps Orca entries from the real home and drops Orca-owned
  trust records; flag-off downgrade re-arms the existing legacy system-home
  sweep, which removes the entry and its trust keys cleanly
- the legacy system-home sweep is suppressed only while the real-home lane
  owns ~/.codex/hooks.json, so managed installs cannot delete the entry

* fix(codex): resolve the trust-grant entry without requiring electron

The grant bridge is reachable from plain-Node CLI entries, where the
plain-node entry guard rejects any chunk containing require("electron").
Resolve the bundled session entry from __dirname (root chunk and chunks/
layouts) with an app.asar -> app.asar.unpacked rewrite for packaged runs,
instead of electron's app path APIs.

* fix(codex): keep session backfill off main thread

Use asynchronous, sequential filesystem operations for the one-time rollout backfill, and avoid repeated target-directory probes. Treat inaccessible managed session roots as retryable failures instead of writing a false completion marker.

* fix(codex): harden app-server trust grant fallback

* fix(codex): install cross-volume session backfill copies atomically

On a real Codex home whose filesystem supports no hardlinks (exFAT/FAT,
some network mounts), the staged cross-volume copy was installed with a
non-atomic copyFile(..., COPYFILE_EXCL) straight into the final
rollout-*.jsonl name. An install interrupted mid-copy (app quit, crash,
ENOSPC during the deferred run) could strand a truncated rollout that the
next run then skips as already-present, defeating the staging design's own
guarantee that a failed copy never leaves a partial session behind.

Install the fully-staged copy with an atomic rename instead, guarded by an
existence re-check so it keeps the never-overwrite contract (and the rename
source is the same immutable managed rollout, so any clobber would be
byte-identical). Cover the no-hardlink-support target and an interrupted
install that must leave no partial in the user's sessions tree.

* fix(codex): resolve grant entry from __dirname so plain-node CLI entries stay electron-free

The build guard rejects any electron require reachable from plain-node
entries; the bridge now maps app.asar to app.asar.unpacked by string
replacement instead of consulting electron app paths. CLI typecheck project
lists the new trust-grant module graph.

* fix(codex): harden trust grant reconciliation

* fix(codex): restore trust config permissions on rollback

* fix(codex): harden real-home routing cleanup and retries

* fix(codex): preserve unicode trust RPC responses

* fix(codex): preserve remote env and complete real-home cleanup

* fix(codex): preserve real-home lane invariants

* test(terminal): isolate replacement idle reset assertion

* fix(codex): preserve real-home dotfile links

* fix(codex): preserve verified trust grants across launch prep

* fix(codex): preserve dangling config symlinks on rollback

* fix(codex): don't revoke a just-granted WSL home on a false 'missing' probe

The async wsl.exe canonical-path settlement could report the runtime home
'missing' immediately after a verified RPC grant (a false negative — codex
had just written and re-listed trust there), which drove the reconciliation
'remove' branch to delete all six granted [hooks.state] tables, leaving a bare
[hooks.state] the launching pane read as 'hooks need review'. A 'missing'
settlement now revokes only when no successful install ran this generation; a
genuinely moved home still resolves to a different path and reinstalls.

* test(codex): model codex config/batchWrite faithfully on Windows

The grant-lane stub simulated codex by calling Orca's upsertHookTrustEntries,
which writes both separator variants for a Windows key (a fallback-lane compat
shim real codex never does) — fabricating duplicate tables and whitespace the
RPC path never produces, so the byte-stable and no-duplicate assertions failed
on win32. Replace it with a single-variant, blank-line-separated writer that
matches the real 0.144.x binary's output.

* feat(codex): collapse duplicate session listings across Codex roots

Backfilled/bridged rollouts are hardlinked into both the real ~/.codex and
Orca's managed runtime home, so AI Vault listed each session once per root
(#7521). Dedup candidates by rollout file name pre-parse and parsed sessions
by session id post-parse, keeping the canonical root: host real home first
(unprefixed resume), then the managed runtime home, then other homes. Applies
to local, WSL, and SSH-remote scans.

* feat(codex): background sqlite index heal for backfilled sessions

Codex's own state-DB metadata backfill is one-shot, so rollouts hardlinked in
by Orca's session backfill never become visible to Codex's DB-driven surfaces.
Extract the app-server stdio JSONL transport into codex-app-server-session
(shared with the trust-grant client) and add a bounded, resumable background
pass that drives Codex's lazy indexing via thread/read per backfilled session:
recent-first, batched onto one short-lived server per batch with small
concurrency, ledger + marker so steady-state startups are a no-op, stop-aware
on quit, and capability-aware on CLIs without the app-server surface.

* fix(codex): preserve session identity during dedup heal

* fix(codex): preserve user trust during real-home cleanup

* fix(codex): harden real-home heal boundaries

* fix(codex): fail closed on unsafe backfill install

* fix: harden real-home hook cleanup

* fix(ai-vault): preserve execution boundaries and reap children

* fix(codex): narrow app-server unsupported detection

* fix(codex): bound user hook trust rebase retries per host

The rebase lane ran a codex app-server session on every launch prep while a
host was stuck (CLI without app-server support, or keys hooks/list cannot
match). Gate the transaction on the shared capability cache and add the same
5-minute transient cooldown the grant lane uses, so sweep and legacy-cleanup
retries cost plain fs reads instead of a codex session per pane spawn.

* fix(codex): enforce real-home resume and heal boundaries

* fix(codex): establish real-home lane before cleanup

* fix(codex): stop index heal before delayed spawn

* fix(codex): protect symlinked rolling backups

* fix(ai-vault): preserve resume env deletion through drag

* fix(codex): strip inherited Codex homes on mobile real-home resume

The mobile resume surface types a bare real-home codex resume into a
freshly created pane, but never asked for CODEX_HOME/ORCA_CODEX_HOME
deletion at pane spawn, so an agentDefaultEnv-pinned or daemon-inherited
Codex home rerouted the resume away from the user's real ~/.codex while
the same session resumed correctly on desktop. Share the deletion helper
from the AI Vault resume builders and forward it through the mobile
launch and session.tabs.createTerminal call.

* fix(codex): gate session migration on real-home lane

* fix(codex): stop session backfill after opt-out

* fix(codex): keep session heal failures retryable

* fix(codex): keep session migration state recoverable

* fix(codex): retry republished missing session heals

* fix(codex): preserve hook symlink trust path

* fix(codex): disambiguate POSIX trust paths

* fix(codex): align hook trust source paths

* fix(codex): harden trust grant lifecycle

* fix(codex): restore envToDelete on client invocation type after base reconcile

* test(codex): type child.stdout as PassThrough for oversized-output write

* Assemble RC: reconcile app-server transport API across PRs

Unify on the object RPC surface from the index-heal transport (#8921) while
preserving the default-home env strip (#8828) and the narrowed missing-app-server
capability signal (#8847): adapt the user-hook-trust-rebase consumer + tests,
port envToDelete stripping into the shared session, and route stderr
classification through the canonical capability-signal module.

* RC: enable system-default real-home routing by default (flag ON)

Flip codexSystemDefaultRealHomeEnabled to default ON for this RC's staged
rollout (a user can still opt out by setting it false, which stays byte-identical
to managed-home behavior). This is the only intended behavior difference between
the RC branch and the individual PRs. Updates the two tests that assumed the
prior OFF default.

* fix(codex): snapshot hooks.json bytes+parse in one read to close real-home clobber race

The install/sweep/legacy-cleanup paths parsed hooks.json, then did a separate
later read to capture the previous bytes for the pre-write generation guard.
A concurrent save (second Orca instance or the user editing the file) could
land between the parse and that second read and be silently overwritten.
readHooksJsonWithRaw returns the raw bytes and parse from a single read so the
guard compares against exactly what it parsed. Adds a regression test that
mutates hooks.json mid-RPC and asserts the sweep aborts without clobbering.

* fix(codex): sanitize managed account config trust

* fix(codex): guard OAuth add for custom providers

* fix(codex): persist outgoing managed tokens before real-home lane takeover (PR-C)

prepareForCodexLaunch returns null early for the real-home / system-default
lane before syncForCurrentSelection runs. If a managed account is still
recorded as synced when the selection has dropped to the system default
(nulled without a sync pass, or auto-deselect on missing managed auth), a
Codex-refreshed token stranded in the shared runtime home is never persisted
to its canonical per-account home -> token loss.

Read the outgoing managed account's refreshed token back before the real home
takes over. The real-home lane implies host === null, so running the
managed->system-default transition restores only Orca's runtime mirror from
~/.codex and never writes the real ~/.codex. It is a no-op once the selection
has already been reconciled, so the normal select path does not double-write.

* fix(codex): preserve refreshes across all default transitions

* feat(codex): show system-default/real-home account identity in switcher (PR-B)

The account switcher modeled the system-default Codex account as
activeAccountId:null with no identity fields, so the null row rendered
blank ("System default" / generic subtitle) even though its effective
login is whatever ~/.codex/auth.json currently is.

Add a CodexSystemDefaultIdentity descriptor {hasAuth, authKind, email,
providerAccountId, workspaceLabel} to CodexRateLimitAccountsState,
resolved live and READ-ONLY from ~/.codex by the accounts service and
returned from listAccounts()/getSnapshot(). The settings switcher now
renders the null (system-default) row as that real identity: the OAuth
email when signed in, "Custom provider — no usage tracked." for
env-key/custom-provider logins (auth.json with OPENAI_API_KEY, or an
OPENAI_API_KEY env with no auth.json), and the generic fallback when
signed out. Identity is host-scoped (per-distro WSL keeps the generic
label). Orca never writes ~/.codex; managed-account switches only touch
Orca-owned homes, so the system-default identity stays a stable,
displayed source of truth. Usage already routes to the real home via
getSystemCodexHomePath, so the switcher now attributes it to a real face.

Tests (sandboxed temp homes only): OAuth email/provider resolution,
api-key auth.json and env-key (no auth.json) as custom-provider,
signed-out, and select/deselect of a managed account never mutating
~/.codex/auth.json.

* fix(codex): parse multiline provider pins in OAuth guard

* fix(codex): harden managed trust sanitization

* fix(codex): harden system-default identity rendering

* feat(codex): give each managed account a self-contained CODEX_HOME; retire shared mirror (PR-E)

With the real-home flag ON, a host managed account now launches directly
against its own codex-accounts/<id>/home instead of the shared runtime
mirror + auth.json hot-swap:

- codex-home-paths: syncSystemCodexResourcesIntoManagedHome links system
  resources into any managed home (ownership-marker discipline; never
  symlinks into / mutates ~/.codex).
- runtime-home-service: prepareForCodexLaunch / prepareForRateLimitFetch /
  syncForCurrentSelection route the per-account home directly and skip the
  shared-home hot-swap + token read-back; each home keeps its own auth in
  place (fixes GAP-5 concurrent auth race). Session discovery scans every
  per-account home.
- hook-service / hook-trust-promotion: install/getStatus/refresh accept a
  runtimeHomePath so hooks + RPC-granted trust land in the per-account home.
- service: config mirror into a self-contained home uses the trust-
  preserving merge so granted hook/project trust survives account switches.
- codex-session-root-dedup: rank codex-accounts/<id>/home as canonical
  managed alongside the shared runtime home.

Flag-OFF and the system-default real-home (null) lane are unchanged; the
nested-Orca CODEX_HOME===ORCA_CODEX_HOME daemon strip (#5370) is preserved.
Sandboxed tests only; ~/.codex is never mutated.

* fix(codex): validate per-account home ownership

* fix(codex): keep managed rollouts discoverable across real-home opt-out

WI-4 lossless migration/rollback validation for pre-E shared-mirror managed
accounts. Session discovery gated the per-account home scan on the real-home
flag, so opting back out (flag OFF) hid every rollout an account accumulated
while the flag was ON — the data stayed on disk but vanished from the AI Vault
until the flag flipped back on.

Scan a managed host home whenever it holds a sessions/ tree, independent of the
flag; a never-enabled install keeps its homes credential-only so opt-out stays
byte-identical to today. Forward migration was already lossless (the shared
mirror is always scanned) and the opt-out credential read-back already refuses
to overwrite a fresher per-account token; add tests locking all three
invariants. Sandboxed tests only; ~/.codex is never touched.

* fix(codex): migrate stranded shared auth on E takeover

* test(e2e): isolate Electron from developer Codex home

* test(codex): add real-account validation harness

* fix(codex): finish C and E matcher composition

* fix(codex): bound validation harness shutdown

* test(codex): isolate hook lifecycle user data

* test(codex): cover realistic account-home migration

* fix(codex): keep standalone home tripwire active

* test(codex): fingerprint system auth in validation reports

* fix(codex): bind managed homes to account ownership

* fix(codex): normalize Windows trust source identity

* fix(codex): make Windows trust upgrade transactional

* test(codex): use TypeScript pipeline for validation scripts

* test(codex): run validation modules through native node

* test(codex): allow slow Windows tripwire startup

* fix(codex): survive lingering Windows codex login processes in add-account

On Windows, codex login can keep running (with descendants) after it has
written auth.json, holding OS handles on the per-account managed home
(log/codex-login.log). That made doAddAccount's post-login cleanup fail
with ENOTEMPTY (rmSync) and left an orphaned codex-accounts/<id>/home.

- runCodexLogin now watches for auth.json on Windows and force-kills the
  login process tree (taskkill /t) if it lingers past a short grace
  period; the forced exit is treated as a successful login. The 120s
  timeout path also kills the whole tree instead of only the direct
  child. macOS/Linux behavior is unchanged.
- safeRemoveManagedHome now removes homes with rmSync maxRetries /
  retryDelay (mirroring the local-worktree-filesystem Windows policy)
  and no longer lets a cleanup failure mask the original add error.
- run-codex-real-account-validation.mjs accepts --temp-parent /
  ORCA_CODEX_VALIDATION_TEMP_PARENT so the disposable root can live
  outside %USERPROFILE% on Windows, and fails with an actionable message
  before creating anything when the temp parent is inside the primary
  home. The real-home guard is unchanged.

* fix(codex): preserve managed-account MCP .credentials.json on per-account-home migration (#8440)

Codex file-mode MCP OAuth tokens live in $CODEX_HOME/.credentials.json,
keyed by MCP server URL with no account identity of their own. The legacy
shared-mirror -> per-account-home migration only carried auth.json, so an
existing managed account with authed MCP servers had its tokens stranded on
upgrade and silently needed re-auth.

Carry the shared mirror's .credentials.json into the same identity-proven
per-account home alongside auth.json: only into the single uniquely-matched
active account (no cross-account leak), only when the destination has none yet
(never clobber a newer file the account authed in its own home), atomic 0600,
absent-source no-op. New MCP auth already lands in the per-account home since
that home is CODEX_HOME.

* fix(codex): preserve Windows reauthentication login flow

* test(codex): build real-account validation harness cross-platform on Windows

The harness built its app with execFileSync('npx', ['electron-vite', ...]),
but npx resolves to a .cmd shim on Windows that execFileSync cannot launch
(ENOENT), so the harness could not build its own app there and required
--skip-build with a prebuilt out/main/index.js.

Extract resolveElectronViteBuildCommand(repoRoot): it runs the repository-local
electron-vite JS entry (node_modules/electron-vite/bin/electron-vite.js) with
the current Node binary (process.execPath), which resolves identically on
macOS, Linux, and Windows with no shell. It throws a clear error if the local
entry is missing (install deps or pass --skip-build). --skip-build behavior is
unchanged.

Add regression coverage asserting the build command uses process.execPath and
the repo-local JS entry (not npx), and that a missing entry fails clearly.

* fix(codex): version the MCP creds migration independently of the auth marker

The auth carry and the MCP .credentials.json carry (#8440) shared one
existence-only v1 marker, so any build that stamped the auth-only marker
first would strand the MCP store forever. The MCP carry now concludes via
its own per-account-mcp-creds-migration-v1.json marker and runs even when
the auth marker is already present; ordering is code-enforced instead of
landing-discipline-enforced.

Also isolate per-account read failures: one stale or deleted account home
no longer aborts the whole migration. The broken account stays in the
unique-identity ambiguity gate via its stored fields but is never read or
written, so the active account still migrates.

* fix(codex): fail corrupt managed auth.json without echoing credential bytes

A raw JSON.parse SyntaxError from loadOAuthCredentials could carry auth
file fragments into logs and the add/reauth error surface. Throw a
sanitized error instead; filesystem errors still propagate unchanged.

* fix(mobile): give the pairing runtime a disposable home for the E2E boot guard

The main-process guard now refuses to start with ORCA_E2E_USER_DATA_DIR
set but the real user home, and this was the one caller not updated —
the temporary pairing runtime crashed before emitting its pairing URL.

* test(codex): canonicalize harness containment guards and retry cleanup

Resolve symlinks before the disposable-root containment checks so a
symlinked temp parent cannot smuggle the throwaway home inside the
primary home, and give the final cleanup rm Windows retry/force so a
briefly lingering codex handle cannot strand the credential-bearing
root.

* test(codex): add lane-aware containment mode to the real-account harness

The Windows gate-D run proved strict zero-event whole-profile containment
is structurally unreachable with the real-home flag ON: system-default
spawn sites deliberately delete CODEX_HOME so native codex resolves the
real ~/.codex, and on Windows the binary ignores the USERPROFILE sandbox.
Its own volatile runtime churn (root sqlite/WAL/SHM, tmp/, log/) is the
shipped Phase-1 design, not a candidate defect.

--lane-aware-containment records those designed events without aborting
while every other real-home write — auth.json, config.toml,
.credentials.json, hooks.json, sessions/, anything unknown — remains a
hard violation and still aborts the run. Default behavior is unchanged
(strict); the absolute zero-event claim stays carried by macOS runs,
where HOME does sandbox native codex.

* test(codex): allow the real-account harness to pin the real-home flag off

--system-default-real-home off seeds and env-pins the flag OFF so every
codex spawn gets an explicit managed CODEX_HOME and native codex never
resolves the OS profile. This is the only Windows configuration where the
strict zero-event whole-profile tripwire is reachable, and it matches the
stable-rollout default; flag-ON runs keep lane-aware classification.

* test(codex): correct the flag-off harness comment to kill-switch rationale

The rollout ships all codex-home changes at once (no phased rollout), so
flag OFF is the emergency kill-switch lane, not the stable default.

* test(e2e): canonicalize the isolated E2E home path

The disposable HOME lives under os.tmpdir(), whose spelling is an alias
on CI (macOS /var symlink, Windows 8.3 RUNNER~1). Git canonicalizes
worktree paths, so worktrees created under the aliased home never
matched the app's listing — golden core flows and the packaged
crash-survival harness failed with 'worktree created but not found in
listing'. Resolve the home to its canonical spelling at creation in
both the e2e helper and the packaged-app driver.

* fix(codex): address CodeRabbit review on the landing PR

- carry envToDelete through the mobile agent-resume startup plan so a
  real-home Codex resume cannot inherit an ambient CODEX_HOME
- strip Orca-owned Codex overrides in the commit-message WSL fallback,
  matching the host fallback
- strip ELECTRON_RUN_AS_NODE in the computer-e2e driver like every other
  home-isolation caller
- drop the unused hooksEnabled parameter from isRealHomeCodexHookLaneUsable

* feat(codex): ship real-home routing unconditionally, remove the rollout flag

The codexSystemDefaultRealHomeEnabled setting is gone from types and
constants and the helper no longer consults settings — the system-default
real-home lane and per-account homes ship for everyone in one release.
This also un-strands profiles that rc-era builds stamped with false (the
setting had no UI, so every stored false was a seeded artifact that would
have silently kept those users on the legacy mirror forever).

The ORCA_CODEX_SYSTEM_DEFAULT_REAL_HOME env override survives strictly as
a test-rig control: the containment harness pins the legacy lane for
strict zero-event Windows runs, e2e home isolation pins lanes inside
disposable homes, and the legacy-lane test suites now route their
per-test lane selection through it.

---------

Co-authored-by: OrcaWin <alpha-eng@stably.ai>
2026-07-20 14:34:53 -07:00
Brennan Benson f8b430f725 feat(skills): ship orca-cli as a first-generation hybrid stub (#9238)
* feat(skills): ship orca-cli as a first-generation hybrid stub

Convert the installable orca-cli SKILL.md from a full fat guide into a
hybrid discovery stub: a safe CLI resolver, an `orca skills get orca-cli`
pointer, and a bounded read-only fallback for pre-guide binaries. The
version-matched command reference now lives only in the Orca binary
(embedded guide table, served by `orca skills get`), so the distributed
file can no longer drift from the binary that runs the commands.

- generator projects STUB_TOPICS from skill-stubs/<name>.md, reusing the
  guide's own frontmatter so the routing/description surface is unchanged;
  the embedded full guide (bundled-skill-guides.ts) is untouched.
- manifest regenerated: orca-cli releaseRevision 32->33 as an append-only
  snapshot; existing fat installs classify `outdated` and get the targeted
  `npx skills update` nudge (no in-app writes).
- tests: command-guidance assertions repointed to the guide source (their
  home now), plus stub-projection + safety coverage.

Only orca-cli converts; the other skills stay fat. Per
notes/skill-freshness-design.md, the E.3 pointer-compliance spike and the
E.5 RC window remain before any further thinning. allowed-tools is
intentionally not added yet (frontmatter kept byte-identical to the guide).

* fix(skills): distinguish guide lookup failures

* chore(skills): refresh released skill mapping
2026-07-20 13:13:21 -07:00
Brennan Benson 1d2ba1e39d fix: never surface coding-agent scratch worktrees in the sidebar (#9535)
* fix: never surface coding-agent scratch worktrees in the sidebar

Sub-agent runs (e.g. Claude Code worktree isolation) create throwaway
git worktrees at tool-internal paths like <repo>/.claude/worktrees/.
These showed up as workspace rows for repos whose non-Orca worktree
visibility is 'show', and would otherwise ping the discovery card and
new-worktrees inbox on every fan-out.

Classify them as 'agent-scratch' via a curated path-segment matcher and
suppress them unconditionally: sidebar, discovery card, inbox, dialog
counts, add-handoff reveal trigger, and the metadata fallback. Selected
checkouts and explicitly imported paths still win.

Fixes #9388

* fix: preserve scratch worktree visibility boundaries

* fix: hide scratch worktrees from linked checkouts

* docs: explain scratch root normalization
2026-07-20 12:50:47 -07:00
4f81dfc128 perf(ssh): cut warm high-latency connects from 88.7s to 7.7s (#9015)
Move managed agent-hook filesystem work behind one relay RPC so high-latency SSH connects pay one WAN round trip instead of hundreds. Keep installers serial, lock shared account config across relay processes, and fence cancelled connection generations from replacement state.

Co-authored-by: nasagong <zinho2000@gachon.ac.kr>
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-07-20 12:18:57 -07:00
Brennan Benson aca7d50bab Stop attaching stale closed PRs/MRs to default-branch checkouts (#9469)
* Stop attaching stale closed PRs/MRs to default-branch checkouts

On the repo default branch, the implicit head-branch PR lookup (state=all)
could attach a historical closed/merged PR whose head ref was the default
branch name and show its wrong diffs and checks (#9171).

Add a shared default-branch guard: an implicit branch-name match on the
repository's default branch never surfaces a non-open review. Applied at
the branch-lookup choke point of all five provider clients (GitHub,
GitLab, Bitbucket, Azure DevOps, Gitea). Explicitly linked reviews are
exempt; open reviews from the trunk stay visible; resolution is lazy
(zero git calls unless a non-open candidate appears), TTL-cached,
transport-aware (local/WSL/SSH), probe-time-bounded, and fails open.

* Treat stuck-locked GitLab MRs as non-open in the default-branch guard

Three code-review lanes flagged (one reproduced) that 'locked' — normally
a seconds-long merge transition, but a known GitLab wedge state — leaked
past the closed/merged-only check and would re-create the #9171 symptom
for a stuck-locked MR whose source branch is the trunk.

* Bound default-branch lookup to one refresh budget

* Coalesce default-branch resolution probes
2026-07-20 11:54:30 -07:00
Jinjing 99df4158b9 docs(readme): drop Windows RC download notice
Stable v1.4.147 includes the Windows fixes, so point users at latest again.
2026-07-20 11:40:17 -07:00
Brennan Benson ccd72f5909 Add unified mobile onboarding for session view and notifications (#9478)
* Add a mobile native-chat opt-in so users pick terminal vs chat once

Mirror the notifications one-time opt-in for the native-chat default view.

After pairing, a full-screen modal (modeled on notification-opt-in) lets the
user choose whether supported agent sessions open in the terminal or in native
chat, then persists the choice to the existing orca:defaultSessionView key.

- Expose readDefaultSessionViewPreference() (tri-state; absent key = undecided)
  so the gate can prompt exactly once; loadDefaultSessionView() is unchanged.
- shouldPresentSessionViewOptIn() gates the screen; the home focus effect shows
  it after the notification opt-in.
- Settings -> Native chat toggle (already shipped) remains the recovery path.

* fix(mobile): preserve onboarding flow after pairing

* refine mobile session view opt-in copy

* Unify mobile onboarding prompts
2026-07-20 11:11:23 -07:00
github-actions[bot] d9d939a33b Update README downloads badge 2026-07-20 12:58:56 +00:00
NeilandOrca 633cad3551 refactor(comments): slim verbose comments in renderer components (#9546)
* refactor(comments): slim verbose comments in renderer components

Collapse multi-line explanatory comment blocks into single-line "why" statements
per AGENTS.md ("Document the Why, Briefly"): drop restatements of the code and
mechanism narration; keep the non-obvious reason, external refs, and directives.

Comments-only — verified no code changed via a Babel/esbuild comment-strip
token-equality gate against origin/main; typecheck and oxlint clean.

Area: renderer — components. 64 files changed, 2610 insertions(+), 8794 deletions(-).

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

* fix(comments): restore comment markers pinned by source-boundary guard tests

Four *-boundary.test.ts guards assert on exact comment strings in the source
(e.g. '// Why: issue #4756 keeps project-view actions on the direct'). Slimming
reworded them, breaking the guards. Restored the pinned prefixes while keeping
the comments to one line. Guard suite (16 files/110 tests) green locally.

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

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-20 04:01:58 -07:00
NeilandOrca 0989128287 refactor(comments): slim verbose comments in shared/cli/relay/preload (#9544)
Collapse multi-line explanatory comment blocks into single-line "why" statements
per AGENTS.md ("Document the Why, Briefly"): drop restatements of the code and
mechanism narration; keep the non-obvious reason, external refs, and directives.

Comments-only — verified no code changed via a Babel/esbuild comment-strip
token-equality gate against origin/main; typecheck and oxlint clean.

Area: shared, cli, relay, preload. 26 files changed, 1039 insertions(+), 3300 deletions(-).

Co-authored-by: Orca <help@stably.ai>
2026-07-20 03:34:44 -07:00
NeilandOrca c6f0ac4040 refactor(comments): slim verbose comments in mobile (#9547)
Collapse multi-line explanatory comment blocks into single-line "why" statements
per AGENTS.md ("Document the Why, Briefly"): drop restatements of the code and
mechanism narration; keep the non-obvious reason, external refs, and directives.

Comments-only — verified no code changed via a Babel/esbuild comment-strip
token-equality gate against origin/main; typecheck and oxlint clean.

Area: mobile. 11 files changed, 339 insertions(+), 1137 deletions(-).

Co-authored-by: Orca <help@stably.ai>
2026-07-20 03:18:35 -07:00
NeilandOrca 0f7250879a refactor(comments): slim verbose comments in renderer state (store/hooks/lib) (#9545)
Collapse multi-line explanatory comment blocks into single-line "why" statements
per AGENTS.md ("Document the Why, Briefly"): drop restatements of the code and
mechanism narration; keep the non-obvious reason, external refs, and directives.

Comments-only — verified no code changed via a Babel/esbuild comment-strip
token-equality gate against origin/main; typecheck and oxlint clean.

Area: renderer — store, hooks, lib, runtime. 36 files changed, 1673 insertions(+), 5677 deletions(-).

Co-authored-by: Orca <help@stably.ai>
2026-07-20 03:18:32 -07:00
NeilandOrca 190de8223e refactor(comments): slim verbose comments in main integrations (git/providers/…) (#9543)
Collapse multi-line explanatory comment blocks into single-line "why" statements
per AGENTS.md ("Document the Why, Briefly"): drop restatements of the code and
mechanism narration; keep the non-obvious reason, external refs, and directives.

Comments-only — verified no code changed via a Babel/esbuild comment-strip
token-equality gate against origin/main; typecheck and oxlint clean.

Area: main — git, source-control, providers & integrations. 40 files changed, 1432 insertions(+), 4473 deletions(-).

Co-authored-by: Orca <help@stably.ai>
2026-07-20 03:18:28 -07:00
NeilandOrca 98b00d3a64 refactor(comments): slim verbose comments in main core (runtime/ipc/daemon/pty) (#9542)
Collapse multi-line explanatory comment blocks into single-line "why" statements
per AGENTS.md ("Document the Why, Briefly"): drop restatements of the code and
mechanism narration; keep the non-obvious reason, external refs, and directives.

Comments-only — verified no code changed via a Babel/esbuild comment-strip
token-equality gate against origin/main; typecheck and oxlint clean.

Area: main — core runtime, ipc, daemon, pty, providers. 73 files changed, 3206 insertions(+), 10475 deletions(-).

Co-authored-by: Orca <help@stably.ai>
2026-07-20 03:18:24 -07:00
Jinjing 53aeeb710c docs(readme): point Windows users to v1.4.147-rc.4
Bump the Windows RC download notice and installer link to the newer RC.
2026-07-20 01:43:22 -07:00
github-actions[bot] f98eaeb8a2 release: v1.4.147-rc.4 v1.4.147-rc.4 2026-07-20 08:24:22 +00:00
Jinjing c240271c72 docs(readme): add French translation and reorder language links
Add a native French README and put Chinese, Japanese, and Korean first in the language switcher.
2026-07-20 01:18:55 -07:00
Neil 24cfb97d39 fix(readme): stop star/license badges showing 429 from badgen
badgen.net rate-limits against GitHub and paints "429" into the badge.
Switch stars to shields.io (repo stargazers_count), use a static MIT
license badge, and drop /stargazers links which now 404 for the public
after GitHub's July 2026 stargazer access restrictions.
2026-07-20 01:14:55 -07:00
7386ef2857 perf(renderer): stop full durable-state save on every top-level view switch (#9002) (#9393)
* perf(renderer): stop full durable-state save on every top-level view switch (#9002)

Persist activeView in a tiny profile-scoped sidecar instead of mutating the monolithic recovery snapshot. Active-view-only updates now bypass the broad UI normalization and durable save scheduler, while a 100ms atomic writer coalesces rapid switches and a synchronous shutdown checkpoint closes the immediate-exit race. Legacy state remains a migration and downgrade fallback.

Coordinate renderer shutdown capture through one guarded checkpoint so workspace sessions and the active-view preference both survive graceful reloads, restarts, and quit cancellation.

Add a persistence-boundary test proving the sidecar stays below 64 bytes while orca-data.json remains byte-for-byte unchanged, plus repeated Windows Electron restart coverage and a path-normalization-safe restart fixture.

* harden active-view sidecar: prototype-safe validator, race-free async swap, independent shutdown flush

- isTopLevelView uses Object.hasOwn so a corrupt sidecar can't smuggle
  inherited keys (constructor/__proto__) through as a valid view.
- writeAsync guards the generation check and rename synchronously (renameSync)
  so a shutdown flushOrThrow can no longer interleave and let a stale async
  rename clobber the freshly-written view.
- shutdown checkpoint flushes the durable store and the active-view sidecar in
  independent try/catch blocks so one store's failure can't skip the other.
  Added regression tests for all three.

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

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
2026-07-20 01:14:17 -07:00
Jinjing 207db02798 docs(readme): shorten Windows RC notice under Download Orca
Keep the callout secondary and clear that the RC has Windows-specific fixes.
2026-07-20 01:11:48 -07:00
Jinjing a69943432e Direct Windows users to v1.4.147-rc.3 for bug fixes
Updated README to guide Windows users to the latest RC release,
which includes critical Windows-specific bug fixes not yet in the
stable build. Added prominent notices at the top of the download
section and in the direct-download links.
2026-07-20 01:10:12 -07:00
257b0b441e fix(new-workspace): ignore IME composition Enter in workspace name field (#9526)
Co-authored-by: Orca <help@stably.ai>
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
2026-07-20 00:53:15 -07:00
OrcaWin ddb5b930e1 fix(rate-limits): surface Fable weekly usage when is_active is false (#8979) (#9389) 2026-07-20 00:39:08 -07:00
fsdwen f25fb9f400 fix(i18n): 操作按钮/指令的翻译"开放"改为"打开" (#9533) (#9534) 2026-07-20 00:31:28 -07:00
OrcaWin 4d49b9342d fix(wsl): forward ORCA_ROOT_PATH/ORCA_WORKTREE_PATH setup vars across the wsl.exe boundary (#9206) (#9390)
* fix(wsl): forward ORCA_ROOT_PATH/ORCA_WORKTREE_PATH and setup vars across the wsl.exe boundary (#9206)

Worktree setup scripts running under WSL saw empty ORCA_ROOT_PATH /
ORCA_WORKTREE_PATH ("cp: cannot stat /.env"): the vars were set on the
Windows-side spawn env, but wsl.exe only imports Windows env vars listed
in WSLENV, and the addOrcaWslInteropEnv allowlist omitted them.

Register ORCA_ROOT_PATH, ORCA_WORKTREE_PATH, and the CONDUCTOR/GHOSTX
compat aliases with a per-value flag (same pattern as
ORCA_AGENT_HOOK_ENDPOINT): /u when hooks.ts already Linux-translated the
value for a WSL worktree (a /p flag would double-translate and corrupt
it), /p when a wsl.exe terminal runs over a Windows worktree and the
value is still a C:\ path WSLENV must translate. ORCA_WORKSPACE_NAME is
a display name, never a path, so it is always /u.

* fix(wsl): populate WSLENV for runHook's direct wsl.exe invocations (#9206)

runHook spawns wsl.exe via execFile for archive hooks and for setup when
no renderer window exists (headless/CLI/RPC/mobile-created worktrees).
It set ORCA_ROOT_PATH etc. on the execFile env, but wsl.exe only imports
Windows env vars named in WSLENV, so the guest never saw them. Register
the setup vars in WSLENV via a helper factored out of the PTY path's
addOrcaWslInteropEnv, so the per-value /u-vs-/p flag decision stays in
one place. Also make the runHook WSL test assert on captured execFile
options after the promise resolves — expects thrown inside the mock were
swallowed by runHook's own error handling.
2026-07-20 03:26:40 -04:00
github-actions[bot] 904762fa96 Update README downloads badge 2026-07-20 07:22:55 +00:00
OrcaWinandOrcaWin 2e67af82d2 fix(worktree): bound teardown RPCs so Windows workspace deletion can't hang (#9516)
* fix(worktree): bound teardown RPCs so Windows workspace deletion can't hang

Native-Windows workspace deletion failed with "Timed out waiting for
physical PTY teardown". On win32 the daemon PTY adapter is the local
provider, and destructive teardown's kill/listSessions RPCs used the
DaemonClient 30s default — larger than the 10s sweep deadline — so a
slow/wedged daemon let the outer deadline fire with the confusing error
and blocked deletion.

Thread an optional `timeoutMs` through IPtyProvider.shutdown/listProcesses
and bound every RPC on the destructive-removal path (provider sweep,
registry sweep, and the runtime-graph sweep via stopAndWait) to the
remaining sweep budget minus a margin. The daemon adapter shares one
budget across ensureConnected + the RPC; the SSH provider forwards the
bound to the relay mux. stopAndWait splits the budget across its two
sequential RPCs and bounds the cold-start wait, failing closed.

Result: a wedged backend now fails fast with the accurate "Failed to
physically stop every PTY" (retry succeeds once the process is truly
gone), and the misleading deadline error no longer blocks deletion.
Fail-closed safety is preserved: a genuinely-live process still blocks
removal. Non-teardown callers pass no timeoutMs and keep the 30s default.

* refactor(worktree): thread an absolute teardown deadline instead of a relative timeout

Elegance pass on the teardown-RPC bounding. Instead of passing a relative
`timeoutMs` and reconstructing an absolute deadline + "remaining budget" at
three layers (stopAndWait, the daemon adapter's shutdown/listProcesses, and
per-RPC in the sweeps), thread one absolute `deadlineMs` (epoch ms) through
IPtyProvider.shutdown/listProcesses. Each RPC leaf converts to a relative
timeout exactly once, at the moment it issues (`max(1, deadlineMs - now)`).

Why this is cleaner:
- The threaded value's identity is a point in time, not a duration, matching
  the codebase's existing deadline-based shared-budget idiom.
- Sequential RPCs (kill then liveness verify) share the budget structurally:
  a later leaf converting the same deadline naturally gets less time, so the
  explicit recompute-after-shutdown bookkeeping disappears.
- Removes the per-layer deadline re-anchoring that let the effective deadline
  drift slightly later at each hop.
- The 500ms margin is now applied once (`teardownRpcDeadline`), and the dead
  `: opts.timeoutMs` else-arms in the adapter are gone.

No behavior change: non-teardown callers still pass nothing and keep the
30s default + original connect behavior; fail-closed safety is intact.
Tests strengthened to pin the exact leaf-observed budgets and the margin.

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-07-20 03:16:26 -04:00
OrcaWin b3b5031883 test(e2e): deliver node burst via temp file so PowerShell quoting can't break OSC title (#8521) (#9391)
The e2e terminal helpers typed `node -e ${JSON.stringify(script)}` into the
PTY. JSON.stringify emits POSIX-style \" escapes, which PowerShell does not
honor: it re-splits the program on `;` inside the payload, node throws
'Expected unicode escape' before emitting a single byte, and the OSC-title
assertions fail deterministically on Windows (default shell = PowerShell).

Stage the program in a unique temp .cjs file instead and send
`node "<forward-slash path>"` — no shell ever parses the program source, so
delivery is byte-identical on PowerShell, cmd, bash, and zsh (verified with
hexdumps: 07 1b 5d 30 3b ... 07 matches exactly across shells). Forward
slashes keep the quoted path valid in both POSIX shells and PowerShell; the
Codex startup marker moves from argv into the script body so no argument
quoting remains. macOS/Linux payload bytes are unchanged — only the delivery
mechanism differs. Test infrastructure only; no product code touched.
2026-07-20 03:04:28 -04:00
Mark Xian 936ec06e5a fix(search): bind content results to runtime owner (#9262)
Capture the worktree and runtime route that produced each committed content-search result set, then reuse that owner for opens and retries. This prevents active-worktree and ambient-runtime changes from retargeting remote matches while preserving explicit local and SSH routing. Closes #9185.
2026-07-20 02:55:56 -04:00
Brennan Benson 5fcf777617 feat(mobile): Quick Commands (terminal + agent-prompt presets) (#9298)
* feat(mobile): add Quick Commands (terminal + agent-prompt presets)

Brings the desktop Terminal Quick Commands feature to mobile: saved
agent-prompt or terminal-command presets that launch a new terminal tab.

Entry point sits in the session tab strip next to the "+" new-terminal
button (with a divider) — quick commands spawn a tab, so they live with
tab creation, mirroring desktop's tab-bar split button.

- Launcher button + Quick Commands bottom sheet (search, This project /
  Global groups, run/edit/delete rows, add row).
- Add/Edit sheet mirroring desktop TerminalQuickCommandDialog: Label,
  Action toggle (Terminal Command | Agent Prompt), Agent select, Prompt /
  Command Text, Advanced (Append Enter, Scope Global/Project), validation
  and save-failure feedback.
- Launch reuses handleCreateTerminal (extended with enter + toast copy):
  agent prompts launch the agent then deliver the prompt; terminal
  commands run the (Enter-appended) command text.
- Expose terminalQuickCommands over the remote/mobile RPC surface
  (getClientSettings/updateClientSettings allowlists, RuntimeStore type,
  and the strict SettingsUpdate zod schema).
- Mirror the agent-prompt support predicate mobile-side (stdin-after-start
  agents are unsupported) with a parity test guarding drift from desktop.
- Mock server: sample quick commands + settings.update handler for QA.

* fix(mobile): harden quick command execution

* fix(mobile): harden quick command persistence and launch

* test(mobile): preserve unexpected quick command errors

* fix(mobile): harden quick command launch performance

* fix(runtime): reject malformed quick command updates

* refactor(mobile): reuse shared quick-command logic instead of mirroring

The mobile quick-commands mirror was built on a false premise — that
runtime-importing src/shared/terminal-quick-commands breaks the RN bundle
/ Vitest. It doesn't: tui-agent-config → orca-cli-command-name is a pure
leaf with no module-load Node APIs (verified via probe + bundle-graph).

- Mobile now reuses the canonical desktop helpers (action/agent/scope/
  matchesRepo/support/flatten) directly from src/shared; only genuinely
  mobile-specific pieces (agent-branded labels, native row truncation,
  the launch plan) stay local.
- Multiline runnable terminal commands now flatten via the shared
  flattenTerminalQuickCommand (";"-join) — unity with desktop, so a
  command saved on one runs identically on the other.
- Drop the MOBILE_TUI_AGENT_PROMPT_COMMAND_UNSUPPORTED mirror + its parity
  test; use the shared supportsTerminalAgentQuickCommand predicate.
- Export the shared MAX_QUICK_COMMAND_* length caps for reuse.

* fix(mobile): protect quick command data boundaries

* fix(mobile): enforce quick command limits

* fix(mobile): make quick command updates atomic

* fix(mobile): keep quick command filters recoverable

* fix(mobile): use filled play icon for quick commands

* Revert "fix(mobile): use filled play icon for quick commands"

This reverts commit 169bf053b0.

* fix(mobile): gate quick commands on host capability
2026-07-19 23:42:31 -07:00
OrcaWinandOrcaWin 49149a05d4 fix(agent-status): stamp renderer SSH ownership (#9505)
* fix(agent-status): stamp renderer SSH ownership

* fix(agent-status): block stale renderer reconnect writes

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-07-20 02:11:05 -04:00
NeilandOrca 4d25de383e perf(renderer): stop agent spinners from keeping the frame pipeline awake (one shared clock, hidden-window stop) (#9380)
* perf(renderer): drive agent working-spinners from one shared clock

Per-element infinite CSS spin animations kept Chromium's frame pipeline
awake for the whole agent run — measured live (interleaved A/B/A/B,
renderer+GPU): 86.0/76.0 ms CPU/s with the CSS animation vs 61.0/61.0
with the clock, one working agent, and the CSS cost scales per element
while the clock is one flat timer for N spinners.

The clock ticks 12 steps/s at 30° — frame-for-frame identical to the
retired animation — writes style.transform on registered elements, and
stops on document hidden (native visibilitychange post-#9395), under
prefers-reduced-motion (static ring keeps the filled top border from
#9515), and when the last spinner unmounts. The stale-visibility latch
keeps it spinning when proven user input contradicts the occlusion
tracker.

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

* fix(github): restore explicit space after Filter fallback in PRFilterSections

Pre-existing failure on main: i18n-jsx-spacing-guard requires {' '} after
the 'Filter' translate fallback, but the file had a bare JSX space (which
oxfmt collapses). Wrap the subject in a span so the explicit {' '} survives
formatting, matching the pattern in the other guarded files.

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

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-19 22:53:32 -07:00
OrcaWinandOrcaWin 64dae13bc9 fix(sidebar): stop agent 'working' spinner freezing as a broken ring under reduced motion (#9515)
Under prefers-reduced-motion (Windows 'Animation effects' off), the agent
'working' spinner froze mid-rotation as a partial (3/4) ring, reading as a
broken spinner in the terminal tab and worktree card. Fill the top border so
it becomes a complete static ring, and apply the same treatment to
StatusIndicator so the sidebar dot honors reduced motion consistently instead
of stepping. Mirrors the existing feature-tour-preview-glyphs pattern.

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-07-20 01:18:27 -04:00
Neil a02389cea8 Update code comments section in AGENTS.md
Clarified guidelines for code comments, emphasizing the importance of explaining the 'why' behind non-obvious code.
2026-07-19 21:40:49 -07:00
NeilandOrca e90dc38f01 fix(dev): stabilize macOS safeStorage Keychain key name across dev branches (#9400)
Co-authored-by: Orca <help@stably.ai>
2026-07-19 21:31:36 -07:00
NeilandOrca 74e2cb702c perf(window): re-enable macOS main-window background throttling (#9395)
Co-authored-by: Orca <help@stably.ai>
2026-07-19 21:30:01 -07:00
Neil 0d97653e39 fix(ui): restore missing spaces in i18n JSX fragment copy (#9513) 2026-07-19 21:24:24 -07:00