* feat(dictation): fixed-size dictation pill with cancel, timer and finish
* fix(dictation): abort a cloud upload cancelled while finishing; time recorded audio
* refactor(dictation): reuse Esc/IME helpers, keep the pane on the target, isolate meter renders
* fix(dictation): pause the pill timer while hidden; compile under React Compiler
Cloning always went into <parent>/<repo name>, so a same-named repository
from another owner (or any non-empty folder of that name) could not be
cloned into the same parent. Before cloning, Orca now checks the target
on the clone host. If it is taken, the clone forms show a Folder name
field with a free suggestion (<repo>-<owner>, then <repo>-2...) and clone
into that instead. RPC repo.clone and `orca project setup-clone` accept
the folder name too.
* fix(vm-recipes): record each VM before create runs so a quit or crash can't leak it
Orca now saves a "provisioning" record for a recipe VM before running its
create command, and updates it when create finishes. If Orca quits or
crashes mid-create, or create fails without returning a result, the VM
stays listed in Settings > Cloud VMs as Failed with its instance id, so the
user can run Cleanup (destroy gets the instance id) or remove it from the
list. On startup, provisioning records with no create running are marked
as interrupted.
Records without a recipe result live in a separate file that older builds
never read, so downgrading keeps working. Records also store the repo path
so Cleanup, Copy command, suspend and resume still work after the repo is
removed or re-added under a new id (#17293).
* fix(vm-recipes): keep no runtime row when create is cancelled or rejected before its script starts
* fix(vm-recipes): harden write-ahead records from review
- Never overwrite a provisioning-records file this build cannot read; refuse new records instead.
- Remove the provisioning-records file when empty instead of rewriting it on every store write.
- Only fall back to the saved repo path while it still defines the same recipe.
- Don't let Cleanup or Attach act on a create that is still running or never returned a result.
- Show Provisioning before Cleanup disabled; stamp running records with the save time.
* fix(vm-recipes): keep new store code off direct sync fs calls and render-path translate
* fix(vm-recipes): detect a missing provisioning-records file without a type assertion
Rich-text pastes now keep their hyperlinks as Markdown links, and pasting a
URL over selected words links them, as other message editors do.
Fixes#39153Fixes#39150
* refactor(main): organize SSH transport and managed server owners
* Remove orphan deploy readiness fixture and shrink test baselines
* refactor(wsl): import lock records from their defining owner
* refactor(wsl): use the canonical deferred update contract
While a phone is paired, the desktop window publishes its own Agents-list
classification for each pane (listed, status, unread, clearable, title,
worker parent) and worktree.ps attaches it to each agent row, so the phone
lists agents exactly as the sidebar does. agentActivity.setRead/clear apply
through the sidebar's own Activity actions and resolve once the new
verdicts are stored.
* refactor: import journal queue state from defining owners
* refactor(shared): organize execution and automation owners
* fix: retain type imports used by surviving test cases
* refactor: finish work item owners and refresh validation metadata
* test: infer reconnect status fixture type
* test(claude): resolve source files beside same-named directories
* test(runtime): group repository creation mock registrations
* fix(window): cancelling a quit prompt now cancels the quit
Renderer-owned quit prompts (running-work "Close Window?", Save dialog,
close-guard and shutdown-checkpoint vetoes) never reach
will-prevent-unload, so main kept isQuitting latched after Cancel. A later
plain close was treated as a quit and renderer crashes were not recovered.
Add a window:close-cancelled IPC (preload ui.cancelWindowClose; no-op in the
web client) that the renderer sends on each cancel path. Main accepts it only
from its own renderer, only while quitting, and only once that renderer has
acknowledged the newest close request, then runs the same abort path as a
beforeunload veto.
Fixes#39596
Co-authored-by: Neil <neil@stably.ai>
* test(window): expose webContents.send on the renderer recovery harness type
---------
Co-authored-by: tomarai85 <tomarai85@users.noreply.github.com>
Co-authored-by: Neil <neil@stably.ai>
Adds commit actions to the Git Graph: check out a commit or branch, cherry-pick, revert, and create a branch from a commit. Conflicts hand off to Source Control with continue and abort. Works on local, WSL and remote hosts.
Restored tabs whose local daemon session is gone no longer mount and
respawn a shell at startup; they park and respawn on open.
- Separate pty:getGoneSessionIds probe, started unawaited at startup so
it never gates reconnect; the authoritative-capability call is unchanged.
- Binding a PTY id (spawn, cold restore, attach) clears its gone mark so a
revived pane gets parked exit/byte watchers again; late answers for bound
ids are ignored.
- Agent tabs (launchAgent or sleeping record) and mixed live/gone splits
keep the mounted restore path.
Replaces #37410.
Co-authored-by: m4air <m4air@Mac.localdomain>
Co-authored-by: thefrikidude <100475418+thefrikidude@users.noreply.github.com>
Adds a Custom… option to the App Icon switcher. The picked PNG/JPEG is
header-checked before decoding (square, 64–2048 px, ≤10 MB), re-encoded as
PNG into userData, and applied to the macOS Dock and every window icon. It
persists across restarts and can be removed, which resets to Classic.
Closes#13316
App themes may set background.videoPath: a muted, looping MP4 or WebM (H.264
recommended, VP9 or AV1; one video track, no audio; <= 60 s, 8 MB, 2.3 MP)
played over the required static background.path. Main checks it from container
headers without decoding and serves it on the orca-plugin-image slot
theme-<i>-video; images stay static-only. The renderer reveals it on its first
frame, keeps the still on any failure or a decoded size past the limits, plays
it only while main reports the window focused, shown and not minimized and no
dialog is open, and omits it under prefers-reduced-motion.
Animated WebP was measured and declined: software-decoded every frame, it cost
11-23 points of CPU vs 3-10 for the same loop as video.
Implements #38363 proposed by @dohaidang.
* feat(pairing): show which devices still sign in with their original pairing code
Settings lists paired phones and access links that signed in with a pairing
code but never exchanged it (apps from before single-use pairing) with
"Uses an older pairing method. Update ...". Display only; rows from older
builds are never flagged.
* fix(pairing): no update hint for links used by plain-http browsers or still pairing
A link that served a session-only browser key is marked, so the hint stays
off after a host restart drops those keys. Codes still inside the pairing
window are not flagged either: an updated client exchanges within moments.
* feat(dictation): send desktop dictation audio straight to the speech process
* test(speech): pin that a desktop audio port reaches only its own stream
* test(speech): give the speech IPC test's ipcMain mock the on listener the port handoff registers
* refactor(speech): track an open audio port by its map entry and require the port capability
* Add Git Graph view showing all branches and commit history
Adds a full-pane Git Graph tab that visualizes the whole repository —
all local branches, remote-tracking branches, tags, merges, and HEAD —
instead of only the current branch's linear history shown in the Source
Control sidebar today.
- Add an opt-in allRefs scope to the shared git-history loader so the
graph query enumerates --branches --remotes --tags HEAD; the default
current-branch path is unchanged. The flag is forwarded through the
local IPC, SSH provider, and runtime RPC boundaries so it works in all
environments.
- New git-graph tab content type with a full-pane GitGraphPane that
reuses the existing graph SVG/row components, virtualizes rows, and
opens a commit's combined diff on click. Wired into the tab strip as a
chip (activate/close/keyboard-cycle) mirroring the simulator pattern.
- Entry point: an Open Git Graph button in the Source Control commits
header (Cmd/Ctrl-click opens in a split).
Closes#4521
Co-authored-by: Orca <help@stably.ai>
* Address review: virtual-tab guards, nav-order, truncation copy
- Treat the Git Graph (and mobile emulator) tab chip as a virtual tab:
add an isVirtual contract to EditorFileTab/EditorFileTabContextMenu so
rename, copy-path, copy-relative-path, and reveal-in-finder are disabled
for tabs whose filePath is a display label, not an on-disk path. Prevents
a rename on the synthetic 'Git Graph' path running a real file operation.
- Forward gitGraphIds through the legacy reconcileTabOrder fallback in
getActiveTabNavOrder so keyboard tab navigation does not drop git-graph
tabs when no split-group layout exists yet.
- Make the truncation banner explicit: 'Showing first N commits across all
branches' (was 'of many'), across all locales.
- Tests: virtual-tab hides path actions; git-graph survives the legacy
nav-order fallback.
Co-authored-by: Orca <help@stably.ai>
* Forward allRefs over SSH relay and web runtime paths
Two boundaries re-destructured git.history params and dropped the new
allRefs flag, leaving the repo-wide Git Graph inert (linear default-branch
history) for SSH and web-runtime worktrees:
- src/relay/git-handler.ts: the relay host's git.history handler forwarded
only { limit, baseRef }. Add allRefs so the SSH host enumerates all refs.
Adds an end-to-end relay test (a feature-branch commit absent from linear
history appears only when allRefs is forwarded).
- src/renderer/src/web/web-preload-api.ts: the web preload history shim
destructured only { worktreePath, limit, baseRef }. Forward allRefs.
Also fix a request-id race in useGitGraphHistory: bump requestIdRef before
the null-context early return so an in-flight fetch from a prior worktree
can't resolve and overwrite the 'idle' state after the worktree clears.
Co-authored-by: Orca <help@stably.ai>
* Support filtered and pinned Git history
Resolve branch and tag roots on the host, keep paging tied to the chosen revision set, and echo the applied filter across Git transports.
Co-authored-by: Matheus Breguêz <matbrgz@gmail.com>
* Add the Git Graph tab and commit details
Open the all-branch view from Source Control or the command palette, preserve older clients' layouts, and keep the sidebar HEAD-only.
* Restore files the main merge left at stale versions
The earlier merge of origin/main kept older or missing copies of 61 files
unrelated to the Git Graph (CI script tests, reliability gates, shard
timings and five terminal runtime files). Take main's version of each so
the PR changes only its own files.
* Limit pinned history to explicit graph scopes
Keep All and HEAD snapshots free of redundant ref names, and filter ref searches before nested-tag peeling. Default history requests retain unpinned paging without evicting graph tokens.
Co-authored-by: Matheus Breguêz <matbrgz@gmail.com>
* Preserve canonical placement for older graph clients
Pass graph-free record deltas through without rebuilding layouts, retain existing empty groups, and translate content creation into canonical groups while preserving hidden graph tabs.
* Retain Git Graph browsing state for the tab lifetime
Own history retrieval, selection and scroll in one renderer controller that retires on close or owner/filter changes. Accumulate parent pages before publishing, share action-time workspace navigation, serialize ref searches, and scope refreshes to the workspace.
* Apply Git Graph branch selections on picker close
Keep checkbox changes local until dismissal, then normalize and apply one chosen ref set. Allow picker reads to use a qualified host scope.
* Keep Git Graph navigation bound to its tab owner
Retain the tab's qualified owner and Git connection across workspace focus changes. Newer selections and refreshes supersede pending parent navigation without losing fetched pages.
Report stale workspace switches and let the commit menu handle empty space in the Branch / Tag cell. Cover twin-owner retention, superseded navigation and picker apply-on-close with renderer regressions.
* Translate desktop layout commands against canonical state
Resolve older desktops' visible groups, tab gaps and split paths inside the canonical mutation, including detach and cross-partition moves. Keep initial admission visibility-aware and cache graph presence for binding-only frames.
* Refine Git Graph picker and owner transitions
Discard branch drafts on Escape or owner changes, follow updated tab ownership, and report workspace lookup failures for the current owner. Keep parent navigation queued behind pending history loads.
* Drop the redundant default from the exhaustive layout command switch
* Align Git Graph with changed-code quality rules
Use typed fixtures and an event guard, keep primitive styling on content wrappers, and prune loaded-commit bookkeeping after render.
* Route Git Graph opening through the existing editor action
Use workspace record selectors for stable owner inputs and remove two
loading labels orphaned by the graph changes from all locale catalogs.
* Preserve Git Graph selection and keep row content visible
Refresh and token recovery retain loaded depth and selection in one
accumulating operation. Keep the published view until it settles, and
only fall back to the first commit after history is exhausted.
Prioritize current and open-workspace refs, reserve their markers, clip
wide graphs and retain filenames when long paths need truncation.
* Bound Git Graph refresh and allow graph column resizing
Restore the loaded depth without searching for vanished selections, retain the published result on retrieval errors, and keep newer selections visible. Allow the bounded graph column to expand to every lane through the existing resize mechanism.
* Retain Git Graph history for errors without a message
Detect failed retrieval independently of its message and cover an empty-message rejection in the existing partial-failure regression.
* Retain Git Graph widths and preserve shifted selections
Separate committed and live resize widths, retaining the chosen width for the graph tab lifetime. Allow one bounded extra history page for selections shifted by new commits, and give the resize separator a translated label.
* Preserve nullable Git Graph owner fallback
* Fix Git Graph layout imports after the RPC method move
* Route the Git Graph owner connection through the source-control connection helper
* Keep the Git Graph button idle in hidden Source Control sections
* Re-probe Git Graph support when the owner's runtime status changes
---------
Co-authored-by: Orca <help@stably.ai>
Co-authored-by: Matheus Breguêz <matbrgz@gmail.com>
* Separate editor tab placement from content ownership
* Show scoped Source Control sections for attached worktrees
* Preserve active diff refresh and validate section catalogs
* Complete folder section test worktree metadata
* Use contextual types for scoped Source Control actions
* Preserve optional scoped action parameters
* Preserve editor ownership through restore, mirroring and workspace removal
* Retire Source Control drafts only after confirmed owner removal
* Limit section work to visible expanded repositories
* Keep editor follow-up actions and bulk closes in their invoking views
* Track the consolidated editor order writer in its owning module
* Use complete typed fixtures for repair contracts
* Preserve live editor placement during layout readback
* Pass projected editor placement directly to serialization
* Read attached worktree lineage from shared graph module
* Retain inferred owner checks and scope placement retirement
* Preserve selection in untouched workspaces during purge
* Exercise editor placements in existing launch and lifecycle fixtures
* Preserve shared buffer prompts and editor strip order
* Pause inactive section context subscriptions
* Keep section recovery and file actions in the folder workspace
* Describe attached Source Control in folder workspace CLI help
* Attribute Git metadata signals once per dispatch
* Watch mounted source control sections outside the warm budget
* Resolve section watches only for served filesystem roots
* Restore restart-stable editor identity across SSH opens
* Apply section terminal routing to explicit Chat choices
* Include cross-store descendants in folder workspace panels
* Keep cross-host recovery on its existing workspace route
* Start cross-host section agents in their workspace without navigation
* Update memoized Source Control labels on language changes
* Remove unused terminal close-to-right implementation
* Suspend inactive Source Control body observers
* Retain base ref query while section observation pauses
* Cover retained base ref search across section visibility
* Resolve note menu ownership from the retained snapshot
* Preserve section agent working directories and background placement
* Keep Source Control sections active while their overlays are open
* Read current settings when Source Control commands execute
* Adapt editor and browser test stores to view subscriptions
* Preserve bound store hooks for ordinary app views
* Trust agent starts outside the placement workspace
* Trust the section worktree through transient launch ownership
* Extract editor follow-up file actions from the panel
* Fail section launches when trust capability lookup fails
Preserve explicit unsupported-host fallback, propagate lookup failures to the existing launch error path, and let the existing transport retry refresh rejected capability probes.
* Follow the moved pane launch test in web configs
* Preserve section file ownership during refresh and close
* Share tab order removal across editor close paths
* Verify companion editor close through the real store
* Close all workspace split copies for legacy mobile file requests
* Keep unmatched mobile close requests within their workspace
* Reuse the agent menu render snapshot
* Keep attached folder roots scoped to their resolved host
* Apply repository trust decisions to the launch trust owner
The host now records, per paired device, whether its most recent
sign-in used the legacy E2EE framing (set on a legacy sign-in, cleared
on a v2 one). The field rides on the existing deferred lastSeen write,
so it adds no extra registry saves, and older builds keep unknown row
fields as they are.
Settings shows a notice under such rows:
- Mobile paired devices: "Uses older encryption. Update the Orca app on
this device."
- Runtime access links: "Last used with older encryption. Update Orca
where this link is used." (one link can be shared by several clients)
Local only: nothing is sent anywhere, and no connection is refused.
* Ask once per repo before pre-trusting agent folders
* Route the trust prompt's repo lookup by the launch host; use dialog primitives as designed
* Bound the first-launch trust hold and honour the answer for the launch
* Move agent folder trust settings into their own settings group
* fix(shell): show linked scripts in the editor and reveal programs instead of launching them
Markdown file links that would run code no longer go to the OS default
app. Main inspects the target (following symlinks, checking every name
in the chain plus the executable bit and the file header): readable
scripts open in an Orca editor tab, binaries and bundles are revealed in
the file manager, and everything else opens as before.
* fix(shell): reveal linked archives instead of unpacking them
Zip, tar, gzip, bzip2, xz, zstd, 7z, rar, cab and zip-based packages
(.war, .ear, .apk, .ipa, .xpi, .crx, .vsix, .nupkg) are revealed in the
file manager. Archive headers are also treated as binary content, so an
archive under a script name or an extensionless executable name is
revealed rather than shown as text.
* feat(pairing): unscanned desktop pairing offers expire (QR 10 min, link 60 min)
Headless serve/orcad mint offers without a TTL, so they never expire.
Paired rows and rows without offerExpiresAt are unaffected.
* feat(pairing): refresh the mobile QR before it expires and show runtime link expiry
The QR re-mints a minute before expiry while visible; an unused runtime
link shows its expiry and, once expired, is replaced by a notice. Web
clients rejected while pairing now suggest asking for a new link.
* feat(mobile): say a refused pairing code expired or was replaced
A first pairing the desktop refuses (direct or Relay) now asks for a new
code instead of showing the raw transport error.
* refactor(pairing): replace type assertions in code-refusal detection and tests
* test(mobile): re-record pairing race golden for refused-code error
* fix(pairing): keep pairing codes valid across sleep and stale registry writers
- QR refresh rechecks the wall clock every 15 s and on window focus, so a
computer waking from sleep never keeps showing an expired code.
- A registry save from a process with an older copy can no longer reset a
paired row back to unscanned (which would let its offer expire).
- The QR caption says how long a copied code works.
* test(pairing): annotate the IPC handler stub cast
Multi-repo folder workspaces can parent workspaces on this machine and on paired servers, and their children nest under them in the sidebar and on the phone. Task parents are inferred at creation instead of on every lineage refresh.
* fix(hooks): run a created worktree's setup once its own content is approved
Adds worktrees:runApprovedSetup (IPC) and worktree.runApprovedSetup (RPC,
capability worktree.run-approved-setup.v1). The host re-hashes the worktree's
setup and default-tab commands, runs them only on a match, and otherwise
reports the new content. After a create mismatch the host opens only the
command-less default tabs; approving in the trust prompt starts setup and the
command tabs. Clients whose host lacks the capability keep setup skipped.
* fix(hooks): review a created branch's different setup from the workspace, not instead of it
The trust prompt now says the workspace was created and its branch's setup
differs; approving runs it for that workspace without replacing the primary
checkout's approval (which made the next create prompt twice). Declining shows
a toast whose Review setup action reopens the prompt. Repo-wide trust returns
the content's hash so the host can run it.
* fix(worktrees): import approved setup from its path after the RPC methods move
* feat(cli): add orca host rm for saved SSH targets
orca host rm --host ssh:<target-id> calls the runtime RPC ssh.removeTarget,
the same removeSshTarget the Settings UI reaches, so the tombstone keeps the
passive ~/.ssh/config import from adding the alias back.
Also document how the import treats Match blocks and Include files.
* fix(ssh): clear a removed target's live state and rename in main for every removal path
Removing an SSH target through ssh.removeTarget (orca host rm, paired clients)
left open tabs with stale reconnect state and kept the host's rename, because
only the Settings dialogs cleaned up after their own IPC call. The shared
removal mutation now drops the display-label override and tells the renderer,
which clears the target's state once for all callers.
Fixes#37949
* fix(ssh): clean up after a removed target in the teardown every removal ends in
A recipe VM's cleanup removes its SSH target through removeRegisteredSshTarget
directly, so the window kept that target's live state. The broadcast and the
rename drop now run there, after the target is gone.
Fixes#37949
* test(cli): keep host rm's retarget-flag refusal next to its other cases
Its old file was removed on main.
Fixes#37949
* refactor(cli): give the host, managed-server and VM handler groups their own module
Keeps the handler manifest under the file-length limit.
Fixes#37949
---------
Co-authored-by: Florin Timbuc <florin@sowild.design>
* fix(terminal): a host with a window keeps a pane a client moved to a new tab
Dragging a split pane onto the tab strip in a paired worktree whose host runs
the desktop app with a window flickered and snapped back. The client draws the
new tab and calls session.tabs.movePaneToNewTab, but the host answered
not_held whenever it had a window, because the window owns the layout and only
headless hosts committed the move. The client reverted, and even without the
revert the host window's next snapshot would have rebuilt the old tab.
Main now asks its window to make the move and waits for the answer, the way
other client requests reach a window-owned host. The window moves the pane
with its own drag-out path under the tab id the client already drew, at the
client's drop position, mounting a parked source tab first. A pane the window
cannot reach is refused, so the client puts it back; a host without the
window bridge still answers not_held. Older clients and hosts are unaffected.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(web): rely on the optional pane-move bridge instead of no-op stubs
The web client never receives host pane-move requests, and the bridge
already skips registration when these members are absent; dropping the
stubs keeps web-ui-api.ts under the line limit after rebasing on main.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(e2e): a window host keeps a pane a paired client moved to a new tab
The window-host case asserted the old refusal, where the client drew the new
tab and put the pane back. It now drags a pane out on a paired client and
checks the pane stays in the tab the client drew: the host window holds the
leaf alone under that tab id, no snapshot pulls it back, and the source keeps
one pane. It fails on main, where the tab count falls back after the drop.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(terminal): refuse a pane move the host window cannot answer
When a paired client asks a host with a window to move a pane to a new
tab, the host relays the request to that window. If the window is gone,
closes, or does not reply within 10 seconds, the relay rejects. The
host returned that rejection as an RPC error instead of a move result.
The host now answers { status: 'refused', reason: 'pane_unavailable' }
in that case. The client already puts the pane back on a refusal, and
this is the reply the relay timeout was sized for.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(terminal): fence a window's pane move once the client heard it timed out
The relay told the client "refused" after 10s but left the window's move
running, so a late commit moved the pane on the host after the client had
put it back. Main now fences the commit: a move the client was told timed
out commits nothing, and a commit answers the client at once, before the
deadline can turn it into a refusal.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(terminal): keep the pane-move fence Electron-free and let a claimed commit answer
The leaf-move IPC is shared with the plain-Node runtime, so the fence moves to its own
Electron-free module. A window that goes away mid-commit no longer refuses a move its commit
landed, and a second move cannot replace a fence a timed-out move still holds.
Fixes#37082
* chore(terminal): follow moved shared modules after rebasing the pane-move relay
Refs #37082
* fix(terminal): hold a client's pane move's source tab mounted until the move settles
The timed split-mount lease could expire while the window was still moving the
pane, so cold parking could unmount the source tab after main had already told
the client the move landed. The move now holds the tab mounted for its whole
duration and releases it when it settles.
Fixes#37082
* fix(terminal): don't toast the host window for a pane move another client asked for
Fixes#37082
* fix(terminal): relay a client's pane move only to a window that owns the layout
Where main owns the layout (the dark ORCA_LAYOUT_OWNER=main path), the window's
move goes back through main, which names its own tab, so the client's tab id
could not be kept. Main answers not_held there, as before this change.
Fixes#37082
* fix(terminal): name the renderer a relayed pane move belongs to
Fixes#37082
---------
Co-authored-by: Seunghwan <hyunsh.cycle@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* feat(gitlab): filter Tasks merge requests to ones you wrote or review
Tasks > GitLab > MRs could only filter by state, so on busy projects you
had to scan every open MR to find yours. A second set of chips (Everyone,
Mine, Needs review) now combines with the state chips, so "my merged MRs"
works too.
The filter runs on the host that owns the repo's GitLab sign-in, looks up
the signed-in user fresh on the project's own host, and filters by
author_id or reviewer_id in one request per project. A failed lookup
shows an error instead of everyone's MRs, and older paired servers that
would drop the filter ask to be updated.
Co-authored-by: Yash Kakade <yashkakade2015@gmail.com>
* fix(gitlab): keep filtered MR lists honest while loading and on old servers
- The reviewer chip reads "Reviewing": GitLab lists MRs you were asked to
review even after you reviewed them, so "Needs review" overpromised.
- Rows belong to the query that loaded them: after a chip change the list
shows loading, never the previous filter's rows with Start enabled.
- A refresh checks each paired server's filter support once, not once per
project; failed checks show the real error instead of "update the server",
and the web client sends the filtered list to the runtime it checked.
- Signed-out GitLab gets a sign-in message, and an unresolved SSH project
under a filter shows an error instead of an empty list.
Co-authored-by: Yash Kakade <yashkakade2015@gmail.com>
* fix(gitlab): pin filtered MR lists to the server that confirmed support
A refresh now shares one support check per paired server and pairing, and
the filtered list is sent to the exact runtime and pairing that answered,
so a server replaced mid-refresh can't serve "Mine" unchecked. The web
client scopes its shared check to the current pairing and reuses the
existing result unwrapping. The capability test now counts real status
probes: one per server per refresh for 12 or 120 projects.
Co-authored-by: Yash Kakade <yashkakade2015@gmail.com>
* fix(gitlab): discard filter-support answers from a replaced pairing
A paired server re-paired while Orca was asking whether it supports the
MR filter no longer leaves "update the server" from the old pairing's
answer; the check fails with the usual pairing-changed error instead.
Signed-out detection no longer matches a 401 inside a host or port.
Co-authored-by: Yash Kakade <yashkakade2015@gmail.com>
* test(gitlab): type the filter-change test's props without a cast
---------
Co-authored-by: Yash Kakade <yashkakade2015@gmail.com>
Agent sleep only treated the host window's own foreground tabs and phone
driver locks as in use. A paired desktop showing a tab, or anyone typing
into it from another client, never reached the planner, so the host slept
the tab the user was looking at.
The runtime now answers "is this PTY in use elsewhere": a remote view
that reports hidden panes (it pauses output while hidden) and is not
paused, a phone driver, and the last user input from any client. The host
window asks its main process over IPC for its own PTYs; a client planning
runtime-hosted tabs gets the same facts from terminal.list
(includeAttention). The planner treats a shown PTY like a phone-driven one
and folds remote input into its input guard.
Clients too old to report hidden panes never count as showing a tab, and
hosts too old to report attention leave sleep as it was, so a missing
signal never turns into never-sleep.
Fixes#36427
* Keep dropped-macOS updaters on their last compatible build and explain why
Stamp minimumSystemVersion into every mac update manifest from the built
app's LSMinimumSystemVersion, and refuse to publish an ungated release.
When the updater withholds a newer release for this macOS, show a one-time
notice and say so in Settings and the update card.
* Trim the unsupported-Mac notice copy
* feat(lineage): store cross-store parent links beside same-store lineage (inert)
A workspace's parent can live in another Orca store. Save that link in the
child's store under a new top-level key, crossStoreLineageByChildKey, so older
builds' same-store cleanup never reads it. Nothing writes records yet.
- Types, load normalization (malformed entries dropped one by one), section label.
- Store get/set/remove by child; a child keeps at most one parent record.
- Every maintenance path acts by the child only: deletion and missing-worktree
pruning, repo removal and deregistered-repo residue, identity migration,
profile project transfer, WSL move projection and UNC drive conversion, and
the orcad migration dormant payload (export, validation, import, subtraction).
* fix(lineage): gate cross-store links on host capability; one parent per child
- Migration stage/commit carrying crossStoreLineage requires the new
orcad.migration-cross-store-lineage.v1 capability; older hosts are refused
before staging instead of silently dropping the links. Empty arrays are omitted.
- Payload validation and destination prepare reject a child that has both a
cross-store and a same-store parent, through one shared conflict check.
- Saved entries stay on disk as written; the map is Record<string, unknown> and
readers go through validCrossStoreLineage. Re-keying moves unreadable entries unchanged.
- Tests: literal expectations; drop touched mock-call and work-count assertions.
* test(lineage): drop clone and save work-count assertions from project group removal test
* feat(lineage): resolve cross-store parents into the sidebar parent map (inert)
- One resolver: a saved record nests its child under the single visible
row whose store reports the parent's installationId and whose instance
matches; copied installs, unknown stores and stale instances stay standalone.
- getSidebarLineageParentsByIdentity merges resolved cross-store parents with
same-store edges before cycle breaking; a valid same-store edge wins.
- Workspace delete collects descendants only from the parent's own store.
- Store identity: paired stores from their verified status hostDescriptor;
this store from the published descriptor, else host-installation.json.
- Nothing passes a view or writes records yet, so the sidebar is unchanged.
* fix(lineage): import WorkspaceKey from workspace-scope after main move
* fix(lineage): resolve sidebar parents once over complete rows; keep unowned rows' delete rule
- resolveSidebarLineageParentIdentities resolves same-store edges and cross-store
records once over every visible store's unfiltered rows, then drops cycles.
Ancestor injection and nesting reuse that map, so a filtered-out twin still
makes a parent ambiguous.
- Removed, retired or refusing paired servers leave the view: their records stay
saved and their children standalone. Offline servers keep their last-known id.
- A row with no owner is unknown, not ours: it never resolves, and delete only
excludes descendants whose store is known and differs from the parent's.
* fix(lineage): refuse UNC rekey collisions and transfers giving a child two parents
The UNC drive conversion rekeys cross-store child keys through the shared
collision merge, so a UNC key and its drive key with different records throw
instead of one silently overwriting the other. Profile project transfer runs the
shared child-key conflict check before writing the target.
* feat(lineage): write, carry and nest cross-machine parents
- Capability worktree.lineage.crossMachine on the child's store; the CLI
refuses before any request when the server lacks it.
- worktree.set / worktree.create accept crossStoreParent
{ installationId, workspaceKey, instanceId } and write only
crossStoreLineageByChildKey (refused when it names this store, or the
child has no identity). Remove Parent clears both.
- worktree.crossStoreParentRef: the parent's own store names it with its
installationId; reparent refuses a parent whose chain already reaches
another store. Same-store Set Parent refuses the same.
- worktree.ps rows and lineage lists carry the records; same-store lineage
fields stay null so older readers show the child standalone.
- The desktop sidebar resolves parents over its own and paired stores'
records (PR 2's resolver) at the visible-worktree and row-plan call sites.
- CLI: active/current or an inferred terminal parent against a server is
sent as a cross-store parent.
* fix(lineage): read worktree rows through the record selector; mark the test runtime cast
* fix(lineage): hydrate every store, fence refreshes, honest CLI fallback
- Each host's catalog hydration fetches its lineage snapshot, so this
desktop's identity and records load whichever store is focused.
- Cross-store records join the per-child request-start fence: a stale
lineage list can't restore a removed record or override a newer one.
- A failed inferred cross-store parent sends noParent instead of
forwarding the selector a server could match to a same-store twin.
- Inferred cross-store creates record env-workspace/inferred capture.
- The writer states its trust contract; the chain rule runs at the mint.
* feat(lineage): cross-machine parents on the phone, pickers, chat resume and labels
- Phone: the list nests a child under a parent on another machine, from the
records rows already carry and the store identities its host verified
(status.get descriptor; relayed servers' installationId on the host list).
- Set Parent popover, context menu and the composer's parent picker offer
parents in other visible stores. The parent's own store names it
(worktree.crossStoreParentRef); the window checks it against that store's
verified installationId and the child store's capability before any write.
Remove Parent and Open parent cover cross-machine links.
- Native chat resume ancestors follow resolved cross-machine parents.
- Host sections keep a nested child under its parent and keep its own
machine label.
- Main sidebar feed resolves cross-store parents (rows carry records; desktop
facts and paired-host facts carry identities); paired web windows read
their host's installationId; reveal without an occurrence plan resolves
them; `worktree create --agent` names a parent on another machine.
* test(lineage): type the mint test's parent row through unknown
* test(lineage): give the cross-machine tests' partial-row casts their SAFETY reasons
* fix(cli): worktree set names a parent on another machine
* feat(lineage): name the machine of parent candidates on another machine
* fix(lineage): Set Parent and Remove Parent act on the clicked row's own host
* fix(lineage): phone resolves parents over every row before the repo filter
* fix(lineage): chat resume nests by host-qualified identity across machines
* fix(lineage): local create refuses a same-install parent before checkout and shares the RPC recorder
* fix(lineage): name parent machines without a focus read; keep create's probes synchronous
* fix(lineage): chat resume keys offers by their own machine; only cross-machine parents leave a section
* fix(lineage): name an older parent server before asking it for a cross-machine ref
* fix(lineage): an app-picked parent on another machine records as manual
* fix(lineage): chat resume keeps base grouping; only resolved cross-store parents move a chat
* revert(lineage): drop chat resume from cross-machine parents; it groups by machine as before
* refactor(lineage): CLI builds cross-machine parents; explicit only
- Drop worktree.crossStoreParentRef and its reparent chain walk: the CLI
builds the ref from this desktop's installationId (worktree.lineageList
now reports it) and the parent row it reads. Cross-machine chains are
allowed; same-store cycle validation and same-install refusal stay.
- No inferred cross-machine parent: remove crossStoreParentInferred and
the fallback states. Against a server, this machine's terminal, cwd and
env context is never sent; the CLI prints how to nest explicitly.
* refactor(lineage): build cross-machine parent refs from verified ids; pickers hide only loops; one desktop install id source
* refactor(lineage): refuse moving a linked workspace instead of carrying the link
Orcad migration and profile project transfer now refuse, before any staging or
write, when a workspace in the move has a cross-store parent record (raw entries,
readable or not). Remove the carry machinery: the dormant crossStoreLineage
field, its runtime capability and destination gate, import/subtract/claim code,
transfer carry and the two-parent conflict checker. Also drop the unused
removeCrossStoreWorkspaceLineage.
* docs(lineage): describe cross-store parent refs without the removed mint RPC
* test(lineage): drop the RPC call-list assertion from the cross-store ref test
* fix(lineage): refuse linked moves before any write and for shared-repo children
- Migration refusal passes the child's own host stamp to the scope matcher and
refuses an unstamped key whose shared repo id the move includes, so such a
child can't move while its link stays behind.
- Profile transfer checks the read-only source snapshot before pending-move
recovery or storage migration; the payload check stays as the final guard.
* fix(lineage): refuse a linked profile move before the profile lookup can restore the index
* fix(lineage): startup hydration of other stores loads only cross-machine links
* fix(lineage): list the child's own machine first in the parent picker
* fix(lineage): keep profile argument errors ahead of the linked-move preflight
* fix(worktrees): let the first listing replace a restored server workspace stub
A restored active server workspace starts as a session stub with no head.
If git status fills in its branch before the first listing lands, the
listing merge took that as a concurrent branch switch and kept the stub,
dropping the catalog row (instance id, lineage, provenance). The workspace
then showed standalone until the next listing.
* fix(worktrees): accept a headless row's listing only when it confirms git status
A stale listing could otherwise roll back a branch switch git status saw on
an empty-head row (e.g. an SSH fallback row) during the first connected listing.
* feat(lineage): CLI nests a create on another machine under the caller workspace
- No parent flag and no --no-parent against a server: the caller's workspace
on this machine (env, else cwd) is sent as an inferred cross-store parent,
the way --parent-worktree active builds it. The host records it as
inferred from environment or cwd.
- A server without worktree.lineage.crossMachine, or a caller workspace that
can't be linked, still creates unnested and prints one note.
- An inferred parent falls away for folder targets, as same-machine folder
creates record none.
* fix(lineage): CLI cross-machine inference reads the caller terminal and suppresses fallbacks
- The caller terminal's workspace joins env and cwd as an inferred parent,
with same-machine precedence: env and terminal must agree, cwd only alone.
- A fallback (old server, unlinkable or unresolved caller, ambiguous cwd)
prints one note and sends noParent, so the server's own create-time
inference can't nest what the CLI reported as not nested.
* feat(sidebar): drag a workspace onto one on another machine to nest it
Dropping a workspace on a workspace from another machine now sets the
parent through the same cross-machine path as Set Parent: same eligibility
(loops, older servers), same refusal messages. Drop targets carry host
identity so the child's own copy is the one written.
* refactor(lineage): this machine's Orca resolves the caller parent for a create on another machine
- New read-only worktree.resolveCallerParent runs the same-machine create
resolver over the caller's env, terminal and cwd context, so stale
contexts are skipped and only validated parents can conflict, and returns
the cross-store ref or a reason.
- The CLI's own precedence and terminal lookup are removed; it keeps the
capability gate, one note with noParent on any fallback, and silence with
no Orca context.
* feat(orchestration): remote worker-start --worktree new-child nests under the coordinator's workspace
worker-start --on <server> --worktree new-child creates the server's worktree
under the coordinator's workspace on this machine, using the same verified
cross-machine parent ref as the CLI's caller auto-nest. A server without
cross-machine parents still starts the worker, top-level, with a warning in
the receipt. Guide, CLI help and docs describe it.
* fix(lineage): resolveCallerParent reads this store's identity without creating it
- Uses the published host descriptor's installationId, else the existing
host-installation.json; never creates or repairs it. Missing identity
returns a reason, so the CLI creates unnested with its note.
- Drops the read-only wording: parent resolution does the same catalog
upkeep a same-machine create does.
* fix(sidebar): validate every cross-machine parent before a multi-select drop writes
A drop of several workspaces builds every cross-machine parent ref first, so an
older server's refusal leaves the whole selection unchanged. Drops the touched
preview-timing test cases.
* refactor(orchestration): build the remote new-child parent ref with the shared caller resolver
Orchestration still picks the caller workspace; resolveCallerParentForAnotherStore
verifies it and builds the ref, and both read the installationId the same way.
Drops the copy and mock-call tests.
* fix(lineage): narrow the inferred source with a guard instead of casts
* fix(lineage): a composer parent picked on another machine nests normally once the destination is that machine
The retained host-qualified pick is classified against the current destination:
same host gives the ordinary parent fields, another store the cross-machine ref.
Also corrects two stale comments.
* perf(lineage): read only the requested child's cross-store entry
* perf(lineage): request each store's lineage list once at startup
* perf(lineage): cap optional caller-parent inference at 3 s and skip a timed-out local Orca
* perf(lineage): return cached sidebar parents when selector inputs are unchanged
* fix(lineage): snapshot selector inputs without a type assertion
* Route startup lineage and the sidebar lineage memo through their owners
* fix(browser): Escape typed in a page exits grab, find and the password list
A focused page keeps its keys, so the renderer's Escape listeners for
element grab/annotate, the find bar and the saved-password list never
fired. Main now hands Escape from browser and HTML preview guests back
to the renderer for that page; during a grab pick the page doesn't
also get it, like C/S.
* test(browser): say why the grab Escape test waits out the toast
* refactor(browser): share one page-Escape subscription hook
* Add collapse and expand all projects controls
Support a sidebar button, keyboard shortcut, and Alt-click on project
headers, with a contextual tip to introduce the feature.
* rm mockup file
* Clean up project header toggle handling and fixtures
* feat(browser): save and fill passwords in Orca's browser (experimental)
Off by default under Settings > Experimental > Saved passwords.
* fix(browser): secure saved passwords and fix login lifecycles
* fix(browser): keep the guest preload self-contained so saved passwords load
The shared uuid import split into a chunk the sandboxed preload can't require, so the
whole preload failed on every page. Element tokens now come from crypto.getRandomValues.
* fix(browser): fill a saved login on mouse press so clicking a row works
Pressing Orca's UI blurs the page field, which closed the list before the click landed.
* feat(browser): manage saved passwords from a Passwords section in Settings › Browser
Replaces the Manage passwords dialog with an inline section: save and Touch ID
switches, import menu, a searchable list grouped by site with last-used times and
hover actions in a capped scroll box, delete confirmations, and an Add/Edit dialog.
* feat(browser): turn saved passwords on for everyone instead of behind Experimental
Drops the experimental flag, its Experimental toggle and the badge; the Passwords section in
Settings › Browser now shows on desktop for the local host without opting in. With no toggle,
the guest disable message and the apply-to-open-pages step had nothing left to trigger them.
* fix(browser): refuse protected password actions when Touch ID is set up but unavailable, and keep fills on the matched page
Touch ID lockout or a closed lid made canPromptTouchID() false, which counted as a Mac without
Touch ID, so show, copy and protected fill went through unchecked. Enrollment from bioutil now
tells the two apart. A fill also kept no hold on the page across the Touch ID prompt, so a page
that navigated meanwhile got the password; it now goes only to the frame and origin it was matched for.
* fix(browser): keep a browser profile when its saved logins can't be deleted
Profile deletion removed the profile first and only logged a failed vault delete, so the
encrypted vault could stay on disk with nothing left to retry it. The vault now goes first,
and the profile stays (deletion reports failure) when it can't.
* fix(terminal): only resume a terminal stream where xterm is, so escape tails never print
After switching workspaces, Claude Code's prompt could show stray text such as
"43B": the tail of ESC[43B from a frame whose start was lost when output was
dropped (hidden panes, the pending cap, daemon background thinning, the
renderer backlog cap) and the stream resumed raw.
Main marks every delivery that cannot prove it starts at escape-parser ground
(midEscape: true, or 'unknown' after bytes it never saw), never batches bytes
across a gap, and routes salvaged query copies through the drop sentinel.
The renderer tracks where xterm's parser stands in the stream. Bytes enter
xterm only as an exact continuation of what it parsed, or from a boundary main
proves is ground (grounded with CAN); anything else is skipped whole, still
answers its queries, and is repainted by a restore. Snapshot and reattach
replays anchor the position; Orca's own CAN-led writes invalidate it. This
replaces the gap-label and seq-proof admission from #37269.
* refactor(terminal): tidy stream-boundary plumbing
Share the drop-sentinel check, build sentinel bytes once, name main's start field as a boundary, and drop unused test fixtures.
* Add Chat UI choice to onboarding and tailor agent tours
* Add setup guide controls for Chat UI and Yolo permissions
* Refresh native chat agents and improve surface radio controls
- Re-read the host inventory alongside workspace refreshes, including WSL
- Recognize downloaded, ready chat runtimes without a CLI on PATH
- Standardize onboarding and checklist surface choices on accessible radio variants
* Show full-access agent overrides in Yolo mode settings
* Polish Chat UI onboarding and clarify terminal fallbacks
- Group agents by launch surface and refresh selection previews
- Add scroll-edge fades to reveal clipped onboarding content
* Recognize ready custom Chat commands and nested scroll changes
* Advance agent tour after selecting a new tab type
* Install onboarding skills for selected chat providers
- Route Chat UI setup to local provider-specific skill roots
- Improve tour focus, Escape handling, and delayed target transitions
* Keep split lesson while a new pane mounts; update renamed skill in test
* Assert agentDefaultSurface in Chat UI onboarding e2e
* Open the chosen Chat UI agent when a new project's default checkout opens
* Make the onboarding chat preview match native chat
- Soften the selected preview tile styling.
- Accept a leading separator in dev:fresh arguments.
* Fix scrolling in the onboarding agent step
* Preserve initial working directory when opening empty workspaces
Also check only remaining tour steps for unfinished actions.
* Fix tour resumption after completed split steps
* fix(browser): size the pane to an agent-set viewport so wider pages scroll instead of being cut off
* fix(browser): an agent's viewport survives a pane remount and pans with the wheel
* test(browser): drive the wheel without casts