Commit Graph
1147 Commits
Author SHA1 Message Date
44f77a3b7d [codex] Continue agent work in a new session (#9170)
* feat: continue agent work in a new session

* fix: harden new-session continuation

* fix: source last prompt from provider-authenticated transcript records

Preview user entries can be tool results or harness-injected skill text,
so the continuation prompt's last-prompt hint now comes from the vault
scanner's provider-authenticated lastUserPrompt. Also softens the
continuation instructions for already-complete tasks and adds a cost
warning to the full-transcript option.

* fix: move Continue in New Session row action into the hover group

Edge-usage action; keep the resting row at three icons and reveal it with
the other session actions on hover, matching Resume's gating.

---------

Co-authored-by: jz.feng <jz.feng@aftership.com>
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
2026-07-21 18:01:31 -07:00
OrcaWin 1fef1e1ddd Relaunch macOS orca serve safely after updates (#9634) 2026-07-21 17:44:40 -07:00
OrcaWinandBrennan Benson 54c1ec5e7a fix(remote-runtime): recover terminals after network loss (#9774)
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
2026-07-21 15:18:45 -07:00
Brennan Benson e986a7ba1a feat(codex): surface nested subagents (#9637)
* feat(codex): surface nested subagents

* fix(codex): retire child rows on root stop

* fix(codex): preserve nested agent state on restart

* fix(codex): preserve nested agent identity

* fix(codex): preserve subagents across relay restarts

* chore(skills): refresh generated manifests

* fix(codex): keep inferred interrupts terminal

* chore(skills): record latest release snapshots
2026-07-21 15:12:14 -07:00
Brennan Benson e09ed30a0b fix(dashboard-popout): zoom the pop-out window, not the main window behind it (#9769)
* fix(dashboard-popout): zoom the pop-out window, not the main window behind it

The View menu's zoom handlers always sent terminal:zoom to the main
window, so zooming while the dashboard pop-out was focused zoomed the
window behind it. The pop-out also never applied the persisted
uiZoomLevel, so it always rendered at 100% in a zoomed app.

The pop-out now applies uiZoomLevel on dom-ready and follows app-zoom
changes while open (main-window zoom, settings control, mobile ui.set).
Menu zoom routes to the pop-out when it is the focused window, stepping
its own webContents zoom, and a narrow before-input-event handler
resolves the zoom.in/out/reset chords (honoring keybinding overrides)
since the pop-out has no renderer-side shortcut plumbing. The step/clamp
constants move to shared/ui-zoom-level.ts so main and renderer share one
definition.

* fix(dashboard-popout): isolate window zoom

* fix(dashboard-popout): deny unused permissions

* docs(dashboard-popout): clarify wheel zoom path
2026-07-21 14:55:14 -07:00
OrcaWinandOrcaWin 15362fde16 fix(web): preserve paired runtime ownership (#9776)
* fix(web): preserve paired runtime ownership

* fix(web): validate runtime port scan payloads

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-07-21 17:48:59 -04:00
Brennan Benson a10a2ba53c feat(linear): add MCP-style save issue (#9670)
* feat(linear): add MCP-style save issue

* fix(linear): harden save issue parity

* fix(linear): close save issue contract gaps

* docs(linear): bundle project discovery with save issue
2026-07-21 13:25:22 -07:00
Brennan Benson 87af1c8673 feat(linear): add complete issue relations (#9674)
* feat(linear): add complete issue relations

* fix(linear): harden relation reads and writes

* fix(linear): classify ambiguous relation writes
2026-07-21 13:21:19 -07:00
Brennan Benson 42a4f017b4 feat(linear): add MCP-compatible issue listing (#9672) 2026-07-21 13:16:44 -07:00
Brennan Benson be066fe8e9 feat(linear): expose issue activity history (#9667) 2026-07-21 13:13:05 -07:00
c24ebcade5 fix(rate-limits): unstick Claude "Limited" usage — respect Retry-After and feed live usage from session statuslines (#9617)
* fix(rate-limits): unstick Claude "Limited" usage and feed live usage from session statuslines

The OAuth usage endpoint's 429 Retry-After (~50 min) was ignored, so the
30s-15min automated retry lanes kept landing inside the throttle window and
the status bar stayed on a bare "Limited" indefinitely while Claude itself
worked fine.

- Respect Retry-After on 429: carry it through usageMetadata.retryAtMs and
  gate automated refetches (activation lane, poll cycles) until it expires;
  user-directed refreshes still bypass.
- Keep the last-known usage snapshot visible through rate-limited windows
  (24h) instead of dropping it after the generic 30-minute stale threshold.
- Add a managed Claude statusLine command that forwards each session's
  rate_limits (Claude Code >=2.1.80) to a new /statusline/claude loopback
  route, feeding live usage windows with zero usage-endpoint calls; OAuth
  polling pauses while the live feed is fresh. User-owned statusLine
  settings are never overwritten.

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

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

* fix(rate-limits): keep last-known window when a statusline post carries only one

Statusline payloads may report five_hour and seven_day independently; a
partial post must not wipe the other bar to null. Also document the
seconds-vs-ms epoch heuristic.

Addresses CodeRabbit review on #9617.

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

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

* fix(rate-limits): unstick Claude usage with live statusline feed

The OAuth polling endpoint is rate-limited; Claude's status often shows
"Limited" until the next poll cycle, even when quota remains. Live posts
from the statusline command update usage within 100ms, eliminating false
"Limited" displays during active sessions.

Manages install lifecycle via marker to respect user deletions. Handles
Windows payload buffering and guards before curl spawn. Protects against
live-post/OAuth-fetch races and cross-attribution during account switches.
Gracefully tolerates schema drift in statusline parsing.

* test(rate-limits): assert stale outgoing post doesn't affect incoming

Capture usedPercent before ingesting and assert it remains unchanged,
rather than checking for a specific value. This is more precise and less
brittle when testing session switch isolation.

---------

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: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
2026-07-21 00:36:42 -07:00
Brennan Benson 22d9c87872 Fix Codex automatic resume across account switches (#9696)
* fix(codex): preserve session home on automatic resume

* fix(codex): keep automatic resume homes launch-authoritative

* fix(codex): resume compressed rollout representations

* fix(codex): keep resume migration best effort

* fix(codex): reject stale automatic resume provenance

* fix(codex): enforce real home on automatic resume

* fix(codex): keep automatic resume home authoritative
2026-07-20 23:51:11 -07: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
OrcaWin 05c32c4757 fix(runtime): isolate navigation across paired clients (#9664) 2026-07-20 21:36:15 -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
OrcaWin 1293e1c0d8 Fix mixed-version mobile Codex session resume (#9678) 2026-07-20 21:16:08 -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
OrcaWin cc44acaaa3 Fix Windows ConPTY OSC color reply leaks at the PTY owner (#9651) 2026-07-20 20:50:25 -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
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
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
Brennan Benson 580f8eb492 feat(status-bar): consolidate agent usage into a single roster popover (#8761)
* feat(status-bar): consolidate agent usage into a single roster popover

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

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

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

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

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

* chore: remove unrelated formatting change

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

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

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

* chore: remove unrelated formatting changes

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

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

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

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

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

* fix(dashboard): gate and harden agent popout

* test(ipc): isolate dashboard handler registration

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

* fix(dashboard): address review feedback

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

* fix(mobile): allow legacy Codex resume preparation
2026-07-20 17:10:07 -07:00
Brennan Benson f655ae3929 fix(pi): resolve OMP tab identity from the outer wrapper, not the wrapped pi (#6364) (#9600)
* fix(pi): resolve OMP tab identity from the outer wrapper, not the wrapped pi (#6364)

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

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

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

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

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

* fix(pi): harden wrapper foreground ownership

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

* fix(pi): retain OMP owner when process scans fail
2026-07-20 16:16:34 -07:00
e218dfbf8b fix:Keep Jira linked work items attached when the composer project changes (#7770)
* Keep Jira linked work items attached when the composer project changes

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

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

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

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

---------

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

* Fix cancellation and retry handling for capped status

* Harden capped status for conflict-heavy repositories

* Harden capped status recovery and cancellation

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

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

---------

Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
2026-07-20 14:50:12 -07:00
Brennan BensonandOrcaWin e58de71f5e feat(codex): real-home routing + self-contained multi-account homes (#9501)
* feat(codex): backfill managed-home sessions into the real Codex home once per host

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

* fix(codex): harden trust grant reconciliation

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

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

* fix(codex): preserve unicode trust RPC responses

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

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

* test(terminal): isolate replacement idle reset assertion

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

* fix: harden real-home hook cleanup

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

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

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

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

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

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

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

* fix(codex): protect symlinked rolling backups

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

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

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

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

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

* fix(codex): keep session heal failures retryable

* fix(codex): keep session migration state recoverable

* fix(codex): retry republished missing session heals

* fix(codex): preserve hook symlink trust path

* fix(codex): disambiguate POSIX trust paths

* fix(codex): align hook trust source paths

* fix(codex): harden trust grant lifecycle

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

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

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

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

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

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

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

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

* fix(codex): sanitize managed account config trust

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

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

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

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

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

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

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

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

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

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

* fix(codex): harden managed trust sanitization

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

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

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

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

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

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

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

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

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

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

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

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

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

* fix(codex): bound validation harness shutdown

* test(codex): isolate hook lifecycle user data

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

* fix(codex): keep standalone home tripwire active

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

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

* fix(codex): normalize Windows trust source identity

* fix(codex): make Windows trust upgrade transactional

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

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

* test(codex): allow slow Windows tripwire startup

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

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

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

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

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

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

* fix(codex): preserve Windows reauthentication login flow

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

---------

Co-authored-by: OrcaWin <alpha-eng@stably.ai>
2026-07-20 14:34:53 -07:00
Brennan Benson 1d2ba1e39d fix: never surface coding-agent scratch worktrees in the sidebar (#9535)
* fix: never surface coding-agent scratch worktrees in the sidebar

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

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

Fixes #9388

* fix: preserve scratch worktree visibility boundaries

* fix: hide scratch worktrees from linked checkouts

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

Co-authored-by: nasagong <zinho2000@gachon.ac.kr>
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-07-20 12:18:57 -07:00
NeilandOrca 0989128287 refactor(comments): slim verbose comments in shared/cli/relay/preload (#9544)
Collapse multi-line explanatory comment blocks into single-line "why" statements
per AGENTS.md ("Document the Why, Briefly"): drop restatements of the code and
mechanism narration; keep the non-obvious reason, external refs, and directives.

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

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

Co-authored-by: Orca <help@stably.ai>
2026-07-20 03:34:44 -07:00
7386ef2857 perf(renderer): stop full durable-state save on every top-level view switch (#9002) (#9393)
* perf(renderer): stop full durable-state save on every top-level view switch (#9002)

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

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

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

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

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

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

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
2026-07-20 01:14:17 -07:00
Brennan Benson 5fcf777617 feat(mobile): Quick Commands (terminal + agent-prompt presets) (#9298)
* feat(mobile): add Quick Commands (terminal + agent-prompt presets)

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

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

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

* fix(mobile): harden quick command execution

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

* test(mobile): preserve unexpected quick command errors

* fix(mobile): harden quick command launch performance

* fix(runtime): reject malformed quick command updates

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

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

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

* fix(mobile): protect quick command data boundaries

* fix(mobile): enforce quick command limits

* fix(mobile): make quick command updates atomic

* fix(mobile): keep quick command filters recoverable

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

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

This reverts commit 169bf053b0.

* fix(mobile): gate quick commands on host capability
2026-07-19 23:42:31 -07:00
Jinwoo Hong c0f0810dd9 Fix native Windows PTY startup query handling (#9500)
* Fix native Windows PTY startup query handling

* Fix daemon boot smoke protocol lookup

* Fix Windows daemon repro protocol lookup
2026-07-19 20:25:52 -07:00
Jinwoo Hong 3a847bfac9 fix(ssh): clear stamped agent status on disconnect (#9484)
* fix(ssh): clear stamped agent status on disconnect

Batch transient cleanup by accepted SSH connection authority and use a monotonic cutoff so reconnect replay wins over delayed clears. Preserve pane launch, resume, acknowledgement, and retention metadata.

Caveat: legacy or renderer-owned rows without an accepted connection stamp are intentionally left to existing pane/PTY teardown; clearing them by host would be ambiguous.

* docs(ssh): explain stale status watermark

* fix(ssh): preserve status ordering after restart
2026-07-19 22:14:15 -04:00
Jinwoo Hong 808299cd1f fix(cli): avoid Windows PATH status timeout (#9483) 2026-07-19 21:59:22 -04:00
Hugo SáezandJinjing e3721e8cf2 fix(pi): detect ask_user_question and surface as blocked state (#9457)
* fix(pi): detect ask_user_question and surface as blocked state

   Maps Pi tool_call/tool_execution_start events with ask_user_question
   to blocked state (was working), so worktrees surface in attention sort
   and trigger notifications — matching Claude/Codex/Grok behavior.

   Guards interactivePrompt derivation to Pi-only, OMP unchanged.
   5 new tests covering blocked transition, regression, malformed input,
   and OMP guard.

* fix(pi): gate ask_user_question blocked on raw tool_name and cover state exit

Address code-review findings on the Pi ask_user_question detection:

- Gate the Pi blocked classification on the event's own tool_name (matching the
  Claude/Grok normalizers) instead of the merged snapshot, so a partial
  follow-up event can't inherit a stale ask_user_question name from the tool
  cache and spuriously re-enter blocked. resolveToolState moves back after the
  state-name guard, so it no longer runs on discarded events.
- Make extractPiToolFields' agentKind parameter required; the sole call site
  always supplies it, and optional risked a future Pi caller silently falling
  back to OMP-safe (no interactivePrompt) behavior with no type error.
- Add coverage for the transition OUT of blocked (tool_execution_end -> working,
  agent_end -> done) and that a following regular Pi tool clears interactivePrompt.

* test(pty-connection): gate confirming null sample in idle-exit veto test

CI failed on a flaky call-count assertion: one timer advance can start
multiple getForegroundProcess reads, so the confirming null sample could
land before the replacement hook owner was installed. Hold 2nd+ null
samples until the veto owner is in place instead of requiring exactly
one extra call.

---------

Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
2026-07-19 17:42:41 -07:00
Jinjing 1def694e80 Add native chat skill picker with host-aware discovery (#9480)
* Add native chat skill and command picker with host-aware discovery

Adds a unified, keyboard-first skill and command picker to native chat that:

- Uses agent-native invocation syntax (slash for Claude/OpenClaude/Grok, dollar for Codex)
- Discovers skills only on the pane's execution host (local, WSL, SSH-unavailable, or runtime)
- Groups or separates commands and skills per agent configuration
- Deduplicates by canonical path but preserves visibility through all contributing roots
- Handles IME composition, loading states, and errors without claiming PTY-level control
- Records picker telemetry (open, item accepted, send classification, discovery outcomes)
- Extends shared agent profiles to define per-agent skill grammars and source ownership

* Remove obsolete reference and design documentation

Clean up stale design specs, implementation plans, and investigation notes from
docs/reference/. These documents predate the current implementation and are no
longer actively maintained or referenced by the codebase.

* Extract shared skill discovery utilities and add skill invocation envelo

- Move skill comparison and source classification to shared module for native/WSL reuse
- Extract display text sanitization to prevent control/zero-width character spoofing
- Add native-chat command envelope parser and surfacer for skill invocations
- Extend discovery timeout backstop to account for WSL metadata read sequence

* Localize skill picker UI for Spanish, Japanese, Korean, Chinese

Translate skill picker UI strings including commands, skills, loading
states, error messages, and scope labels for the new skill picker feature
across four language locales.

* Fix skill picker bugs and improve code robustness

- Fix i18n plural handling: rename `count` to `sourceCount` to prevent unintended plural-key resolution in localized strings
- Fix skill discovery array mutations: copy `root.providers` to prevent bugs during dedup merge
- Fix image attachments being silently dropped when message text starts with /skill or agent prefix
- Extract `quoteBashString` utility for WSL command code reuse across builders
- Add line-separator safety characters (0x2028/0x2029) to skill display filter
- Remove stale doc reference links and clarify inline comments

* Add reference docs for git compatibility and headless Linux server setup

Track previously untracked operational guides in `docs/reference/` that
explain Git binary compatibility requirements across host types and how to
run `orca serve` on headless Linux. Update AGENTS.md and README.md to link
to these references.
2026-07-19 17:31:34 -07:00
Jinjing daf22f0720 Make Create PR handle sync by fast-forwarding behind-only branches (#9481)
- Create PR now fast-forwards behind-only branches before committing, using
  git pull --ff-only. This prevents the dirty-then-ahead+behind stall that
  occurred after commit without prior sync.
- Refactor runRemoteAction to return explicit status ('ok', 'failed',
  'superseded', 'skipped') instead of boolean ok + nullable error. Allows
  callers to distinguish real failures from action supersession or skips
  without stale-cache issues.
- Remove isCreatePrIntentSyncConflictError function and sync-conflict-specific
  copy since --ff-only fails cleanly if branch diverged; no merge conflicts
  to resolve.
- Extract isBehindOnlyUpstream predicate to shared module so eligibility
  checks and the one-click flow always agree.
2026-07-19 17:13:19 -07:00
Jinjing d67ede1594 Implement confirm-only PR panel composer with classified error blocking (#9428)
* Clarify PR panel guidance: classify errors and confirm-only composer

Replace the ambiguous GitHub hosted-review boolean with a four-state evidence
model (found/positive_unresolved/not_found/unknown) so "No PR found" never
appears without an accepted lookup result. Classify GitHub refresh failures
into types (rate_limited, auth, network, permission, repo_unavailable,
gh_unavailable, unknown) for stable, honest copy. Confirmed-only composer:
preserve drafts across transient failures; hide Create during hard errors and
positive-unresolved evidence. Hard errors clear only when an eligibility
request starts after the error and returns an accepted outcome. Propagate
error types and unified retry schedule through the store. Sync mobile parity
with shouldOpenChecksPanelCreateComposer gating. Localize all new copy.

* Clarify PR panel guidance: classify errors and confirm-only composer

Add reviewLookupOutcome to hosted-review eligibility and thread it through
the panel so it never claims "No PR found" without accepted evidence. A
failed lookup is unavailable, not a settled no-PR. Fail closed on positive
unresolved evidence, hard refresh errors, and unavailable lookups. Add
structured GitHub refresh-error classification with Retry-After parsing.
Implement confirmed-only composer gating based on fresh, matching-context
eligibility with hard-error clearing. Mobile gates on reviewLookupOutcome
to prevent false Create claims. Surface throwOnFailure variants for each
provider so transport failures cross the RPC boundary instead of collapsing
to null. (Design success criteria 1–4; invariant 8.)

* Add exec-error helpers for subprocess error classification

Extracts stderr/stdout parsing and Retry-After detection into a
lightweight module that can be imported without pulling in the heavier
runner machinery. Supports PR-refresh error classification and proper
rate-limit handling for gh commands.

* test(mobile): include reviewLookupOutcome in create eligibility fixtures

Create / Push & Create now fails closed unless the lookup is not_found.
Update mobile test fixtures so accepted-no-PR cases can still proceed.

* Add OrThrow mock variants to forge-provider test mocks

forge-provider resolves branch reviews via the OrThrow variant so
lookup failures surface as unavailable instead of "no PR found".
2026-07-19 16:32:36 -07:00
JinjingandOrca c5d40565af Clarify Orca Mobile pairing connection paths (#9425)
* Restructure mobile pairing setup into a clear stepped layout

* Rework mobile pairing UI: radio-style path selector with sign-in gate fo

* fix(mobile): resolve mobile-pairing review findings

Applies the code-review findings for the reworked pairing UI:

- Clear a displayed Relay QR on sign-out in both MobilePage and MobilePane
  (the rework dropped the wasSignedInRef watcher, leaving a stale Relay QR
  next to a "sign in required" prompt). Anywhere stays selected; the QR
  re-mints as local-only. [#1, #3]
- Extract useMobilePairingConnectionMode so both panes resolve the saved
  preference identically instead of duplicating the state + resync effect. [#6]
- Delete ~24 i18n keys the rework left unreferenced; sync locale catalogs
  to parity. [#4]
- Give the connection-path radiogroup roving tabindex + arrow-key nav and
  document why it diverges from SettingsSegmentedControl. [#7]
- Add MobilePane.test.tsx (previously untested safety logic) and a
  MobilePage saved-local-only restore test. [#2, #5]

Verified: 22 targeted tests pass, typecheck + oxlint clean, localization
catalog/coverage and max-lines ratchet green.

* fix(mobile): resolve adversarial-review findings for pairing UI

Address accepted findings from the mobile pairing UI rework:

- MobilePane: add a request-generation epoch so a late getPairingQR
  response can't paint a stale Relay QR after sign-out, a mode switch,
  or an address change; arm rotation when discarding a pending mint.
- MobilePage: on sign-in, upgrade a signed-out local-only fallback QR to
  Relay (invalidate + rotate-regenerate); handle null->connected too.
- Extract useMobilePairingQrInvalidation so both sign-in/out edges and
  cross-window preference syncs invalidate/re-mint the QR consistently.
- MobilePane: clear + rotate the QR when the selected address changes
  (manual pick or refresh-driven) so it can't encode the old endpoint.
- MobilePairingConnectionOptions: guard the Sign in CTA on configured;
  show an Unavailable panel on unconfigured builds instead of dead CTA.
- Drop the duplicate sign-in helper from MobilePairingSetupSection.
- Remove dead i18n keys (title, recommended, signInToGenerate) and add
  relayUnavailable across all locale catalogs.
- Add tests: deferred sign-out/mode-switch races, sign-in upgrade,
  cross-window sync, unconfigured build, arrow-key radiogroup.

TODO left for the relay-label-honesty finding: getPairingQR does not
expose the actually-encoded mode when an automatic offer degrades to
local-only, so the mismatch can't be surfaced without a new return field.

* fix(mobile): resolve relay-pairing deep-review regressions

- MobilePane: invalidatePairing now clears loading so a superseded
  mid-flight generate can't wedge Generate disabled forever
- MobilePage: stop auto-minting a local-only QR under the Relay label
  when signed out with Anywhere; gate Step 2 auto-generate and the
  Generate button on a shared canMintMobilePairingOffer helper, align
  with Settings, clear QR + loading on sign-out, mint Relay on sign-in
- qr-invalidation: clear pairQrDataUrl (and loading) on every
  invalidation path so a stale QR can't stay scannable during rotation
- Strengthen MobilePane/MobilePage tests for the aligned behavior and
  stuck-loading coverage
- Translate relayUnavailable in es/ja/ko/zh

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

* fix(mobile): refuse to mint QR when signed-out with Anywhere selected

Replace silent degradation of Anywhere mode to local-only QR with explicit
refusal. Add canMintMobilePairingOffer guard across all mint paths (address
change, network invalidation, connection mode switch). This ensures the UI
honestly encodes the selected pairing path. Improve keyboard focus visibility
on the path selector by adding a persistent focus ring.

* fix(mobile): detect and flag Relay provisioning degradation

When Relay provisioning fails during automatic (Anywhere) pairing offer creation, the offer silently degrades to local-only. This confuses users who selected Anywhere expecting cellular capability.

Add connectionMode field to the pairing offer result to expose what the offer actually encodes. The UI now flags degradation when the offered mode mismatches the user's selection.

Also move credential rotation logic to the main process: rotate when requested mode differs from the pending token's encoded mode. This ensures QR codes displayed under an old policy can't pair under a new one, and windows reminting after preference sync converge on one token.

Rename MobileRelayBetaAvailability → MobileRelayBetaNotice.

* test(mobile): verify pairing codes don't flash during Relay mint

Assert that the pairing QR and URL remain hidden while the Relay mint is
pending, preventing confusing intermediate states when signing in unlocks
Relay.

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-19 16:10:32 -07:00
Jinwoo Hong 3efc930908 fix: preserve WSL CWD across daemon sleep wake 2026-07-19 16:04:05 -07:00
Brennan Benson 624c8d4120 Make plain-text file:// links clickable in the terminal (#9467)
* Make plain-text file:// links clickable in the terminal

Printed file:// URIs (e.g. a report path echoed by a tool or agent) were
neither http links nor bare filesystem paths, so the terminal's URL and
local-path detectors both skipped them and the link was dead.

Orca already resolves and opens file:// URIs for OSC 8 hyperlinks. Reuse
that exact resolver for plain-text URIs so a printed file:// behaves the
same whether or not the emitter wrapped it in an escape sequence:

- Promote the (dependency-pure) file-url target resolver into src/shared
  so the OSC path and the new plain-text path share one implementation.
- Add a file:// detector that decodes the URI to a filesystem path and
  routes it through the existing file-link pipeline (existence probe +
  openDetectedFilePath), so line/col anchors, %20, Windows drive paths,
  html-in-browser, editor reveal, and SSH/runtime resolution all just work.

Lines without file:// are unchanged: the pass short-circuits to the prior
result, so only file://-bearing lines gain a link.

- Add unit + integration coverage for detection, decoding, and no-double-link.

* Harden plain-text file URI detection

* Split terminal file link detection modules
2026-07-19 15:21:06 -07:00
Jinjing e074bc3f05 Gate certificate trust proceed action on runtime capability (#9070)
Only offer 'Proceed Anyway (Unsafe)' if the connected remote runtime
advertises browser.certificate-trust.v1 support. Older runtimes cannot
honor the request and would fail silently, creating a false affordance.

Centralize certificate error normalization to prevent divergence
between main and renderer certificate matching, and unify URL redaction
for Kagi session token stripping across load-error paths.
2026-07-18 23:40:01 -07:00
gatsby74andJinjing 8ed8f0d109 feat(ssh): download folders from remote explorer (#7793)
* feat(ssh): download folders from remote explorer

* fix(ssh): harden remote folder downloads

* Add missing getRepo stub to worktree cwd test mock

Restoring headless mobile tabs looks up the repo for the active
worktree id; the mock lacked getRepo, so the test only passed
incidentally. Add it explicitly and return undefined since wt-1
is a worktree id, not a registered repo.

* Enable SSH folder downloads, gated for system-SSH connections

- Folder downloads require SFTP, unavailable on system SSH (which
  offers only raw file operations). Add supportsFolderDownload flag
  to gate the feature in the UI layer.
- Reject symlinks at directory-entry level, preventing tree escapes
  and eliminating unnecessary stat calls.
- Check abort signal before opening dialog for better responsiveness
  when renderer closes.
- Log cleanup errors without re-throwing to preserve underlying
  transfer failures.

* Gate SSH folder downloads to SFTP-capable connections

Enforce fail-closed gating and add Windows path traversal validation to
ensure downloads are only available when explicitly supported and safe.

* Gate SSH folder downloads to SFTP-capable connections

Enforce fail-closed gating and add Windows path traversal validation to
ensure downloads are only available when explicitly supported and safe.

* fix(ssh): keep provider types under max-lines after main merge

Move FolderDownloadOptions next to the SFTP download implementation and
narrow IFilesystemProvider.downloadFolder options to AbortSignal only so
types.ts stays within the 300-line oxlint budget when merged with main.

---------

Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
2026-07-18 23:01:29 -07:00
Neilandgatsby74 4905b78282 fix(status-bar): show live session reset countdown in collapsed usage bar (#5399)
Derive the collapsed session limit label from resetsAt and tick it live via a shared boundary-scheduled countdown clock (no polling). Combines #6252/#6585.

Fixes #5399.

Co-authored-by: gatsby74 <166927047+gatsby74@users.noreply.github.com>
2026-07-18 15:16:10 -07:00
Jinjing 45c4a1f61e refs/heads/create-pr-should-handle-sync (#8534) 2026-07-18 00:21:00 -07:00
Brennan Benson d1532956fd fix(keybindings): AltGr-safe default for Add Review Note (#9257)
* fix(keybindings): use AltGr-safe default for Add Review Note

The editor.addReviewNote default was Mod+Alt+N, which resolves to
Ctrl+Alt+N (AltGr) on Windows/Linux. On diacritic layouts AltGr+N
types a real character (e.g. Polish n-acute), so the editor-scope
chord hijacked normal typing. Switch the default to Mod+Shift+A,
which is AltGr-safe and keeps a mnemonic (A for annotate).

* test(keybindings): cover Add Review Note chord end to end
2026-07-17 18:57:26 -07:00
Brennan Benson 24ac5a556e fix(updater): humanize update-error card and hide raw error behind Show details (#9248)
* fix(updater): humanize update-error card and hide raw error behind Show details

Windows auto-update failures surfaced the raw electron-updater message as the
card headline — most visibly the PowerShell "Command failed: … Get-Authenticode
Signature …" dump when antivirus/EDR blocks the post-download signature check.
That reads as a crash, not an actionable state.

Classify each failure and lead with one plain-language sentence + the right
action, keeping the raw error one click away:

- New shared classifier (updater-windows-signature-check): distinguishes an
  AV/EDR-blocked signature check (environment) from a genuine wrong-publisher
  mismatch (security). The two are mutually exclusive so a real integrity
  failure is never softened into "try again".
- UpdateCard: raw error moves behind a collapsed "Show details" toggle; adds a
  security-stop variant (wrong publisher → no retry, "Open official releases")
  and the AV-blocked variant ("Update Verification Blocked"). HTTP/2 and generic
  paths keep their existing actions, now with the same details disclosure.
- Main process records a windows_signature_check_blocked lifecycle event so we
  can size the affected Windows cohort in the field.

Verified each error scenario in a running Electron build (signature-check
blocked, wrong-publisher security stop, HTTP/2, generic, and the expanded
details view).

* fix(updater): make Show details a caret disclosure above the action row

Move the raw-error toggle directly above the Retry/Download buttons and give it
a rotating chevron; the Last error block now expands in place beneath the caret
instead of appearing above the summary, with the action buttons pinned below.

* fix(updater): prevent signature-check bypass

* fix(updater): surface retry start immediately
2026-07-17 18:00:06 -07:00
eisen0419andJinjing 3f335efdb9 feat(agents): pi session resume support (#8876)
* feat(agents): pi session resume support

* fix(pi): require persisted session files for resume

* test(sleeping-agent): use non-resumable sentinel in malformed-record fixture

The 'drops malformed sleeping agent resume records' test used agent:'pi' as
its example of an unknown/non-resumable agent, expecting the record to be
dropped. This PR added 'pi' to RESUMABLE_TUI_AGENTS, making that fixture
valid and retained, so the toBeUndefined assertion broke. Switch the
malformed-case fixture to a genuinely non-resumable sentinel
('definitely-not-an-agent') so the drop-malformed path is still exercised;
no other assertions changed.

* Add durable resume identity for Pi sessions without fabricating turn sta

Pi's `session_start` hook now carries the session file needed to resume
a sleeping pane, but until now Orca either discarded it or treated it
as a fake status transition. Thread a `providerSessionOnly` envelope
through the hook listener, relay, main-process server, and renderer
store so resume identity (and its session-file-scoped equality/claim
key) can be persisted and replayed without emitting prompt telemetry
or a visible working/done row.

* Add durable resume identity for completed Pi sessions

Pi's agent_end hook marks a turn done, but the underlying TUI session
stays alive and resumable. Previously a `done` status wiped sleeping
records and launch config as if the session ended, so hibernation,
manual worktree sleep, and quit-capture all lost Pi's resume identity.

- Track a "live recovery" record for done-but-still-resumable Pi
  sessions, exempting it from the usual done-state cleanup paths in
  agent-status.ts and agent-hibernation-planner.ts
- Gate providerSessionOnly rows and sleeping-agent schema records on
  actual resumability (getAgentResumeArgv) instead of trusting the
  presence of a provider session
- Wait for Pi to persist its session file before advertising resume
  metadata, and treat `/reload` as a non-terminal event so it doesn't
  clobber visible status
- Extend SSH relay envelopes to carry providerSessionOnly so remote
  hosts get the same behavior

* Add explicit periodic/quit mode to sleeping-agent session capture

Split captureAllSleepingAgentSessions into 'periodic' and 'quit' modes
so a background checkpoint can no longer downgrade a confirmed-quit
record or promote a completed Pi session without an authoritative
transcript path. Updates all call sites and tests accordingly.

* Use normalizeAgentStatusPayload for default pi status

Remove unnecessary JSON.stringify wrapper and call the appropriate normalization function directly.

---------

Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
2026-07-17 17:29:04 -07:00