Commit Graph
6986 Commits
Author SHA1 Message Date
NeilandOrca efea87d2c0 perf(editor): keep the fs-watcher delete path O(deletes) when no move is in flight
Co-authored-by: Orca <help@stably.ai>
2026-07-21 00:59:46 -07:00
NeilandOrca f51d258803 fix(editor): flavor-aware separator folding in relative-path recompute (POSIX backslash)
Co-authored-by: Orca <help@stably.ai>
2026-07-21 00:50:43 -07:00
NeilandOrca bb94d4acea fix(editor): flavor-aware trailing-separator strip; preserve POSIX literal backslashes
Co-authored-by: Orca <help@stably.ai>
2026-07-21 00:45:23 -07:00
NeilandOrca e10cfc33ac test(editor): lock POSIX ancestor-backslash destination flavor
Co-authored-by: Orca <help@stably.ai>
2026-07-21 00:38:59 -07:00
NeilandOrca bda8bc9321 fix(editor): infer moved-path flavor by syntax, preserving legal POSIX backslashes
Co-authored-by: Orca <help@stably.ai>
2026-07-21 00:37:42 -07:00
NeilandOrca dbabd94fe9 fix(editor): reconstruct moved path by segment count (WSL alias / duplicate-separator safe)
Co-authored-by: Orca <help@stably.ai>
2026-07-21 00:33:31 -07:00
NeilandOrca 1498ee9c99 fix(editor): use flavor-aware path containment for move selection (Windows/UNC case)
Co-authored-by: Orca <help@stably.ai>
2026-07-21 00:26:32 -07:00
NeilandOrca 9f89548b15 fix(editor): record mirrored-close intent synchronously to close the ghost-tab window
Co-authored-by: Orca <help@stably.ai>
2026-07-21 00:20:27 -07:00
Neil 7376a9f00c Merge remote-tracking branch 'origin/main' into nwparker/file-editor-work-save
# Conflicts:
#	src/renderer/src/hooks/useEditorExternalWatch.ts
#	src/renderer/src/store/slices/editor.ts
2026-07-20 23:28:51 -07:00
NeilandOrca 2a80df44ea chore(editor): trim move-fix comments to the why; drop redundant rename quiesce
Co-authored-by: Orca <help@stably.ai>
2026-07-20 23:25:27 -07:00
63da958813 fix(github): keep work-item searches repo-scoped (#9668)
* fix(github): prevent unscoped work item queries

* fix(review): translate non-English rationale comment to English (client.ts:1151)

Restores the English '// Why:' convention and re-states the allSettled partial-results rationale that was dropped when the comment was replaced. Flagged by Ce-code-review (maintainability/previous-comments) and a prior PR reviewer. No behavior change.

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

* fix(tasks): surface repos with no resolvable GitHub source

Since #9660 an unresolvable GitHub source returns an empty null-source
envelope instead of an unscoped search. That empty was indistinguishable
from a genuine zero-result query — no log, no telemetry, no UI signal.

Surface it in the Tasks list: a per-repo info row with Retry for each
fetched repo that resolved neither an issue nor a PR source (told apart
from genuine-zero and not-yet-fetched purely via the cached `sources`).
Renderer-only — the signal was already threaded through the envelope.

- selectTaskPageUnresolvedSourceRepos selector (+ unit tests)
- per-repo row reusing the existing banner region + handleRetryIssuesFetch
- suppress the generic empty state while those rows show
- assert the null-source envelope contract in client-work-items test

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 23:19:35 -07:00
NeilandOrca f0c6ca0bf5 perf(checks-panel): gate the terminal-cwd poll on window visibility (#9695)
The Checks panel polls window.api.pty.getCwd every 4s to follow the active
terminal's cwd. It was gated only on panel visibility (isPanelVisible), not
window visibility — so a hidden or minimized window kept spawning lsof on macOS
every 4s (the 4s cadence exceeds getCwd's 1.5s per-pid cache TTL, so every tick
is a guaranteed miss and a fresh subprocess).

Swap the raw window.setInterval for installWindowVisibilityInterval — the shared
helper every sibling timer already uses — which pauses while the window is hidden
and runs an immediate refresh on becoming visible so a cd made while hidden is
picked up on return. A visible-but-unfocused window (second monitor) keeps
polling, matching prior behavior.

Co-authored-by: Orca <help@stably.ai>
2026-07-20 23:11:12 -07:00
Cal 4877d0b65c Fix Windows Resource Manager memory and CPU attribution (#8845)
Use a single CIM sample for Windows process memory and CPU attribution, with bounded Typeperf memory fallback and safe CPU delta recovery.
2026-07-21 02:04:37 -04:00
Brennan Benson 6785f9d1a8 feat(dashboard): open-worktree dialog control + finished-time parity (#9686)
* feat(dashboard): open-worktree dialog control + finished-time parity

Pop-out agent dashboard polish:
- Terminal dialog: replace the footer Close button with the standard top-right
  X, rename the primary action from Focus worktree to Open worktree, and prefix
  it with an open-in-window icon.
- Card time column: finished agents now read time-since-finish (matching the
  left worktree sidebar) instead of time-since-start. Extracts the sidebar's
  finished-timestamp logic into a shared helper so the two surfaces cannot
  drift, and threads finishedAt through the dashboard snapshot contract.

* fix(dashboard): thread finishedAt through payload validator + test fixture

CI typecheck caught a DashboardCard fixture in dashboard-payload-validation.test.ts
missing the new finishedAt field; also validate finishedAt in the relay guard.

* feat(dashboard): swap pending-question emoji for MessageCircleQuestion icon

The amber pending-question chip used a raw hand emoji (renders differently
per-OS and off the design system); use the lucide chat-with-question icon.
2026-07-20 22:55:22 -07:00
NeilandOrca 9b7d15362d perf(linear): lazy-load @linear/sdk off the launch parse path (#9698)
Co-authored-by: Orca <help@stably.ai>
2026-07-20 22:41:59 -07:00
NeilandOrca d3f2a8c39a perf(stats): move the debounced stats write off the main thread (#9697)
Co-authored-by: Orca <help@stably.ai>
2026-07-20 22:41:39 -07:00
NeilandOrca e62b7e8172 perf(orchestration): skip per-terminal dispatch scan when no dispatches exist (#9694)
Co-authored-by: Orca <help@stably.ai>
2026-07-20 22:40:33 -07:00
NeilandOrca 13f83e894d fix(editor): prefix-suppress the move root so late tabs under a dir move aren't flagged (round 8)
Co-authored-by: Orca <help@stably.ai>
2026-07-20 22:04:47 -07:00
Neil 98d2233d17 fix(terminal): stop advertising the unimplemented Bold Text color override (#9661)
xterm.js has no bold-color theme slot (upstream xtermjs/xterm.js#6032), so the Bold Text override was silently dropped — a false affordance. Makes the Settings copy and the Ghostty importer honest that it isn't applied yet, while preserving any persisted value for a future real implementation. No terminal-rendering change.

Closes #8595
2026-07-20 21:56:09 -07:00
NeilandOrca cca28dc218 fix(editor): scope move rekey to the initiating execution host (round 6)
Co-authored-by: Orca <help@stably.ai>
2026-07-20 21:42:43 -07:00
OrcaWin 05c32c4757 fix(runtime): isolate navigation across paired clients (#9664) 2026-07-20 21:36:15 -07:00
NeilandJohn Debay 008c71df40 fix(terminal): stop leaking Orca's own NODE_ENV into spawned terminals (#9649)
Orca spread its whole process.env into every spawned terminal, so a dev build leaked NODE_ENV=development into integrated terminals (breaking next build, Vitest, etc.). Strips the inherited value at all three PTY-spawn sites (daemon, local provider, relay); a value the user sets themselves still wins.

Supersedes #9058, which patched only the relay path and so missed the reported (local daemon) repro.

Closes #9057

Co-authored-by: John Debay <john@debay.net>
2026-07-20 21:25:20 -07:00
Neil 91ee29a808 feat(editor): add an opt-in editor font family (default: follow terminal font) (#9658)
The code editor was hard-wired to the terminal font, so Nerd Font 'Mono' CJK glyphs overlapped in the editor. Adds an opt-in editorFontFamily setting; default is empty so the resolved value is byte-identical to today for every existing user.

Closes #9628
2026-07-20 21:25:17 -07:00
Neil 8847f615ac fix(terminal): restore clickable link hover after returning to the Orca window (#9659)
xterm's Linkifier only re-runs providers when the hovered cell changes; window blur fires mouseleave which clears the current link but leaves the cell cache, so on refocus with a stationary pointer the link stays dead until a scroll. Adds the same guarded hover-cache reset the reveal/on-write paths already use, to the window-wake path.

Closes #9116
2026-07-20 21:25:04 -07:00
Neil a6f54acd0f fix(sidebar): stop the Live Ports hover card flickering on compact worktree cards (#9662)
In compact worktree-card mode, two Radix HoverCard roots shared one open-state boolean, causing an open/close flicker loop when hovering the Live Ports plug. Un-shares the control so each hover owns its own state.

Closes #9304
2026-07-20 21:24:54 -07:00
OrcaWin 1293e1c0d8 Fix mixed-version mobile Codex session resume (#9678) 2026-07-20 21:16:08 -07:00
OrcaWin c540ab6d8b fix(mobile): restore notification opt-in route compatibility (#9675) 2026-07-20 21:15:39 -07:00
bf4b2e3ffb fix(sidebar): attribute spinner-only pane titles to the launched agent (#9647)
* fix(sidebar): attribute spinner-only pane titles to the launched agent

A braille-spinner terminal title proves activity but carries no agent
identity, so the title-derived sidebar row resolver dropped it. Agents
without working status hooks — Codex over SSH (#8711) — only ever emit
spinner+cwd titles, so their worktrees showed no agent row at all while
Claude (hook-backed) rows appeared.

Fall back to the tab's launch identity (tab.launchAgent) for
spinner-only titles, mirroring the precedence the hook-row path already
uses. Explicit title identity still wins, Claude-specific prefixes
(✳ / '. ' / '* ') are unaffected, and panes without launch metadata
keep the existing no-row behavior.

* test(sidebar): pin spinner fallback no-launchAgent gate; note split-pane residual

Adds a regression test asserting a spinner-only title with no tab launch
identity produces no row, pinning the `launchAgent ?? null` gate that the two
existing tests do not exercise. Expands the fallback comment to record why it is
spinner-gated (vs the hook path's unconditional launchAgent fallback) and the
split-pane braille-title residual.

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 21:12:28 -07:00
Brennan Benson e2e3f82e40 fix(editor): surface file-save failures instead of swallowing them (#9657)
* fix(editor): surface file-save failures instead of swallowing them

handleSaveForFile wrapped requestEditorFileSave in an empty `catch {}`, so a
rejected save vanished with no toast, no dirty-state change, and no log — the
edit stayed dirty in the buffer and was lost on tab close (silent data loss,
STA-2027). Only main.trace.ndjson revealed the file was never written.

Route the rejection through a new notifyEditorSaveFailure helper that logs the
error and shows a toast. Extracted to its own module so the reusable callers
(mobile bridge, file-deletion autosave) can adopt it and to keep EditorPanel
under the code-line budget.

Follow-up to the multi-byte reconcile crash (that was the known trigger); this
hardens the swallow that turned any save failure into silent loss.

* fix(editor): preserve failed save result
2026-07-20 21:10:11 -07:00
Brennan Benson e3c8d96638 Access the Floating Workspace from mobile (#8405) (#9523)
* Access the Floating Workspace from mobile (#8405)

Surface the desktop Floating Workspace (the global, repo-less scratchpad
of terminal tabs under the synthetic `global-floating-terminal` id) on the
mobile app so a Claude session left running there is reachable from a phone.

Adds a terminal-icon button to the mobile host header (phone + tablet
sidebar) that opens the existing Session screen for the floating id. The
sentinel already had host-side RPC support (#5946: local runtime, homedir
cwd, explicit-id fast paths in session.tabs.*); this wires up the mobile
surface and gates it on a new `floatingWorkspaceEnabled` status flag so the
entry hides on hosts that predate it or where the feature is disabled.

The Session screen learns an `isFloatingWorkspaceRoute` flag (mirroring the
existing `folder:` route pattern) that hides repo-backed surfaces — Files,
Source Control, PR/checks, agent history — skips the diff-comment and GitHub
probes, routes terminal URL taps to the phone browser, and limits the New
Tab drawer to terminals + agents (browser/markdown creation resolves a real
worktree host-side and stays desktop-only). useLiveWorktreeName
short-circuits for the sentinel so it no longer polls worktree.show forever.

Extracted the host status.get gating into a useHostStatusGates hook to keep
the host screen under the max-lines ratchet.

* Harden mobile Floating Workspace routing

* Fix mobile host gate reuse race

* Harden floating mobile session polling

* fix(mobile): harden floating workspace route reuse

* fix(mobile): skip floating workspace repo lookup

* fix(mobile): clarify floating workspace header action
2026-07-20 21:05:40 -07:00
6c9be228ad fix(automations): retire owned desktop terminals (#9493)
* fix(automations): retire owned desktop terminals (#9479)

* fix(automations): diagnose late settlement failures

* fix(automations): clear retired terminal identity and guard finalize close

- Clear the run's terminalSessionId/PaneKey/PtyId when finalize() actually
  retires the owned terminal, so 'View run' resolves to the workspace/snapshot
  instead of dead-ending on a now-closed terminal.
- Guard finalize()'s closeTab in a try/catch; a throwing close reports
  not-closed so the run keeps its still-valid terminal identity and no stale
  clear runs.

Review follow-ups from PR #9493 (does not attempt the durable cross-reload
reconciliation that #9479 needs).

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 20:53:31 -07:00
OrcaWin cc44acaaa3 Fix Windows ConPTY OSC color reply leaks at the PTY owner (#9651) 2026-07-20 20:50:25 -07:00
OrcaWinandOrcaWin ede57cf723 fix(windows): paste copied image files (#9640)
* fix(windows): paste copied image files

* fix(windows): reject multi-file image paste

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-07-20 23:16:36 -04:00
Brennan Benson cabb5dc05a fix(editor): reconcile multi-byte markdown saves without byte-offset crash (#9492) (#9642)
* fix(editor): reconcile multi-byte markdown saves without byte-offset crash (#9492)

@sanity/diff-match-patch's adjustIndiciesToUcs2 treats patch.start1 (a char
index) as a UTF-8 byte offset, so on multi-byte text (Chinese/emoji/accented
Latin) its byte counter overshoots the seed target and throws "Failed to
determine byte offset" when saving via Ctrl/Cmd+S.

Pass allowExceedingIndices to applyPatches so it returns the nearest index
instead of throwing; the fuzzy match relocates the hunk and branch 6's
round-trip proof already rejects any misplacement, so the seed offset need not
be exact. Regression introduced by #8862.

Adds a position-sweep regression test over a mostly-Chinese doc.

* fix(editor): preserve Unicode reconciliation seeds

* test(editor): cover rolling Unicode patch seeds
2026-07-20 19:56:38 -07:00
Neil 995ee88891 perf(persistence): single full-state serialization per save (#9381)
## Description

Every durable save serialized the ~1.5 MB durable state **twice**, synchronously on the main Electron thread: once in `computeStateHash()` (the plaintext hash for the no-op-write guard) and again in `buildStateToSave()` (the encrypted on-disk payload). The guard couldn't hash the payload directly because `encrypt()` on random-IV platforms produces different bytes each save for identical state.

This PR collapses the two into **one** `JSON.stringify`. `buildStateToSave()` now returns `{ payload, stateHash }`: each encrypted secret slot is serialized as a **fresh per-slot random sentinel** (`orca-secret-slot-<uuid>`), and after the single stringify each sentinel is substituted exactly once — to its ciphertext for the on-disk `payload`, and to its plaintext for the guard `hashInput`. The guard hash is therefore a pure function of the plaintext state (`hashInput === JSON.stringify(plaintext durable state)`), and the on-disk `payload` is byte-identical to the previous two-stringify output.

No change to the save API, debounce timings, guard semantics, or on-disk format (compact JSON, same 3 encrypted fields).

### Why sentinels (not a string replace of the ciphertext)

The substitution must be **position-exact**. A naive `payload.replace(ciphertext, plaintext)` — or even a `"key":"ciphertext"`-anchored replace — can be **mimicked by user-controlled state**, which would substitute the wrong site and let two *distinct* states normalize to the same guard hash → a real change is silently **not written** (data loss). This is reachable on deterministic-IV platforms (macOS / legacy-Linux OSCrypt use a constant IV, so a user can read their own ciphertext out of `orca-data.json`):

- **Value vector**: a plaintext free-text field (e.g. `httpProxyBypassRules`) whose value equals a secret's ciphertext.
- **Key vector**: `agentDefaultEnv` lets the user name an env var exactly after a secret field (e.g. `browserKagiSessionLink`) with a ciphertext value, producing a `"browserKagiSessionLink":"<ciphertext>"` token inside `settings` (before `ui`).

A per-slot random sentinel is minted **after** all user data is captured, so it cannot occur anywhere else in the serialized state and matches only the intended secret slot — closing the entire class. (Both vectors were found by an adversarial fable→gpt-5.6-sol review loop; each has a store-level regression test that fails on the earlier approaches and passes now.)

The empty-secret / `safeStorage`-unavailable / encrypt-failure cases (`blob === plaintext`) get no sentinel and reproduce `main`'s behavior byte-for-byte.

## Evidence

- Full-state `JSON.stringify` calls per changed save: **2 → 1** (call-counting test).
- Frontier-review benchmark on an 8 MiB real-method state: **no perf regression** (median 12.73 ms sentinel vs 13.77 ms two-stringify baseline).
- Independent review modeled 625 adversarial durable states: every payload byte-identical to the pre-PR implementation, and every distinct state produced a distinct guard hash (no dropped-write collision).

## No-Regression Proof

`src/main/persistence-single-serialize.test.ts` (nondeterministic **and** deterministic cipher mocks):

- identical state with secrets set → no-op (inode unchanged); real change (incl. secret rotation) → writes
- on-disk payload is ciphertext (plaintext appears nowhere), round-trips through reload/decrypt
- empty secret + `isEncryptionAvailable() === false` → plaintext payload, guard still skips
- sync `flushOrThrow()` also skips on identical state
- exactly one full-state serialization per save
- **regression (value vector)**: a plaintext field equal to a secret's ciphertext, then swapped → write is not skipped, round-trips
- **regression (key vector)**: an `agentDefaultEnv` var named after a secret field holding its ciphertext, then swapped → write is not skipped, round-trips

Suites: `persistence-single-serialize.test.ts` + `persistence.test.ts` all green; `pnpm run typecheck:node` clean; oxlint clean.

## ELI5

Orca saves your app state whenever something changes, and it used to build the whole (large) state twice each time — once to check "did anything actually change?" and once to write the file. Now it builds it once and reuses that copy for both jobs. To compare states safely it swaps each encrypted secret for a random one-time marker while checking, so no value you type can ever be mistaken for a secret and trick it into skipping a real save. What lands on disk is exactly the same, and your secrets stay encrypted.

Made with [Orca](https://github.com/stablyai/orca) 🐋
2026-07-20 19:47:58 -07:00
NeilandOrca 0e6bf30abb fix(editor): don't consume move-echo provenance in the safety-net verify (round 5)
The round-3 proactive post-commit verify ran resolveLiveMoveVerification,
which consumed pendingSelfMoveEcho. On FSEvents/SSH the real destination
watcher event reliably lands AFTER the fast local read, so it then found no
provenance, took the immediate changed-on-disk mark (no baseline check when
there is no recent self-write), and raised a false conflict banner on the
just-moved dirty tab — the exact data-loss this change removes.

The proactive verify is a safety net: it now releases the autosave gate but
leaves the provenance, so a later destination event is still recognized as
the move's own echo and content-verified. Only a real watcher event consumes
the provenance. Keeping it is safe — every watcher consumer verifies by
content, which is strictly safer than the immediate mark.

Co-authored-by: Orca <help@stably.ai>
2026-07-20 19:41:30 -07:00
NeilandOrca adbb913ae6 fix(editor): review round 4 (gate strand, cross-worktree verify path, leak)
- Don't install the move-echo verify gate on a tab already showing the
  changed-on-disk banner: it's autosave-suspended via externalMutation and
  verification skips a 'changed' tab, so the gate would strand forever.
- Content-verify reads each moved tab's own filePath. A cross-worktree/
  floating tab's relativePath is relative to its own root ('../…') and must
  not be joined onto the initiating worktree path (would read the wrong file
  and raise a false conflict banner on unsaved work).
- Coordinator settles the in-flight source suppression in a finally so a
  throw between rename and commit can't leak it, and only after the rollback
  rename so a late forward-rename delete stays suppressed.
- Migrate pendingEditorReveal.fileId across the rekey (matcher prefers it).

Co-authored-by: Orca <help@stably.ai>
2026-07-20 19:26:55 -07:00
NeilandOrca 8182141fcd perf(sidebar): stabilize worktree card props so cards memo-bail on order-preserving epoch bumps (#9392)
* perf(sidebar): stable per-group onLineageToggle identity so lineage-parent WorktreeCards memo-bail

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

* perf(sidebar): reuse array identity for visible/rendered/selected worktrees so cards memo-bail on epoch bumps

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

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-20 19:14:27 -07:00
Brennan Benson d834cc6303 chore: remove stray mock folder and design-notes docs (#9641)
* chore: remove stray agent-dashboard-mocks folder

Design mockups (index.html + RESEARCH.md) accidentally committed alongside
the agent dashboard popout feature in #9604. These are scratch design
artifacts and don't belong in the shipped repo.

* chore: remove stray notes/ design docs

Internal skill design/research scratch docs committed in #8624. Two of the
three are explicitly marked superseded/folded, none are referenced anywhere
in the codebase. Planning artifacts, not shipped documentation.
2026-07-20 19:14:18 -07:00
NeilandOrca 17bd38c8a6 fix(editor): resolve the move verify gate proactively (review round 3)
The rekey gates every dirty moved tab pending a destination content check,
but verification only ran when a watcher event arrived for that path. If the
watcher was down, throttled, or the event coalesced away, the gate never
cleared and autosave stayed suspended for the tab.

The coordinator now drives verification for every tab it gated once the
rename has committed, so the gate resolves on its own. That makes the
destination-side event latch redundant, so the in-flight tracker is
source-only again.

Co-authored-by: Orca <help@stably.ai>
2026-07-20 19:11:06 -07:00
OrcaWinandOrcaWin 88c78611b7 fix(ssh): patch node-pty helper in Windows relay (#9638)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-07-20 19:09:18 -07:00
OrcaWinandOrcaWin 085fc6ad89 Keep embedded browser commands on the registered Orca target (#9633)
* fix(browser): keep embedded commands on registered target

* fix(browser): cover internal helper ownership paths

* test(browser): cover stale helper routing in Electron

* fix(browser): preserve direct command result semantics

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-07-20 22:04:01 -04:00
github-actions[bot] fea0004a01 release: v1.4.148-rc.1 v1.4.148-rc.1 2026-07-21 02:01:29 +00:00
NeilandOrca afb82bc336 perf(renderer): patch the by-worktree agent index instead of full rebuild per status ping (#9383)
* perf(renderer): patch sidebar live-agent index on within-state status pings instead of full rebuild

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

* refactor(renderer): extract live-agent index patch into worktree-agent-live-index-patch module (max-lines)

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

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-20 18:56:56 -07:00
971b167548 fix(github): load PR diffs for Enterprise remotes (#8932)
* fix(github): load PR diffs for Enterprise remotes

* fix(github): encode PR content paths by segment

* Fix PR review actions failing on GitHub Enterprise remotes

- 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

* Route gh host qualification through runner options instead of argv sniff

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.

* Fix gh CLI GHES host pinning and rate-limit scope leaks

- 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.

* Fix GitHub Enterprise host identity loss across PR/work-item paths

- 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.

* Make repository identity host-aware across all GitHub surfaces

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>

* Fix expected slug to include host field in GitHub PR link test

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.

* Surface per-host gh auth state for GitHub Enterprise

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>

* Bound GHES identity caches and preserve non-default ports in host identity

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>

* Thread GitHub host through issue detail requests

Incorporates ghes-issue-host-support (ed6bb96ef): one hosted issue
repository identity is resolved before the details fan-out so comments,
timeline, participants, and mention lookups cannot drift across hosts,
with SSH guards so unresolved issue/PR repositories never fall through
to gh's default host.

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

* Scope remaining GitHub rate-limit accounting

* Resolve typed PR lookups across hosted repository candidates

getWorkItem's PR path probes upstream-then-origin hosted candidates
instead of origin alone, so fork checkouts resolve the base repo's PR
with the right host; issue detail resolution reuses the up-front hosted
identity and keeps the SSH unresolved-host guards.

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

* Refactor GitHub repository execution setup

* Carry host on smart-submit link intents

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

* Carry the project host on GitHub item dialog origins

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

* Keep GHES web ports but drop SSH transport ports in host identity

Supersedes PR #9118 on this branch: http(s) remote ports identify the
Enterprise web/API endpoint and are preserved, while ssh/git transport
ports (including ssh.github.com:443) never leak into gh's host identity.
Replaces the ssh.github.com:443 special case with the structural
protocol split and ports the PR's parsing test suite.

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

* Support GitHub Enterprise diffs and mutations with host-scoped caches

Parse GitHub host identity from work-item URLs and carry it through PR/issue mutations, labels, and assignments. Bound rate-limit and scope-probe caches (1024 and 512 entries) to prevent unbounded growth when interacting with multiple GHES instances. Normalize repository identity keys to include host so github.com and GHES slugs don't collide in cache and equality checks.

* Support GitHub Enterprise diffs and mutations with host-scoped caches

- Carry host identity through PR mutations and reads so fork PRs on
  different GHES instances don't collide in cache or state tracking.
- Validate host authentication before routing requests to unconfigured
  Enterprise servers; ambient credentials must never reach untrusted hosts.
- Scope rate-limit guards and spend tracking per host so GHES quota stays
  independent from github.com quota.
- Respect explicit --hostname arguments in gh CLI calls ahead of GH_HOST or
  ambient defaults, so breaker state follows the actual request target.
- Detect implicit WSL runtimes from UNC paths for consistent host auth and
  execution-options scoping across mobile and desktop clients.

* Support GitHub Enterprise work-item diffs with host-scoped execution

Enterprise PRs must use their selected host consistently across diff, comments,
and file-content loads. Validate repository slugs before authenticated execution
to prevent path-injection via renderer overrides. Scope project browsing cache
and rate-limit tracking by host to prevent cross-host pollution. Use parsed
URLs as authoritative over ambient hosts for project resolution.

* Support GitHub Enterprise work-item diffs with host-scoped execution

Preserve host identity on PR/issue work items throughout the mutation and diff
pipeline so Enterprise instances (including ported endpoints like
github.acme.test:8443) can execute mutations without ambiguity. Rate-limit gh
commands by the pre-qualified --repo host, cache auth state per ported host,
and surface Enterprise hosts in project metadata and error messages.

* fix(review): drop dead rateLimitGuard/noteRateLimitSpend re-export

Both callers (project-view.ts, mutations.ts) moved to the host-scoped
repositoryRateLimitGuard/noteRepositoryRateLimitSpend; the bucket-only
re-export in internals.ts had zero importers left.

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

* fix(ci): split Enterprise host work-item tests under max-lines

Move GHES/SSH host-routing cases out of work-item-details.test.ts so
the suite stays within the 800-line test max-lines budget.

* test(github): align mocks with host-scoped repository resolution

- Route origin repository resolution through getOwnerRepoForRemote, not getOwnerRepo, to match production path
- Pin github.com host on origin results so host-less fixtures pass host gate in resolveGitHubApiRepository
- Add generation-based invalidation to prevent stale slug-cache writes from in-flight resolutions
- Fix ref-sync race in ProjectPicker: use useLayoutEffect so committed tree owns browse cache key
- Defer handledCrossRepoUrlRef assignment in SmartWorkspaceNameField until resolution succeeds
- Update Enterprise host routing: found work items must not silently fall back to default host when unresolved
- Normalize GHES avatar URLs: accept explicit port 443 as canonical form, not a fallback trigger

---------

Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
2026-07-20 18:55:45 -07:00
JinjingandOrca 71544450d5 fix(terminal): gate xterm contrast correction by terminal background luminance (#7934) (#9599)
* fix(terminal): gate xterm contrast correction by theme mode (#7934)

minimumContrastRatio: 4.5 (added in #5986 to rescue invisible
white/bright-white ANSI text on light backgrounds) was applied globally,
over-correcting vibrant ANSI colors on dark themes. Keep 4.5 on light
themes; disable it (1) on dark themes.

applyTerminalAppearance re-gates it in its per-pane loop, so live
auto/system dark<->light flips are handled for free. buildDefaultTerminalOptions
keeps 4.5 as the initial value so the first paint on a light theme never
flashes low-contrast text. Supersedes #7939.

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

* fix(terminal): gate contrast correction by terminal background luminance

Contrast correction rescues white text on light backgrounds but over-corrects
on dark. The previous mode-based gate failed when the terminal theme didn't
match the app mode (e.g., terminalUseSeparateLightTheme=false). Now gates by
the theme's background luminance via isTerminalBackgroundLight() instead.

* refactor(terminal): extract contrast correction gating into shared helpe

Centralizes the minimumContrastRatio gating logic (#7934) into a
dedicated module so both the settings preview and live terminal
appearance apply the same contrast-by-luminance rule, preventing drift.
Adds value-gating to skip redundant writes that would clear xterm's
contrast cache on re-applies.

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-20 18:54:45 -07:00
Brennan Benson 9680b22e60 fix(terminal): heal stale PTY grids on window wake (#9626)
* fix(terminal): heal stale PTY grids on window wake

* fix(terminal): bound window-wake size recovery
2026-07-20 18:54:44 -07:00
Jinjing dd20c11e9d cleaning up files 2026-07-20 18:44:24 -07:00
aad34cbb32 docs(headless-server): add upgrade SOP for orca serve on Linux (#9575)
* docs(headless-server): add upgrade SOP for orca serve on Linux

The headless Linux guide covered install/run/systemd but had no upgrade
section, leaving operators to guess how to move to a new AppImage without
losing state.

Add an "Upgrade" section documenting the manual SOP (serve mode never
auto-updates) and one troubleshooting bullet:

- State lives under the service user's ~/.config (orca + Orca dirs),
  independent of /opt/orca, and orca-data.json is forward-migrated on load,
  so a forward upgrade is safe.
- Replace the binary with an atomic same-filesystem rename (download to
  .new, verify, mv) — never curl -o over the FUSE-mounted live binary.
- Back up the whole .config before upgrading, because rollback is NOT
  binary-only safe: an older build strips newer orca-data.json fields it
  doesn't recognize, and the .bak.* ring is corruption-recovery, not a
  pre-upgrade copy.
- Note there is no headless version command; track the release tag instead.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(headless-server): harden the orca serve upgrade/rollback runbook

Address CodeRabbit review on #9575:

- Fail closed: run the upgrade block under `set -euo pipefail`, remove any stale
  `.new` file before download, and gate the atomic `mv` on an explicit ELF check
  so a failed/partial/non-ELF download can never be promoted.
- Keep /opt/orca/VERSION tied to the installed binary: a single `TAG` variable
  drives both the download URL and the recorded VERSION, saved as VERSION.prev on
  upgrade and restored on rollback so the audit file never drifts.
- Crash-loop troubleshooting now points to Roll back first (restores the
  pre-upgrade orca-data.json) instead of re-running Upgrade.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(headless): harden server upgrade SOP

---------

Co-authored-by: fanyunqian.1 <fanyunqian.1@bytedance.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-07-20 21:38:39 -04:00
OrcaWinandBrennan Benson 6444be3a01 fix(accounts): follow the runtime for WSL provider account detection (#9537) (#9611)
* fix(accounts): follow the runtime for WSL provider account detection (#9537)

On Windows + WSL, provider-account detection (usage recognition and the
status-bar account switcher) was pinned to the Windows host even when the
project runs in WSL, so WSL accounts were never recognized and the WSL
switcher group never appeared.

Root cause: `localAccountRuntime` hard-defaulted to 'host', which
short-circuited `getInitialClaude/CodexRateLimitTarget` before the existing
"follow the global Windows runtime default" branch could run. That branch was
therefore dead for every real user.

Fix: add an 'auto' value for `localAccountRuntime`, make it the default, and
migrate the untouched legacy 'host' default to 'auto' once (guarded by
`localAccountRuntimeDefaultedToAutoForAllUsers`; explicit 'wsl' is preserved).
'auto' resolves via a shared `resolveLocalAccountRuntimeTarget` helper: on a
windows-host default it stays host (no behavior change); on a WSL default it
follows WSL, so WSL accounts are recognized and the WSL group appears.

Wired the shared resolver into the managed-account default target, the
status-bar WSL-group gate, and the Accounts settings location toggle.

Note: detection follows the global Windows runtime default, not the live
active project's runtime (the fetch target is a single global value); the
latter is a larger follow-up.

* fix(accounts): align auto runtime consumers

* fix(accounts): keep runtime polling aligned with settings

---------

Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
2026-07-20 18:35:14 -07:00