Commit Graph
2199 Commits
Author SHA1 Message Date
Jinwoo-HandOrca b9d80a4cbc feat(ssh): define relay runtime artifact contracts
Co-authored-by: Orca <help@stably.ai>
2026-07-14 07:43:28 -04:00
Jinwoo-HandOrca c4259d94fc fix(ssh): resolve complete remote node toolchains
Co-authored-by: Orca <help@stably.ai>
2026-07-14 06:28:26 -04:00
Brennan Benson 31f643ca42 Add version-matched skill guides to the CLI (#8624)
* Add version-matched bundled skill guides

* Clarify skill freshness rollout PRs

* Add canonical skills show alias

* fix(skills): address guide review feedback

* fix(skills): make guide commands cross-platform

* fix(skills): apply the ORCA convention to the emulator guides

Review follow-up: the emulator guides still instructed literal
`orca emulator ...` in sh fences with no Linux disambiguation, so on
unmanaged Linux they could launch the GNOME screen reader — the exact
failure the executable-selection preamble prevents. Both emulator
guides now carry the preamble and ORCA placeholder across fences,
tables, and prose, and the cross-platform safety test covers all four
converted guides. Also replaces computer-use's "unless a block names a
shell" carve-out, which contradicted its own POSIX example, with the
unconditional placeholder rule.
2026-07-14 02:17:55 -07:00
Rod BoevandOrcaWin 59460f7576 fix(terminal): derive zsh wrapper ZDOTDIR from %x so non-ASCII WSL logins load .zshrc (#8003) (#8209)
The shell-ready zsh wrapper restored ZDOTDIR from the env-imported
`${ZDOTDIR}`. On Windows+WSL the wrappers are generated with a Windows
path baked in but sourced via /mnt/c, and for non-ASCII Windows
usernames (e.g. a Korean login) zsh corrupts environment values whose
UTF-8 bytes fall in its 0x84-0x9D token range while processing startup
files. The corrupted `${ZDOTDIR}` failed the self-check, so the wrapper
fell back to the unusable baked Windows literal and the user's ~/.zshrc
never loaded — a bare `HOSTNAME%` prompt with no theme/aliases/PATH.

Derive the wrapper dir from `${${(%):-%x}:h}` instead — %x is zsh's
internal script name for the file being sourced and is not subject to
the env-import corruption. `${ZDOTDIR}` is kept only as a fallback when
%x expansion yields nothing; the existing final restore still validates
with -f before trusting the value. On native macOS/Linux the derived
value equals the old one, so behavior there is unchanged.

Covers local PTYs, the daemon, and the Windows→WSL launch path, which
all share getZshEnvTemplate. Adds a live-zsh regression test that sources
the wrappers from a non-ASCII (token-range) runtime path.

Supersedes the ORCA_ORIG_ZDOTDIR path-normalization approach with a wrapper self-location fix that also covers the non-ASCII (token-range) corruption trigger.

Co-authored-by: OrcaWin <alpha-eng@stably.ai>
2026-07-14 01:55:04 -07:00
NeilandOrca 840d3277d1 fix(daemon): replace a permanently wedged daemon instead of preserving it forever (#8689) (#8697)
* fix(daemon): replace a permanently wedged daemon instead of preserving it forever (#8689)

A daemon whose socket accepts connections but whose event loop never answers the
'hello' handshake was adopted by the launcher unconditionally and never re-evaluated,
so every terminal spawn failed with 'DaemonProtocolError: Hello response timed out'
with no recovery.

- daemon-init.ts: bound the launcher's 'preserve any unresponsive-but-connectable
  daemon' with a grace window. A transient wedge (Windows update-relaunch AV/disk
  pressure) drains within ~20s and is preserved WITH its live sessions; a permanent
  wedge exhausts the grace and is replaced. Stays well under the 60s local-PTY
  fail-open cap.
- daemon-pty-adapter.ts: isDaemonGoneError now treats 'Hello response timed out' as
  daemon-gone, so a runtime wedge triggers withDaemonRetry's respawn (re-entering the
  same grace-bounded launcher) instead of failing every spawn until app restart.

Tests pin the grace magnitude so it cannot be silently shrunk.

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

* fix(daemon): widen wedged-daemon grace window to ~60s

Bump WEDGED_DAEMON_GRACE_RETRIES 3 -> 11 (~20s -> ~60s) to keep live-session
loss on the transient-wedge (Windows update-relaunch) path as close to zero as
possible. A transient wedge drains early and stays under the 60s fail-open cap;
only a permanent wedge runs the full window. Export the constant and pin its
floor in tests so it can't be silently shrunk.

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

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-14 01:50:56 -07:00
Neil 8e0977d295 fix(mobile): resync worktree list + idempotent notification replay on reconnect (#8498 #8129)
Take-over of #8605 (issue #8591). Ships #8498 (worktree resync + pull-to-refresh + cache write-through) and #8129 (idempotent notification replay on reconnect). Fixes the original PR's field mismatch (seq vs notificationSeq) and adds the missing notifications.getMissedSince mobile RPC allowlist entry. #6784 and #4500 held back to avoid conflicting with the relay work (#8536). Co-authored-by: Brandon Bennett (@branben).
2026-07-14 00:23:53 -07:00
Brennan Benson 36cd8a3347 fix(terminal): retire sessions when tabs close (#8628)
* fix(terminal): retire sessions when tabs close

* fix(terminal): close remaining session lifecycle gaps

* fix(terminal): close review-discovered lifecycle gaps

* fix(terminal): revalidate bulk session retirement

* fix(terminal): harden retirement review edges

* fix(agent): reverify restored pane authority

* test: make terminal retirement gate portable on Windows

* test: use POSIX join in Linux PATH assertion

* test: keep simulated Linux PATH host-consistent
2026-07-14 00:15:34 -07:00
moseohandOrcaWin 7d8c4fdca1 fix(github): pin work-item list ordering to updated-desc so cursor pagination reaches every page (#8658)
* fix(github): pin work-item list ordering to updated-desc so cursor pagination reaches every page

The Tasks page paginates work items with an updatedAt cursor
(updated:<oldest-item), but the underlying gh calls never pinned a sort:
'gh issue list' defaults to created-desc and '--search' defaults to
best-match. Items created long ago but updated recently therefore never
appeared on any page — page 0 (created order) skipped them and every
later page excluded them via the cursor — so the pager advertised pages
the fetch chain could never reach, clicks on them clamped to the last
real page, and cross-page ordering was scrambled.

Append sort:updated-desc to every list/search invocation so the fetch
order matches the cursor field on the first and all subsequent pages.

Verified against a live 588-issue repo: the cursor chain previously
died around page 5; it now traverses 585/588 unique issues (the
remainder is the pre-existing strict '<' boundary edge for items
sharing the cursor's exact timestamp).

Fixes #8649

* fix(github): make work-item cursor pagination lossless at updatedAt boundaries

Builds on the sort-pin fix: switch the pagination cursor from strict
'updated:<' to inclusive 'updated:<=' so items sharing the boundary row's
exact updatedAt are no longer skipped between pages (the residual 3/588 edge
in #8649).

The inclusive bound re-fetches the boundary rows, so dedupe them by repoId+id
(a bare item.id like 'issue:9' collides across repos). Extract the page
accumulation out of the 12k-line TaskPage component into a pure, unit-tested
helper (accumulateWorkItemPages) that dedupes and backfills: it accumulates
fresh rows across fetches and emits uniform pageSize pages, so deduped pages
never shrink below the size totalPages (count / effectivePageSize) assumes —
which would otherwise strand the tail items and break the no-count degraded
pager.

Also hoist the updated-desc ordering into a named WORK_ITEM_LIST_SORT_QUALIFIER
constant so the cursor's ordering contract has one home.

Tradeoff: when per-repo fetch size equals pageSize, the boundary dedupe costs
one extra fetch per page; acceptable for interactive pagination and bounded by
the gh rate-limit guard. Persisting the cursor/buffer across calls is a
possible follow-up.

---------

Co-authored-by: OrcaWin <alpha-eng@stably.ai>
2026-07-13 22:37:46 -07:00
Jinwoo HongandOrca b379610cbe fix(browser): handle Cmd-click popups without adopted WebContents (#8659)
Co-authored-by: Orca <help@stably.ai>
2026-07-13 20:48:00 -07:00
JinjingandOrca 1ef0551bc1 Create pr not working for stacked worktree (#8651)
* Fix stacked-worktree PR creation targeting a local-only parent branch

- Resolve the eligibility default base to a remote-tracking ref instead
  of blindly trusting the submitted parent branch, since a stacked
  worktree's base is often a local-only branch the remote can't resolve
- Add a create-time hard block (base_not_on_remote) so a stale or
  unpushed submitted base fails with actionable copy instead of the
  provider's opaque error
- Update the dialog's default-base resolution and blocked-action/
  dropdown copy to match the new remote-validated default

* Split hosted-review-creation.test.ts to fix max-lines lint error

Moved getHostedReviewCreationEligibility tests to a separate file (hosted-review-creation-eligibility.test.ts) to reduce the original file size from 880 to 579 lines, satisfying the max-lines lint constraint.

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

* Fix Create PR intent flow to use remote-validated eligibility default fo

Prefer eligibilityDefaultBaseRef over the raw compare base when resolving
the review base for the one-click Create PR intent flow, since eligibility
is recomputed from the same compare base right before creation and already
corrects a local-only stacked parent to the repo default. Falls back to
the compare base only when eligibility supplies no default.

* Simplify base-ref remote existence check into a single for-each-ref call

Combine the wildcard and exact-tracking-ref lookups into one for-each-ref
invocation with multiple patterns instead of two sequential git calls,
removing the redundant rev-parse fallback path.

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-13 20:10:50 -07:00
NeilandOrca 8662e5a7ab perf(ssh): merge the two identical 5s keepalive timers into one per connection (#8653)
Co-authored-by: Orca <help@stably.ai>
2026-07-13 19:46:08 -07:00
Jinjingandbbingz 1d2aaf1bf5 Fix recipe serve desktop promotion (#8646)
* fix(runtime): preserve terminals during headless desktop activation

* rm design doc

* Fix desktop activation launch ordering and blocked-window status resolut

- Check desktopWindowStatus before spawning the Orca app so a blocked
  runtime no longer launches a doomed second instance.
- Reuse resolveDesktopWindowStatus for remote runtime status so it
  honors the same authoritativeWindowId fallback as local status.
- Re-check the authoritative window at spawn time instead of trusting
  a possibly-stale snapshot, since it can be destroyed mid-await.
- Harden the e2e activation spec against silent spawn failures.

---------

Co-authored-by: bbingz <zzb@gxsmjx.com>
2026-07-13 19:41:24 -07:00
NeilandOrca a68bb39ab1 perf(emulator): stop serve-sim watcher waking the daemon 4×/sec when unused (#8525)
Co-authored-by: Orca <help@stably.ai>
2026-07-13 19:18:37 -07:00
Jinjing f93e92646c P2 watcher lifecycle bounds (#8640)
* fix watcher lifecycle cancellation bounds

* fix(review): guard remote watcher installs against post-shutdown resurrection

closeAllWatchers aborted the in-flight install *tokens* it could see, but a
same-key joiner awaiting a 'cancelled' resolution (and a fired retry tick)
calls installRemoteWatcher directly and, on the fresh-generation recursion,
builds a brand-new non-aborted AbortController and calls provider.watch()
after teardown — leaking an SSH watcher into the just-cleared remoteWatchers
map. Latch the subsystem shut in closeAllWatchers and refuse installs while
latched; a genuine new fs:watchWorktree clears it. Adds a regression test
(fails without the latch) plus a test for the same-tick handoff-revival guard
that had no coverage.

Also extract the duplicated isolated-quarantine-vs-fuse branch shared by
retireSlot and releaseFailedRoot into quarantineOrFuse (behavior-preserving).

* Add lifecycle generation guard to refuse stale remote-watcher joiners

- A boolean latch alone can't distinguish a pre-shutdown joiner from a
  fresh call once a genuine new watch reopens the subsystem, letting a
  stale joiner recurse and register a post-shutdown provider.watch()
- Each installRemoteWatcher call now captures a generation counter that
  closeAllWatchers bumps, so a waiter that resumes after a later
  shutdown+reopen is refused instead of resurrecting
- Adds a regression test covering the shutdown-then-reopen race
2026-07-13 19:12:30 -07:00
Jinjing 79369d5396 Fail loudly on chmod errors in remote CLI launcher install (#8648)
Silently swallowing chmod failures let installs proceed with a
non-executable launcher, surfacing as a confusing runtime error later
instead of a clear install-time failure.
2026-07-13 19:08:46 -07:00
JinjingandOrca 01d7cd779f P2 mobile firewall scope (#8639)
* fix(mobile): validate Windows firewall remote scope

* fix(review): simplify string-guard ternary to boolean AND

The ternary returned only boolean literals, so cond ? f() : false is
equivalent to cond && f() (addressScopeIsSufficient returns boolean).

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

* fix(mobile): accept dotted-netmask firewall scopes and lock in fail-safe edges

- Parse dotted-netmask CIDR (192.168.0.0/255.255.255.0) via a contiguous-mask
  check, failing closed on holey masks.
- Factor subnetFromParsed so CIDR parsing no longer re-parses the address.
- Document why single-host (/32, /128) subnets and family-specific keywords with
  an unknown interface family fail closed, and add regression tests covering
  those deliberate false-deny edges plus policy-defined keywords (Intranet, DNS).

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

* Add explanatory comment on why firewall scope check isn't unioned

Documents the fail-safe rationale behind checking coverage per rule
instead of merging rule scopes, so future edits don't "fix" this
into a less conservative union check.

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-13 19:02:52 -07:00
Jinjing 302b97029a P2 windows cli hardening (#8638)
* fix(cli): harden Windows launcher transports

* Fix csc.exe compile failures on space-bearing Windows install paths

- Legacy csc.exe mangles absolute paths containing spaces, so the
  compile step now cd's into the bin directory and passes bare
  file names for /out and the source file instead of full paths
2026-07-13 18:58:21 -07:00
NeilandOrca 5886ffab63 Fix orchestration skill coverage for provider-home skill roots (#8256) (#8510)
Co-authored-by: Orca <help@stably.ai>
2026-07-13 18:13:36 -07:00
Brennan Benson 2f660a6028 fix(source-control): route GitHub Enterprise Server remotes to the GitHub provider for PR creation (#8312) (#8603)
* fix(source-control): route GHES remotes to the GitHub provider for PR creation

A GitHub Enterprise Server user could not submit a PR — Orca demanded
ORCA_GITEA_TOKEN — while issue sync worked fine (#8312).

Root cause: GitHub owner/repo resolution (parseGitHubOwnerRepo) hard-rejects
any host that is not literally github.com. A GHES remote lives on a custom
host, so GitHub's forge resolveRepository returned null and provider detection
fell through the list to Gitea, whose KNOWN_NON_GITEA_HOSTS denylist cannot
enumerate arbitrary GHES domains. Issue sync was unaffected because gh
issue/pr list run with cwd=repoPath and let gh resolve the GHES host natively.

Fix mirrors GitLab self-hosted detection (getGlabKnownHosts): a new
getEnterpriseGitHubRepoSlug resolves a custom-host origin to owner/repo only
when gh is authenticated to that host — gh only ever manages GitHub/GHES
credentials, so a logged-in host is definitively GitHub. Wired into:
- forge-provider GitHub resolveRepository (fallback after github.com miss),
  so detection claims GHES before Gitea is consulted;
- createGitHubPullRequest owner/repo resolution;
- isGitHubAuthenticated, which now probes the repo's real host instead of a
  hardcoded --hostname github.com.

github.com repos keep the cached getRepoSlug fast path and never spawn the
extra gh auth probe.

* fix(github): host-qualify GHES gh commands and probe auth in the repo runtime

Addresses two correctness issues found in review of the #8312 fix.

1. GHES host was discarded before `gh pr create`. `--repo owner/repo` shorthand
   resolves against gh's default host (usually github.com), so for a user
   authed to both github.com and GHES it could target a same-named github.com
   repo or fail — deterministic for SSH repos, which run gh with no cwd. Now
   `createGitHubPullRequest` and the `findOpenPRByHeadBase` fallback pass a
   host-qualified `HOST/owner/repo` for GHES (github.com keeps the shorthand).
   Also generalize `parseCreatePRPayload`'s URL regex off github.com so a GHES
   PR URL parses directly instead of limping through the list fallback.

2. GHES auth was probed on the wrong gh runtime. `getAuthenticatedGitHubHosts`
   ran a global `gh auth status` with no cwd/WSL/SSH context and cached every
   runtime under one "local" key, so a GHES login present only in the repo's
   WSL distro was missed and the repo fell back to Gitea. Replaced with
   `isGitHubHostAuthenticated`, which runs `gh auth status --hostname <host>`
   with the repository's execution options (cwd/WSL distro, or SSH-local like
   the create path) and caches per runtime+host — mirroring GitLab's
   isGlabConfiguredForRemoteHost. This also honors GH_ENTERPRISE_TOKEN inferred
   from repo context. Spawn failures stay indeterminate (uncached).

Adds createGitHubPullRequest-level tests asserting the actual gh `--repo`
arguments (create + fallback) and the WSL/SSH runtime of the auth probe.

* perf(source-control): drop redundant GHES gh auth probe in eligibility

Review follow-up. Detection only routes a GHES remote to the GitHub provider
after getEnterpriseGitHubRepoSlug has confirmed gh is authenticated to its
host, so isGitHubAuthenticated can trust a non-null slug as authenticated and
skip a second, rate-limited `gh auth status` spawn per eligibility poll.
Reaching the github.com probe now implies the remote is github.com. Tests
assert the enterprise path fires no redundant gh probe.
2026-07-13 18:06:06 -07:00
NeilandOrca 1450db85ba fix(grok): don't tell users to re-run grok login on refreshable token expiry (#8508)
Co-authored-by: Orca <help@stably.ai>
2026-07-13 17:55:47 -07:00
NeilandOrca 82573d70ae fix(orchestration): compare stale-dispatch timestamps with julianday (#8514)
Co-authored-by: Orca <help@stably.ai>
2026-07-13 17:46:25 -07:00
1724eef2f0 fix(window): extend startup reveal fallback to Linux so first launch never stays hidden (#8425)
* fix(window): extend startup reveal fallback to Linux so first launch never stays hidden (#8421)

On Linux/X11, ready-to-show can never fire (GPU/driver quirks), leaving the
only BrowserWindow hidden until a second launch triggers the second-instance
reveal path. Reuse the existing bounded Windows fallback timer on Linux; the
handledInitialReadyToShow guard and headless E2E check already make the
reveal idempotent and safe.

Claude-Session: https://claude.ai/code/session_017rio4rnPiCUh8jHWxkq4xH

* docs(window): drop win32-only qualifier from tray-fallback comments

The tray-create fallback comments referenced 'createMainWindow's win32 10s
reveal fallback', but #8421 extends that reveal fallback to Linux too. Since
the tray itself is win32-only (createSystemTray no-ops off win32), naming a
platform in these comments is both stale and misleading. Drop the qualifier;
the surrounding Windows-only tray context already scopes it.

---------

Co-authored-by: kaynan <kaynan.camargo@terceiro-sky.com.br>
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
2026-07-13 17:36:26 -07:00
833724830f Fix folder workspace Git status path (#8326)
* Fix folder workspace Git status path

* fix(git): resolve folder-workspace path for all local filesystem ops

Extend the folder-workspace suffix stripping beyond git status: every
local Git subprocess cwd, the WSL-context probe, the first-work
branch/folder rename hooks, and the renderer session placeholders now
resolve the synthetic `::workspace:<uuid>` instance id to its backing
folder via splitWorktreeIdForFilesystem. Without this they would spawn
Git (or fs ops) against a nonexistent directory (ENOENT).

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

* Preserve folder workspace rename instance ids

* Read folder workspace suffix from worktree id

---------

Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
2026-07-13 15:47:17 -07:00
f90cd6ebc9 fix(cli): preserve WSL cwd through the Windows bridge (#6965) (#7640)
* fix(cli): preserve WSL cwd through the Windows bridge (#6965)

# Conflicts:
#	src/cli/index.test.ts
#	src/cli/index.ts

* fix(cli): preserve bridge exit codes (#6965)

* fix(cli): harden WSL cwd bridge compatibility

* chore(cli): align cwd tests with main

* fix(cli): repair deleted WSL cwd before path conversion

* chore: preserve main formatting after merge

---------

Co-authored-by: Brennan Benson <brennanbenson@Brennans-MacBook-Pro.local>
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
2026-07-13 15:44:32 -07:00
Brennan BensonandBrennan Benson 53a09afbef feat(mobile): match desktop's Smart workspace source picker exactly (#7985)
* feat(mobile): start a workspace from a branch, issue/PR, or Linear ticket

Unify mobile workspace creation with desktop. The "+" Create Workspace
modal now has a primary "Start from" field that opens a tabbed search
drawer (Branch · GitHub · GitLab · Linear), letting a user start a
workspace from an existing/new git branch, a GitHub issue/PR, a GitLab
issue/MR, or a Linear ticket — in addition to the default blank workspace.

No new backend is required: the search RPCs (github.listWorkItems,
gitlab.listWorkItems, linear.searchIssues/listIssues, repo.searchRefs) and
the worktree.create linked-item params were already used by the mobile
Tasks screen. This surfaces them in the create flow, reusing the existing
pure modules (buildTaskWorkspaceCreateParams, shouldResolveHostedReviewStartPoint,
filterAvailableTaskProviders).

Details:
- New pure modules: workspace-source-selection, use-workspace-source-search,
  source-workspace-create, worktree-create-retry, blank-workspace-create
  (the blank/retry path extracted from the modal for reuse + line budget).
- New UI: WorkspaceSourcePickerDrawer (+ row) and SetupHookTrustDrawer
  (extracted from the modal).
- Older paired desktops (missing the mobile.tasks.v1 capability) degrade to
  Branch + Blank only; GitLab/Linear tabs appear only when available.
- GitHub/GitLab sources pin their repo; switching repos resets the source.
  PR/MR sources resolve their base branch at create time; SSH repos gate
  search until connected (Linear search is repo/SSH-independent).

* fix(mobile): hydrate settings/trust before availability probes settle

Review fixes for #7985: setTrustedOrcaHooks/setRuntimeSettings no longer
wait on status.get/preflight.check/linear.status (a first-open
preflight.check can take seconds, widening the spurious setup-trust
re-prompt window). Also adds param-parity tests for createBlankWorkspace
and a GitLab MR base-resolve test.

* feat(mobile): match desktop's Smart source picker exactly

Rework the mobile create-workspace source picker to be a faithful port of
desktop's Smart picker instead of the earlier divergent "Start from" drawer.

The mobile field is now the workspace-name input AND the source search, with the
exact desktop tabs — Smart · GitHub · Linear · GitLab · Branch · Name. "Smart"
fans out across GitHub + GitLab + Linear + branches, prepends a "Use '<name>'"
row, and resolves pasted URLs / #123 / STA-42 to exact items (with a cross-repo
switch prompt). Selecting a source shows a pill and moves the editable name into
Advanced. The invented "Blank workspace" concept is removed — the neutral state
is just a typed/empty name (blank submit still yields a creature name).

DRY: the pure desktop logic (smart-workspace-source-results, -command-value,
github-links, gitlab-links, work-item-link-query-bounds, github-work-item-identity)
moves to src/shared/new-workspace/ with re-export shims at the old renderer paths,
so both renderer and mobile share one implementation. composer-branch-selection
and workspace-name were already shared and are reused directly.

Two read-only lookup RPCs are allowlisted for mobile so pasted GitLab URLs and
cross-repo GitHub URLs resolve to exact items (github.workItemByOwnerRepo,
gitlab.workItemByPath).

New mobile modules are split for max-lines: use-mobile-composer-source (selection
state + desktop-parity handlers, PR/MR base resolve), use-smart-workspace-source
+ smart-source-fan-out/-search-requests/-paste-intent (RPC orchestration),
composer-linked-work-item / work-item-lookup-text / mobile-smart-source-modes
(pure logic), and SmartWorkspaceSourceField/Drawer/Row + SmartWorkspaceAdvancedFields.
Replaces WorkspaceSourcePickerDrawer/Row, workspace-source-selection,
use-workspace-source-search, and MobileWorkspaceNameInput.

Reviewed by three adversarial agents + re-reviewed after fixes: GitHub search now
returns issues AND PRs (not issues-only), Linear defaults to assigned, create-branch
preserves slashy names, cross-repo PR base resolves against the item's own repo,
displayName is suppressed for user-edited names, and the smart-mode GitHub fan-out
respects availability. tsc/oxlint/max-lines-ratchet clean; 1328 mobile tests pass.

* fix(mobile): keep smart source drawer fully visible

* refactor: share workspace creation behavior across clients

* fix: address workspace creation review findings

---------

Co-authored-by: Brennan Benson <brennanbenson@Brennans-MacBook-Pro.local>
2026-07-13 15:38:02 -07:00
Jinjing 476b6f97d0 fix(native-chat): preserve initial chat mode on paired-host launches (#8567)
* fix(native-chat): preserve initial chat mode on paired-host launches

* fix(native-chat): keep paired launch mode authoritative

* fix(native-chat): preserve mode through PTY materialization
2026-07-13 13:06:59 -07:00
Kaynan Sampaio de Camargoandkaynan dc4fb2aa03 fix(native-chat): retry not-yet-flushed transcripts instead of settling into a permanent error (#8418)
* fix(native-chat): retry not-yet-flushed transcripts instead of settling into a permanent error

A freshly-created session's transcript .jsonl lands on disk seconds to
minutes after the process starts. Native chat's one-shot read raced that
first flush: a miss became a permanent "No transcript found" error and
the live-tail subscription silently degraded to a no-op, so the pane
never recovered even after the file appeared.

- transcript-reader/read-cache: mark the miss with notFound so callers
  can tell "not flushed yet" from a real parse/IO error (never cached).
- transcript-watch: poll resolve+install (500ms backoff, 5s cap) for the
  subscription's lifetime instead of returning a dead no-op watcher.
- use-native-chat-live-session: retry a notFound read with backoff for
  up to 60s while staying in the loading state, and let live appends
  render over a stale initial-read error.

Fixes #8401

Claude-Session: https://claude.ai/code/session_01HA5g3X7wCakBttBpDru9Fp

* fix(native-chat): address CodeRabbit review — ENOENT retryable, content over spinner, blank-id guard, unref poll timer

- transcript-reader: an ENOENT after a successful resolve is the same
  first-flush/rotation race as an unresolved path — mark it notFound.
- use-native-chat-live-session: live appends landing mid-retry render
  instead of the loading state (mirrors the stale-error gate).
- transcript-watch: bail out for a blank session id with no explicit
  file (nothing to resolve-poll), and unref the poll timer so headless
  serve shutdown is never held open by an unresolvable session.

Claude-Session: https://claude.ai/code/session_01HA5g3X7wCakBttBpDru9Fp

---------

Co-authored-by: kaynan <kaynan.camargo@terceiro-sky.com.br>
2026-07-13 12:45:46 -07:00
Jinjing 527c692b71 Improve git pull on remote (#8524)
* Fall back to a merge when a divergent pull has no reconciliation strateg

- Git 2.27+ refuses `git pull` on divergent branches unless pull.rebase or
  pull.ff is configured. Retry with `--no-rebase` (Git's historical default)
  so pulls succeed out of the box on fresh hosts.
- Skip the fallback whenever the caller already specified a reconciliation
  strategy (e.g. --ff-only, --rebase) so explicit policies still fail as
  expected on divergence.
- Applied identically in the local git pull path and the relay/SSH git
  handler so both surfaces behave the same way.

* Refactor divergent-pull merge fallback into shared helper

Extracts the retry-as-merge logic (duplicated between local git and
relay SSH pull paths) into `runPullWithDivergenceFallback` in
git-remote-error.ts, so both callers share one implementation and
test coverage.
2026-07-13 12:42:55 -07:00
JinjingandOrca 13ed697cc2 Fix windows serve wsl barrier (#8559)
* fix(startup): bound serve WSL reconciliation wait

* test(startup): cover WSL barrier fail-open on early reconciliation rejection

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

* feat(serve): surface managed WSL reconciliation status to headless clients

Expose reconciliation state ('pending'|'settled'|'failed') in the
orca_server_ready payload and a wsl-cli-barrier startup milestone, so
headless/SSH agents can tell the fail-open barrier outlived its budget
and a WSL PTY launch may still race an un-migrated registration.

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

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-13 05:40:09 -07:00
Jinjing 6091995e75 perf(worktree-watcher): skip head-identity reads on index-only bursts (#8558)
An index rewrite cannot move HEAD, yet every quiet local Git-common status
burst was calling readGitCommonHeadIdentities, which scans the primary
checkout plus every linked worktree sequentially (~1,500 file reads for 500
worktrees) even when only index files changed.

Split the conflated git-status signal into two: index churn stays
`gitStatusRepoIds`, while logs/HEAD (and other reflog head moves) become a
distinct `headIdentityRepoIds` signal. Source Control is still notified for
both, but readGitCommonHeadIdentities now runs only for true head triggers
(and structural ticks, which re-baseline silently). Index-only bursts do zero
head-identity reads.

Adds deterministic tests: index-only bursts read zero head identities across
linked and primary checkouts; logs/HEAD still refreshes identities for both;
plus coalescing and debounce coverage.
2026-07-13 05:40:02 -07:00
Jinjing 17f032dbd5 Fix WSL orchestration CLI preambles (#8561) 2026-07-13 05:39:52 -07:00
Brennan BensonandBrennan Benson 0b65d725c9 "Hide sleeping" never hides a workspace with an open agent session (#7197) (#7511)
* Keep running-agent workspaces visible under "Hide sleeping" (#7197)

The "Hide sleeping" sidebar filter judged a workspace active only when it
had a live PTY (or a browser tab), so a workspace with a running agent
whose live-PTY entry was momentarily absent — an SSH reconnect grace
window, an unmounted pane, a remote surface not yet `ready`, or an
orchestration worker reporting before its tab is mirrored — was
classified as "sleeping" and hidden while its session was still open.

The smart sort already treats a fresh `agentStatusByPaneKey` entry as
"working" independent of live-PTY, so the filter and sort disagreed. Add
`getWorktreeIdsWithLiveAgent`, which derives the worktrees with an open
agent session from the live agent-status map (sleep/teardown drop those
entries via dropAgentStatusByWorktree, so slept/hibernated workspaces
still hide), and consult it in `hasActiveWorkspaceActivity`. Wire it
through the sidebar list, Cmd+J jump palette, and kanban board.

* fix(agent-status): align live workspace attribution

* fix(mobile): preserve live-agent workspace activity

* fix(mobile): prefer newest agent status source

* chore: restore unrelated benchmark formatting

* fix(mobile): resolve projected agent worktree ids

* perf(mobile): index projected worktree summaries

* fix(mobile): preserve projected activity under limits

* fix(mobile): preserve POSIX path identity

* perf(mobile): cache projected summary fallbacks

* fix(mobile): preserve remote path and priority contracts

* perf(mobile): index projected paths by host flavor

* test(mobile): prove projected path index keys

* perf(mobile): bound projected path fallback

* perf(mobile): reuse projected repo platforms

* test(mobile): enforce projected lookup bounds

* chore(runtime): remove review instrumentation

* perf(mobile): skip unresolved repo platform scans

* perf(mobile): batch represented project runtimes

* perf(mobile): batch cold project runtime scans

* fix(mobile): couple worktree platform snapshots

* fix(sidebar): prioritize attributed headless agents

* fix(sidebar): activate smart sort for headless agents

* fix(sidebar): prefer mirrored agent ownership

* fix(mobile): follow mirrored agent ownership

* fix(sidebar): resolve mirrored unstamped agents

---------

Co-authored-by: Brennan Benson <brennanbenson@Brennans-MacBook-Pro.local>
2026-07-13 01:10:38 -07:00
Kaynan Sampaio de CamargoandJinjing c3ab805d12 fix(agent-hooks): drain stdin before hook script early exits so agents never hit EPIPE (#8430)
* Fix hook scripts to drain stdin before any early-exit path

Generated agent hook scripts and missing-script launchers could exit
successfully before consuming the payload written to their stdin,
leaving the writer with a broken pipe (EPIPE/ERROR_BROKEN_PIPE) once
the reader closed early. Capture stdin (or drain it via a shared
epilogue/fast-path guard) before any whole-script success exit across
all POSIX, batch, PowerShell, and Git Bash launcher variants, and add
a cross-agent lifecycle test suite plus a live Electron verification
script to guard the contract going forward.

* Harden hook scripts against unreadable managed scripts and add a Claude/

- Extend the POSIX launcher guard to also require `[ -r ]`, not just `-f`/`-x`,
  so an executable-but-unreadable managed script still drains stdin instead of
  erroring or silently misbehaving.
- Add a verifier case (`verifyClaudeDevinSkip`) that spins up a local HTTP
  server and confirms the Claude hook never forwards a request that Devin
  already imported, catching accidental double-forwarding.
- Update installer-utils tests and stdin-lifecycle docs to match the new
  readable-file guard and the added verification case.

* Fix hook-launcher verification to derive script paths from the installed

Extract the quoted path from the launcher's `if [ -f '...'` clause instead of
reconstructing it via join(home, ...), so missing/failing-script test cases
can't silently fall through to the real script if the install layout changes.

---------

Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
2026-07-13 00:26:22 -07:00
db9421dd3c Prevent index churn from refreshing worktrees (#8431)
* Prevent index churn from refreshing worktrees

* Cover IPC contract in worktree reliability gate

* Refresh background worktree heads without re-entering structural fanout

External commits, amends, and soft resets in non-active worktrees now reach
store rows through spawn-free Git metadata reads diffed in the watcher's
existing debounce, emitted only on real head moves. HEAD reflog appends become
status-only triggers, config.worktree becomes structural for sparse-flag
freshness, and the non-darwin poller gains a periodic ungated index re-stat
so in-place rewrites on coarse-mtime filesystems cannot be missed forever.

* Reject unsafe symref paths and validate object ids in the head reader

Ref content comes from repo files an attacker can craft. Backslash segments
traverse on Windows where join treats them as separators, and colons are
forbidden in Git ref names; both now fail isSafeRefName before any path is
built. Resolved values are additionally emitted only when they match a hex
SHA-1/SHA-256 object id, so no file content can leak through the identity
event even in principle.

---------

Co-authored-by: Brennan Benson <brennanbenson@Brennans-MacBook-Pro.local>
Co-authored-by: Brennan Benson <>
2026-07-12 23:43:33 -07:00
Brennan Benson 43e481b1c3 Revert "Decouple feature copy from translated locale catalogs (#8488)" (#8500)
This reverts commit a5e9e139b1.
2026-07-12 23:43:07 -07:00
Brennan Benson a5e9e139b1 Decouple feature copy from translated locale catalogs (#8488)
* Decouple feature copy from locale catalogs

* Update PR workflow contract tests

* Address localization review findings

* Document localization cache context
2026-07-12 23:42:45 -07:00
91f56c7255 Fix Windows focus stealing from agent foreground-process scan (hide conhost window) (#8053)
* Hide console window for Windows agent foreground-process scan

Agent foreground-process inspection re-forks powershell.exe (or the wmic fallback) to detect which agent runs in each terminal. Both spawns omitted windowsHide, so on Windows each fork popped a fresh conhost console window that flashed and stole keyboard focus from the foreground app — including Orca's own terminal — recurring roughly once every few tens of seconds while an agent session was open (and more often under continuous agent output).

Add windowsHide: true to both probes (matching the codebase-wide convention) plus a regression test asserting the spawn options.

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

* test(windows): include scan root in foreground fixtures

---------

Co-authored-by: xucongwei <xucongwei@bytedance.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com>
2026-07-12 23:32:17 -07:00
Jinjing 34a9df7795 Fix wsl cli rc4 migration (#8471)
* fix(cli): migrate managed WSL launchers to native exe

* refactor(cli): reconcile managed WSL registrations

* fix(cli): harden WSL registration reconciliation

* fix(cli): serialize and harden WSL CLI registration lifecycle

- Cap per-distro concurrent repairs and add a filesystem lock so two
  Orca installs can't race the same distro's launcher/bridge files
- Skip re-adopting a legacy managed launcher when its bridge is user-
  owned, avoiding a doomed install retried on every startup
- Track per-distro reconciliation against launcher target + app
  version so unchanged registrations aren't re-probed each boot
- Replace command install with a single atomic rename instead of a
  move-based backup, closing a window where no orca-ide exists
- Remove the registry invalidate-on-persist-failure path now that
  writes are atomic and repair is disk-authoritative
- Add brief negative caching to WSL distro discovery to bound
  wsl.exe respawns on persistently broken hosts
- Extract keyed-promise-queue as a shared serialization primitive
2026-07-12 23:19:15 -07:00
Jinjing 52388c1ab7 Fix worker done pane identity (#8473)
* fix(orchestration): bind lifecycle sends to dispatched pane

* fix(orchestration): bind injected worker messaging

* Fix worker_done pane identity gating and rename SSH linear CLI files

- Lifecycle reconciliation now returns an explicit `rejected` action
  (instead of `ignored`) for persisted sender_not_assignee markers, and
  only treats a payload's `_orcaLifecycleRejection` as trusted state
  when it exactly matches that reserved shape, preventing caller-
  supplied markers from spoofing a rejected send as success.
- SSH legacy fallback CLI now fails closed with
  `no_active_sender_terminal` when a worker_done/heartbeat send has no
  resolvable sender identity, rejects mixed raw/structured payload
  flags, and reconstructs structured lifecycle payload fields
  (task/dispatch id, files-modified, report-path, phase) instead of
  dropping them — extracted into ssh-remote-orchestration-send.ts.
- Renamed ssh-remote-linear-argument-error.ts to
  ssh-remote-cli-argument-error.ts since RemoteCliArgumentError is now
  shared beyond the Linear CLI.

* Add re-read logging for already-converted lifecycle rejections

Send-path reconcile converts worker_done/heartbeat rejections with a
no-op logger, so the coordinator's later re-read is the only chance
to surface the rejection message. Log it there instead of dropping it.
2026-07-12 22:45:33 -07:00
Jinjing 5856a9f038 fix(agent-status): surface tool-failure errors across Cursor/Copilot/Grok (#8483)
* fix(agent-status): surface Claude tool failures

* Fix compact sidebar hiding tool-failure errors behind stale tool name

Extract a shared clearActiveToolFieldsUpdate() helper and apply it to
Cursor's postToolUseFailure, Copilot's PostToolUseFailure/ErrorOccurred,
and Grok's post_tool_use_failure events, matching the existing Claude
behavior so the failure message surfaces instead of the last tool name.
2026-07-12 22:21:37 -07:00
81ed5734d6 fix(grok): prefer auth.x.ai session and mention Grok in OSC52 toast (#8392)
* fix(grok): prefer auth.x.ai session and mention Grok in OSC52 toast

auth.json can hold multiple issuer keys; Object.values order could pick a
stale entry before the live auth.x.ai OIDC session. Prefer auth.x.ai keys
and fall back only when none exist. Also list Grok in the OSC52 blocked
clipboard toast so Grok TUI copy failures point at the same setting.

* fix(grok): harden preferred auth session selection

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

---------

Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
Co-authored-by: Orca <help@stably.ai>
2026-07-12 21:45:36 -07:00
Jinjing e3c47eff17 Fix ssh watcher isolation (#8463)
* fix(ssh): isolate relay filesystem watchers

* Fix relay watcher fault-harness pid file and in-process fallback isolati

- Use exclusive ('wx') creation for the fault-harness pid file so a leaked
  ORCA_WATCHER_CHILD_PID_FILE env var can't clobber an existing file, and
  have the harness remove the file after reading a replacement pid.
- Force useInProcessVitestFallback to false in the relay watcher pool so a
  leaked VITEST env var can never load the native watcher addon in-process
  on the relay; fail closed instead when the isolated child is missing.
- Thread an injectable RelayWatcherProcessPool into FsHandler/
  RelayFilesystemWatchRegistry for tests, and add coverage for both fixes.
2026-07-12 21:34:59 -07:00
Jinjing 433df4be3c fix(runtime): prevent file watcher SIGSEGV from crashing orca serve (#8370)
* Fix crash-isolated file watcher process pool for orca-serve SIGSEGV afte

Replace the worker-thread runtime file watcher with a forked, crash-isolated
@parcel/watcher child process pool so a native FSEvents fault can no longer
take down the main/serve process, and add bounded event batching, delivery
backpressure, and quarantine-based recovery for faulty watch roots.

* Fix crash-isolated file watcher teardown and shutdown leaks

- Fault harness could throw before mkdtemp/realpath completed, skipping
  cleanup; now tracks each temp path independently and races an async
  watcher-callback error so it can't escape the try/finally unhandled.
- In-process fallback swallowed unsubscribe failures via a bare rejection
  handler that could still throw; use .catch() instead.
- Watcher process entry's cancel-subscribe handler now reuses the async
  unsubscribe path when a crawl already finished, releasing the native
  handle instead of leaking it (blocks worktree unlock on Windows).
- Runtime watcher process pool exposed no real dispose(); shutdown now
  kills pooled children so they don't outlive the main process.

* Fix disposeSlot double-iteration bug in file watcher pool teardown

Remove the unnecessary array snapshot in dispose(): disposeSlot mutates
allSlots by deleting the slot being visited, and deleting the
in-progress element during Set iteration is well-defined, so the spread
copy was dead weight left over from prior debugging.

* Fix pending file watcher installs not aborting on unsubscribe

- Local/WSL watcher installs and SSH fs.watch setup now honor the
  in-flight AbortSignal, so the last unwatch cancels a slow native
  subscribe or remote setup instead of waiting for it to finish.
- Thread signal through IFilesystemProvider.watch and SSH-backed
  file explorer watches for the same early-cancel behavior.

* Fix crash-resubscribe hangs and SSH watch teardown races in file watcher

- Add a bounded deadline for post-crash resubscription crawls so one
  stuck root quarantines instead of pinning its whole shard forever.
- Report FSEvents overflow as recoverable so delivery continues after
  a dropped-events error instead of surfacing as terminal.
- Make WSL watcher abort errors real DOMException instances so
  AbortSignal-based cancellation checks recognize them.
- Rework SSH watch registration so ownership of the shared setup
  request (not just the first caller) decides teardown, preventing
  one caller's abort from cancelling another's shared watch and
  guaranteeing exactly one fs.unwatch per registration.
- Reformat reliability-gates.jsonc arrays and refresh WSL/SSH coverage
  entries and evidence runs to match the above.

* Add CI gate to run the file-watcher SIGSEGV fault harness under Electron

- The reliability gate and release workflows (mac, Linux) previously only
  exercised the crash-isolation harness under vanilla Node, which doesn't
  catch runtime differences in the actual Electron binary that ships to
  users.
- Adds an `ELECTRON_RUN_AS_NODE=1 pnpm exec electron ...` run of the same
  harness alongside the existing Node run, so #8212's SIGSEGV-survival
  contract is proven against both runtimes before packaging.

* Add CI gate blocking Linux/macOS release packaging on watcher fault reco

Adds a contract test asserting release-cut.yml and release-mac-build.yml
run the runtime-file-watcher-fault-harness after building and before
publishing artifacts, so a regression in watcher process fault recovery
fails release packaging instead of shipping silently.

* Fix use-after-clear crash in failAllWatcherSubscriptions

Snapshot the records map before iterating, since onTerminalError
hooks can dispose the supervisor and clear `records` mid-loop,
causing a crash. Also update the matching test to assert against
the shared buildParcelWatcherIgnoreOptions helper instead of a
loose arrayContaining match.

* Fix use-after-clear crash in failAllWatcherSubscriptions

Snapshot watcher records with Array.from instead of spread, since
spread syntax over an iterator that's mutated mid-loop by
onTerminalError hooks can produce inconsistent results.
2026-07-12 18:15:15 -07:00
Brennan BensonandBrennan Benson 00fc452c21 Split client-ui RPC schemas into client-ui-schemas.ts (#8446)
#7598 pushed client-ui.ts to 301 lines, one over the max-lines limit, which
breaks repo lint for every PR that merges current main (verify does not run
on main pushes). Follow the existing browser-schemas.ts/computer-schemas.ts
pattern: zod schemas move to a sibling module, the method registry stays.

Co-authored-by: Brennan Benson <>
2026-07-12 18:07:06 -07:00
Brennan BensonandOrca 3c87873dca Fix Command Code smart-sort bump on new prompt while working (STA-350) (#5286)
Co-authored-by: Orca <help@stably.ai>
2026-07-12 18:05:11 -07:00
nikg24andnikg24 67cd462b24 Add Cursor orchestration group routing (#8436)
Co-authored-by: nikg24 <228026988+nikg24@users.noreply.github.com>
2026-07-12 17:39:18 -07:00
Neil d452385bf7 fix(mobile): offer scoped Windows Firewall repair (#8439) 2026-07-12 17:30:22 -07:00
Jinjing 88d6fdad27 Harden SSH import path handling against traversal and Windows device nam (#8435)
- Validate each remote path segment (name, symlink target, directory
  entry) via assertSafeRemotePathSegment before it reaches the remote
  filesystem, since Windows canonicalizes reserved device names and
  NTFS streams in ways POSIX checks miss.
- Extract directory pre-scan/upload logic into
  filesystem-import-ssh-directory.ts and add captureLocalUploadRoot to
  detect a selected root swapped out from under an in-progress import.
- Add path-safety tests covering traversal, reserved Windows names,
  and TOCTOU root replacement.
2026-07-12 16:53:43 -07:00
7fe6742b8b fix(pr-comments): let users mark comment authors as bots for the Humans/Bots filter (#7598)
* fix(pr-comments): let users mark comment authors as bots for the Humans/Bots filter

Some review bots post from regular user accounts that defeat both provider
bot metadata and login heuristics, so their comments were misclassified as
human. Adds a persisted prBotAuthorOverrides setting with a "Mark author as
bot" comment action, applied consistently across desktop and mobile.

Fixes #7597

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(pr-comments): address review feedback on bot-author overrides

- Cap sanitized prBotAuthorOverrides at 500 entries so malformed payloads
  can't bloat GlobalSettings or slow comment classification
- Reuse the shared normalizePRCommentAuthorLogin in isBotPRComment on
  desktop and mobile instead of duplicating the normalization inline
- Pass botAuthorOverrides from CommentRow to CommentMoreMenu instead of
  re-subscribing per menu instance
- Re-fetch mobile bot-author overrides alongside each PR refetch so they
  don't stay a stale one-shot snapshot for the whole session

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(pr-comments): harden bot author override sync

* fix(pr-comments): bound and recover override updates

* fix(pr-comments): merge overrides from canonical settings

* fix(pr-comments): make bot override updates atomic

* fix(pr-comments): surface rejected bot overrides

* fix(i18n): translate bot override warning

* fix(i18n): translate bot author actions

---------

Co-authored-by: Dzmitry Bachko <dbachko@users.noreply.github.com>
Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Brennan Benson <brennanbenson@Brennans-MacBook-Pro.local>
2026-07-12 14:09:15 -07:00
Brennan BensonandBrennan Benson b01df38590 Show agent CLI failure output for branch auto-naming and generation instead of parsing per-CLI error formats (#8045)
* Surface the Pi CLI's real error when branch auto-naming and commit generation fail

Pi failures (missing provider credentials, HTTP 4xx/5xx, connection errors)
previously collapsed to the generic 'Pi CLI command failed with code 1.'
because extractAgentErrorMessage only recognized Error:-prefixed lines.
Add two stderr-only extraction passes for pi's failure formats and narrow
the unix-path redactor so pi's /login remedy token survives redaction.

Fixes the OP variant of STA-1492 (#7808).

* Replace per-CLI failure parsing with sanitized raw output excerpts

Every agent CLI formats errors differently, and the parsing passes only
ever covered the vendors someone had already debugged (the pi passes
fixed pi and nothing else). Show the output itself instead: a positional
excerpt (first two non-empty lines plus the last one) of stderr, falling
back to stdout when stderr is silent, path-redacted and capped as
before. Exit-0 runs with empty stdout now stay 'returned an empty
<result>' instead of misreporting a command failure. The sanitizer also
strips Cf characters (bidi overrides) now that provider-controlled
bodies flow through verbatim, and OSC sequences are stripped alongside
CSI.

* Keep the full CLI output of failed branch-name generation for on-demand viewing

The persisted rename-failed badge carries only a sanitized excerpt (it
syncs to paired clients), so the complete diagnosis was previously
buried in the main-process log. Failed generations now capture their
full stdout/stderr (bounded to 64 KiB per stream, head and tail) into a
main-memory-only store keyed by worktree — never persisted or synced.
The rename-failed dialog fetches it on demand and shows it in place of
the excerpt, ANSI/control/bidi-stripped; after a restart or on paired
web clients it falls back to the excerpt. Renderer-bound generation
results explicitly drop the capture so IPC payload shapes are
unchanged.

* Cover the rename-failed dialog's full-output fetch and excerpt fallback

* Split the folder-workspace title rename flow into its own module

first-work-branch-rename.ts sat at the max-lines ceiling; the folder
title flow is a self-contained concept and main's concurrent additions
to this file pushed the CI merge ref over the limit.

* Harden the failure-output capture and dialog against review findings

- Flatten bounded captures so V8 sliced strings no longer retain the
  multi-megabyte parent stream in the capture map (128 MiB -> ~4 MiB in
  a 32-entry probe).
- Require an OSC terminator and stop its char class at newlines so an
  unterminated/boundary-truncated sequence can no longer swallow the
  omission marker and diagnostic tail.
- Exclude stdout from the persisted branch-name failure detail (it can
  echo the prompt into synced metadata); the full local-only capture
  still keeps it for the dialog.
- Refetch and reset the dialog's full output when the persisted error
  changes so an open dialog never shows or copies a stale run.
- Report signal-terminated generators (null exit code) as 'was
  terminated before exiting' instead of 'code null'.

* Redact JSON-escaped Windows drive paths without breaking scheme URLs

Provider JSON bodies double backslashes (C:\\Users\\name), which slipped past the drive-letter redaction into the persisted, client-synced failure excerpt. Allow repeated backslashes only — a URL's :// must stay single so remedy links survive.

* Fix branch rename failure IPC re-registration

---------

Co-authored-by: Brennan Benson <brennanbenson@Brennans-MacBook-Pro.local>
2026-07-12 13:02:29 -07:00