Commit Graph
3509 Commits
Author SHA1 Message Date
9964d1ccbf perf: overlap sidebar-scope loads with worktree scan at startup (#7225) (#7306)
* perf: overlap sidebar-scope loads with worktree scan at startup (#7225)

Continues the renderer-chain parallelization proposed in #7225 (proposal 2),
on top of the merged #7266:

- Run project-groups → folder-workspaces concurrently with the per-repo
  `git worktree list` fan-out. Neither reads the repos store nor worktrees,
  so a slow remote host's 15s scope RPCs no longer queue ahead of the scan.
- Raise worktree refresh concurrency 5 → 8 so multi-core machines run fewer
  sequential scan batches, still bounded so one moment can't launch every
  git probe at once.

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

* Test overlapping sidebar scope loads and worktree hydration at startup

Verify that sidebar scope loads and worktree hydration operations run
concurrently before session hydration. This protects against regressions
in startup performance under the perf/startup-lag optimization.

Additionally, document the rationale for the worktree refresh
concurrency limit in the worktrees slice.

* Clarify worktree refresh concurrency comment

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
2026-07-04 01:34:40 -07:00
f215a48064 fix: pr-bug-scan validated finding from #6952 (#7180)
* fix: address pr-bug-scan validated finding from #6952

throwNodeNotFound() now re-raises AbortError when the shared signal is aborted, so a signal-cancelled node probe no longer launders into 'Node.js not found'; sequential fallback runs.

* fix(ssh): make session-limited (MaxSessions=1) relay deploys actually succeed

Review of #7180 verified the parent fallback end-to-end against a real
MaxSessions=1 sshd and found the connect still failed. Four gaps, in order
of discovery:

- isSshSessionLimitError missed stock OpenSSH, which refuses session
  channels over MaxSessions with SSH2_OPEN_CONNECT_FAILED (2) and 'open
  failed' — reason 4 never matched, so the fallback never triggered.
- execCommand settled aborted commands before the channel finished
  closing, so the sequential fallback reissued execs while sshd still
  counted the old session.
- SshConnection.waitForSshCallback rejected aborts mid-channel-open
  immediately, leaking a confirmed-late channel that held the only
  session slot; it now settles after the late channel closes (bounded)
  and drains its streams so ssh2 emits 'close'.
- Session channel opens now retry transient session-limit refusals
  (sshd frees the slot only after processing our close-ack, which the
  next open can beat by microseconds), and the remote orca CLI shim
  install is non-fatal like the managed-hook install — after the relay
  bridge occupies the sole session slot, raw-connection extras must
  degrade instead of failing the connection.

Verified live against Docker sshd (OpenSSH 9.2, MaxSessions=1): fresh
deploy (upload + native deps + launch), reconnect cycles, and a PTY
round-trip all succeed; unrestricted-sshd regression run also passes.

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

* Handle ssh execution aborts immediately during retry backoff or hangs

- Cancel the session-limit retry delay immediately if the operation is
  aborted during backoff.
- Limit the wait time to a 5-second grace period when aborted during a
  channel open that is hung and never invokes its callback, rather than
  waiting for the full connection timeout.

---------

Co-authored-by: orca-bug-scan-bot <orca-bug-scan-bot@stably.ai>
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
2026-07-04 01:26:55 -07:00
Jinjing 0982882436 Exclude submodule worktree-only changes from Stage All action (#7298)
- Prevent the "Stage All" button from being enabled when only nested
  submodule worktree changes are present.
- Remove the disabled stage button on submodule worktree-only rows,
  replacing it with an explanatory tooltip.
- Fix WSL terminal environment assertion in daemon PTY tests to allow
  inherited agent-hook environment variables.
2026-07-04 00:21:55 -07:00
Jinjing 7c77ccab7d Fix notes send targets for manual agents (#7300)
* Fix notes send targets for manual agents

* Split agent title merging into manual and launch-agent paths

Separate the merging logic for terminal titles depending on whether the
tab has a launch agent or is a manually started CLI.

- Launch-agent tabs carry an owner bit, allowing their live titles to
  promote a stale status row on the same pane.
- Manually started agents have no owner bit, so they only ever add a
  row and must not override existing status evidence.
- Remove the temporary TitleHintAgentTarget type and the need to strip
  metadata when pushing targets.
2026-07-04 00:10:53 -07:00
Jinwoo Hong a074ed6182 Fix Windows ConPTY overlay after cold restore (#7310) 2026-07-03 23:24:24 -07:00
slashdevcorpseandJinwoo Hong 9fa1313179 Fix Windows native chat title fallback (#7313)
* fix: resolve title-only native chat agents

* test: cover native chat unsupported identity fallback

* fix: harden native chat agent fallback precedence

---------

Co-authored-by: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com>
2026-07-03 23:02:12 -07:00
Neil 3fdeec7c2f cleanup(renderer): drop dead code, dedupe helpers, skip wasted work, fix garbled comments (#7322)
Quality pass on renderer/shared PRs merged 2026-07-03:

- WorktreeTitleInlineRename: skip the truncation measure + ResizeObserver in `wrapTitle`
  mode, where wrapped titles never truncate — it could only churn unused state (#7307).
- editor slice: reuse the `removeEditorStateForReplacedPreview` helper this PR added
  instead of a hand-rolled copy of the same six-field eviction (drops ~50 lines) (#6476).
- useFileExplorerTree: extract `readWorktreeDirectory` so the connectionId/settings
  assembly for `readRuntimeDirectory` lives in one place, not three (#6321).
- comment-markdown-github-attachment-media: extract a shared `AttachmentFallbackLink`
  for the image/video error-fallback link (#6759).
- repository-icon-github: fold the two near-identical live resolvers into one
  parameterized `resolveRepositoryIdentityLive`; trim a 3-line comment to 2 (#6507).
- resource-usage-open-slices: delete the `shouldReadPopoverSlices` identity wrapper and
  inline `open` at the four call sites (#7275).
- BrowserPane: drop the pointerEvents assignment already applied inside
  `ensureBrowserPageWebview` for the reused-webview path (#6958).
- github slice: fix two garbled "…a commit main confirmed…" comments (#7277).
- runtime-file-client: trim the binary-file fallback comment to its whys (#6606).
- composer-branch-selection: drop the inline comment that restated the JSDoc (#6748).
- TabBarQuickCommandsButton: correct the stale "+ Command" comment (button shows no +).

No behavior change (the editor-helper reuse is behavior-equivalent, only more
conservative on an edge case); typecheck, oxlint, react-doctor, oxfmt, and touched
unit suites all pass.
2026-07-03 22:31:56 -07:00
Neil 529c06ff62 cleanup(terminal): dedupe pane teardown, reuse platform check, harden shadow-cursor tracking (#7320)
Quality pass on terminal PRs merged 2026-07-03:

- pane-split-close: extract a shared `teardownManagedPane` so `closeManagedPane` and
  `detachManagedPaneForExternalMove` can't drift apart on future edits (#7215).
- terminal-link-activation: reuse the existing `isMacPlatform()` instead of a second
  inline `navigator.userAgent` copy, keeping one source of truth for Mac detection.
- pty-connection: gate the shadow-cursor moves on empty CSI params so parameterized/
  modified cursor keys (e.g. Ctrl+Left word-jump, `ESC[1;5D`) reset command tracking
  instead of silently desyncing the buffer used to infer a typed agent name; relocate
  the `getAuthoritativePaneAgent` JSDoc to the function it documents (#6954).

No behavior change except the cursor-tracking hardening; all touched unit suites pass.
2026-07-03 22:31:54 -07:00
Jinwoo HongandOrca 52e2eb01b1 Tab strip: judge click-vs-drag at pointerup and flush gesture state on window focus (#7316)
Co-authored-by: Orca <help@stably.ai>
2026-07-03 22:22:35 -07:00
Neil 22d5989ed9 Fix manual sorting for project headers and groups
Fixes #6609.
2026-07-03 21:38:34 -07:00
Neil c29ee51a0a Make sidebar worktree hover title editable (#7307) 2026-07-03 20:21:09 -07:00
NeilandOrca e4ac582e16 Revert "Update quick command button to use plus icon and clearer label (#6517)" (#7304)
This reverts commit a94d692cd5, restoring the
shorter "Command" label and the Play icon on the quick command entry
(PRs #5528 and #5674).

Co-authored-by: Orca <help@stably.ai>
2026-07-03 20:20:23 -07:00
Brennan BensonandOrca bdd8bb0a3b fix(agent-status): keep harness-injected turns out of sidebar prompt labels (#7274)
Co-authored-by: Orca <help@stably.ai>
2026-07-03 19:49:17 -07:00
Neil 29c7fa7a03 Include Claude Fable reset countdown
Fixes the PTY fallback path so Claude Fable usage carries a concrete reset timestamp and the existing status tooltip renders the Fable reset countdown.
2026-07-03 19:36:10 -07:00
Brennan BensonandOrca b099e27703 fix(checks): keep a merged PR visible when the worktree sits behind its own PR head (#7277)
Co-authored-by: Orca <help@stably.ai>
2026-07-03 19:30:10 -07:00
d5c4372b3a fix(file-explorer): batch dirCache writes on tree refresh to remove O(N²) freeze (#6321)
Co-authored-by: Orca <help@stably.ai>
Co-authored-by: brennanb2025 <brennankbenson@gmail.com>
2026-07-03 19:06:13 -07:00
Brennan BensonandOrca acfec3fd85 Record hibernation activity from real user input and protect typed drafts (#7283)
Co-authored-by: Orca <help@stably.ai>
2026-07-03 19:04:12 -07:00
andrewroxby f8591d6991 Remember Markdown TOC state while tabs are open (#6476)
* Keep markdown TOC open per document

* Clean up Markdown visibility state for replaced previews
2026-07-03 18:23:28 -07:00
Brennan BensonandOrca 748bced2e6 Fix Resource Manager going empty when a runtime server is selected or stale (#7275)
Co-authored-by: Orca <help@stably.ai>
2026-07-03 17:52:59 -07:00
Brennan BensonandOrca 4951168d4a Unify tab agent identity with the agent-status pipeline (#7059)
Co-authored-by: Orca <help@stably.ai>
2026-07-03 17:39:59 -07:00
727c7ae21b fix(runtime): render remote-server binary files (PDF/images) in the editor (#6606)
Co-authored-by: Orca <help@stably.ai>
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
2026-07-03 17:37:06 -07:00
f7a4100bc7 Preserve slash branch names from branch composer (#6748)
Co-authored-by: Orca <help@stably.ai>
Co-authored-by: Neil <charlie-eng@stably.ai>
2026-07-03 17:33:22 -07:00
mehmet turac ca36072295 Handle GitHub attachment image load failures (#6759) 2026-07-03 17:32:08 -07:00
8796261eed feat: allow custom worktree branch names (#6454)
* feat: allow custom worktree branch names

* refactor(ui): render custom branch name field unconditionally under advanced container

* fix(composer): hide manual branch field when a work-item source drives the branch

A tracked PR/issue/MR/Linear source derives the branch itself, and a linked
GitHub PR re-resolves the branch name at submit — so an override typed in the
Advanced branch field was silently ignored. Only render the field for the
typed-name and base-branch flows, where the manual override is honored.

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

---------

Co-authored-by: Wolfgang Schoenberger <221313372+wolfiesch@users.noreply.github.com>
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
2026-07-03 17:21:50 -07:00
2789a67604 feat(browser): add Copy to context menu when text is selected (#7159)
Co-authored-by: Orca <help@stably.ai>
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
2026-07-03 16:52:08 -07:00
473c4e8680 fix(markdown): restore TOC navigation for h4 and h5 (#7063)
Co-authored-by: Orca <help@stably.ai>
Co-authored-by: Neil <charlie-eng@stably.ai>
2026-07-03 16:51:26 -07:00
2c03133975 fix(i18n): apply persisted UI language at startup instead of OS locale (#7193)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: rubin <RubinCarter@users.noreply.github.com>
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
2026-07-03 16:44:56 -07:00
Jinwoo Hong 2f3c7e8660 Fix Windows slow startup and multi-minute UI freezes (#7225) (#7266) 2026-07-03 19:40:45 -04:00
a10e1d7584 fix(gitlab): recognize self-hosted GitLab on non-default ports over SSH connections; stop one project failing the whole issues panel (#5400)
* fix(gitlab): port-aware self-hosted host recognition

Use the URL host (including a non-default web/API port) as the GitLab
host identity instead of the port-less hostname, and match known hosts
port-aware:

- A known-host entry without a port matches any port of the same
  hostname (preserves legacy bare-host and gitlab.com recognition).
- A known-host entry WITH a port matches only that exact host:port, so
  two services sharing a hostname on different ports (e.g. a GitLab and
  a Gitea) are no longer conflated.
- For ssh/git remotes the port is a transport port (e.g. ssh :2222) and
  is dropped; for http(s) remotes the port is the endpoint and kept.
- Also capture an optional :port in parseGlabAuthStatusHosts so a
  self-hosted GitLab on a non-default port is discovered correctly.

* fix(gitlab): per-connection known-hosts cache + port-aware auth-status parsing

getGlabKnownHosts() was connection-blind and cached process-globally,
and on any failure it cached [gitlab.com] forever — so a repo on an SSH
connection never discovered its self-hosted host once a probe failed
before the tunnel was ready.

- getGlabKnownHosts(connectionId?) now caches per connection so a
  connected repo's authenticated hosts don't leak into the local
  context (or vice versa).
- The failure fallback (canonical default) is no longer cached, so a
  later probe can re-discover the real host once auth/tunnel is ready.
- parseGlabAuthStatusHosts captures an optional :port on both the
  'Logged in to <host>' and header-style lines, keeping two services on
  the same hostname distinct by port.

* fix(gitlab): isolate unresolvable projects instead of cwd-fallback that hits exit 128

listIssues/getIssue fell back to an unscoped 'glab issue list' / 'glab
issue view' that infers the project from cwd. For a repo on an SSH
connection cwd is not the repo dir, so glab runs git resolution in a
non-repo dir and fails with 'git: exit status 128'. In an 'All projects'
aggregate one such failure could sink the whole issues panel.

When a projectRef cannot be resolved, return a structured, isolated
per-project result (listIssues: { items: [], error: not_found };
getIssue: null) and spawn no glab subprocess. Behavior is unchanged when
a projectRef IS resolved (the scoped '-R' / 'api projects/...' path).

* fix(gitlab): recognize modern /-/work_items/<iid> issue URLs

Modern GitLab emits issue URLs as /-/work_items/<iid> in addition to the
legacy /-/issues/<iid>. The URL classifiers only matched /-/issues/, so
work-item-form issue links went unrecognized.

Extend the gitlab-links parsers (parseGitLabIssueOrMRNumber /
parseGitLabIssueOrMRLink, which also backs isWorkItemLookupText) and
isGitLabIssueUrl to accept /-/work_items/<iid>, mapping it to an issue
work item with the same project-path + iid extraction.

* fix(gitlab): thread connectionId into getGlabKnownHosts call sites

Follow the existing connectionId-threading pattern: pass the repo's
connectionId into every getGlabKnownHosts() call (client.ts,
work-item-details.ts, orca-runtime.ts) so the per-connection known-hosts
cache is keyed correctly and self-hosted hosts are discovered against
the right glab context.

* docs(gitlab): use generic example hosts in comments

* fix(gitlab): pass self-hosted host:port via GITLAB_HOST (glab --hostname rejects ports)

* polish: satisfy oxlint curly + oxfmt on merged gitlab port-recognition code

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

---------

Co-authored-by: Ptah-CT <auctor@xinfty.space>
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
2026-07-03 16:24:01 -07:00
Brennan BensonandOrca 6213f43016 Wake hibernated agent panes on reveal instead of leaving an inert ghost (#7145)
Co-authored-by: Orca <help@stably.ai>
2026-07-03 14:27:36 -07:00
332490e919 Fix stale GitHub repo avatars after owner changes (#6507)
* Fix stale GitHub repo avatars after owner changes

* Simplify avatar-refresh effect deps (repo covers its fields)

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

* Preserve known fork identity when live upstream lookup is inconclusive

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

* Remove dead avatar-icon wrapper; drop no-op update fallbacks

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

---------

Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
2026-07-03 14:20:30 -07:00
3614cbc40c Add default browser action for terminal links (#6129)
* Add terminal link browser context action

* Polish terminal link default-browser action

- Route Open in Default Browser through the shared openHttpLink funnel
  (forceSystemBrowser) so loopback dev-server links get the same
  worktree-label rewrite as shift+click's system-browser path.
- Translate the new menu label in es/ja/ko/zh (was en-only).
- Extract TerminalQuickCommandMenuItem to keep TerminalContextMenu under
  the max-lines lint after adding the link item.
- Hit-test the link only when the menu actually opens (skip on Windows
  right-click copy/paste path).
- Add scheme-safety unit tests (file:/javascript:/smb:/vscode: rejected)
  and a menu-item render/callback test; fix TerminalContextMenu.test
  props for the new required fields.

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

---------

Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
2026-07-03 14:09:52 -07:00
42de074152 fix(browser): keep isolated profile storage on its partition (#6958)
* fix(browser): keep isolated profile storage on its partition

* fix(browser): keep cloned isolated tabs on their resolved partition

* fix(browser): thread resolved partition through UI profile switches

Keep the isolated-storage invariant universal: UI-initiated profile
switch/create now persist the resolved partition alongside the profile
id, matching the runtime path, so a tab stays on its partition even if
the renderer profile mirror is later stale (issue #6923).

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

* fix(browser): persist resolved partition through session restore

Add sessionPartition to the workspace-session zod schema so the resolved
partition survives persist->load; without it zod strips the field and a
restored isolated tab whose profile mirror is stale at startup falls back
to the shared default partition, reopening the storage leak (#6923).

Fold the webview teardown branches (parent drift or partition mismatch)
into one and re-resolve the viewport container once, matching the
pre-refactor null-guard behavior.

Split the sleeping-agent parse tests into their own file to keep both
under the 800-line cap.

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

* fix(browser): keep CLI-created tabs inheriting the default profile

browserTabCreate without an explicit profile was sending sessionProfileId:
null, which the renderer store treats as 'no inheritance' (its guard is
!== undefined). That forced CLI-created tabs onto the shared default
partition even when the user had configured a default browser session
profile, silently changing behavior a #6923 fix should not touch.

Leave sessionProfileId/sessionPartition undefined when no profile is named
so the renderer applies default-profile inheritance; thread the resolved
partition only when a profile is explicitly chosen.

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

---------

Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
2026-07-03 13:58:40 -07:00
Brennan BensonandOrca 133ebcee5d Update stale mobile Android download links to 0.0.21 (#7262)
Co-authored-by: Orca <help@stably.ai>
2026-07-03 13:42:02 -07:00
97c30c5ccc fix: drop stale repos fetches so deleted projects don't reappear (#7020) (#7024)
* fix: drop stale repos fetches so deleted projects don't reappear

Deleting a project group with "Remove contained projects" could leave the
removed projects as stale, unusable sidebar rows until an app restart (#7020).
`repos:changed` fires once per removal and the renderer starts an unsequenced
repos fetch per event; an earlier fetch that read pre-removal state could
resolve last and overwrite the newer result, reintroducing the deleted repos.

Guard fetchRepos and fetchReposForAllHosts with a monotonic token so a fetch
drops its own result once a newer repos fetch has superseded it — only the
latest fetch, which reads the final persisted state, applies. Add a regression
test that a stale fetch resolving after a newer one can't resurrect a removed
repo.

* test: isolate stale-fetch race in a dedicated file; scope guard to fetchRepos

Move the #7020 regression test out of repos.test.ts into a focused
repos-stale-fetch.test.ts and add a reject-path case (a superseding fetch
that later rejects must still block the older stale fetch). Scope the
monotonic guard to fetchRepos only: the original shared-counter guard on
fetchReposForAllHosts let an unrelated fetchRepos bump the counter and
abort an in-flight all-host load, dropping every host's repos.

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

* chore: restore origin/main cdp-ws-proxy.test.ts (drop merge artifact)

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

---------

Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
2026-07-03 13:40:17 -07:00
2c0be62df6 Fix empty nested import folder fallback (#6719)
* fix: allow empty nested import as folder

* Track open-as-folder recovery action and localize its strings

Completes the empty-nested-import fallback: adds the open_as_folder action to the existing add_repo_nested_import_action funnel (integer counts only, no paths) so adoption of the recovery path is measurable, and lands the five-locale catalog entries the new UI strings require.

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

* Pin runtime-kind mock return to the literal union in nested import flow test

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

---------

Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
2026-07-03 13:38:20 -07:00
Parker Rex 02dfb4b334 Fix Discord icon and CreateWorktree label spacing (#6505)
* Fix landing create worktree label

* Update Discord help menu icon
2026-07-03 13:12:14 -07:00
2153ef9456 Accept any hostname (and optional :port) in manual network address entry (#7223)
* Allow arbitrary hostnames in manual network address entry

parseManualNetworkAddress only accepted an IPv4 address or a Tailscale
MagicDNS (*.ts.net) hostname, so users behind a dynamic residential IP
who rely on a DDNS domain or self-hosted relay had no way to enter it
in the desktop UI short of bypassing validation via DevTools/IPC.

The main process already resolves any host: resolvePairingEndpoint
and parsePairingAddressOverride in src/main/runtime/runtime-rpc.ts
accept an arbitrary hostname and an optional host:port. This change
brings the renderer-side validation in line with what pairing already
supports: any RFC 1123 hostname (a superset that still covers
*.ts.net), optionally suffixed with :port (1-65535). IPv4 validation
is unchanged, including still rejecting malformed dotted-numeric input
instead of silently treating it as an all-digit hostname.

Updates the custom-address dialog copy in NetworkInterfacePicker.tsx
to describe the wider grammar.

* Polish manual-address takeover: fix bare-numeric guard, sync 5 locales, lint

- Require a dot in the IPv4-typo guard so a bare numeric label (`123`)
  validates as a legal RFC 1123 hostname, matching the code's own comment
  and the main-process resolver; add coverage.
- Update en.json + es/ja/ko/zh placeholder/hint to the broadened grammar
  (translate() reads en.json before the TSX fallback, so the copy change
  was previously inert; the other locales described the old ts.net-only rule).
- Replace indexOf(...)!==-1 with includes() to satisfy oxlint.

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

* Keep validator a strict subset of the backend resolver

Review surfaced two ways the renderer could accept an address the main
process handles differently:
- All-numeric hosts (bare `123` and dotted `256.0.0.1`) are now rejected.
  The WHATWG URL host parser downstream reinterprets a numeric host as IPv4
  (`123` -> `0.0.0.123`), so accepting one would validate an address the
  pairing resolver silently dials as a different host.
- Ports with leading zeros are rejected. `^[0-9]+$` let an arbitrarily long
  zero-padded string past the range check and inflate the returned address
  beyond the hostname length cap that the old whole-string check enforced.

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

* Reject any numeric final label, not just fully-numeric hosts

WHATWG URL host parsing treats a host whose last label is numeric
(`foo.123`, `foo.0x1`) as an IPv4 signal, so the pairing resolver would
fail to parse it and silently dial a fallback host. Widen the ambiguous-IP
guard to a single last-label check that subsumes the earlier all-numeric
case, keeping the renderer a strict subset of what the backend resolves
correctly. Normal hostnames whose last label merely contains digits
(`host2.example.com`) are unaffected.

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

---------

Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
2026-07-03 12:52:55 -07:00
492306f223 fix(sidebar): keep independent same-host checkouts of one repo as separate projects (#7237)
* fix(sidebar): keep independent same-host checkouts of one repo as separate projects

Re-regression of #5374: PR #6320 removed the per-host-surface separation
added by #5375, so independent local clones of the same repo (shared origin
=> same github:owner/repo identity) collapse into one sidebar project again.

Restore the surface-key separation in worktree-list-groups.ts, scoped by
ProjectHostSetup.setupMethod: 'provisioned' (ephemeral-VM runtime copies)
still nest under the project header (preserving #6320), while real user
checkouts (legacy-repo / imported-existing-folder / cloned) each get their
own header when duplicated on one host surface.

Revert the two tests #6320 inverted and add coverage for a provisioned copy
nesting alongside a same-host user checkout.

* test(cmd-j): update palette grouping tests for same-host checkout split

The Cmd+J palette shares getProjectGroupingForRepo (via
getProjectHeaderRevealTarget) with the sidebar, so restoring same-host
checkout separation also splits independent `cloned` checkouts into
per-setup palette entries. Update the multi-setup test to assert the split
and add coverage that a `provisioned` runtime copy still nests under one
project key.

* test(sidebar): cover split + provisioned-nest on one host together

Guard the intersection of #5374 and #6320: when a project has two real
same-host user checkouts AND a provisioned copy, the real checkouts each get
their own per-setup header while the provisioned copy stays under the plain
project key (never a path-scoped ::setup: header). Locks the two features'
combined behavior so a future change can't silently regress either half.

* refactor(sidebar): reuse shared Windows-path helper, single-pass surface count

Take-over polish of #7237:
- Use shared isWindowsAbsolutePathLike instead of an inline drive-letter
  regex, deduplicating the pattern and also bucketing UNC \\server\share
  paths as a Windows surface instead of falling through to default.
- Collapse buildProjectGroupingIndex's two passes over projectHostSetups into
  one, computing each surface key once and flagging ambiguous projects from
  the aggregated counts.
- Tighten why-comments to AGENTS.md's two-line rule.

No behavior change to the fix; all 123 sidebar/palette grouping tests pass.

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

---------

Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
2026-07-03 12:44:34 -07:00
37c8ff4935 fix: block resuming host-stored AI Vault sessions into SSH workspaces (#7130)
* fix: block resuming host-stored AI Vault sessions into SSH workspaces

AI Vault scans session history from this machine's disk only (host home
dirs plus local WSL homes), but since #6685 resume actions accepted any
SSH workspace as a target. Resuming a host-stored session there queues
cd '<local cwd>' && <agent> --resume <id> into a remote shell where
neither the cwd nor the session file exists, so it always fails.

Gate resume targets on where the session file lives: local targets stay
unrestricted, SSH targets now require a WSL-stored session (the
SSH-to-local-WSL setup #6685 was built for). Applies to the sidebar
resume buttons, direct resume, and pane drag-and-drop, with a toast
explaining the host mismatch. Catalog sync also picked up two
pre-existing drifts (missing failedUnnestWorkspace fallback, two
unreferenced SSH-timeout keys).

* i18n: translate host-session SSH-resume block message (es/ja/ko/zh)

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

---------

Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
2026-07-03 12:44:31 -07:00
1fd9547501 fix(agent-completion): stop PermissionRequest hooks from firing false completion notifications (#6316)
* test(agent-completion): cover done after waiting pause

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

* test(agent-completion): assert attention fires alongside done-after-waiting

Wire dispatchAttention into the done-after-waiting regression test so it
verifies both halves of the #5698 fix: the mid-turn permission pause
surfaces as attention exactly once, and the later genuine 'done' still
completes exactly once. Matches sibling tests that assert on both paths.

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

---------

Co-authored-by: brennanb2025 <brennankbenson@gmail.com>
Co-authored-by: Orca <help@stably.ai>
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
2026-07-03 12:44:28 -07:00
Neil c8e571f40c fix(sidebar): prevent hovercard text selection drags (#7228) 2026-07-03 03:07:17 -07:00
Brennan Benson 653f349b5f Fix terminal theme selection persistence (#7227)
Fixes #7166.
2026-07-03 02:55:27 -07:00
Neil ce680071a2 Place terminal URL tooltip at pane corner (#7221) 2026-07-03 02:15:24 -07:00
Neil a84f737cf7 fix(terminals): restore pane detach tab placement (#7215)
* fix(tabs): restore deferred tab activation so dragging a terminal tab doesn't switch panes mid-gesture

PR #5927 shipped terminal-pane drag (reorder tabs, move a tab into another
pane, edge-split into a new column). It deliberately DEFERRED tab activation
to pointer-up and suppressed it during a drag, so pressing a tab to drag it
never switched the active tab or stole terminal focus mid-gesture.

PR #6395 (d32d62a395) deleted tab-strip-pointer-activation.ts and made
SortableTab/EditorFileTab/BrowserTab activate eagerly on pointerdown — fixing
click-to-switch-after-reorder but regressing the drag: grabbing a tab now
flips the active tab + focused pane and yanks xterm keyboard focus before the
drag is even recognized (aggravated by PR #6210 raising the dnd-kit threshold
5px->12px). The move itself still lands, but the gesture feels broken.

Restore the deferred-activation hook, but gate it on measured pointer
DISPLACEMENT instead of the drag-active context ref the old hook used — that
ref clears asynchronously relative to the drop's pointerup, which is exactly
what made #6395's click-after-reorder misfire. Displacement mirrors dnd-kit's
own activation threshold: a release within TAB_DRAG_ACTIVATION_DISTANCE_PX is
a click (activate); crossing it is a drag (suppress). Because each press
measures its own gesture, a click after a reorder always activates.

- Recreate src/renderer/src/components/tab-bar/tab-strip-pointer-activation.ts
  (displacement-gated) + unit tests, incl. an explicit #6395 regression case.
- Rewire onPointerDown in SortableTab, EditorFileTab, BrowserTab to use it.
- BrowserTab.test.tsx shallow-renders via mocked React hooks; add useRef/
  useCallback stubs so the new hook works under that harness.

Verified end-to-end in a dev build via CDP: pressing a tab no longer
activates/steals focus on pointerdown; a click still activates on release;
dragging a tab into another pane still moves it; clicking tabs after a drag
still switches (the #6395 guard in tests/e2e/tabs.spec.ts stays green because
it uses zero-displacement clicks). 233 unit tests pass; oxlint + renderer
typecheck clean.

* fix: restore pane detach tab placement
2026-07-03 01:45:15 -07:00
Neil 7c9059c4d4 Restore desktop terminal URL modifier clicks
Restore desktop terminal URL and OSC link activation to require Cmd/Ctrl while preserving mobile tap routing.
2026-07-03 01:32:22 -07:00
Dvitash 0ff2c09002 Fix typed OMP remote title flicker (#6954) 2026-07-03 00:26:37 -07:00
Jinwoo HongandOrca 10e8f86899 Coalesce PTY input write bursts (#7205)
Co-authored-by: Orca <help@stably.ai>
2026-07-02 23:43:35 -07:00
Mark Xian 2848c5daf4 Fix rename inputs committing on CJK IME Enter confirmation (#6980)
Fixes #6980.
2026-07-02 23:37:12 -07:00
Neil 0fd2d67c69 Allow manual sorting of Project Groups (#7191) 2026-07-02 22:23:18 -07:00