Commit Graph
3978 Commits
Author SHA1 Message Date
OrcaWinandOrcaWin d628c9300e fix(terminal): harden revealed inline-TUI convergence (#9503)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-07-19 22:54:39 -04: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
Brennan Benson 245d79cc8c Preserve surviving workspace state when removing a runtime host (#9468)
* fix(runtime): preserve surviving workspace state on host removal

Avoid purging worktree-scoped tabs and editor state when an exact worktree id still exists on another host. Also retire legacy unhosted rows when the removed runtime was their repo's sole unambiguous owner.

* fix(runtime): purge rows when all owners are removed

* fix(runtime): include host setups in purge ownership

* fix(runtime): purge session-only state on host removal

* fix(runtime): respect restored session ownership on host purge

* fix(runtime): preserve surviving restored sessions on host purge
2026-07-19 16:36:00 -07:00
Brennan Benson 098db33638 Keep lineage children with their parent when dragging to another status lane (#9472)
Dragging a parent worktree card to a different status lane in the sidebar
moved only the parent, orphaning its visible lineage children in the old
lane even though the drag preview showed them moving as one unit. All
status-lane and board drop commit paths (plus their hover previews) now
use the same lineage-expanded dragged set the reorder path already uses.

Fixes #9083
2026-07-19 16:35:12 -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
Jinjing 08f446190a Standardize per-repo Source Control AI save UX (#9427)
* Standardize per-repo Source Control AI save UX with global recipe patter

Extract draft/save logic into `useRepositorySourceControlAiGlobalUx` hook and
serial persist queue, matching how global action recipes save: selects persist
immediately, CLI args and command template draft until per-action Save.

* Support draft/commit for per-repo custom-command input

- Drafts custom commands locally on keystroke without backend writes
- Commits to persistence only on blur or mode selection change
- Adds forceRepoMode local state to fix mode-select snapping with empty fields
- Mirrors existing pattern used for action recipe text editing
- Improves persist queue with repo-switch safety (returns boolean, pins repoId at schedule time)
- Adds comprehensive test coverage for settings hook and persist queue

* Fix forceRepoMode logic to preserve REPO intent mid-edit

Always set forceRepoMode based on field content instead of
only when a repo command is present. This preserves REPO
intent when the field is empty during editing, only exiting
when a value is explicitly entered. Add test for draft
discard and revert behavior.
2026-07-19 15:59:36 -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
Brennan Benson fbe7780566 Fix diff editor flashing "Loading…" on every save (#9424)
* Fix diff editor flashing "Loading…" on every save

Saving a file open in a single-file diff tab blanked the editor to a centered "Loading…" for a frame on every content-changing save. The DiffViewer's React key embedded the modified-content signature, so each save changed the key and forced a full unmount/remount of the Monaco diff editor — which shows its built-in loading placeholder while re-initializing, and also discarded scroll position and undo history.

The modifiedModelKey → modifiedModelPath rotation already refreshes diff content in place without a remount, so the signature in the outer key was redundant. Drop it; keep the view-state scope and explicit reload nonce for a stable per-tab identity.

Adds a regression test asserting a save does not remount the diff editor.

* Harden in-place diff model refresh

* Preserve diff view state across model swaps

* Fix retained diff model disposal race
2026-07-19 14:30:59 -07:00
OrcaWinandBrennan Benson 37039cd095 fix(source-control): bound Create PR eligibility probe so it can't hang (#9292)
* fix(source-control): bound Create PR eligibility probe so it can't hang

The local Electron eligibility path had no renderer-side timeout, so a hung
main-process git/gh subprocess (plausible on Windows) left the Create PR header
stuck in its "Checking whether this branch can create a pull request..." loading
state with the button permanently disabled. The runtime-hosted path was already
bounded by callRuntimeRpc's 30s timeout; the local path was not.

- Wrap the local window.api.hostedReview.getCreationEligibility call in a 30s
  timeout so a never-settling probe rejects instead of hanging.
- On probe failure/timeout, synthesize a local-status blocker snapshot
  (dirty -> no_upstream -> needs_sync -> needs_push, mirroring the main-process
  ordering) so a dirty branch still offers the commit/preparation intent and a
  clean unpublished branch the publish intent, instead of an inert disabled
  button. Fall back to the retryable failed state only when local status can't
  determine a blocker.

Extracts the eligibility snapshot builders into their own module to stay under
the max-lines cap. Provider-aware copy and host-scoped (local/SSH/runtime)
behavior preserved; no git commands changed.

* fix(source-control): guard synthesized eligibility fallback (default branch, provider parity)

Mirror the main-process canReturnLocalBlocker guard in the renderer's
probe-failure fallback so a timed-out/failed probe can't synthesize a
'dirty'/commit blocker on the default branch or a detached HEAD — which
would have surfaced an enabled Create PR that commits onto the base
branch. The ahead-only needs_push case is likewise dropped to match main
(it won't offer a push it can't auth-check first).

Infer the provider from the repo remote host so a GitLab (etc.) repo with
no linked review shows its own review copy during loading and after a
failure instead of the GitHub default.

Also: assert the timeout error by type, and rename the snapshot test to
match its module.

* fix(source-control): keep eligibility timeout stable

* fix(source-control): keep eligibility timeout bounded

* test(source-control): assert eligibility effect dependencies

---------

Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
2026-07-19 13:18:34 -07:00
Brennan Benson a833b5cba6 Clear a removed remote host's leftover repos and worktrees from the sidebar (#9463)
When a runtime host identity (runtime:<envId>) is removed from the saved
list, retire the repos, project host setups, and worktree rows it owned so
the same physical checkout stops duplicating in the sidebar.

setRuntimeEnvironments now diffs the previous in-memory saved list against
the new one and purges state owned by any environment that just left, routed
through the existing worktree purge cascade (tabs/PTY/browser/editor/agent
maps). Scoped to the removal diff, not an absolute keep-set, so a serving
instance's locally-persisted runtime-stamped repos (whose env id was never in
this instance's saved list) are never torn down.

A tombstone set (removedRuntimeEnvironmentIds) guards the three repo-catalog
merges so an in-flight fetch for a just-removed env can't re-add purged repos,
while a runtime env merely absent from a not-yet-hydrated saved list still
merges normally.

Refs #8881
2026-07-19 13:15:14 -07:00
Brennan Benson 37cd830192 Confirm before merging a PR/MR from the sidebar strategy dropdown (#8911)
The right-sidebar Checks panel merge control merged the PR/MR immediately when
a strategy was chosen from its dropdown, with no confirmation — so opening the
dropdown to switch strategies (e.g. Squash -> Merge commit) merged on the spot.
Every other PR/MR merge surface (PullRequestPage, GitHubItemDialog, TaskPage)
already confirms first, and the close/reopen path in this same hook confirms too;
only the hosted-review merge path was missing the gate.

Add the same confirmation dialog to the hosted-review handleMerge, provider-aware
so it reads "Squash and merge PR #N?" / "Squash and merge MR !N?". Selecting a
strategy now asks before merging; the merge still runs when explicitly confirmed.

Fixes #7943
2026-07-19 13:09:36 -07:00
Jinjing b4e85d95a6 feat(editor): add Toggle H5 to markdown editor (#9448) 2026-07-19 12:13:16 -07:00
NeilandOrca 5848550b5e fix(composer): show full worktree path on hover in Run on dropdown (#9398)
* fix(composer): show full worktree path on hover in Run on dropdown

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

* refactor(composer): use native title tooltip for Run on paths

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

* fix(composer): reliable native-style tooltip for Run on paths

The native title attribute won't re-trigger between adjacent rows in
Chromium, so the tooltip flickered/dropped as you moved down the list.
Use the shared (controlled) Tooltip instead, styled compact + arrowless
to read like an OS tooltip; the app-level provider's skip-delay makes
row-to-row hovers reliable.

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

* fix(composer): stop Run on path tooltip wedging shut

Radix's uncontrolled hover state could flash the tooltip open then
wedge it closed inside the cmdk list (shared pointer-transit state
getting stuck), so re-hovering never reopened it. Drive open state
with a controlled hover-intent handler (open after a delay, close
after a short grace) so re-entry always reopens and transient
enter/leave thrash debounces to a stable state.

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

* fix(composer): rebuild Run on path tooltip as a portal (no loop/wedge)

Both the native title and Radix Tooltip (controlled or not) misbehaved
inside the cmdk list: flashing, wedging shut, or opening/closing in a
loop. Replace with a self-contained tooltip that renders a fixed,
pointer-events-none portal on document.body and only changes state on
pointer enter/leave/down (never pointermove). Opening it cannot reflow
the list or become the hover target, so it structurally cannot create
the enter/leave feedback loop.

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

* fix(composer): trigger Run on path tooltip on the path line, always below

- Scope the hover trigger to the truncated path line itself (not the
  whole row), so only hovering the path reveals it.
- Always position the tooltip below the hovered line for predictable
  placement (was flipping above near the viewport bottom).

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

* fix(composer): anchor Run on path tooltip under the path line

The old placement anchored to the window's right half when the trigger
sat past viewport-center, which shoved the tooltip to the far right edge
of the window, disconnected from the dropdown. Always left-align it
directly under the hovered path line and cap max-width to the remaining
viewport width so long paths wrap instead of overflowing.

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

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-19 02:34:39 -07:00
Jinjing e885cec154 Allow hiding Orca Mobile sidebar button once a device is paired (#5785)
* Allow hiding Orca Mobile sidebar button once a device is paired

Introduce a shared external store (`paired-mobile-devices.ts`) to cache
and synchronize the paired mobile device list across the sidebar,
Settings pane, and Mobile page. This prevents redundant backend IPC
requests when multiple surfaces mount simultaneously.

Using this shared pairing status, display an inline "Hide from sidebar"
control next to "Orca Mobile" in the navigation sidebar once a device
has been paired. This allows users to easily declutter their sidebar
after completing setup.

* Handle device load errors with recovery on focus/reconnect

- Add error flag to distinguish failed device loads from empty device lists
- Recover from transient startup IPC failures by retrying on window focus or online
- Validate revoke device success before routing or showing success toast
- Only show hide control after first device is actually paired
- Use snapshot getter to avoid manual device ref tracking in MobilePane

* Handle device load errors with recovery on focus/reconnect

- Shared module-level listeners for failed load recovery reduce IPC spam
  compared to per-consumer event listeners on every focus/online event.
- Optimistically remove revoked devices if post-revoke reload fails,
  keeping success toast and intro routing correct.
- Prevent repo/branch label overflow with CSS text truncation.
- Fix HTML entity rendering: use plain spaces instead of &nbsp; in
  translations so React text nodes render them correctly.
- Add comprehensive tests for the paired devices hook covering load,
  navigation, revoke, and error recovery paths.
2026-07-19 00:09:57 -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
Jinjing 99b59ad7be fix(floating-terminal): position panel at z-45 above notifications (#9420)
Raise the floating terminal panel from z-30 to z-45 so it sits above
notification cards (z-40) but below the modal layer (z-50), preventing
notifications from burying the terminal. Raise the toggle button to z-46
to keep it clickable where it overlaps the panel.
2026-07-18 22:29:26 -07:00
JinjingandOrca f7c2c1276c Add review note chord guards (#9412)
* Guard add-review-note chord to prevent remount (product B)

- Scoped guards consume chord at composer level to prevent remount and leakage
- Handle OS key-repeat: ignore when no draft, consume when one is mounted
- Clear stale block keys in markdown preview when content renumbers
- Flush pending selection before reading targets to fix timing races

* fix(editor): repair add-review-note guard tests and close remaining chord gaps

- Update the product-B guard tests to the Mod+Shift+A default binding
  (#9257 retired Mod+Alt+N as AltGr-unsafe); they asserted the old chord,
  so seven landed red and the rich-editor repeat test passed vacuously.
- Monaco: recompute the annotation target from the live selection at
  keydown instead of the render-lagged ref, so a chord right after a drag
  cannot open on the previous selection or miss a fresh one.
- Preview: key the stale-block-key cleanup on renderedContent (the DOM
  the block keys live in), which can lag content during external-edit
  section preservation.

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

* fix(editor): mirror shortcut-guard refs in effects instead of render body

CodeRabbit on #9412: render-body ref writes can leak from a render pass
React replays and discards. Move the state->ref mirrors for
commentPopoverRef / shouldShowMarkdownAnnotationsRef (MonacoEditor) and
activeAnnotationBlockKeyRef (MarkdownPreview) into effects; same-tick
keydown paths keep their eager event-handler writes.

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

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-18 22:10:22 -07:00
Brennan Benson 02ff7c7465 Preserve agent chats and drafts through reconnects (#9242)
* fix(chat): preserve drafts through reconnects

* fix(terminal): bound stale handle reconnect polling

* refactor(chat): extract native view props

* fix(chat): harden reconnect lifecycle

* fix(terminal): replace stale reconnect identities atomically

* fix(terminal): recover mirrors after reconnect polling

* docs(reliability): record merged reconnect coverage

* fix(terminal): preserve reconnect lifecycle state

* fix(terminal): keep reconnect polling truly bounded
2026-07-18 20:31:14 -07:00
Jinwoo-HandOrca 965a3f9a05 fix(terminal): keep setup split PTYs distinct
Co-authored-by: Orca <help@stably.ai>
2026-07-18 17:10:28 -07:00
NeilandOrca 6a573f376f fix(create-worktree): keep composer open when adding a project from the dialog (#9397)
Co-authored-by: Orca <help@stably.ai>
2026-07-18 16:36:52 -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
Neil 403d681d83 fix(terminal): re-linkify streamed URLs so links underline/click on first hover (#9320)
Invalidate xterm's linkifier hover cache (throttled) when output streams into a visible pane, so a URL printed under a stationary pointer underlines and becomes Cmd/Ctrl+clickable on the next pointer move instead of after several clicks. Skips while a link is hovered (no flicker) and re-arms so the reset is never dropped mid-hover. Reuses the visibility-resume primitive (#9061).
2026-07-18 14:27:34 -07:00
NeilandOrca 057a81493a fix(sidebar): guard every worktree sort against undefined displayName (crash 99657ab1) (#9315)
* fix(sidebar): guard every worktree sort against undefined displayName (crash 99657ab1)

Worktree.displayName is typed non-optional but arrives undefined at runtime for
persisted/discovered worktrees (crash 99657ab1). PR #8683 fixed the kanban board;
this extends the same guard to the five other sort sites that share those worktree
objects — including the Cmd+J switcher (order-empty-query-worktrees) — via one
shared compareWorktreeDisplayName helper in lib/. Behavior-neutral when names are
present; a missing name now sorts as '' instead of throwing.

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

* docs(agents): add over-commenting anti-pattern example to the comment rule

Anchors the existing 'document the why, briefly' rule with a concrete
before/after (the worktree-displayName guard) so agents stop dumping the
crash id, mechanism, file location, and every call site into a comment.

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

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-18 01:55:00 -07:00
Jinho Choi 72d0a403a3 fix(editor): use Monaco's registered proto language id for .proto files (#9286) 2026-07-18 00:22:20 -07:00
Jinjing 45c4a1f61e refs/heads/create-pr-should-handle-sync (#8534) 2026-07-18 00:21:00 -07:00
hanjoonchoe 0c73e3c33d fix(i18n): use full-sentence keys in RemoveFolderDialog description (#9296) 2026-07-17 23:55:12 -07:00
OrcaWinandBrennan Benson cb9c42d605 fix(terminal): filter focus reports after Codex history resume (#9259)
* fix(terminal): filter focus reports after Codex history resume

* test(terminal): preserve focus reports outside Codex resume

---------

Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
2026-07-17 19:25:13 -07:00
Brennan Benson bd7205684a test(pty-connection): drain async confirm reads between tests (#9260)
The connectPanePty suite intermittently fails in CI on
`clearAgentLaunchConfig` call-count assertions (e.g. expected exactly
once, received 5 with pane keys belonging to other tests).

Root cause is cross-test async leakage: pane foreground-agent trackers
kick off `confirmForegroundProcess` reads whose continuations settle on
the microtask queue, not on fake timers. A test that ends with one still
in flight leaks it into the next test, where it resolves against that
test's fresh store mock and calls clearAgentLaunchConfig with the prior
test's pane key.

Drain pending microtasks in afterEach (before tearing down fake timers,
while the test still owns its store mock) so each test absorbs its own
async fallout. No behavior change; full suite stays green (446/446).

Note: this is a CI-timing flake not reproducible locally in isolation
(ordered runs pass repeatedly, incl. under GC pressure); the fix targets
the confirmed leak mechanism and removes the timing dependence.
2026-07-17 19:17:33 -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
NeilandOrca c436df0551 feat(tray): mark dev-instance tray icons with a DEV indicator (#9253)
Co-authored-by: Orca <help@stably.ai>
2026-07-17 18:50:21 -07:00
Brennan Benson 64c7f14c3b fix(codex): clarify account-switch restart card actions (#9254)
The account-switched restart card had two near-synonymous dismissive
buttons (Dismiss / Collapse) whose distinct behaviors weren't legible,
and body copy that attributed "keep working with the current account"
to Collapse — which only minimizes the notice.

Remap to intent-named actions:
- Dismiss  -> "Keep old account" (clears the notice; pane stays on the
  old account)
- Collapse -> "Not now" (minimize to the pill; decision deferred)
- Body no longer claims the minimize action commits you to an account.

Restart, the title, and the heading are unchanged. New English strings
get fresh auto keys so stale es/ja/ko/zh translations don't persist;
catalog re-synced.
2026-07-17 18:42:59 -07:00
slashdevcorpse ad206027b8 fix(terminal): replay cold restores at recovered grid (#9043) 2026-07-17 18:42:31 -07:00
NeilandOrca 3a27fb59cd fix(settings): stop shortcut filter rail overlapping the list below xl (#9252)
Co-authored-by: Orca <help@stably.ai>
2026-07-17 18:37:55 -07:00
NeilandOrca ed28dec592 feat(new-workspace): always offer "Add a new project" in Create worktree picker (#9251)
Co-authored-by: Orca <help@stably.ai>
2026-07-17 18:37:20 -07:00
Jinwoo HongandOrca 360b2423ea Fix duplicate and lost IME terminal commits (#9235)
Co-authored-by: Orca <help@stably.ai>
2026-07-17 18:29:44 -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
Asurada 7c7bf259a2 fix(i18n): correct Chinese UI terminology (#9154) 2026-07-17 17:27:18 -07:00
1bd757ad2f fix(i18n): correct zh translation of "Pin Tab" to 固定标签 (#9157)
Co-authored-by: weixin <weixin@thunisoft.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 17:25:55 -07:00
hanjoonchoe 2a22dd0f2a fix(i18n): translate untranslated Korean catalog strings (#9019)
Translate 105 ko.json entries that were still identical to their English
source after the machine bootstrap — dialog bodies, settings descriptions,
shortcut recorder labels, Windows firewall notices, Jira connection copy,
crash-report notices, and source-control panel text.

Intentionally left in English: brand/product names, command and config
examples, CSS/selector strings used by animated visuals, and lowercase
concatenation fragments whose word order cannot be reordered safely.
2026-07-17 17:23:44 -07:00
Jinjing 546cc8237f Improve native chat ui (#9246)
* refactor(native-chat): extract option appliers and improve toggle UI

- Extract setOption apply logic to native-chat-session-option-apply.ts
- Add invokeAction method for toggle-only options without tracked baseline
- Replace checkbox UI with On/Off radio groups for boolean options
- Display option values only in pills (remove redundant label prefixes)
- Serialize concurrent applies so later dispatches win in order

* Remove focus-visible border from composer container

The container uses a steady hairline border (no focus/click flash), with focus-visible styling delegated to the inner textarea. The container is a layout wrapper, not a focus target.

* Prevent option commits on model switch during dispatch

When a user changes an option and the model switches before dispatch
completes, committing the stale option would overwrite state under
the new model. Guard by capturing the baseline option state before
dispatch and validating it hasn't changed post-dispatch.

* Use invokeAction for model option in NativeChatComposer test

Codex model is an agent picker mid-session, so setOption rejects.
Update test to use invokeAction to match the actual UI behavior.
2026-07-17 17:18:27 -07:00
Brennan Benson 5907816457 feat(keybindings): swap tab-switch chords to the common convention for new users (#9240)
* feat(keybindings): swap tab-switch chords to the common convention for new users

New installs now get the widespread mapping — Mod+Shift+[ / ] cycles across
all tabs, Mod+Alt+[ / ] cycles within the active tab type. Pre-existing installs
keep today's mapping: a one-time cohort seed (frozen on first launch via the
fileExistedOnLoad signal, mirroring the telemetry migration) pins the legacy
chords into keybindings.json, skipping any action the user already customized.

- shared registry: swap the four tab.*SameType / tab.*AllTypes defaults; export
  LEGACY_TAB_SWITCH_BINDINGS for the seed
- persistence: migrateTabSwitchKeybindings freezes the existing-vs-fresh cohort
  (tabSwitchKeybindingSeed = pending | done)
- keybinding-file: seedLegacyTabSwitchBindings writes the legacy pins into the
  active-platform section so Settings reset still works
- refresh stale default-chord comments
- tests for the swapped defaults, the seed (fresh/existing/customized/idempotent),
  and the cohort migration

* test(keybindings): prove existing-user parity + make the seed strictly per-action

seedLegacyTabSwitchBindings now pins each un-customized action individually
instead of skipping all four when any one is customized. A partially-customized
existing user keeps their rebound action AND the pre-swap default on the rest;
no existing user's behavior changes. The skip check keys on this platform's
effective overrides so a foreign-platform-only override can't leave the active
platform on a new default.

Adds keybinding-service.test.ts: constructs a real KeybindingService and asserts
effective bindings + real keystroke matching for both cohorts across darwin/
linux/win32, plus partial-customization, idempotency, and seed-failure retry.

* fix(keybindings): preserve legacy files during tab shortcut seed

* fix(keybindings): preserve valid pre-swap overrides
2026-07-17 16:58:04 -07:00
Trevin Chow 613dcd8c9b fix(pr-comments): make the review sidebar legible for structured bot markdown (#9209)
Restore readable block flow, typography, nested spacing, and heading semantics for structured review Markdown in the PR sidebar. Preserve the compact renderer's existing remote-image trust boundary.
2026-07-17 16:39:02 -07:00
Brennan Benson 7c0b84f2b6 fix(terminal): invalidate cached glyphs when WebGL atlas changes (#8899)
* feat(terminal): add flag-gated render-desync sentinel for WebGL panes

Detects the buffer-clean/render-stale glyph garble class in the field: per
visible WebGL pane, compare the cells the xterm buffer says hold glyphs
against the ink actually present on the canvas, sampled in the same task as
a forced synchronous redraw so a divergence proves the render model/atlas is
wrong rather than a missed present. A trip requires the same screen cells to
stay divergent across three samples (real desync is pinned; scroll lag moves),
then records a webgl-render-desync breadcrumb, stashes evidence (canvas PNG +
buffer text) for bug reports, and runs the same shared-atlas recovery a tab
reveal performs, so a stuck-garbled pane self-heals within seconds.

Off by default; arm on any build via
localStorage.setItem('orca:render-desync-sentinel', '1') and reload.

* fix(terminal): invalidate glyph cache on atlas replacement

Reproduce the WebGL atlas identity mismatch with two live terminals and force cached geometry to rebuild whenever a different shared atlas is attached. Persist flag-gated render-desync evidence and retain the investigation tooling used to validate the field signature.

* fix(terminal): harden render desync diagnostics

* docs(reliability): clarify Linux WebGL evidence gap
2026-07-17 16:16:17 -07:00