Commit Graph
13 Commits
Author SHA1 Message Date
c3bf22b9a8 [P2] perf(windows): stop the capability poll respawning blocking wsl.exe probes (#11698)
* perf(windows): stop the capability poll respawning blocking wsl.exe probes

#11295 added a 30s renderer interval to `useWindowsTerminalCapabilities` whose
early-return only fires when WSL is available with at least one distro, so on the
common Windows host (no WSL) it re-ran a full capability read forever. Each read
IPCs four probes whose main-process handlers were synchronous `execFileSync` calls
to wsl.exe/pwsh.exe, blocking the Electron main event loop for up to 5s a time.

The un-latching intent is kept: a host that answers "no WSL" is still re-checked,
now on an exponential backoff (30s, +60s, +120s) that parks once the answer stops
moving, re-arms on window focus, is shared by all consumers of an owner key, and
stops entirely when the last consumer unmounts. The wsl/pwsh IPC handlers now use
async twins that share the existing caches and back off identically.

* fix(windows): classify async wsl/pwsh probe failures with the execFile error shape

The async twins feed `execFile` callback errors into classifiers written for
`execFileSync`: a non-zero exit lands on `error.code` as a number rather than
`error.status`, and a timeout is a SIGTERM kill rather than ETIMEDOUT.

So a Windows host without WSL (wsl.exe ships in System32, so it exits non-zero
instead of ENOENT) was cached as retryable, shrinking the shared window from
10min to 45s and making the still-sync callers re-pay their blocking spawn ~13x
more often; and a pwsh cold start past 5s cached "pwsh missing" for 30s,
demoting the user's PowerShell 7 preference — the exact case the ETIMEDOUT
branch exists to prevent.

Also drops a literal NUL byte from the new re-probe module's signature
separator, which made the file binary to git, and seeds `lastProbeAt` at
registration so focus churn right after mount cannot defer the first re-probe
indefinitely.

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

* perf(windows): route relay host-capability probes through the async wsl/pwsh twins

A paired web/mobile client resolves `useWindowsTerminalCapabilities` to a local
target (TabBar's `isWebClient` gate, and `useSettingsNavigationMetadata` forces
`{kind:'local'}`), so the new re-probe arms there too. But `window.api.wsl/pwsh`
on a web client is not the ipc/app.ts channel — it is `host.wsl.*`/`host.pwsh.*`
over the runtime RPC, which still ran the sync probes and blocked the desktop
main event loop on `execFileSync('wsl.exe' | 'pwsh.exe')` for up to 5s per call.

Switch those handlers and the relay preflight capability probe to the async
twins added here; they share the same caches, dedupe and backoff, so remote
callers see no behavior change.

* fix(windows): harden async capability reprobes

* fix(windows): dedupe PowerShell shell probes

---------

Co-authored-by: Orca <help@stably.ai>
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-08-07 21:59:24 -07:00
Wooseong KimandJinwoo-H f057cbc85f fix(serve): recognize CLI-form serve args on the Electron process (#12818)
* fix(serve): recognize CLI-form serve args on the Electron process

When the binary is launched as `… serve --port …` without the CLI rewrite
that injects `--serve`, normalize argv so isServeMode, headless GPU flags,
and serve option parsing all engage.

Preserves existing `--serve*` flag behavior for the CLI-spawned path.

Fixes #12677

* fix(serve): treat only CLI subcommand position as serve

Parse bare `serve` as the first positional token after flags/values so an
option value named `serve` cannot enable headless mode.

Addresses CodeRabbit on #12818.

* fix(serve): keep CLI redirects ahead of the serve argv rewrite

Rewriting argv before maybeRedirectAppImageCliLaunch replaced the `serve`
positional with `--serve`, so the redirect's command-name lookup saw a port
number and bailed — dropping AppImage serve launches out of the CLI path.

Also translate `--port=6768` (the CLI accepts it, getServeOptions only reads
the next token) and the mixed `--serve --port` form, so a security-shaped flag
like `--no-pairing` can no longer read as accepted while pairing stays on.
Map lookups replace `in` on object literals, which turned a stray `serve
toString` positional into a function spliced onto argv.

* fix(serve): close the CLI-form serve gaps found in review

second-instance: shouldActivateDesktopForSecondInstance matched only `--serve`,
so a duplicate `<binary> serve --port …` — the ExecStart shape documented in
docs/reference/headless-linux-server.md — promoted the live headless server to a
desktop window, un-fixing #11935 on exactly the launch shape this PR legitimizes.

findServeSubcommandIndex consumed a flag's value unconditionally while the
rewrite consumed it only when the next token was not flag-shaped. The two could
disagree and swallow the `serve` token, leaving `--serve` uninjected: #12677
again in a new shape (`--port --port serve`, `--port -- serve`). Both scans now
share one definition of value consumption.

`<binary> serve --help` / `serve help` bound a network-exposed runtime server
with pairing on and printed nothing; the AppImage redirect already routes those
three tokens to the CLI, so refuse them here too.

`--no-pairing=false` translated to `--serve-no-pairing` with the value dropped,
disabling pairing for an operator who asked for the opposite. The CLI reads its
serve booleans as `flags.get(name) === true`, so a boolean is now translated only
in its bare form and the `=` form rides through as the CLI treats it.

Tests: spec-derived parity between src/cli/specs/serve.ts and the rewrite,
covering both ends of the contract (serveOrcaApp and getServeOptions); a
source-text lock on the index.ts redirect/rewrite ordering, which reverted
silently green before; an exhaustive self-consistency property test; and the
real GUI launch argv shapes that must never enter serve mode.

---------

Co-authored-by: Jinwoo-H <Jinwoo-H@users.noreply.github.com>
2026-08-06 23:56:34 -07:00
Jinwoo Hong 84df99e2f1 test(serve): pin zero duplicate agent resumes across headless serve desktop promotion (#12666)
STA-1716 reported that a packaged `orca serve` could become the single-instance owner after the desktop app exits, leaving Dock/Finder unable to restore a window — and that forcing a reopen made the headless process hydrate a renderer that interrupted and DUPLICATED live agent sessions.

Verification against main found every criterion already fixed (#8646 for desktop promotion and the fail-closed CLI, #12212 for duplicate serve activation, #12574 + #9729 for the resume/ownership guards). The genuine gap was criterion 6: the ticket's own automated regression never existed. An existing reliability gate asserted PTY identity survives promotion, but nothing asserted what the incident was actually about — how many agents the promoted renderer resumes.

This adds that coverage: a unit/service-level journey that drives the real single-instance lock, activation gate, settle and focus paths, then runs the real resume logic against a store seeded as a renderer freshly mounted inside the serve process, asserting zero duplicate resumes.

`settleServeDesktopActivation` moved from `index.ts` into its own module with identical semantics, so the test drives the real decision rather than re-implementing it — the earlier repro had to mirror that logic locally, which is the "test passes without running the scenario" failure mode.

Proven to be a real oracle: breaking each guard individually turns it red, and reverting the pre-#12574 pane form reproduces the incident exactly (two duplicate `codex resume` tabs).
2026-08-05 00:34:10 -07:00
NeilandOrca fdb58695e9 [P1] fix(checks): stop skipped and manual checks reporting as failures (#11700)
* fix(checks): stop skipped and manual checks reporting as failures

Route every check-classification surface through one shared helper so
desktop renderer, desktop main and mobile agree on the same verdict.

- GitLab `manual` jobs and pipelines are neutral again, not action_required/failure
- `skipped` counts as passed everywhere, including mobile
- a neutral check no longer demotes a summary that has passing checks

* fix(checks): move the check-classification parity test into the renderer project

The parity table lived in src/shared but imported a renderer module, and both
config/tsconfig.node.json and config/tsconfig.cli.json are composite projects
that include src/shared without that renderer path, so `pnpm typecheck` failed
with TS6307 on two of its three projects. Only the web project spans both trees.

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

* fix(checks): stop the Tasks-grid pill contradicting its own verdict

The checks pill's label, tone and icon all read one ProviderCheckSummary, but
getChecksLabel short-circuited on the raw `neutral` counter while the tone and
icon key off `state`. After the classification fix a PR with 19 success + 1
neutral renders an emerald CheckCircle2 pill that reads "1 unresolved", and
mobile's own label (which keys off `state`) reads "19/20 passed" for the same
summary.

Move the label into src/shared/provider-check-summary.ts so desktop and mobile
cannot fork it again, and key it off `state`.

Also covers deriveWorkItemCheckSummary, the desktop-main producer of the summary
that reaches the Tasks grid and the relay-paired mobile client. It was rewritten
here with no test at all; the parity table stands in derivePRCheckStatusFromRollup,
which is a different normalizer. The new main-process test drives getWorkItem with
a real statusCheckRollup fixture, pinning the StatusContext `state` fallback that
would otherwise be deletable with the whole suite still green.

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

* fix(gitlab): route the pipeline job-array rollup through the shared check classifier

The array path in derivePipelineStatus kept its own copy of the rollup rules, so
manual-only read green and one unrecognized job status demoted a passing pipeline
to neutral — both disagreeing with every other check surface.

Also retry the packaged-CLI smoke temp cleanup on Windows: the copied Orca.exe can
still be locked by AV/indexers after every assertion passed, failing the package job.

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

* fix(gitlab): stop the skipped pipeline string diverging from the Checks tab

- classifyPipelineString now counts a skipped pipeline as passing, matching
  the per-check classifier; canceled stays neutral and is pinned as an
  explicit, sign-off-pending divergence.
- Pin the production string path (head_pipeline.status) in the parity table
  and note that the job-array branch has no production caller yet.
- Count skipped checks in the Checks panel's passing header so it agrees
  with the checks pill.
- Correct the packaged-CLI smoke retry comment: the EBUSY is the smoke's own
  just-exited Electron process, not AV/indexers.

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

* fix(checks): finish cross-surface check parity and back out the skipped MR-card flip

Review follow-ups on the check-classification PR.

- PullRequestPage and GitHubItemDialog kept private copies of getCheckCounts /
  getChecksSummaryLabel that still counted only `success` as passing, so a
  2-success/3-skipped PR read "2 passing · 3 skipped" there and "5 passing" in
  the sidebar. Both copies move to pr-check-counts.ts, which routes the passing
  bucket through classifyCheckOutcome; action_required keeps its own amber
  bucket. The summary icon now keys off passing count, so an all-neutral PR
  stops painting a green tick above "0 of N checks passing".
- The sidebar checks header and triage strip still called
  `{status: completed, conclusion: null}` pending, contradicting the grey
  "Unresolved checks" pill. Both now read summarizeProviderChecks and render an
  unresolved chip/strip instead of an amber spinner that can never resolve.
- classifyPipelineString('skipped') is reverted to neutral. That flip painted
  MR cards green for pipelines that never ran, on the only GitLab path with
  production callers, and contradicted the same function's deferral of
  `canceled`. Both tone changes stay deferred, pinned by one test.
- classifyPipelineString('manual') resolves to pending rather than neutral: a
  blocked pipeline is outstanding, and neutral let the worktree card fall
  through to its emerald `open` default while GitLab still refuses the merge.
- TaskPage's checks pill helpers move to task-page-checks-pill.ts so the
  "1 unresolved on a green pill" fix is actually pinned by a test.
- smoke-packaged-cli no longer lets an EBUSY cleanup replace the real failure.

* fix(checks): stop completed unknown checks from spinning

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-31 04:58:15 -07:00
OrcaWinandOrcaWin 3b7ea59c5b fix(windows): make the GPU fallback actually remove the GPU child, and stop WSL latching absent (#11295)
* fix(windows): make the GPU fallback actually remove the GPU child, and stop WSL latching absent

Three Windows crash/regression fixes from shipped 1.4.156/1.4.158/1.4.159 crash reports.

GPU fallback (cluster D, 14 reports, exit 0x80000003 STATUS_BREAKPOINT):
the software-rendering fallback called disableHardwareAcceleration() plus
--disable-gpu, neither of which removes the GPU child process — Chromium still
spawns it to host Viz and merely drops the backend to software GL. Measured on
Windows 11 / Electron 43.1.0: gpuProcessCount stays 1. So a GPU process being
killed by a bad driver or an injected DLL kept dying after the fallback engaged,
on every launch, for the life of that build (the marker is sticky per version).
The crash tails show exactly this: gpu_fallback_applied followed by another GPU
crash 1.3s later. --in-process-gpu is the only switch that drops the child count
to 0; --disable-software-rasterizer is deliberately excluded because it also
kills SwiftShader, which would drop every terminal to the DOM renderer.

WSL distro list: a successful-but-empty `wsl --list --quiet` was cached for the
process lifetime. `wsl --install` reports zero distros while one is still
provisioning, so an early probe latched "no WSL" until restart — WSL appeared
during setup and then vanished from the terminal picker. Empty results now
re-probe on an exponential window (15s doubling to a 5min cap) while staying
readable, so a missing distro is still visible to isKnownMissingDistro.

WSL availability: isWslAvailable() latched false on any failure via a bare catch,
so one slow wsl.exe activation disabled WSL for the whole session. Failures are
now classified — a numeric exit status or ENOENT is answer-shaped and holds for
10min, anything else (timeout, spawn failure) retries after 45s — and both back
off per consecutive failure, mirroring isPwshAvailable.

Windows-only: every changed path is behind an existing process.platform check,
so macOS and Linux behaviour is unchanged.

* fix(windows): drop a stale WSL availability failure once a distro list succeeds

The distro-list and availability caches expire independently, and
getWslRepairReason checks availability first. So a definitive availability
failure (numeric exit status or ENOENT) held for 10-30min would keep reporting
`wsl-unavailable` even after `wsl --list --quiet` successfully returned a
distro — i.e. over a WSL that demonstrably just answered. That is the same
latch class this branch fixes, surviving in the gap between the two caches.

A non-empty distro list proves wsl.exe ran, so drop the negative availability
cache and let the next call re-probe. Scoped to non-empty lists only: those are
cached for the process lifetime, so this cannot re-spawn the blocking 5s probe
more than once. An empty list keeps its failure cache, since it re-probes on a
15s-to-5min schedule and would otherwise pay the blocking probe far too often.

* fix(windows): harden GPU safe mode and WSL recovery

* fix(wsl): make capability refresh cleanup explicit

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-07-30 17:49:32 -07:00
Neil 2a01b41638 perf(main): make worktree path dedupe linear (#8177) 2026-07-10 20:52:24 -07:00
Neil 69776e8d2b Upgrade to TypeScript 7 and Electron 43 (#8189) 2026-07-10 19:08:12 -07:00
Brennan BensonandOrca 674639205c Show and Filter Automation-Created Workspaces (#5697)
Co-authored-by: Orca <help@stably.ai>
2026-06-18 13:05:57 -07:00
Trevin ChowandJinjing 7361bff698 feat: rename worktree folder to match branch on first work (#4743)
* feat: rename worktree folder to match branch on first work

When the first agent message auto-renames a freshly created creature branch to a
short, work-derived name, also align the on-disk worktree folder and the sidebar
display name with it. Re-key every worktree-scoped slice of state — renderer
store maps plus the persisted main-process state — through the resulting id
change so the live worktree survives the rename instead of being treated as a
deletion (its tabs, terminals, browser panes, and git status all follow).

The rename is best-effort and local-only: a skip or failure (remote runtime,
Windows lock, destination taken) leaves the folder as-is and never undoes the
branch/display rename that already landed.

Squashed from the original PR #4743 commits, rebased onto upstream/main to drop
accumulated merge commits and i18n formatting churn so the branch carries only
the feature diff:
- Rename worktree folder to match branch on first work
- migrate renamed worktree session ids
- keep the live worktree alive through a folder rename
- address PR review feedback

* Address PR review feedback (#4743)

- orca-runtime: emit in-process worktreesChanged client event on folder rename, mirroring notifyBranchRenamed so onClientEvent listeners aren't left stale
- worktrees: re-key rightSidebarExplorerViewByWorktree and activeWorkspaceKey through a worktree-identity rename (both were worktree-scoped but missed by buildWorktreeRenameState)
- branch-name-from-work: treat prefix-only model output as an empty slug so the caller skips the rename instead of producing a doubled prefix
- worktree-folder-rename-target: document why posix.dirname is safe (Windows filtered out earlier)
- tests for each of the above

* Extract branch rename test helpers to a separate harness file

Move git responders, mock builders, and test event fixtures out of
first-work-branch-rename.test.ts into a new test harness file. This
reduces file length and removes the max-lines ESLint disable directive,
complying with project style guidelines.

* Wrap entire OnboardingFlow in TooltipProvider

Enable the use of tooltips anywhere within the onboarding flow, rather
than restricting them to the step indicators.

---------

Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
2026-06-15 19:58:55 -07:00
Jinjing c29e6e4a8c Support Bitbucket, Azure DevOps, and Gitea PRs in worktree flows (#5382)
Extend worktree creation, remote-conflict detection, and the Checks panel UI to support linked PRs from Bitbucket, Azure DevOps, and Gitea alongside GitHub and GitLab.

* Extract shared metadata lookup helpers to map provider-specific review identifiers.
* Update remote-conflict validation to check hosted reviews on target providers during worktree creation.
* Propagate provider-specific PR states through the RPC layers and into the Checks and Source Control UI panels.
2026-06-14 23:23:31 -07:00
Brennan BensonandOrca 812ca5488b fix(preload): collapse index.d.ts into type-checked api-types.ts (#1197)
Co-authored-by: Orca <help@stably.ai>
2026-04-27 21:46:17 -07:00
b037ca6864 fix(tabs): repair mixed tab shortcut switching (#1124)
* fix(tabs): repair mixed tab shortcut switching

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* fix(typecheck): unblock tc:web project checks

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* fix(tabs): split group/fallback id matching into separate branches

Why: matching both `tabId` and `id` in one findIndex predicate mixed two
identifier domains and risked a pathological collision between a tab's
backing entity id and another tab's unified id. Keep the group-path
(strict tabId match) and the fallback-path (backing-id match) in
separate branches.

Also clarify the comment on the dual `setActiveFile` + `activateTab`
write so future readers know why both calls are needed for split-group
disambiguation.

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

---------

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
2026-04-26 16:39:14 -07:00
Jinjing d546af1b51 refactor to clean up the codebase (#412)
* chore: clean up repo root for faster README visibility

- Delete unused images (debug_orca.png, orca_3d.jpg, screenshot.png)
- Delete stale design docs from docs/
- Move tsconfig sub-configs, electron-builder config, and vitest config to config/
- Move file-drag.gif to docs/assets/ and design doc to docs/
- Update all path references in package.json, tsconfig.json, and moved configs

* fix: remove stale worktree dialog callback dependency
2026-04-08 22:49:16 -07:00