Cap the origin-repo and host-auth caches like ownerRepoCache; keep ports
from remote/link URLs so GHES on a non-default port is a distinct
identity; make positional github.com slugs explicit against GH_HOST;
compare work-item sources by host-aware identity key; bail cwd-less
branch lookups when no repository candidate resolved; thread host
through the renderer work-item slug lookup.
Co-authored-by: Orca <help@stably.ai>
diagnoseGhAuth accepts the host a surface needs credentials for, scopes
the account/scope diagnosis to that host, and reports whether gh has any
login there; GhAuthErrorHelp renders host-qualified login/refresh
commands so an unauthenticated GHES host stops masquerading as a
github.com scope problem. Also fixes the mobile paste-intent expectation
for host-carrying parsed links.
Co-authored-by: Orca <help@stably.ai>
Updates the smart-source paste-intent test fixture to match the
repository slug shape that now carries a `host` field, keeping GHES
host identity intact through the paste-intent parsing path.
Generalize the auth-gated enterprise resolver to any remote and build a
cached hosted-identity family (origin/issue/candidates/source) on top of
it, then migrate every github.com-only consumer: Tasks listing/counting,
branch-to-PR discovery, push targets, fork upstream, issue operations,
Projects, web links, avatars, and PR-link facts. Scope the rate-limit
breaker probe per runtime:host and classify WSL UNC cwds correctly.
Co-authored-by: Orca <help@stably.ai>
- Thread `host` through mobile PR RPC params, IPC work-item lookups, and
RPC schemas so GHES identity survives the renderer/mobile/main boundary
instead of silently falling back to a same-named github.com repo.
- Qualify `--repo`/`-R` args for github.com too (not just GHES), since
gh resolves bare shorthand against a process-level GH_HOST that can
redirect pinned github.com commands.
- Cache `getOriginGitHubApiRepository` to avoid a per-call uncached
`git remote get-url` round trip on connection-backed repos.
- Add a local-fork fallback in `getWorkItemDetails` so PRs living on a
base repo (not visible via the origin slug) still resolve via cwd.
- Centralize the github.com-vs-GHES host predicate in
`isDefaultGitHubHost` so cache keys, quota scoping, and identity
checks can't drift out of sync.
- Pin `--host` on every gh call site so a process-level GH_HOST can't
silently redirect requests, and qualify `-R`/`-R=` repo shorthand
alongside the existing `--repo=` handling.
- Check the target scope for an active rate-limit block before each
WSL/native or host fallback retry, not just on the initial attempt,
so a blocked scope can't be hit again through a fallback path.
- Compute idempotency once per call instead of re-deriving it after
fallback reassigns args.
Move GHES/GH_HOST resolution from parsing --hostname/--repo out of gh argv to an explicit options.host passed through ghExecFileAsync, since SSH-backed repos spawn gh with no cwd and argv sniffing couldn't reliably detect the target host. The runner now injects --hostname and qualifies --repo/-R at spawn time from options.host, and rate-limit scoping/guards use the same explicit host instead of inferring it. Also adds a shared githubRepoIdentityKey helper to keep cache/store keys consistent with the new host-aware repository identity.
- Threads GitHub host identity (not just owner/repo) through the client,
work-item-details, issues, and RPC layers so gh commands target the
correct Enterprise server instead of silently falling back to github.com
- Adds a shared github-api-repository helper to resolve/host-qualify repo
identity consistently across REST, GraphQL, and CLI shorthand calls
- Scopes the gh rate-limit breaker and singleton rate-limit snapshot by
host/runtime so a github.com block or probe can't affect GHES or WSL
- Coalesces concurrent host-auth probes and paginates PR file fetching
beyond 100 results
- Propagates `host` through renderer PR caches, checks-panel keys, and
preload IPC types so Enterprise and github.com data never collide
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>
* 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
* 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
* 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>
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>
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>
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>
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>
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>
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.
* 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>
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.
* 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.
* 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>
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.
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.
* 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
* 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>
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>
* fix(ssh): clear stamped agent status on disconnect
Batch transient cleanup by accepted SSH connection authority and use a monotonic cutoff so reconnect replay wins over delayed clears. Preserve pane launch, resume, acknowledgement, and retention metadata.
Caveat: legacy or renderer-owned rows without an accepted connection stamp are intentionally left to existing pane/PTY teardown; clearing them by host would be ambiguous.
* docs(ssh): explain stale status watermark
* fix(ssh): preserve status ordering after restart