Commit Graph
8651 Commits
Author SHA1 Message Date
Brennan Benson 9599cb4426 Merge remote-tracking branch 'origin/main' into brennanb2025/global-worktree-visibility-defaults
# Conflicts:
#	src/renderer/src/i18n/locales/en.json
2026-08-13 20:17:53 -07:00
Langning ZhangandJinwoo-H 3246b73add Forward Unix launcher signals to orca serve supervisor (#14071)
* fix(cli): forward Unix launcher signals to serve

* test(cli): retry incomplete listener state writes

* test(cli): cover both Unix launcher termination signals

* test(cli): cover macOS launcher signal oracle

* refactor(cli): keep Unix launcher exec atomic

---------

Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
2026-08-13 20:05:49 -07:00
Neil 8c3a1197d5 test(terminal): fix ordering pins that pass when the needle is absent (#14409)
Five pins compared indexOf positions without asserting presence, so a missing needle returned -1 and the assertion passed. Deleting the legacy-shim-dir capture in both Windows wrappers stayed green that way, disabling legacy-dir PATH removal. All five now route through a helper that asserts both operands exist first.

Adds coverage for four properties proven load-bearing by execution: the path_entry_kept guard (without it an empty cleaned PATH resolves a cwd-local git), the cleaned PATH on exec, and multi-separator matching in the shared path matcher.
2026-08-13 19:19:30 -07:00
Neil 0a0ae974d4 fix(workspaces): gate the GitLab palette number match on repo identity (#14381)
* fix(workspaces): gate the GitLab palette number match on repo identity

After a full GitLab identity compare failed, matching fell through to an
unguarded iid compare, so a pasted URL could activate a workspace from another
project or host. GitLab iids are per-project and start at 1, so small-number
collisions across projects are the norm.

Mirror the GitHub shape rather than rejecting outright: gate the number-only
fallback on a tri-state repo identity check that stays permissive when identity
is unresolvable, so forks and host aliases keep matching. A stored URL that
parses to a different project with the same type and number is a direct
contradiction and is rejected even when identity is unknown.

Refs STA-4155

* fix(workspaces): keep the GitLab repo gate permissive for fork checkouts

deriveGitRemoteIdentity keeps a single remote and ranks `upstream` above
`origin`, so a fork checkout resolves to the upstream project and the fork's
own origin is invisible to the gate. Rejecting on that mismatch dropped MR
URLs from the fork the user actually checked out — a false negative the
tri-state was meant to prevent. Treat an upstream-derived identity as unknown.

Refs STA-4155

* docs(workspaces): note that the GitLab repo identity is a one-shot snapshot
2026-08-13 18:46:36 -07:00
Neil 478bfe111e fix(worktree-scan): stop a stalled admin probe from poisoning repo refresh (#14379)
The scan cache stored the Git-admin fingerprint as an unsettled promise, so a
readdir/stat that never returns (hard NFS/SMB mount, dead sshfs, wedged cloud
FileProvider) left every later refresh awaiting it. The in-flight entry was
never cleared, so the repo fell back to persisted rows indefinitely — and since
computeResolvedWorktrees awaits all repos together, one wedged repo added 5s to
every snapshot for all of them.

Cache the settled value instead, filled by an identity-guarded writeback, and
bound the one branch that actually awaits the probe. withTimeout cannot cancel
a readdir, so an outstanding-probe guard keeps a wedged mount from issuing a
fresh probe every refresh and pinning every libuv fs thread.

Refs STA-4171
2026-08-13 18:44:06 -07:00
Neil e20e76a9f4 fix(orchestration): release federation ack checkpoints once a dispatch settles (#14380)
Checkpoints were inserted per synced federated dispatch and never removed; the
only eviction dropped the whole map, and none of its three call sites fire in
normal operation. A long-running federated coordinator retained one small
object per dispatch for the process lifetime.

Prune from the existing syncOrchestrationFederatedDispatch finally, which is
the one hook covering all five paths that create a checkpoint — including the
two RPCs that sync an already-terminal dispatch with no timer to prune after.

Refs STA-4014
2026-08-13 18:43:56 -07:00
Jinjing b19d99bd00 Center newly opened tabs at end, keep close controls visible (#14314)
* Center newly opened tabs at end, keep close controls visible

- Add end padding to tab strip to preserve close button visibility against fade
- Center scroll position for newly revealed end tabs, avoiding hard scroll-to-end
- Only pin-to-end for new tabs that are also active; restored mid-strip tabs stay in place

* test(tab-scroll): clarify when tabs center vs settle at end

Add lastTabGeometry helper to correctly derive scrollWidth from the pad policy. Update test expectations to document that center requests only land centered when the strip is within 2x the inset of the last tab's width; wider gaps settle at the end with inset.

* fix(tab-bar): sync active tab id ref after render

React Doctor rejects mutating refs during render. Keep the latest
active tab id in a layout effect so overflow navigation still sees
it without an impure render write.
2026-08-13 18:28:41 -07:00
Neil 94df72d9eb ci(windows): cover the worktree admin fingerprint on the Windows runner (#14378)
The fingerprint gate added in #14207 reads Git's administrative layout directly -- `.git` as a file or directory, `commondir`, and per-worktree `HEAD`, `gitdir`, and `locked` -- instead of shelling out to `git worktree list`. That makes it depend on Windows path resolution, CRLF inside those files, and whether `worktree move`/`lock` and deleting a live checkout behave as they do on POSIX.

PR CI runs the vitest suite on ubuntu-latest only, so none of that was exercised. Both suites were verified by hand on a real Windows host (Git 2.55.0.windows.3, Node 24.18.0) and pass 25/25, but nothing kept them passing.

Add them to the existing curated `Test Windows-specific boundaries` step rather than standing up a new job: the `package (windows)` job already checks out and installs dependencies, so this costs only the tests themselves.
2026-08-13 18:21:48 -07:00
Neil 444d30180d test(terminal): pin the wrapper security properties that survived mutation (#14394)
* test(terminal): pin the seven wrapper security properties that survived mutation

The PowerShell drive-relative pin was a prefix that also matched the insecure regex, so re-accepting C:foo left the suite green. Now asserts the full pattern.

Also pins all four PowerShell TrimEnd sites, the fallback wrapper-dir skip, the cmd legacy-dir trailing-separator strip, and C:/-style drive acceptance, plus a behavioural test for the POSIX legacy-dir reject with a distinct ORCA_ATTRIBUTION_SHIM_DIR. Each verified by reverting the property and confirming the suite fails.

* test(terminal): cover the trailing-separator scrub in the path matcher

isLegacyTerminalShimPathEntry sits outside the two generator modules, so the mutation campaign never re-ran it. Dropping its trailing-separator strip survived the suite: a PATH entry spelled with one would not match, leaving the legacy shim directory on the spawned PATH and the wrapper reachable. Mutation-verified on both the POSIX and Windows spellings.
2026-08-13 18:20:33 -07:00
Neil 517a2b3da6 perf(worktrees): bound the duplicate-id scan in reuseEqualCatalogRows (#14271)
* perf(worktrees): bound the duplicate-id scan in reuseEqualCatalogRows

Rows sharing an id are scanned linearly with a deep compare each, so a bucket
of k duplicates costs O(k^2). Both callers key on ids that are unique by
construction, so this is a bound on damage rather than a live fix.

Cap the scan instead of adding a second index: reuse is only an optimization,
so a missed match yields a new object identity, never a wrong row. A fingerprint
index would buy a little more reuse in a case nothing reaches, at the cost of a
second equality implementation that must stay in step with catalogValuesEqual
with no automated guard.

Worst-case duplicate bucket, no matches: k=1000 134ms -> 1.2ms, k=2000 541ms ->
2.2ms. Unique-id path unchanged (2000 rows: 0.86ms both).

* docs(worktrees): lead the duplicate-id cap comment with its reachability

A reader hitting MAX_DUPLICATE_ID_SCAN should learn first that no caller
produces duplicate ids today, so the cap reads as bounding future damage rather
than fixing something live.
2026-08-13 18:14:25 -07:00
Neil eb22e497bb Revert "fix(ssh): reapply the reattach-identity work and stop the fallback fence stranding moved panes" (#14395) 2026-08-13 18:11:33 -07:00
Brennan Benson 9fb9bd2e68 fix(sidebar): preserve reset for matching visibility overrides 2026-08-13 17:49:05 -07:00
Neil b53c6d41df fix(terminal): clear CDPATH when resolving the wrapper directory (#14387)
* fix(terminal): clear CDPATH when resolving the wrapper directory

cd consults CDPATH for a relative operand and echoes where it landed, which the command substitution captured — wrapper_dir came out wrong, the lookup resolved to the tombstone itself, and git died at 127 with a working git on PATH.

Also pins the self-reference guard that turns that failure into a clean 127 rather than repeated self-exec; deleting it previously left the suite green. Both new tests are mutation-verified: an earlier version of each passed with the bug reintroduced because the CDPATH fixture mirrored only the first path segment.

* test(terminal): pin the wrapper guards that survived mutation

A mutation campaign found several security properties with no coverage. Most important: nothing asserted the Windows *reject* path for relative candidates, so deleting it reopened the cwd hijack while every accept-path assertion stayed green.

Also pins the separator variable value (any other character silently un-pins the trailing-separator fix), and grounds the interpreter-search test in the running process cwd — a relative PATH entry resolves against that, so the previous fixture in a tmpdir passed whether or not the guard existed. Each is mutation-verified.
2026-08-13 17:38:33 -07:00
Jinwoo Hong 54aa22b2df fix(mobile): self-heal host opens and harden session liveness (#14333) 2026-08-13 17:15:03 -07:00
Neil 31fcf8e2f0 fix(terminal): name the POSIX interpreter directly when generating on Windows (#14386)
The resolver could never succeed on Windows — no absolute candidate exists there and the PATH search split on the POSIX delimiter — so it always fell back to the ambient lookup it exists to avoid, on a wrapper Git Bash and WSL panes do execute.

Also rejects interpreter candidates containing whitespace (a shebang cannot quote) or that are directories (X_OK alone is true for those), and replaces the sentinel trailing-separator strip, which corrupted paths containing the sentinel, with a comparison against a variable holding the separator.
2026-08-13 17:08:36 -07:00
Brennan Benson d16092e503 fix(updater): recover renderer shutdown checkpoint (#14373)
* fix(updater): recover renderer shutdown checkpoint

* test(updater): cover checkpoint recovery in Electron

* fix(updater): keep staging failures blocking
2026-08-13 17:06:36 -07:00
Brennan Benson 537864a248 Fix Codex hook trust before manual shell launches (#14326)
* fix codex hook trust before shell launch

* fix packaged cli preflight dependency

* fix codex shell preflight safety

* fix Codex shell preflight settings and startup safety
2026-08-13 17:02:28 -07:00
Brennan Benson a6a9a6a482 Merge remote-tracking branch 'origin/main' into brennanb2025/global-worktree-visibility-defaults
# Conflicts:
#	src/renderer/src/i18n/locales/en.json
2026-08-13 16:55:15 -07:00
Neil 6a0c8fa541 fix(ssh): reapply the reattach-identity work and stop the fallback fence stranding moved panes (#14384)
* Reapply #13326 and #13928 (un-revert #14361)

Restores the SSH reattach-identity and daemon-occupancy fixes. Reverting them
reintroduced their P0s, filed as STA-4224, STA-4225, STA-4227, STA-4230,
STA-4232, STA-4233 and STA-4234 against #14361.

The tab loss that motivated the revert is fixed in the commits that follow, so
this reapplication is not a straight redo.

* fix(relay): stop the fallback attach fence refusing a pane that moved tabs

The primary fence was moved to the shell's own incarnation precisely because
paneKey/tabId froze the pane's LOCATION at spawn and refused panes that had
merely moved. The fallback that older clients fall into kept the old rule, so
the correction never reached it — the same 'the rule exists, but this path does
not ask it' leak this work has hit repeatedly.

A refusal here is not recoverable: an identity mismatch never grounds a respawn,
so the pane keeps a live shell it can no longer reach and renders blank.

Narrowed to paneKey, which is the identity; the tab is a location. Restoring the
tabId comparison reddens the new test.
2026-08-13 16:45:24 -07:00
Neil 622de8009c fix(terminal): bake the wrapper interpreter and emit CRLF for cmd (#14382)
* fix(terminal): bake the wrapper interpreter and emit CRLF for cmd

Round-3 review: the shebang resolved bash through the inherited PATH before any script hygiene, so a relative or empty PATH element let an untrusted checkout supply the interpreter. Bakes an absolute verified interpreter with an env fallback.

Normalizes trailing separators in the cmd comparisons; without it a wrapper-dir entry spelled with a trailing backslash escaped self-exclusion and the wrapper tail-chained to itself forever.

Found while verifying on Windows: cmd resolves call targets by byte offset and fails on LF-only files once they grow (worked at 2.4 KB, failed at 3.7 KB), so the Windows wrappers now emit CRLF. Also avoids two cmd parser traps: a trailing backslash before a closing quote in an if comparison, and percent expansion inside rem comments.

* fix(terminal): resolve the wrapper interpreter from absolute PATH entries too (STA-4226)

The well-known-candidate list would fall back to an ambient env lookup on distributions that place bash elsewhere (NixOS, Guix), restoring the exposure. Search absolute PATH entries before giving up, skipping relative and empty ones because those mean the current directory.
2026-08-13 16:39:13 -07:00
Brennan Benson 45736ca8dd feat(dashboard): give the agent map a real filter panel (#14338)
* feat(dashboard): filter the agent map by multiple hosts

The map's host control was a single-choice segmented row in its own header:
pick All, Local, SSH, WSL, or Remote — never two at once. It also sat outside
the shared filter menu, so the menu's badge never counted it and "Clear all
filters" could not reach it.

Hosts are now checkboxes in the filter menu alongside agent state, so a fleet
split across a Mac and a Windows runtime can show both and hide the rest.
Hosts that contribute no agents are omitted, and the section hides entirely
for a single-host fleet.

Lifting `hostFilter` out of AgentMap also drops `compact`, which existed only
to hide the segmented control next to an open terminal panel.

The filter-option builders move to agent-dashboard-filter-options.ts to keep
AgentDashboardToolbar under the 400-line cap; that move is mechanical.

* feat(dashboard): give the agent map a real filter panel

The map's filters were a single-choice host row in its own header plus a few
rows borrowed from the board's dropdown. This replaces them with one panel that
owns every map facet.

- Quick views: Everything / Needs me / Stuck / Unread / Last 30 min /
  Long runners / Stale > 3d / Orchestration. Each replaces the filters
  wholesale rather than stacking on whatever was set.
- Provider (agent type) checkboxes.
- Three time ranges — session lifespan, since last message, time in current
  state — on a non-linear scale so minutes and days are both reachable.
- Orchestration flows select the coordinator *and* the agents it dispatched;
  a children-only filter hid the half that explains the flow.
- Sections collapse, each showing its value when closed. A section that is
  filtering forces itself open so a collapsed row can never be the reason the
  map looks emptier than the filters claim.
- The shown-count moves into the panel header, next to the controls that
  change it, and now reflects the map's own facets rather than the board's.

The map's surface becomes a Popover, not a DropdownMenu: the panel holds range
sliders and a Radix menu swallows the arrow keys those need. The board keeps
its dropdown untouched via a new `filterControl` slot on the shared toolbar.

`ui/slider` renders one thumb per value so a two-value range works; the two
existing single-value callers are unchanged.

* fix(dashboard): preserve agent map filter semantics

* refine agent map filter facets

* perf(dashboard): keep map filters out of main view
2026-08-13 16:31:32 -07:00
OrcaWinandBrennan Benson e1ee1b3ef3 fix(macos): recover severed terminal TCC attribution after updates (#13992)
* fix(macos): recover severed terminal TCC attribution after updates

When a packaged update leaves the daemon healthy but TCC-severed (spawning
binary gone), surface a Manage Sessions toast and replace the daemon before a
new terminal only when zero live sessions remain. Does not broaden FDA or
auto-kill sessions. Addresses the Orca-specific path of #13594.

* fix(macos): coalesce severed-TCC toast checks and sync i18n keys

Add catalog entries for the Manage Sessions toast strings and guard overlapping
mount/focus probes with an in-flight latch so only one infinite toast can fire.

* test(macos): harden severed TCC recovery coverage

* fix(macos): clear recovered TCC warning

* fix(macos): clarify severed TCC warning copy

* fix(macos): bound TCC attribution health checks

* fix(macos): preserve bounded attribution checks after merge

* test(macos): use real execFile callback contract

* fix(macos): cover legacy daemon attribution

* fix(macos): clarify affected Orca terminals

---------

Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
2026-08-13 16:30:44 -07:00
Jinwoo Hong 77b37d85e2 feat(vm): create workspaces from provisioned SSH roots (#14359)
* feat(vm): use recipe-provisioned SSH roots

* fix(vm): preserve ordinary create failure timing

* test(vm): prepare provisioned root SSH fixture

* ci(vm): enable SSH setup for provisioned root E2E
2026-08-13 16:23:22 -07:00
Jinwoo Hong f9f55075f6 fix(vm): adopt provisioned SSH checkout roots (#14353) 2026-08-13 16:23:22 -07:00
Jinwoo Hong 99d19d4635 feat(vm): add provisioned root recipe contract (#14352) 2026-08-13 16:23:21 -07:00
Jinwoo Hong a5a998ea77 fix(vm): make hidden SSH cleanup retryable (#14351) 2026-08-13 16:23:21 -07:00
Brennan Benson 3f0029eb63 Merge remote-tracking branch 'origin/main' into brennanb2025/global-worktree-visibility-defaults 2026-08-13 16:22:28 -07:00
Brennan Benson 04a518216a feat(sidebar): replace the inherited-visibility switch with a Show/Hide picker
Each source row now shows a two-segment Show / Hide control preselected to the
global setting, and explains itself only where the project actually disagrees:
an "Overriding global setting: <value>" card names the value being ignored.
Picking the segment global already holds drops the override instead of pinning
a duplicate, so the same control both overrides and reverts, retiring the
separate "Use global" link. The dialog footer now lists every inheritable
source with its global value.
2026-08-13 16:22:23 -07:00
Brennan Benson c3b8c145e2 fix(agent-status): preserve Codex escape interruption (#14372) 2026-08-13 16:19:26 -07:00
Neil a27d046e5e fix(terminal): reject cwd-resolving PATH entries and harden the legacy wrappers (#14370)
* fix(terminal): guard cmd wrapper PATH walks against an empty variable

An empty PATH or cleaned PATH left the substitution with an unbalanced quote, desynchronizing cmd parsing so the not-found branch emitted a parse error instead of its message. Reproduced and fixed on real Windows.

* fix(terminal): reject relative PATH entries and drop the dirname dependency

Adversarial review found the STA-4169 fix incomplete: it dropped empty PATH elements but kept relative ones, which resolve against the current directory identically. A repo-local git was still executed via PATH=. or node_modules/.bin, reproduced in all three wrappers.

Also removes the external dirname call in the POSIX wrapper (an unresolvable dirname silently made wrapper_dir the cwd, so the wrapper failed to exclude itself and reported git missing while git was on PATH), compares against the cached wrapper dir in the cmd subroutine where %~dp0 is rebound by CALL, and probes .cmd after .exe so a non-.exe git is still found.

* fix(terminal): test rooted paths in pure batch, not via an external tool

The rooted-path guard shelled out to findstr, which cmd resolves from the current directory first — so a repository-local findstr could run, and a malicious one could report success for every entry and defeat the guard entirely. Same hijack the guard exists to prevent. Now a pure-batch substring test with no external process.

Splits the Windows wrapper templates into their own module to stay under max-lines without a suppression.

* fix(terminal): reject drive-relative PATH entries and fix no-slash wrapper dir

Round-2 review: PowerShell IsPathRooted accepts drive-relative C:foo, which still resolves against the current directory on that drive. IsPathFullyQualified is absent on Windows PowerShell 5.1 (verified 5.1 on the test host), so match the same prefixes the cmd wrapper accepts.

The POSIX %/* strip yields the file name when the path has no slash, so wrapper_dir became the name, self-exclusion missed the shim dir, and the lookup resolved back to the wrapper (spurious 127). Handled with a case split.

Also probes .bat, since the replaced lookup honored PATHEXT.
2026-08-13 16:07:49 -07:00
m4air 2f85dda183 Narrow paired browser link fix to registration race 2026-08-13 15:55:23 -07:00
m4air 232e398c35 Fix paired remote terminal browser links
STA-4181
2026-08-13 15:55:23 -07:00
Brennan Benson 36f3036d2e Merge remote-tracking branch 'origin/main' into brennanb2025/global-worktree-visibility-defaults 2026-08-13 15:51:19 -07:00
Jinwoo Hong ff8dda81e8 fix(serve): exit cleanly after headless Linux signals (#14334)
* fix(serve): keep owned Xvfb alive through Electron teardown

* test(serve): gate packaged signal shutdown

* test: harden headless shutdown lifecycle gate

* fix(serve): isolate Xvfb from foreground signals

* docs(serve): preserve Xvfb during systemd stop

* test(serve): pin shutdown policy to owned Xvfb unit

* test(serve): harden shutdown gate portability

* test(serve): bound systemd unit parsing
2026-08-13 15:45:00 -07:00
Brennan Benson 953cfab635 fix(terminal): make the bold font weight its own setting (#14368)
* fix(terminal): make the bold font weight its own setting

Deriving bold as max(700, regular + 200) silently destroyed bold. A family
exposes only a few real faces: the monospace the default chain resolves to on
macOS has exactly two, splitting at 600. Measured by rasterizing each weight to
a canvas — 100-500 are byte-identical (ink 3023) and 600-900 are byte-identical
(ink 3855), at every weight the same advance. So any base weight at or above 600
put both values in the same face and bold stopped existing, on 4 of the 9
positions the slider offers, with no error and nothing the user could do.

Arithmetic cannot fix it — on a two-face family there is no heavier face to
escape to. So bold is now user-owned: a new terminalFontWeightBold setting with
its own control, defaulting to 700. The default pair (500/700) straddles the
boundary, so existing profiles render exactly as before; a collision is now a
choice the user can see and undo.

The old test asserted 800 -> {800, 900} as 'keeps bold heavier', which is where
this hid: numerically heavier, identically rendered.

* fix(terminal): surface bold face collisions accurately
2026-08-13 15:32:01 -07:00
Dong dahaoandJinwoo-H d243137e35 fix(orchestration): resolve explicit worker worktrees directly (#14275)
* fix(orchestration): resolve explicit worker worktrees directly

* fix(orchestration): share worker workspace resolution

* fix(runtime): reject cross-host path ambiguity

* fix(orchestration): share federated workspace resolution

* refactor(runtime): share worktree host identity

* test(orchestration): align worker lifecycle fixtures

---------

Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
2026-08-13 15:26:58 -07:00
BingZ 202ce1ef0e fix(orchestration): make late task readiness atomic (#14163)
Derive initial dependency readiness inside the task INSERT so concurrent completion and creation cannot strand a task in pending.

Add deterministic state-machine coverage and a built-CLI RPC/runtime persistence E2E.

Fixes #14143
2026-08-13 15:25:25 -07:00
Jinwoo Hong e06cdbb4ee fix(orchestration): wait for Claude composer render (#14342) 2026-08-13 15:04:32 -07:00
Neil a12f8be144 fix(terminal): resolve legacy wrapper fallbacks from PATH only (STA-4169) (#14365)
The retained git/gh compatibility wrappers resolved their real binary with an unqualified lookup. On Windows that searches the current directory before PATH, so a repository-local git.exe/gh.exe ran with the user's arguments. The POSIX wrapper had the same exposure through empty PATH elements, which mean the current directory.

All three wrappers now resolve only against the cleaned PATH: cmd and PowerShell walk it explicitly instead of delegating to a lookup that includes the cwd, and the POSIX filter drops empty elements. Candidates inside the wrapper directory stay rejected.
2026-08-13 14:55:28 -07:00
Neil 11cd2b4310 revert(ssh): back out #13326 and #13928 — reconnect loses every tab (#14361)
* Revert "fix(daemon): stop killing live coding agents when the daemon can't report its sessions (#13928)"

This reverts commit 2e8cf589de.

Reverted together with #13326: the 1.4.182-daily.202608131439 build carrying
both loses every tab on an SSH disconnect/reconnect cycle. Reverting first so
main stays releasable and the P0 fixes in the wild remain cherry-pickable,
rather than fixing forward on a shipped regression.

* Revert "fix(ssh): stop SSH reconnect from multiplying terminals and resuming agents twice (STA-3077)" (#13326)

This reverts commit 3ab8b6a117.

Reported on 1.4.182-daily.202608131439: connect to an SSH worktree, disconnect
the host from the hosts popup, reconnect — every tab is gone. That is worse than
the behaviour this PR set out to fix, where most tabs were retained.

Reverting rather than fixing forward, so main stays releasable and the P0 fixes
already out in the wild stay cherry-pickable. STA-3077 stays open.
2026-08-13 14:54:29 -07:00
Brennan Benson 292012043f fix(daemon): isolate durable-history checkpoints per session (STA-4173) (#14346)
PR #14193 routed warm reattach and deep-buffer snapshots through
overlayDurableRestoreSnapshot, which joined a single process-wide
checkpoint tail with no deadline. One never-settling checkpoint blocked
every daemon-backed terminal from reattaching.

Checkpoint exclusivity only protects one session directory's
tmp-write/rename pair, so serialize per session instead. Reattach now
waits on a bounded deadline and degrades to the daemon's live window;
the abandoned compact keeps running and still commits, so a blown
deadline costs restore depth for one reattach, never durable history.
2026-08-13 14:51:59 -07:00
Brennan Benson e799259d3b fix(browser): fence the cookie-clear fallback to the pre-clear snapshot (STA-4170) (#14343)
* fix(browser): fence the cookie-clear fallback to the pre-clear snapshot (STA-4170)

The post-rejection fallback re-read the live cookie jar and removed everything
removable at that moment, while restore only ever covered the pre-clear
identity snapshot. A cookie that arrived mid-clear -- a login the user had just
completed -- was therefore deleted with no identity able to put it back, and a
later removal failure still reported "existing cookies were restored".

Fix the removal plan at the same point as the identity snapshot so the mutated
set can never exceed the restore set. Arrivals are not touched at all, which
matches what the successful bulk-clear path already does.

* test(browser): lock same-coordinate arrival rollback to the pre-clear value (STA-4170)
2026-08-13 14:51:27 -07:00
Neil 281cc77e79 fix(ai-vault): make the merged scan stamp independent of leg order (#14270)
* fix(ai-vault): make the merged scan stamp independent of leg order

The all-host merge picked its stamp with a strict `stampMs > latestMs` and
echoed the winning leg's verbatim string. Two legs reporting the same instant
in different legal ISO shapes ("...:05Z" vs "...:05.000Z") therefore resolved
by position in the results array, i.e. by host-enumeration order (local, then
SSH, then runtime). The prior lexicographic max was order-independent, so this
was a regression with no test covering it.

A merge has no single scan instant, so its stamp is derived data rather than
any one leg's string: return the canonical ISO form of the newest accepted
instant. That is order-independent and format-independent, and drops a
variable instead of adding a tie-break branch.

Also share one request resolver between main and the renderer so the
renderer's merged-scope predicate is equivalent to main's routing by
construction, rather than by a comment that overclaimed it.

* docs(ai-vault): scope the merged-predicate comment to the desktop IPC path

The replacement comment still asserted the result is always several hosts'
legs. The paired web transport drops executionHostScope and serves one host,
so 'all' there is a single scan. State that the predicate is deliberately
over-inclusive and why erring the other way would be unsafe.

* test(ai-vault): pin the merged-stamp Date range boundary

new Date(ms).toISOString() throws RangeError outside +/-8.64e15. That is
unreachable only because Date.parse applies TimeClip, so the NaN guard alone
constrains the argument. Nothing pinned that. Dropping the guard now fails
these two cases with the RangeError they exist to prevent.

* refactor(ai-vault): route session-title scope through the shared resolver

The last character-for-character copy of the request-scope default. Leaving
it would make the shared resolver the single source of truth for two of three
sites, which is the drift this change exists to remove. No behavior change.
2026-08-13 14:49:59 -07:00
Brennan Benson e49897c3c4 Clarify inherited worktree visibility settings 2026-08-13 14:40:18 -07:00
Brennan Benson 3290ca0d9e feat(agent-map): flare fresh questions and finishes (#14349) 2026-08-13 14:31:13 -07:00
erishandJinwoo-H 1f4b731f7c fix(skills): use exported recipe id in environment guide (#14280)
* fix(skills): use exported recipe id in environment guide

* fix(skills): keep recipe-derived Vercel names valid

---------

Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
2026-08-13 14:18:57 -07:00
Brennan Benson f263c6319e Merge remote-tracking branch 'origin/main' into brennanb2025/global-worktree-visibility-defaults
# Conflicts:
#	src/renderer/src/components/sidebar/new-external-worktrees-inbox-candidates.ts
2026-08-13 14:06:53 -07:00
Brennan Benson e68c4566ff fix(native-chat): retain watcher identity during initial drain (#14344) 2026-08-13 14:05:55 -07:00
Brennan Benson 3074c55772 Polish external worktree settings navigation 2026-08-13 14:05:41 -07:00
Brennan Benson 162001eef3 feat(sidebar): replace the external-worktrees inbox list with one card (#14341)
The Non-Orca worktrees modal already lists every hidden external worktree
with search, virtualization, and per-row Show. Its filter is a strict
superset of the sidebar inbox's, so the expanded sidebar list was a second,
worse copy that grew to ~900px at 24 worktrees.

The inbox is now a single clickable card stating the count, which opens that
modal. Drops the expand state, nested list, per-row Import, and the
Keep hidden / Import all footer.
2026-08-13 14:04:09 -07:00