* Keep pending blocking questions through non-answer rows
* Use a canonical delivered human row in blocking question tests
* Remove redundant live-status question test
* plugins: show which theme chip is selected and switch modes for an other-mode pick
A selected chip used the muted secondary style and an unselected one the
filled primary style, so the theme in use looked like the inactive one.
Selected chips are now filled with a check and the others outlined.
Choosing a theme for the other mode only filled that mode's slot, so
nothing changed on screen and the click looked broken. Like installing a
one-mode theme, it now switches the app to that mode and offers Switch
back. The snapshot and toast are shared with applyPluginThemesNow.
Terminal chips were all listed under 'Use now', even a light terminal
theme shown in dark mode. They now sit under the mode they apply to.
Fixes#35922
* fix(plugins): switch back only the settings a theme chip changed
Switch back after an other-mode chip pick restored every theme setting,
so an icon or terminal pick made while the toast was still up was undone
too. It now restores only the keys that pick wrote, plus the mode. The
install flow keeps the full snapshot since it writes most of those keys.
* fix(plugins): list a terminal theme shown in the current mode under Use now
Installing a plugin whose only terminal theme is for the other mode puts
it in the slot that is showing. Grouping terminal chips by their own mode
then showed it selected under 'Use for light', and a click switched the
whole app to light. Treat a theme in the showing slot as that mode's for
the group, the check and the update together.
* fix(plugins): treat the terminal slot light mode displays as Use now
With Match dark mode on, light mode shows the dark terminal slot, but the
chip only checked the light slot. The dark theme on screen was listed
under 'Use for dark' and a click switched the app to dark. Check the slot
that is actually displayed.
* fix(plugins): replace theme chip type assertions
* fix(plugins): preserve appearance when assigning theme chips
* test(plugins): exercise disabled palette across plugin pages
* Prepare plugin terminal palettes only when selected
---------
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
* feat(cli): open a browser tab in a split pane with `tab create --split`
`orca tab create` could only add a tab to an existing pane, so an agent
had no way to put a page beside its terminal. `--split <right|left|down|up>`
creates a new pane next to the calling terminal (ORCA_TAB_ID), falling back
to the active pane, and opens the page there without taking focus.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UNtbMA9iVj9T4rBUjn5u6W
* refactor(browser): share one type for the tab-create IPC payload
The preload listener, its callback, the preload API contract and main's
sender each declared the payload separately, and the listener copy had
already drifted from the new split fields.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(browser): open the CLI split pane and its tab in one step
Creating an empty group and then filling it fails when main owns the
layout, because the window's group doesn't exist until main's frame
lands. createBrowserTab's split option carries both to whichever owner
draws them, so the rollback for a half-made pane goes away.
SSH hosts already cannot choose a new tab's group, so their CLI's split
is stripped like targetGroupId.
* fix(browser): split in the caller's workspace and refuse splits that can't happen
A CLI run outside its workspace's directory sends no workspace, so the
renderer fell back to the active one and missed the caller's terminal.
The caller's tab now picks the workspace when none was named.
An SSH host's split and an empty --split= both turned into a plain tab
reported as success; both are refused instead.
---------
Co-authored-by: Kalvin Mizzi <kalvin@mizzi.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(source-control): highlight the active committed comparison row
* test: bound committed row highlighting projection work
* test: match branch key iterator completion type
* Show effective agent shortcuts in the new tab picker
* Skip window-claimed chords in agent menu labels and keep the picker store-free
- Synthesize one input per binding; drop the guessed AltGr variant.
- Skip a chord main's window-shortcut resolver claims first (e.g. Cmd+= zooms).
- Read labels through a hook beside the projection so AgentLaunchPickerMenu stays presentational; drop its unused model prop and the dead terminal policy argument.
- Truncate long agent names before the shortcut and setup chevron.
- Test Cmd+T through the workspace keydown handler with the real resolver.
* Load the real agent shortcut resolver in the mock factory
Keeps vi.importActual out of test bodies (test-no-in-test-import-actual).
* Hold the real agent shortcut resolver in a hoisted mock without assertions
* Give the agent menu label fixture the required catalog
* Refresh live client projections when connection capabilities change
* Skip unchanged tab stream frames on capability change and test the phone socket wiring
* Keep Pi status audiences on their opening capabilities
* Merge duplicate test import, restore comment placement and cover a skipped capability change
* Say which streams read live client capabilities
* Mark the tab stream test's runtime stub cast with the casting directive
* fix(sidebar): don't scroll to the last child when revealing a parent with expanded children
Expanded child workspaces render inside the parent's option element, so
revealing the parent measured the whole child list and scrolled to its
bottom. Measure only the parent's own card, up to the children container.
Fixes#35916
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(sidebar): reveal the exact keyboard-selected workspace card
* fix(sidebar): preserve selected occurrence and reveal ownership
* test(sidebar): cover compact agent expansion reveal
* test(sidebar): distinguish same-key reveal retry lifetimes
* test(sidebar): use typed reads for reveal visit probe
* test(sidebar): declare selection state on option fixtures
* fix(sidebar): satisfy reveal fixture types and read ratchet
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
* fix(native-chat): drop a selection right-clicked away from
The menu kept offering Copy for an old selection, so right-clicking a link
and choosing Copy rewrote that selection instead.
* fix(native-chat): hit-test the menu press by caret position
Glyph rects miss the line gaps a selection paints, and a missed composer
selection was still offered as Copy.
* fix(native-chat): skip the menu hit-test over text controls
* test(native-chat): name the menu selection cases by outcome
* refactor(native-chat): reuse the transcript selection read for the menu drop
* feat(browser): list restored tabs in tab list and load them on demand
After a restart Orca restores browser tabs lazily: a tab gets a page only once
the user opens it. tab list only reported tabs with a live page, so an agent in
a worktree with 26 restored tabs saw one and concluded there were none.
tab list now reads the tab bar from the session snapshot the renderer already
publishes on every tab change, and reports every tab in tab-bar order. Tabs
without a page carry loaded: false. tab switch and tab close --index resolve
indexes against the same list, and switching to an unloaded tab loads it in
the background first. Waking a page now also waits for its first load to
finish, so the next command does not read a blank page.
Headless serve keeps every page live, so it lists as before.
* fix(browser): list the tab bar even when Orca has no window open
With the window closed the app keeps running and its restored tabs still exist,
so tab list should report them. Headless snapshots are built from live pages
only, and the merge dedupes those, so dropping the window check lists nothing
extra there.
* fix(browser): list an id held by a live page and a client page once
* fix(browser): wait for the first load of a tab headless serve wakes
Headless serve starts a restored page and returned at once, so the next
command could read a blank page mid-load. It now waits for the first load,
as the window path already did.
* refactor(browser): keep the tab listing beside the browser tab catalog
Main moved the runtime browser commands into runtime/browser/ since this was
written. Also drops a window lookup the catalog no longer uses, and corrects the
headless note: those snapshots list saved pages that have not started yet.
* fix(browser): keep paired-server mirrors out of tab list and close indexed rows like --page
A window also publishes the browser tabs it mirrors from paired runtimes,
which this host can never load, so the tab bar is read only for workspaces
this host owns. tab close --index now closes the listed row exactly as
--page does, keeping its worktree, so an unstarted headless tab is retired
instead of reported closed and left saved. With nothing active, the first
live page is marked current again, matching where a pageless command goes.
* fix(browser): wait for first load only on tab switch, and show tab indexes from the listing
Every --page command wakes an unloaded page, including the tab show a
remote browser pane awaits before streaming, so waiting up to 10 s for the
first load there held the stream back. Only tab switch waits now. tab show
reports a tab's index from the same listing tab list and close use.
* fix(browser): list restored tabs in an SSH host's bridged workspace, and reject fractional close indexes
An SSH host's workspace can have no repo row on the desktop, yet its pages
render in the desktop window, so the bridge's scoped workspace counts as
this host's own. A non-integer --index no longer falls through to closing
the active tab.
* test(browser): give the restored-tabs e2e's timeout and lease wait their reasons
* test(browser): read restored-tabs RPC results through the shared e2e reader
Reuses the browser e2e rpc helper instead of a local Reflect.get copy, and
types the listing test's snapshot rows.
* fix(browser): index tab show in the caller's scope, as tab list does
* fix(browser): report an out-of-range close index as browser_tab_not_found
Matches tab switch, so agents can recover by re-listing. The restored-tabs
e2e now asserts the raw loaded value instead of coercing a missing one.
---------
Co-authored-by: fanzzzd <fanzzzd@outlook.com>
* fix(browser): save target=_blank attachments from agent downloads to --path
A new-tab link opened while an agent download claim is armed is downloaded on the claiming page, so the file lands at --path instead of ~/Downloads and no empty tab is left behind.
* fix(browser): keep agent download claims made on popup pages
A claim made through --page popup-N was stored under the popup's id while will-download looks it up under the opener tab's, so the file went to ~/Downloads.
* fix(browser): refuse a second agent download claim on the same tab
A tab and its popups share one claim, so a second concurrent orca download would take the first one's file.
* test(browser): name the popup claim test after both rules it checks
* test(browser): share popup guest fakes and target=_blank setup
* fix(browser): convert only the clicked page's new-tab links during an agent download
A sibling popup's new-tab link no longer takes the tab's claim; it opens as a tab as before.
* fix(computer): preserve synthetic Unicode typing on Linux
* fix(computer): bound typing across xdotool versions
* fix(computer): hold Linux keymaps through text delivery
* test(computer): verify missing Linux keys through requests
Carries over #33221 and #33210. Replaces our #34484 Duo support with the community design, on top of main's rebuilt terminal key bar (#36166).
Fixes#33200
Co-authored-by: Takeshi Matsumoto <zytakeshi520@gmail.com>
Co-authored-by: Lars Keggenhoff <lars@kghf.io>
* feat(browser): upload through picker buttons in any frame
* fix(browser): catch pickers the page opens for a frame button and click SVG controls
* docs(cli): note server-hosted pages need the file input ref for upload
* test(browser): assert both picker interceptions are released
* refactor(browser): flatten picker upload and arm frame sessions in parallel
* test(browser): emit picker events through a real emitter instead of Reflect.apply
* feat(browser): let agents drive popup windows like OAuth sign-in as pages
window.open popups that keep their opener stay native windows, but the
agent bridge now lists each one in tab list as popup-<n> with its opener,
so --page, tab switch and tab close reach it. When a popup the agent
switched into closes, its next command lands on the opener.
* fix(browser): reach popups over SSH, through the Electron sidecar, and by implicit tab close
* fix(browser): keep sidecar popups ordered after tabs and handing back to their opener
* fix(browser): skip the tab wake-up wait for popups and keep the agent's claim when a popup hands back
* fix(browser): refresh the opener's claim on hand-back and settle tab close after the popup is gone
* refactor(browser): share routing retarget and idle-window helpers for popup hand-back
* refactor(browser): move popup routing helpers into bridge state to keep tabs under its line cap
* test(browser): expect the popup's url and title in its snapshot result
* feat(browser): typed snapshot flags and screenshots saved as files for agents
orca snapshot takes --interactive, --compact, --depth, --selector, --urls,
--delta and --full, passed to agent-browser on every route. The header
now shows the page title and URL. orca screenshot and full-screenshot save
the image where the CLI runs and print its path, in CSS pixels, with
--if-changed and --annotate. Flag help for exec, wait and download is
written for those commands, and the orca-cli guide sends web pages to
Orca's browser unless the user picked another tool.
* fix(browser): scale screenshots by the scrollbar-free axis and keep snapshot metadata behind SSH admission
* fix(browser): upscale low-DPR screenshots and wrap a torn-down tab's screenshot error
* fix(browser): give each screenshot its own file and keep typed screenshot errors
* refactor(browser): share the agent-browser screenshot reader between Electron and orcad
* refactor(browser): move snapshot and screenshot params under the line cap
* fix(git): stop sweeping unattributed per-ref locks during maintenance
An old packed-refs.orca-owner marker with a dead pid does not prove ownership of empty refs/**/*.lock files. A live git update-ref transaction can hold an empty lock, so deleting it let competing writers change the ref mid-transaction. Remove the sweep; keep packed-refs marker recovery.
* test(git): drop ref-lock tests made redundant by sweep removal
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* fix(browser): return every step of an exec batch and stop a bare batch waiting on stdin
* fix(browser): keep a passthrough batch's steps when it stops at the first one
* fix(browser): bind refs to the page an exec batch snapshotted
isFullGitObjectId only matched 40-hex IDs, so a saved 64-hex commit base in a SHA-256 repo was treated as missing, triggering an unneeded origin fetch and a fallback to the default branch.
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Buffered gitExecFileAsync reads now always fence login-shell rc output, and
non-zero exits de-fence error.stdout. The per-call captureWslLoginShellOutput
opt-in is removed since it is no longer meaningful.
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* fix(editor): bound inactive combined diff body retention
* refactor(editor): isolate reloadable diff body sizing
* refactor(editor): share reloadable-section predicate for combined diff body budget
Fold the body-size calculation into the view cache so invalidation and budget
accounting use one predicate, drop the checked-in benchmark, and trim tests.
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* fix(source-control): hide discard for present submodule gitlinks
Parent git restore --worktree without --recurse-submodules leaves a dirty or moved submodule untouched, so Discard reported success while the row stayed. Hide row/section discard for present gitlinks and submodule children; deleted submodule dirs and staged gitlink discard (which unstages) keep working.
* fix(source-control): keep submodule discard when submodule.recurse is on
Plain git restore recurses into active submodules when submodule.recurse=true, so status now stamps restoreRecurses on gitlink entries and the shared discard guards keep those rows discardable.
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Route resolveLocalGitUsernameDetailed's config lookups through createGitConfigSnapshotRunner so one resolution runs a single 'git config --list -z' instead of up to five 'config --get' children.
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Read .gitmodules with config -z so a submodule name or path containing a
space (the default when 'git submodule add' targets a spaced path) keeps
its real path instead of being split at the first space.
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
for-each-ref %(refname:short) prefixes a branch with heads/ when a same-named tag exists, so the mobile branch picker sent heads/<name> to checkout and detached HEAD. Use %(refname:lstrip=2) to return the branch name itself.
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Unstaging a staged rename from the Source Control row or diff review
reset only the new path, leaving the old path staged as a deletion.
Send both paths through git.bulkUnstage, matching desktop.
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
The fallback patch-equivalence proof only reads each line's leading +/- marker,
so the subjects printed by `git cherry -v` were unused output.
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* fix(git): resolve current branches without tag ambiguity
* fix(git): share tag-safe current branch reader with push-target setup and trim real-git proof matrix
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* fix(browser): find a hidden tab on the first agent command after a window reload
Right after a renderer reload, the first agent browser command on a hidden
tab failed with browser_tab_not_found. Main sent browser:activateView before
the reloaded renderer had restored its tabs, so nothing mounted; and the
bridge reused the page's agent-browser session bound to the guest the
reload destroyed.
Wait for the main window's existing readiness signal before activating a
hidden tab, and retire a session whose guest changed. agent-browser answers
`close` and exits ~100 ms later, so destroySession waits for the old daemon's
socket to go before the session name is reused.
* test(browser): read the replacement proxy's guest from its constructor call
git show :<path> and git show HEAD:<path> treat a missing blob spec as a
pathspec, exit 0, and print the HEAD commit when the path contains [, *
or ?. Orca then showed the commit header as the original side of new
files such as app/[id]/page.tsx. Read index and tree blobs with
git cat-file blob, and on older Git that only reports 'Not a valid
object name' prove absence with an empty literal ls-files/ls-tree probe.
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* Keep CLI-removed worktree branches unless requested
* Bind branch policy checks to removal connections
* Remove redundant local default removal test row
* Negotiate worktree branch policy on the mutation connection
* Fix removal integration compiler boundaries and type imports
* Use checked annotations in paired removal fixtures
* Reset CLI integration exit status across test runtimes