Commit Graph
11496 Commits
Author SHA1 Message Date
Brennan Benson b81e782ffc fix(browser): stop failing goto when a redirect or download aborts the load (#9822)
* fix(browser): stop failing goto when a redirect or download aborts the load

Electron's loadURL rejects with ERR_ABORTED (-3) when the initial
navigation is superseded — a client-side/meta redirect (common in SSO
flows) or a download-triggered load. Since #9633 drives goto through
wc.loadURL directly, that rejection surfaced as a spurious
'Failed to navigate' browser_error even though the page landed fine.

Treat ERR_ABORTED like offscreen-browser-backend already does: resolve
with the page's actual URL/title. Every other loadURL failure still
fails closed.

* fix(browser): settle replacement navigation after abort

* fix(browser): clean up aborted navigation destruction race
2026-07-21 19:56:07 -07:00
OrcaWin f9f3cd2fbe fix(terminal): prevent reconnect from killing live daemon sessions (#9804) 2026-07-21 19:20:16 -07:00
NeilandOrca 96df5f5715 fix(editor): don't flag editor-initiated moves as changed-on-disk (#9506)
* fix(editor): don't flag editor-initiated moves as changed-on-disk

An in-app move/rename (explorer drag-drop, inline rename, tab rename)
re-homes the open tab to the new path and carries its unsaved draft
forward. The move also physically relocates the file, which the worktree
watcher reports as delete(old)+create(new) a few ms later. Because the
tab already lives at the new path by then, that create echo was treated
as an external write landing on a dirty tab and raised a spurious
"changed on disk" banner.

Add a short-lived self-move registry (the move analog of the existing
self-write registry) stamped at the single remap choke point, and have
the external-watch handler recognize the move's own watcher echo:
suppress the changed-on-disk mark on the re-homed dirty tab and the
tombstone on the source path. Genuine external edits are unaffected.

Covered by unit tests for the registry, the remap recorder, and the
watch-hook suppression (plus a no-over-suppression guard).

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

* refactor(editor): harden move-echo suppression per adversarial review

Addresses review findings on the self-move suppression:

- Stamp the self-move at the call sites BEFORE the on-disk rename
  (recordSelfMoveForOpenTabs), not after the tab re-home, so the
  watcher echo can't win the race. This makes the source-side delete
  guard actually effective and removes a possible one-frame flash.
- Suppress only the move's own create echo, not update events, so a
  genuine external write to the moved path within the TTL still raises
  the changed-on-disk banner (closes an over-suppression gap).
- Track source/target roles independently per path so an immediate
  undo can't clobber the original move's still-in-flight stamp.
- Raise the registry cap above realistic bulk-move sizes so a large
  directory move never self-evicts its own not-yet-echoed stamps.

Updated + added tests: registry undo/role + cap, the new call-site
helper (incl. directory move), and a real-update-still-marks case.

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

* fix(editor): make move-echo suppression watcher- and TTL-robust

Round-3 hardening after adversarial review:

- Suppress the move's own watcher echo regardless of event kind. The
  main-process watcher coalesces a create+attr-change burst into a lone
  update, so a create-only gate let the echo through on some hosts and
  re-exposed the false banner. Suppression is now bounded by the
  self-move TTL; a genuine write to the exact path within that short
  window is the documented trade-off (draft is preserved regardless).

- Bracket the on-disk rename with the self-move stamp via a single
  renameOpenTabsPathOnDisk wrapper: stamp before (to beat the watcher),
  re-stamp on success (a slow SSH/runtime rename can outlive the TTL, so
  the fresh window must start when the file actually moved), and clear on
  failure (a rename that never happened must not suppress real events).
  All move entry points (explorer move, inline/tab rename incl.
  undo/redo, untitled rename) route through it.

- Treat a tab as remote for TTL purposes when it has a runtime owner OR
  an SSH worktree connection (an SSH tab can carry a null runtime owner).

- Registry tracks source/target roles independently per path so an
  immediate undo can't clobber the original move's in-flight stamp; cap
  raised above realistic bulk-move sizes.

Tests: registry role/clear/cap, the rename wrapper (success re-stamp +
failure clear), the call-site helper (dir move + clear), and watch-hook
coalescing-robust suppression + post-TTL surfacing.

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

* fix(editor): refcount self-move roles so a failed move can't clear a live one

Two concurrent moves onto the same destination both stamp that path as a
target; if the second rename fails and clears, it must not erase the
first (successful) move's still-live target stamp. Reference count each
role's registrations and clear only the failed move's own contribution.

Adds a regression test for the shared-destination case.

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

* fix(editor): give self-move stamps per-registration expiries + retract tokens

The refcount model used a single shared expiry scalar per role that only
grew via max() and reset at refs=0, so releasing the max-contributing
registration left survivors inheriting an over-extended window (and an
expired registration could be resurrected by a later stamp on a key the
opposite role kept resident). Both over-suppress genuine changes.

Model each stamp as an independent registration carrying its own expiry
(a list per role). recordSelfMove returns a ticket; clearSelfMove
retracts exactly that registration. A role is live while any of its
registrations is unexpired, so concurrent stamps, failed-move clears, and
expiry are all precise. Wrapper/helper thread the tickets through.

Adds regressions for the over-extension and resurrection cases.

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

* test(editor): cover source-side self-move guard in the pre-remap ordering

Adds the case where the watcher's delete(old) arrives while the tab is
still at the old path (before remap re-homes it): with a live self-move
source stamp the tombstone must be suppressed. Pairs with the existing
naked-delete control (no stamp → deleted) to pin the guard's behavior.

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

* docs(editor): document the failed-move suppression window as a bounded trade-off

A self-move stamp is placed before the rename and retracted if it fails,
so an event consumed during the pre-failure window is swallowed. Note
that this only matters for the rare unrelated-dirty-tab-at-destination
case and the recoverable missed-source-tombstone case, and that a move
has no bytes to echo-verify the way self-writes do.

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

* feat(editor): decide move echo vs external write by content identity

Replaces the time-bounded self-move suppression heuristic with a
correct-by-construction identity check, so a genuine external write to a
just-moved path is never swallowed and the move's own echo is never a
false conflict — regardless of watcher event-kind coalescing or timing.

- Remap now carries the edit-session identity (lastKnownDiskSignature,
  externalMutation, pendingDiskBaselineVerification) onto the re-homed
  tab. Previously the close+reopen dropped it, so a moved tab lost its
  disk baseline (and any pre-existing changed-on-disk conflict silently
  vanished on move).
- On a live self-move-target dirty event the watch hook now reads the
  destination and compares getDiskBaselineSignature(disk) to the tab's
  carried baseline: equal => move echo (suppress), differ/binary =>
  genuine write (banner). Autosave is suspended synchronously before the
  read so a write landing mid-read can't be overwritten; a generation
  token makes overlapping reads safe. Fails CLOSED (marks changed) on a
  missing baseline or read error — never blind-suppresses.
- The self-move registry now only scopes WHEN to verify. The source-side
  delete still can't be content-verified (nothing to read), so it stays a
  bounded, documented suppression.
- Trim the verbose Why-comments across these files to 1-2 lines.

Adds content-identity verification tests (echo/differ/no-baseline/read-
error/binary/autosave-gate) and a remap test for the carried identity;
splits the watch-hook suite to stay under the max-lines limit.

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

* fix(editor): give live move-verification its own autosave gate

The live self-move echo verification reused pendingDiskBaselineVerification
as its autosave gate, but that field is also the always-mounted restored-tab
conflict scanner's work queue: the scanner scans any pending dirty tab,
launches its own read, and clears the flag without checking the live
generation — so it could lift the gate mid-read and let autosave overwrite a
genuine external write. Give live verification a dedicated
pendingLiveDiskVerification field (both suspend autosave; each cleared only by
its owner). Transient, not persisted, not carried across a re-home.

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

* feat(editor): atomic rekeyOpenFilesForPathChange store action (move restructure stage 1)

Foundation for treating an Orca-owned move as an in-place retarget of the
open edit session (not close+reopen), per the locked design. One commit-only
store update migrates every path-derived id + all id-keyed state
(openFiles full-spread, the 6 file-id maps, activeFileId(+byWorktree),
tabBarOrder, unified tabs/groups via the now editor-family-widened
migrateHydratedEditorTabsAndGroups, pendingEditorReveal, untitled consume).
Preflight fails closed on collision (never merges two live sessions) or stale
with zero mutation. Not yet wired to a coordinator (stage 4).

Stage 1 of 5; behind the shipped content-identity fix.

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

* feat(editor): op-scoped in-flight move registry + source integration (stage 2)

editor-path-move-inflight.ts tracks Orca-owned moves for the exact duration of
the rename+rekey (no TTL): source paths suppress the delete tombstone, target
paths latch a destination event seen before the rekey (never suppress). Wired
into the watcher's delete filter alongside the old TTL registry (OR fallback)
so suppression keeps working until the stage-4 coordinator drives every move
through beginEditorPathMove. Stage 2 of 5.

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

* feat(editor): move-echo provenance + autosave gate on OpenFile (stage 3 store)

Adds pendingSelfMoveEcho {operationId,targetPath} to OpenFile and has the
rekey action install it + pendingLiveDiskVerification on dirty autosave-capable
destinations (moveOperationId arg), atomically in the same commit that re-homes
the tab — so the verify gate survives the rekey and its op-id token supersedes a
stale in-flight verification. Replaces the module-scoped generation map (which
broke under rekey). Verification-reader wiring + coordinator follow.

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

* refactor(editor): remap moves via atomic in-place rekey, not close+reopen (stage 4a)

remapOpenEditorTabsForPathChange now builds an owner-aware rekey plan (plain-path
id to the first owner, owner-qualified to the rest; previews resolve their source
to the moved edit's new id) and applies it via rekeyOpenFilesForPathChange in one
commit — preserving the full OpenFile + cursor/view/group/MRU state and closing
the close/reopen watcher-race window. Passes moveOperationId through so dirty
destinations get the content-verify gate. 4545 tests green.

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

* feat(editor): move coordinator drives rename/drag/undo/redo/untitled (stage 4b)

executeOpenEditorPathMove is the single transaction for every in-app move:
quiesce affected saves -> op-scoped begin (per runtime owner) -> on-disk rename
-> atomic in-place rekey (installs the content-verify gate/provenance) -> settle
-> re-verify any destination echo latched before the rekey. On failure the store
is untouched. Verification now triggers off the on-OpenFile provenance (consumed
on resolve) and the watcher latches pre-rekey destination events. Wired into all
five call sites; old renameOpenTabsPathOnDisk + separate remap removed from them.
2751 tests green. (TTL self-move registry now dead; removed next.)

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

* refactor(editor): remove the dead TTL self-move registry (stage 4c)

The coordinator + op-scoped in-flight suppression + on-OpenFile provenance fully
replace the time-bounded self-move registry, so delete it and its two helper
modules (record-self-move-for-open-tabs, rename-open-editor-tabs-path). The
watcher source filter now uses only isActiveMoveSourcePath and the verification
trigger only the tab's pendingSelfMoveEcho. Rewrote the self-move test suite onto
the new primitives. 7225 tests green.

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

* test(editor): end-to-end coordinator move test (stage 4 done)

executeOpenEditorPathMove renames on disk, retargets the session in place with
draft/dirty/baseline preserved + gate/provenance installed, settles the in-flight
transaction; on rename failure the store is byte-identical and the transaction is
released.

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

* feat(editor): mirror-safe move — detach moved mirrored tab + close-notify host (stage 5)

The atomic rekey changes a tab's id, so a moved mirrored tab would be culled by
the host snapshot (losing its draft) or resurrect the old path. Ship the safe
minimum: the rekey detaches a moved tab from the host mirror
(mirroredFromRuntimeSession cleared) and the coordinator close-notifies the
host's old-path tab (close intent suppresses re-mirroring). Prevents the
data-loss/resurrection; the moved tab becomes companion-local. The full
host-rekey path-change protocol (preserving mirror ownership) is a documented
follow-up.

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

* fix(editor): address review round 1 (4 majors)

- Coordinator now propagates the rekey result: a collision/stale AFTER a
  successful on-disk rename triggers an inverse rename + throws, instead of
  reporting success with the source tab stranded at a vanished path (#1).
- Cross-worktree: affected set spans all worktrees at the source path, sub-ops
  scoped per (worktree, owner), and the rekey partitions tab/group/tab-bar
  migration by each file's own worktree (was applied under one scope) (#2).
- Diff tabs: single-file unstaged diff tabs are now retargeted on a directory
  move (rebuild the diff id + relative path) instead of stranding (#3).
- Mirror close-notify moved to AFTER a successful rename so a failed rename
  can't desync the host by closing its authoritative tab (#4).

Adds tests: collision->inverse-rename, diff-tab retarget. 6698 tests green.

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

* fix(editor): review round 2 (mirror ordering, rollback error, diff sources)

- Close the host mirror tab only AFTER the rekey commits (capture pre-rekey
  resolution first): a rekey collision after a successful rename no longer
  desyncs the host by closing its authoritative tab (high).
- Surface a failed inverse rename instead of swallowing it: the thrown error
  now states the on-disk move may remain at the new path (high).
- Restrict diff-tab retargeting to staged/unstaged (purely path-derived ids);
  branch/commit diffs carry compare metadata and combined 'Changes' is
  worktree-rooted, so rebuilding them from path would produce a wrong id (med).

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

* fix(editor): resolve the move verify gate proactively (review round 3)

The rekey gates every dirty moved tab pending a destination content check,
but verification only ran when a watcher event arrived for that path. If the
watcher was down, throttled, or the event coalesced away, the gate never
cleared and autosave stayed suspended for the tab.

The coordinator now drives verification for every tab it gated once the
rename has committed, so the gate resolves on its own. That makes the
destination-side event latch redundant, so the in-flight tracker is
source-only again.

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

* fix(editor): review round 4 (gate strand, cross-worktree verify path, leak)

- Don't install the move-echo verify gate on a tab already showing the
  changed-on-disk banner: it's autosave-suspended via externalMutation and
  verification skips a 'changed' tab, so the gate would strand forever.
- Content-verify reads each moved tab's own filePath. A cross-worktree/
  floating tab's relativePath is relative to its own root ('../…') and must
  not be joined onto the initiating worktree path (would read the wrong file
  and raise a false conflict banner on unsaved work).
- Coordinator settles the in-flight source suppression in a finally so a
  throw between rename and commit can't leak it, and only after the rollback
  rename so a late forward-rename delete stays suppressed.
- Migrate pendingEditorReveal.fileId across the rekey (matcher prefers it).

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

* fix(editor): don't consume move-echo provenance in the safety-net verify (round 5)

The round-3 proactive post-commit verify ran resolveLiveMoveVerification,
which consumed pendingSelfMoveEcho. On FSEvents/SSH the real destination
watcher event reliably lands AFTER the fast local read, so it then found no
provenance, took the immediate changed-on-disk mark (no baseline check when
there is no recent self-write), and raised a false conflict banner on the
just-moved dirty tab — the exact data-loss this change removes.

The proactive verify is a safety net: it now releases the autosave gate but
leaves the provenance, so a later destination event is still recognized as
the move's own echo and content-verified. Only a real watcher event consumes
the provenance. Keeping it is safe — every watcher consumer verifies by
content, which is strictly safer than the immediate mark.

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

* fix(editor): scope move rekey to the initiating execution host (round 6)

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

* fix(editor): prefix-suppress the move root so late tabs under a dir move aren't flagged (round 8)

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

* chore(editor): trim move-fix comments to the why; drop redundant rename quiesce

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

* fix(editor): record mirrored-close intent synchronously to close the ghost-tab window

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

* fix(editor): use flavor-aware path containment for move selection (Windows/UNC case)

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

* fix(editor): reconstruct moved path by segment count (WSL alias / duplicate-separator safe)

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

* fix(editor): infer moved-path flavor by syntax, preserving legal POSIX backslashes

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

* test(editor): lock POSIX ancestor-backslash destination flavor

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

* fix(editor): flavor-aware trailing-separator strip; preserve POSIX literal backslashes

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

* fix(editor): flavor-aware separator folding in relative-path recompute (POSIX backslash)

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

* perf(editor): keep the fs-watcher delete path O(deletes) when no move is in flight

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

* docs(editor): tighten move-fix comments to one-line why-only

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

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-21 19:01:15 -07:00
Brennan Benson ac909c8d83 fix(mobile): stop reporting delivered chat messages as "Message not sent" (#9792)
* fix(mobile): stop reporting delivered chat messages as "Message not sent"

A relay drop or response timeout while terminal.send is in flight rejects
the RPC even though the request usually already reached the desktop — only
the ack was lost. The chat composer treated every failure as definite,
showing "Message not sent" and keeping the draft for a message that is
visibly in the transcript after resync (and baiting a duplicate send).

Mark transport failures that happen after the request frame hit the wire
as delivery-unknown, and hold those sends instead of erroring: when the
transcript echo lands the draft clears silently; only if no echo arrives
within 20s is the failure surfaced. Failures before the frame was written
(and host rejections) still error immediately.

* fix(mobile): close delivery ambiguity races

* fix(mobile): harden ambiguous send reconciliation

* test(mobile): assert ambiguity deadline boundary
2026-07-21 18:39:26 -07:00
Brennan Benson 4d0e3f51ce fix(sidebar): keep branch-discovered PR status visible on worktree cards (#9789)
* fix(sidebar): keep branch-discovered PR status visible on worktree cards

The sidebar status lane and the right checks panel read the same PR
caches but disagreed for unlinked worktrees: every successful GitHub PR
fetch mirrors the result into hostedReviewCache stamped with a
linked-style hint key ('github:<n>'), and getWorktreeCardPrDisplay
suppresses unlinked reviews with a non-empty hint. The checks panel
renders straight from prCache, so it showed "#9387 OPEN" while the same
worktree's card fell back to the plain branch icon.

Thread the branch-keyed PR cache number into getWorktreeCardPrDisplay as
corroboration: when the branch cache names the same PR the hosted entry
holds, the review provably belongs to this branch and stays visible.
WorktreeCard passes its existing cachedBranchFallbackGitHubPRNumber,
which is already guarded for linked metadata and the merged-head rule.

The hint stamping itself is unchanged on purpose: the 'github:' marker
also flags the entry as GitHub-scoped so neutral lookups still re-run
GitLab MR discovery.

* fix(sidebar): preserve PR lookup provenance

* fix(sidebar): preserve merged PR head guard

* test(github): assert exact refresh cache write
2026-07-21 18:31:39 -07:00
Brennan Benson 261a7b714c fix(mobile-relay): back off relay reconnects to stop cellular connect/disconnect churn (#9460)
* fix(mobile-relay): back off relay reconnects to stop cellular connect/disconnect churn

On cellular, the relay path re-dialed instantly on every network flap: a
NAT rebind / Wi-Fi<->cellular handoff silently kills the socket, the
revival trigger treats it as 'link came back' and calls recoverRelay(),
and the relay cell answers the overlapping resume with PEER_DROPPED (4408)
or LIMIT_EXCEEDED (4429). The session collapsed every close to a plain
'disconnected' and re-dialed with no delay, so the phone ping-ponged
connect/disconnect. The documented recovery contract (mobileRelayRecoveryFor,
which prescribes fullJitter backoff) had no callers.

- Add RelayReconnectBackoff: full-jitter exponential backoff (250ms floor,
  30s ceiling) that debounces re-dials via a cooldown window and wires up
  mobileRelayRecoveryFor. Reset on a successful migrate and on a genuine
  background->foreground transition (not on repeat foreground nudges).
- Extract the lease-rotation timer into RelayLeaseRotationTimer so the
  supervisor stays under max-lines (the direct-probe path can't be split
  out — it shares the operationInFlight mutex with recoverRelay).
- Add a deterministic test: repeated network-flap nudges re-dial instantly
  before the fix and are suppressed by the backoff window after.

* fix(mobile-relay): recover drops during direct probes

* fix(mobile-relay): recover half-open relay sessions

* fix(mobile-relay): preserve direct handshakes

* fix(mobile-relay): keep recovery retries bounded

* fix(mobile-relay): avoid redundant recovery dials

* fix(mobile-relay): keep all retries inside cooldown

* fix(mobile-relay): close recovery lifecycle races

* fix(mobile-relay): preserve in-progress direct auth

* fix(mobile-relay): preserve fatal recovery gates

* fix(mobile-relay): preserve backoff across unstable resumes

* fix(mobile-relay): close remaining recovery lifecycle gaps

* fix(mobile-relay): reset backoff only after stable relay
2026-07-21 18:28:16 -07:00
Brennan Benson 51873780c5 fix(dashboard): align terminal-dialog close X with compact header (#9816)
The default DialogContent close button is absolutely positioned at
top-4/right-4 for standard p-6 dialogs; the agent terminal dialog uses
p-0 with a compact py-2 header, so the X floated below the title line.
Render the close control inside the header row instead so it centers
with the title and matches the header's horizontal padding.
2026-07-21 18:24:38 -07:00
OrcaWin 34c160442f Fix headless Linux serve pairing readiness (#9785) 2026-07-21 18:23:20 -07:00
44f77a3b7d [codex] Continue agent work in a new session (#9170)
* feat: continue agent work in a new session

* fix: harden new-session continuation

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

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

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

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

---------

Co-authored-by: jz.feng <jz.feng@aftership.com>
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
2026-07-21 18:01:31 -07:00
Brennan Benson 6e6b7d8195 fix(mobile): retry session capability probe so tab-row actions survive relay cutover (#9794)
* fix(mobile): retry session capability probe so tab-row actions survive relay cutover

The session screen learned host capabilities (quick commands, browser
screencast, agent history, query-reply input) from a single status.get
fired when the screen connected. Over relay, a relay-to-direct transport
cutover rejects every in-flight request while connState stays
'connected', and a request timeout does the same — so one transient
failure latched the capability flags false (or left them null on an
ok:false reply) and the quick-commands tab-row button stayed hidden
until the screen was remounted.

Replace the one-shot probe with startRuntimeCapabilityProbe: retry
promptly after a cutover (the replacement transport is already
authenticated) and with capped exponential backoff on other failures,
until a probe lands or the effect is cleaned up. Also export the
cutover-error predicate from stable-logical-rpc-client and reuse it in
worktree-create-capability instead of a local copy.

* fix(mobile): reset runtime gates before capability reprobe
2026-07-21 17:55:02 -07:00
OrcaWin 1fef1e1ddd Relaunch macOS orca serve safely after updates (#9634) 2026-07-21 17:44:40 -07:00
github-actions[bot] b25c298a2b release: v1.4.150-rc.0 v1.4.150-rc.0 2026-07-22 00:01:29 +00:00
Brennan Benson 71bbfa022d fix(worktrees): bound shared worktree list scans (#9786) 2026-07-21 16:10:01 -07:00
github-actions[bot] 21e26de456 Update README downloads badge 2026-07-21 23:02:49 +00:00
OrcaWinandBrennan Benson 54c1ec5e7a fix(remote-runtime): recover terminals after network loss (#9774)
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
2026-07-21 15:18:45 -07:00
Brennan Benson e986a7ba1a feat(codex): surface nested subagents (#9637)
* feat(codex): surface nested subagents

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

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

* fix(codex): preserve nested agent identity

* fix(codex): preserve subagents across relay restarts

* chore(skills): refresh generated manifests

* fix(codex): keep inferred interrupts terminal

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

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

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

* fix(dashboard-popout): isolate window zoom

* fix(dashboard-popout): deny unused permissions

* docs(dashboard-popout): clarify wheel zoom path
2026-07-21 14:55:14 -07:00
6458ebcf20 fix(dashboard-popout): wire terminal copy/paste in the popped-out window (#9765)
* fix(dashboard-popout): wire terminal copy/paste in the popped-out window

The Edit menu's Paste is a custom item that routes Cmd/Ctrl+V to the
focused window as ui:appMenuPaste, and only the main window's React root
listens for it — the popout dropped it, so paste silently did nothing.
The copy chord similarly resolves in the main window's before-input-event
handler, which the popout window never registers; the menu's role:'copy'
no-ops on xterm's empty hidden textarea.

AgentTerminalPreview now subscribes to onAppMenuPaste (guarded on focus
inside the preview) and pastes via terminal.paste(), which xterm flags as
user input so the existing preview->PTY routing and main-process input
limits apply. A custom key handler honors the terminal.copySelection and
terminal.paste keybindings, skipping plain Mod+V since the menu
accelerator owns that chord (handling it twice would paste double). The
popout bootstrap fetches keybinding overrides so custom bindings apply.

* fix(dashboard-popout): harden terminal clipboard routing

* fix(dashboard-popout): authorize terminal clipboard text

* test(dashboard-popout): harden terminal paste coverage

* fix(terminal): normalize streamed paste newlines

* fix(dashboard-popout): forward macOS IME native-text commits in the preview terminal (#9771)

* fix(dashboard-popout): forward macOS IME native-text commits in the preview terminal

The preview terminal enables xterm's kitty keyboard protocol (via
buildDefaultTerminalOptions), whose encoder can encode and cancel a
printable keydown before Chromium commits the real IME/native text —
silently dropping macOS input-source commits and synthetic Unicode
injection. Main-window panes guard this with the IME native-text
forwarder; the pop-out preview never installed it.

Install the composition tracker + forwarder (macOS-only, mirroring
TerminalPane) and claim native-text key events at the top of the
preview's custom key handler so the committed glyph reaches the PTY via
terminal.input() and the existing user-input routing.

* fix(dashboard-popout): prewarm IME input source

* fix(skills): preserve released history across new tags (#9778)

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>

* fix(skills): record latest Linear release history (#9777)

---------

Co-authored-by: OrcaWin <alpha-eng@stably.ai>
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>

---------

Co-authored-by: OrcaWin <alpha-eng@stably.ai>
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-07-21 14:51:34 -07:00
OrcaWinandOrcaWin 15362fde16 fix(web): preserve paired runtime ownership (#9776)
* fix(web): preserve paired runtime ownership

* fix(web): validate runtime port scan payloads

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-07-21 17:48:59 -04:00
OrcaWin d8378e8ecf fix(settings): stop mislabeling a WSL default shell as PowerShell (#9779)
* fix(settings): stop mislabeling a WSL default shell as PowerShell

The Terminal settings shell toggle coerced a stored `wsl.exe` default to
`powershell.exe` for display, so a WSL default (set in onboarding) showed
"PowerShell" selected and wrongly surfaced the PowerShell-version options.

Drop the coercion and surface WSL as a disabled, already-selected segment
when it's the active default, so the control reflects the real shell. WSL
stays non-selectable here because choosing it needs a companion distro that
only the onboarding step configures; wiring a full WSL picker into Settings
is left for a deliberate design pass against the per-project runtime model.

* test(settings): cover persisted WSL shell display
2026-07-21 14:43:20 -07:00
Brennan Benson 903112ab3b fix(skills): record latest Linear release history (#9777) 2026-07-21 14:17:51 -07:00
OrcaWinandOrcaWin ae12bb1292 fix(skills): preserve released history across new tags (#9778)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-07-21 17:06:31 -04:00
Brennan Benson f1c84d3858 refactor(cli): split oversized command modules (#9775) 2026-07-21 13:50:17 -07:00
Brennan Benson 7ca3e670c5 fix(codex): count per-account homes in usage and state removal blast radius (#9763)
* fix(codex): count per-account homes in usage and state removal blast radius

- usage scanner now includes codex-accounts/*/home/sessions so multi-account
  usage is no longer silently undercounted (audit F2)
- account-removal dialog copy now states that session history and MCP logins
  are permanently deleted with the managed home (audit F1 mitigation)

* fix(codex): harden account usage discovery
2026-07-21 13:28:34 -07:00
Brennan Benson a10a2ba53c feat(linear): add MCP-style save issue (#9670)
* feat(linear): add MCP-style save issue

* fix(linear): harden save issue parity

* fix(linear): close save issue contract gaps

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

* fix(linear): harden relation reads and writes

* fix(linear): classify ambiguous relation writes
2026-07-21 13:21:19 -07:00
Brennan Benson 42a4f017b4 feat(linear): add MCP-compatible issue listing (#9672) 2026-07-21 13:16:44 -07:00
Brennan Benson be066fe8e9 feat(linear): expose issue activity history (#9667) 2026-07-21 13:13:05 -07:00
Brennan Benson 48578a85e2 feat(settings): link Linear capabilities pane to Integrations settings (#9764)
* feat(settings): link Linear capabilities pane to Integrations settings

The Agent capabilities -> Linear pane installs the orca-linear skill but
gave no path to where the connection itself lives. Add a link to the
Integrations settings pane, where connected Linear workspaces and API
keys are managed.

* refactor(settings): move Integrations link to bottom of Linear pane

* refactor(settings): drop icon from Linear Integrations link
2026-07-21 13:04:23 -07:00
Brennan Benson 1bebb24015 feat(status-bar): tooltips for Detailed vs Compact usage modes (#9693)
* feat(status-bar): add tooltips explaining Detailed vs Compact usage modes

The Detailed/Compact density picker in the usage popover named both modes
but didn't explain what they change. Add hover tooltips via a new optional
`tooltip` field on SettingsSegmentedControl's options (no behavior change
for existing segmented controls).

* reword usage tooltips + add zh/ko/ja/es translations

Detailed: drop 'in the status bar'. Compact: 'Condensed usage: only the
tightest window'. Translate both keys into all locales via the repo's
translate+repair policy.

* test(status-bar): wrap UsageRosterPanel renders in TooltipProvider

The new segment tooltips use Radix Tooltip, which requires an ancestor
TooltipProvider (present in the real app at App root). Wrap the test
renders to match, fixing 3 failures.

* i18n(usage-tooltips): fix zh/ja mistranslations of 'full usage'

The zh/ja 'Detailed' tooltips read as an imperative ('make full use of
bars/labels/percentages') instead of the intended noun ('full usage
shown with ...'). Correct to the usage-status noun, align ja Compact to
使用状況 (not 使用法/how-to), and drop a stray trailing period in es.

* chore(skills): regenerate skill bundle manifest for release-tag drift

New release tags cut since the manifest was last committed appended
orca-cli snapshots (releaseRevision 33→35). Append-only regen — released
history is preserved; only unblocks the verify:skill-bundle-manifest gate.

* i18n(usage-tooltips): polish zh Compact tooltip wording

The zh Compact tooltip read like an instruction ('only use the tightest
window'). Reword to a descriptive summary parallel to Detailed. Keep the
'tightest = highest-usage window' meaning (getTightestUsageSection picks
max usedPercent) rather than CodeRabbit's '最短/shortest', which would
misdescribe it as the shortest-duration window.
2026-07-21 13:01:54 -07:00
fsdwen b9b3d98d71 fix(i18n): correct zh.json 'Match Case' mistranslation (#9722) 2026-07-21 12:15:38 -07:00
github-actions[bot] 937a2015ea Update README downloads badge 2026-07-21 18:44:57 +00:00
OrcaWin a97c160363 fix(github): preserve GHES non-default auth ports (#9680) 2026-07-21 11:39:01 -07:00
余辉andJinjing 2add99c44c fix(source-control): keep commit generation available for custom commands (#9706)
* fix(source-control): keep commit generation entry point visible

* fix(source-control): translate and clarify generate button visibility

Translate Chinese-language comments to English and improve clarity
around the showGenerate logic: config errors surface in the generation
dialog, and the Create PR flow owns generation state to prevent stacking
spinners.

---------

Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
2026-07-21 11:19:21 -07:00
Brennan Benson 827cd49f41 Fix Linear skill-install modal CTA hierarchy (#9643)
* Fix Linear skill-install modal CTA hierarchy

Remove the redundant 'Not now' button from the Linear agent-skill
install modal and make Install the filled primary action, matching the
other setup surfaces (filled primary + muted dismiss).

'Not now' was doing exactly what the dialog's × already does (session
snooze via onOpenChange), but styled as an outline button in the
footer-right CTA slot it read as the primary action over Install —
which itself was only an outline button buried in the panel body. Users
reflexively clicked the dismiss.

- Drop 'Not now' + its unused onSnoozeForSession prop / i18n key (all locales)
- Add opt-in installVariant to AgentSkillSetupPanel (defaults 'outline';
  settings surfaces unchanged) and pass 'default' from the Linear modal
- Update tests to drive the session snooze via the dialog ×

* Keep 'Not now' as the safe dismiss; demote 'Don't show again' to a muted link

This modal auto-opens unsolicited (first activation of a Linear-linked
worktree with the skill missing), so the reflexive dismiss must be
non-destructive. Removing 'Not now' left the permanent 'Don't show again'
as the only labeled footer button — the reflexive target — which would
nuke a useful prompt forever.

Restore 'Not now' (session snooze) as a quiet ghost — the easy, safe
dismiss, matching Neil's original ask ('not now is fine, just not the
CTA'). Demote 'Don't show again' to a muted text link so permanent
suppression takes intent. Install stays the filled primary.

Restores onSnoozeForSession wiring + notNow i18n key (all locales);
tests exercise the Not now snooze path again.

* Remove 'Not now' from Linear skill-install modal; × handles session dismiss

Per product call: the modal is typically reached by clicking 'Set up' on
the reminder toast (an opt-in), and the dialog's × already dismisses for
the session. Drop the redundant 'Not now' button. Footer keeps only the
quiet muted 'Don't show again' (permanent opt-out); Install stays the
filled primary.

Removes onSnoozeForSession wiring + notNow i18n key (all locales); tests
exercise the × (Close) session-snooze path.

* Move permanent opt-out to an EyeOff icon next to the × (matches SetupGuideModal)

Replaces the footer 'Don't show again' link with a quiet EyeOff icon
button beside the dialog ×, matching the house pattern for hiding a
setup/teaching surface (SetupGuideModal, StatusBarUsageEmptyCta,
SetupGuideSidebarEntry). The footer is gone entirely, so Install is the
only labeled action; the two 'make it go away' controls (× = for now,
EyeOff = never) sit together and neither is a tempting text button.

- Rendered last in the branch so initial dialog focus lands on Install,
  not the hide button (Enter-on-open must not permanently dismiss).
- Wrapped in a local TooltipProvider so it works outside the app's global
  provider (isolated tests/mounts).
- Reuses the existing dontShowAgain i18n key for aria-label + tooltip.
- Tests locate the control by aria-label now that it has no text.

* Align EyeOff hide icon with the × (top-3 not top-3.5)

The icon-xs button is 24px tall vs the bare 16px × close, so its centered
icon sat 2px low. top-3 lines the two icon centers up (measured pixel-exact
in the running app).
2026-07-21 01:14:58 -07:00
Brennan Benson 16074b27bf fix(dashboard-popout): keep moving card under the open terminal (#9709)
The card column-move uses the View Transitions API, whose snapshot
pseudo-elements render in the browser top layer — above any z-index,
including the terminal dialog's z-50 Radix portal. So a card morphing
columns briefly flickered over the open terminal.

Skip the view transition while the terminal dialog is open; the card
just settles into its new column under the dialog (which covers the
board anyway) instead of animating on top.
2026-07-21 01:12:41 -07:00
OrcaWin 7cf052bcab fix(mobile): stop pairing QR column from starving CJK copy (#9700) (#9711)
Pin the large-QR grid track to a shared size token so long under-QR copy
(relay-degraded notice) cannot max-content the auto column and collapse
the pairing step to one glyph per line. Constrain the QR stack for wrap
and lock the layout contract in tests.
2026-07-21 03:39:05 -04:00
c24ebcade5 fix(rate-limits): unstick Claude "Limited" usage — respect Retry-After and feed live usage from session statuslines (#9617)
* fix(rate-limits): unstick Claude "Limited" usage and feed live usage from session statuslines

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

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

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

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

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

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

Addresses CodeRabbit review on #9617.

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

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

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

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

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

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

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

---------

Co-authored-by: Dzmitry Bachko <dbachko@users.noreply.github.com>
Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
2026-07-21 00:36:42 -07:00
github-actions[bot] 18b7aa301e Update README downloads badge 2026-07-21 07:10:43 +00:00
Brennan Benson 643cbe4358 fix(editor): fail safe when rich-markdown reconcile throws (#9685)
* fix(editor): fail safe when rich-markdown reconcile throws

commitRichMarkdownSerialization called reconcileSerializedMarkdown with no
guard. Its three callers all assumed the only failure was editor teardown —
which commit already handles internally (didSerialize:false) — so their empty
`catch {}` (debounced onUpdate, flush) and missing catch (Cmd+S shortcut)
actually only ever swallowed, or crashed on, a genuine reconcile throw.

When reconcile threw, the Cmd+S path crashed uncaught, and the debounced/flush
paths dropped the draft update silently — leaving editorDrafts stale and
stalling auto-save with no UI signal (the auto-save half of #9158/STA-2027,
diagnosed by @klay7w).

Make the shared choke point degrade to canonical `edited` on any reconcile
throw — the same fallback reconcile already uses internally — so no path can
crash or silently lose content; worst case is a canonical-style save (#6080).
The two remaining catch blocks now log instead of swallowing so an unexpected
serialize failure can't be invisible.

The multi-byte crash that was the known trigger is already fixed (#9642); this
hardens the shared path against any future reconcile failure.

* fix(editor): preserve source EOL on reconcile fallback
2026-07-21 00:04:44 -07:00
NeilandOrca 58bcfe2a33 perf(runtime): gate PTY path-candidate extraction on mobile-connected (#9422)
* perf(runtime): gate PTY path-candidate extraction on mobile-connected

The 3-regex path extractor ran on every PTY chunk of every session for a mobile-only consumer. Now gated behind a sticky mobile-connected flag; first onReady synchronously backfills candidates from the retained 64KB window per original chunk boundary (lazy safety-net fallback). ~28.5us/chunk (~99.8%) saved on the desktop-only hot path; mobile parity preserved (Tier-1 raw-window read unchanged + backfill before any file RPC). 5-round review converged 2-consecutive-clean; independently verified tsc+902 tests.

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

* fix(runtime): guard path-candidate activation on the remote runtime proxy

MobileSocketWiring.onReady called this.runtime.activateRecentPtyPathCandidateTracking() unconditionally, throwing on the E2EE/remote proxy (which lacks the method) and breaking the remote runtime connection (CI verify failure). Guarded with optional chaining (runtime-rpc.ts:900) — activation is a local-host concern. Mutation-verified E2EE-proxy regression test. Integration test + 3 typechecks green.

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

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-20 23:59:09 -07:00
NeilandOrca 4a7bdf9177 perf(mobile): route graph-sync snapshot fanout through the notify coalescer (#9421)
* perf(mobile): route graph-sync snapshot fanout through the notify coalescer

syncWindowGraph no longer unconditionally re-serializes+fans every worktree's mobile snapshot to every client. Serve-hydrate fast path skips the build-then-drop loop when no serve-owned pty exists (desktop case); changed-worktree gating (strict superset of snapshot inputs) routes only real changes through the existing coalescer. No-op syncs 720->0 emits; single-worktree churn 720->120 (~83% fewer bytes to phone). Real changes still propagate. 11-round GPT-5.6-Sol xHIGH review converged 2-consecutive-clean; independently verified tsc+830 tests.

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

* fix(mobile): bump snapshotVersion on preserved prune frames + carry split-group layout

Pre-merge adversarial review found two stale-phone regressions: (1) preserved-headless snapshots did not bump snapshotVersion, so a pruned-but-serve-preserved worktree change was dropped by clients' same-epoch freshness gate (orca-runtime.ts:22316); (2) serve-only no-op hydrate dropped the phone's split-group layout (orca-runtime.ts:3731). Both fixed + mutation-verified regression tests. 3 typechecks + suites green.

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

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-20 23:59:05 -07:00
Jinjing 42bff5e598 fix(native-chat): add reconciliation to catch missed transcript updates (#9701)
* fix(native-chat): add reconciliation to catch missed transcript updates

Periodic reconciliation catches file changes that fs.watch may miss on
remote filesystems. Event-driven drains use a bounded max-wait timer to
ensure responsiveness. Extracts timer logic into a scheduler module for
testability and maintainability.

* fix(native-chat): add reconciliation to catch missed transcript updates

Detects same-size file rewrites and native watcher failures by reconciling
transcript state even when fs.watch events don't fire. Extracts watcher
binding into a dedicated module that gracefully degrades to
reconciliation-only mode when native watch is unavailable. Adds test
coverage for watcher rebind failures, parent-directory replacements, and
unsubscribe races.

* Document transcript watcher reconciliation contract

Clarify that bind() is best-effort with reconciliation fallback,
invalidate() detaches from stale identities, and the caller's polling
loop owns transcript liveness. Supports resilience on remote filesystems.
2026-07-20 23:57:05 -07:00
Brennan Benson 22d9c87872 Fix Codex automatic resume across account switches (#9696)
* fix(codex): preserve session home on automatic resume

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

* fix(codex): resume compressed rollout representations

* fix(codex): keep resume migration best effort

* fix(codex): reject stale automatic resume provenance

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

* fix(codex): keep automatic resume home authoritative
2026-07-20 23:51:11 -07:00
63da958813 fix(github): keep work-item searches repo-scoped (#9668)
* fix(github): prevent unscoped work item queries

* fix(review): translate non-English rationale comment to English (client.ts:1151)

Restores the English '// Why:' convention and re-states the allSettled partial-results rationale that was dropped when the comment was replaced. Flagged by Ce-code-review (maintainability/previous-comments) and a prior PR reviewer. No behavior change.

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

* fix(tasks): surface repos with no resolvable GitHub source

Since #9660 an unresolvable GitHub source returns an empty null-source
envelope instead of an unscoped search. That empty was indistinguishable
from a genuine zero-result query — no log, no telemetry, no UI signal.

Surface it in the Tasks list: a per-repo info row with Retry for each
fetched repo that resolved neither an issue nor a PR source (told apart
from genuine-zero and not-yet-fetched purely via the cached `sources`).
Renderer-only — the signal was already threaded through the envelope.

- selectTaskPageUnresolvedSourceRepos selector (+ unit tests)
- per-repo row reusing the existing banner region + handleRetryIssuesFetch
- suppress the generic empty state while those rows show
- assert the null-source envelope contract in client-work-items test

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

---------

Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
2026-07-20 23:19:35 -07:00
NeilandOrca f0c6ca0bf5 perf(checks-panel): gate the terminal-cwd poll on window visibility (#9695)
The Checks panel polls window.api.pty.getCwd every 4s to follow the active
terminal's cwd. It was gated only on panel visibility (isPanelVisible), not
window visibility — so a hidden or minimized window kept spawning lsof on macOS
every 4s (the 4s cadence exceeds getCwd's 1.5s per-pid cache TTL, so every tick
is a guaranteed miss and a fresh subprocess).

Swap the raw window.setInterval for installWindowVisibilityInterval — the shared
helper every sibling timer already uses — which pauses while the window is hidden
and runs an immediate refresh on becoming visible so a cd made while hidden is
picked up on return. A visible-but-unfocused window (second monitor) keeps
polling, matching prior behavior.

Co-authored-by: Orca <help@stably.ai>
2026-07-20 23:11:12 -07:00
Cal 4877d0b65c Fix Windows Resource Manager memory and CPU attribution (#8845)
Use a single CIM sample for Windows process memory and CPU attribution, with bounded Typeperf memory fallback and safe CPU delta recovery.
2026-07-21 02:04:37 -04:00
Brennan Benson 6785f9d1a8 feat(dashboard): open-worktree dialog control + finished-time parity (#9686)
* feat(dashboard): open-worktree dialog control + finished-time parity

Pop-out agent dashboard polish:
- Terminal dialog: replace the footer Close button with the standard top-right
  X, rename the primary action from Focus worktree to Open worktree, and prefix
  it with an open-in-window icon.
- Card time column: finished agents now read time-since-finish (matching the
  left worktree sidebar) instead of time-since-start. Extracts the sidebar's
  finished-timestamp logic into a shared helper so the two surfaces cannot
  drift, and threads finishedAt through the dashboard snapshot contract.

* fix(dashboard): thread finishedAt through payload validator + test fixture

CI typecheck caught a DashboardCard fixture in dashboard-payload-validation.test.ts
missing the new finishedAt field; also validate finishedAt in the relay guard.

* feat(dashboard): swap pending-question emoji for MessageCircleQuestion icon

The amber pending-question chip used a raw hand emoji (renders differently
per-OS and off the design system); use the lucide chat-with-question icon.
2026-07-20 22:55:22 -07:00
NeilandOrca 9b7d15362d perf(linear): lazy-load @linear/sdk off the launch parse path (#9698)
Co-authored-by: Orca <help@stably.ai>
2026-07-20 22:41:59 -07:00
NeilandOrca d3f2a8c39a perf(stats): move the debounced stats write off the main thread (#9697)
Co-authored-by: Orca <help@stably.ai>
2026-07-20 22:41:39 -07:00