* fix(sidebar): keep discovered worktrees hidden when adding a project
Adding a project no longer switches it to show worktrees Orca didn't create
(Conductor workspaces and other tools' worktrees). They stay behind the
sidebar's "Hiding N discovered worktrees" line, the same as every other
way a project is added.
* fix(sidebar): open a picked linked worktree instead of revealing its siblings
When the folder added as a project is itself a linked worktree, its main
checkout is hidden. Open the picked worktree instead of switching the whole
project to show worktrees Orca didn't create.
* fix(repos): register clone and create paths with symlinks resolved
Git reports checkout paths with symlinks resolved. Clone and create saved the
typed path, so a project cloned under a symlinked folder (e.g. /tmp on macOS)
did not recognise its own checkout and hid it as a discovered worktree. Resolve
the existing part of the destination before registering, as adding an existing
folder already does.
* fix(repos): keep finding projects saved under a symlinked clone or create path
Projects cloned or created before paths were resolved keep the typed
spelling. Match both spellings so retrying the same clone or create reuses
the saved project instead of failing on its non-empty folder.
* fix(repos): keep Windows clone and create paths as typed
Windows Git reports the typed spelling, and resolving would turn mapped
drives into UNC paths. Runtime clone/create tests now use resolved temp
roots, matching what registration saves.
* fix(repos): register Git's checkout path for clone and create
Replace the parent-symlink resolution with the path Git reports after clone
or git init, as adding an existing folder already does. This also covers a
symlink at the target folder itself and WSL paths, leaves plain folders and
Windows spellings unchanged, and matches saved projects by both the typed and
the resolved spelling.
* fix(sidebar): keep opening projects whose own checkout isn't recognised
Projects saved through a symlinked path by older builds or paired hosts don't
recognise their own checkout. Keep main's behaviour for that case: show the
project so the add still opens it.
* fix(repos): match resolved paths when importing a folder of repos
Clone and create now save Git's resolved checkout path, so a nested import
through a symlinked folder must also match that spelling instead of
registering a duplicate project.
* fix(repos): find saved projects through a symlink before the clone target exists
Resolve a missing clone target through its existing ancestors, reusing the
worktree-create alias resolver, and read the lookup keys inside the clone
lock. Queued clones through a symlinked folder reuse the first clone, and a
different repository at a saved project whose folder is gone is refused
before cloning, as on main.
* fix(repos): match a folder import's resolved path only to local projects
The resolved spelling names this machine's folder, so a remote (SSH) project
at the same path string must not be treated as the import.
* fix(repos): limit a desktop folder import's resolved-path match to this machine
Runtime-owned and SSH projects can share the path string; only projects on
this machine may match the import's resolved folder.
* fix(repos): read a nested import row's connection through the host helper
* fix(repos): match only this machine's projects when creating a local project
* perf(agent-hooks): start 5 processes per Claude hook event instead of 17
The managed Claude hook runs for every PreToolUse and PostToolUse. On macOS
each event started 17 processes; on Linux 17 (dash) or 19 (bash).
- Agent identity: Linux reads /proc/<pid>/stat and boot_id with `read`
builtins. macOS skips `uname` when OSTYPE is darwin and runs only
`exec /bin/ps`, without `sed` or `printf` subshells. The identity stays per
event: no shell builtin can tell a reused PID from the process that had it.
- Metadata header: packed once per pane into hook-metadata/pane-<id> beside
the endpoint file, and reused only while all six inputs match exactly.
- Payload: the capture subshell execs /bin/cat, curl reads the body from a
here-document, and the PreToolUse reply subshell execs curl.
Other agents keep their scripts byte for byte. Against a fake hook server,
the old and new scripts give the same server records, stdout, spool records
and time limits; the raw JSON body gains one trailing newline from the
here-document, which JSON parsing ignores.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(agent-hooks): keep the hook payload pipe off the exec path and prune metadata files
- Only the reply subshell that execs curl reads the body from a
here-document. The print-then-post path of Claude-compatible CLIs uses the
printf pipe again, so its body stays byte for byte and needs no TMPDIR.
- The spool drain removes hook-metadata files older than
AGENT_HOOK_SPOOL_MAX_AGE_MS, in main and in the WSL/remote guest.
- The Linux identity reads /proc/<pid>/stat to EOF, so a command name with
a newline keeps its start time.
- Comments are one line each, and the string-index stdin test is deleted;
the runtime stdin tests in the same file cover every generated script.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(agent-hooks): run the refresh locale test on the raw JSON exec-curl path
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(agent-hooks): keep the Claude hook body piped to curl
The here-document added a trailing newline to the raw JSON body and,
under macOS /bin/sh (bash 3.2), wrote every payload to a temp file.
Pipe the body again so the hook server gets the same bytes as before.
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com>
* fix(mobile): re-read project catalog after the window publishes it
reposChanged went out before the window published the new project's
catalog, so the phone keyed a new project repo:<id> and its collapse
was lost when the catalog caught up.
Fixes#37067
* test(mobile): fold catalog re-announce check into the serve test
* fix(claude-accounts): keep /model and effort saved in a managed account across launches
Profile settings.json was overwritten from ~/.claude/settings.json on every
launch. Apply only the top-level keys the default home changed since the
last sync, tracked by per-key digests beside the profile marker, and keep
the rest of the profile copy.
* test(claude-accounts): fail hook install through a broken linked settings file
* fix(claude-accounts): leave an unreadable account settings file alone
* fix(claude-accounts): carry Orca hook changes in the default home to account copies
* refactor(claude-accounts): spell out the no-record case in the settings sync
* revert(mobile): drop the details card on session tab taps
Reverts 852391da0f (#36395). The card popped up on every tab switch.
Tapping a tab switches tabs again, and the session title card goes back
to its own owner from #30389. Keeps later changes to these files: tab
press feedback, the compact top bar's + button, and #36866's keyboard
keep-focus for title taps.
* fix(mobile): hide branch and folder when the setting is off
The phone's workspace row always printed the branch or folder path, so the
Branch name / Branch / folder path toggle under Card display did nothing.
The shared card details now carry that rule, and the row reads it.
* fix(mobile): centre the chevron on Home list rows
* fix(mobile): scroll sheets taller than the screen
Content-sized sheets ride the keyboard by translation, but their height cap ignored that lift, so a tall sheet pushed its top off screen with no way to scroll back to it. The cap now gives back the keyboard lift. Sheets that scroll their own list can now shrink to the cap instead of clipping it, and the GitHub Pages picker no longer nests its list inside the drawer's ScrollView.
* feat(mobile): show each desktop's machine kind and model
The desktop now reports whether it is a laptop, desktop or server, and its
model, on status.get. The phone stores it with the other last-known host
fields and turns it into a line like "MacBook Pro · macOS".
* feat(mobile): edit a host from its screen and align the Automations bar
The host screen's menu gains Edit host and Network diagnostics. Edit host moves
onto the standard bar and gains Remove host. Automations uses the standard bar
with its switcher pinned under it, as Source Control does.
* feat(mobile): arrange Home and bring back stats and account usage
Home drops the Jump back in times, names the repo on every Jump back in row
(#37642), shows each desktop's machine kind, and adds stats and account usage
at the bottom. A new Arrange button reorders and hides sections, saved on the
phone.
* fix(mobile): keep the page bundle within budget and show the machine on the host screen
The edit route gets a web stub so its native-only removal flow stays out of
the page's chunks, and Automations keeps its own segment style. The host
screen header shows the machine line like Home does.
* fix(mobile): give the edit route's page stub its own module
A bare re-export of the catch-all route compiled to the same asset as
[...page], so the page manifest listed it twice and the desktop template's
inventory check refused the build.
* feat(mobile): turn Home into Feed, Usage, Automations and Settings tabs
Feed keeps agents (needs-you first, five then Show all), desktops with a
needs-you count, and Tasks; New workspace moves to a floating button.
Usage shows stats and per-desktop account usage, Automations a per-desktop
summary with on/off switches that opens the page for editing (new=1 opens
its editor). Settings becomes a tab. Arrange Home is removed: tabs replace
the long scrolling Home it was for.
* style(mobile): darken the accent blue slightly
* style(mobile): give the Home Tasks row the desktop tile icon
* style(mobile): condense the Home Feed
Smaller tiles, type and gaps; a desktop is two lines with its needs-you
count as an amber pill; the Tasks row drops its duplicate heading.
* style(mobile): drop the Last known prefix on Home desktop rows
* chore(mobile): bump to 0.0.63 and Android versionCode 27
* test(mobile): re-record Linear task recordings for the darker accent blue
* test(mobile): check the page Back stack on Tasks now that Edit host is native
* fix(mobile): pass the Usage tab's limit window to the shared usage helpers
* fix(mobile): type the Usage tab test to the account-slot limits and keep the tab under 400 lines
Claude Code suggests allow rules with destination localSettings, and Orca
passed them back unchanged, so "Allow for this session" wrote a permanent
rule to the project's .claude/settings.local.json. Scope every suggested
update to the session, as the button says.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix(projects): refuse project removal while a workspace is being created
Removing a project mid-create let the create land a workspace no project
owns, and its armed .orca-preparing checkouts outlived the project: a quit
inside their TTL leaked them locked, since crash reclaim only runs when the
same repo prepares again. Hold each create against its project row so
removal refuses until it settles, and release the repo's armed and
crash-left preparations when the project is removed.
* fix(projects): keep project removal working when its runtime needs repair
Resolving Git options for the dead-owner reclaim threw for a WSL runtime
needing repair, which blocked forgetting the project. Make the reclaim
best effort, and give host-setup deletion the same create fence and
preparation release as project removal.
* fix(projects): skip WSL boot and late prefetch arms on project removal
The removal-time reclaim no longer boots a stopped WSL distro, and a
prefetch that finishes after its project was removed no longer arms a
checkout nothing would reclaim.
* test(projects): assert the removal refusal by code, not copy
* refactor(projects): key the create hold by the shared repo host identity
* refactor(projects): keep workspace creation under the file line cap
* fix(contextual-tours): keep the Split tip until the user splits or closes it
The Split a terminal pane tip was saved as seen the first time it painted,
so any dialog, page change or restart ended it for good and the next tip
never appeared. A step that waits for an action is now done only when the
split succeeds (the saved split record) or the user closes the tour; the
tour resumes at its first unfinished step and keeps its session slot while
paused.
* fix(contextual-tours): a resumed tip never takes focus, at any step
* fix(contextual-tours): keep the pause across an activation that never painted
* fix(contextual-tours): a forced tour can't take a paused Split tip's place
* fix(contextual-tours): type the focus hook's tour ids
* fix(contextual-tours): remember a resumed tour in the store, and stop watching for a sidebar tip while the sidebar is closed
The overlay unmounts between activations, so its pause memory never
survived; the activation itself now says it resumes a tour already
started this session.
* fix(contextual-tours): keep the agent-sessions interaction counted only before a first split
* fix(contextual-tours): a tip that waits for a split never takes keyboard focus
It shows over the terminal the user types into and comes back each
session until they split, so taking focus (first display, after a
restart, or on the next tip after a keyboard split) would send their
keystrokes to its dismiss button. Replaces the in-memory resumed flag.
* fix(contextual-tours): keep focus in the callout when the user navigates inside it
* fix(contextual-tours): let other pages' tips show while the Split tip waits for a split
The Split tip now sits outside the one-tour-per-session limit while it is unfinished: it
neither blocks another page's tips nor is blocked by them, and it resumes afterwards.
* fix(contextual-tours): open the sidebar for a paused Split tip only if it started this session
* fix(contextual-tours): decide the session limit inside the tour effect, as main read its inputs
* fix(ai-vault): list managed Antigravity chats in Agent Session History
Orca's managed Antigravity chat sets GEMINI_HOME under the app's data
directory, so its transcripts land in antigravity-chat/antigravity-acp/brain,
which the history scanner never walked. Scan that root, take each session's
cwd from the server's conversations/<id>.meta so the row shows in its
workspace, and treat rows no chat owns as transcript references, since agy
cannot open the server's conversations.
* fix(ai-vault): scan managed Antigravity chats only for reference-aware clients
* feat(native-chat): let users change or turn off the idle stop for chat agents
The idle sweep stopped every chat's agent after a hard-coded 30 minutes,
which broke peer messaging, Claude Remote Control and background
subagents. Settings > Chat > Behavior now picks the window, including
Never, and the sweep reads it every tick so a change applies without a
restart. Never only keeps agents running: a chat handle with no agent
still retires on the default window.
* test(native-chat): type the childless sweep fixture's journal parameter
* feat(native-chat): add a setting to keep the scroll position when sending
Own sends from a pane bring the latest into view and follow the reply.
A new Chat > Behavior setting, "Scroll to latest when sending" (on by
default), turns that off: a reader who scrolled up stays in place, one
at the end keeps following the reply, and Jump to latest still works.
The gate sits in the one reveal every send path reports to, so the
explicit Jump to latest button and find navigation are unaffected.
Fixes#38153
* fix(native-chat): keep following a reader at the end when reveal on send is off
A reader can sit at the end without following (a rail jump that clamps at
the end, a short nudge up). The setting's gate skipped their reveal, so
the sent message and its reply grew below the viewport. The list now
decides: off skips only readers who stopped away from the end, judged by
where they last scrolled so the send's own echo doesn't count.
* refactor(native-chat): read the scroll-on-send setting through one helper
Also drops the redundant 'scroll' search keyword (the title already
matches) and the settings switch call-shape test.
* fix(native-chat): decide the send reveal from where the reader was at the press
The last-scroll distance stayed small after output grew past a reader who
had nudged up, so a later send still jumped them with the setting off.
The list now reads live geometry, and a structured message, which reveals
only once the host admits it, captures that answer at the press, before
its own echo moves the end.
* fix(native-chat): read the reader before Resume waits for the host
Resume reveals only after the host lifts the pause, by which time the
drain may already have added rows below the reader.
---------
Co-authored-by: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com>
* feat(mobile): show every desktop usage provider on Accounts
* refactor(status-bar): import moved usage helpers from shared
* refactor(mobile): reuse shared usage rules for provider names and data checks
* test(mobile): type account-slot fixtures as Claude/Codex limits
* fix(pi): show compaction in native chat and hold sends until it ends
* fix(pi): wait out compaction before the dispatch liveness checks
* refactor(pi): separate releasing waiting sends from compaction end
* fix(pi): wait out compaction after option RPCs, right before the prompt write
* fix(pi): reapply a send's options after waiting out compaction
A paired server that serves this desktop's own browser row back without
the host stamp from #36447 (e.g. a peer on 1.4.245 or older) made
buildMirroredBrowserTabs reuse the row's workspace and page ids, so the
local page was rebound to that server and its webview destroyed.
A mirrored row can no longer claim a page this desktop holds as its own
or a live other runtime's. Pages of a removed runtime stay reclaimable so
a re-paired server still adopts them.
* fix(native-chat): keep loaded chat images cached across scroll and workspace switches
Inline Markdown images and sent-image thumbnails evicted their cache entry
whenever they left the viewport or their chat was hidden, so every return
re-read the file (over RPC on remote workspaces). Drop the eager release and
let the bounded shared cache evict unpinned entries when it needs room.
Co-authored-by: mmarabel <166927047+mmarabel@users.noreply.github.com>
* refactor(editor): remove unused local image release and version tracking
---------
Co-authored-by: mmarabel <166927047+mmarabel@users.noreply.github.com>
* Give unconfirmed chat messages explicit recovery actions
* Resolve receipt settlement code quality findings
* Complete recovery copy localization and callback types
* Align send lifecycle contracts with explicit recovery actions
* Include the unconfirmed opener in the restart fixture type
* fix(chat): preserve recovery ownership across lifecycle boundaries
* Preserve send ownership across context replacement and recovery
* Separate history ingestion from live send settlement
* Preserve original send truth and reconcile acquired history
* Use summary-shaped history responses in acquisition coverage
* Preserve queued image recovery for released readers
* Prove independent late receipt recovery across reopen
* Preserve queued image sources in bounded catch-up projection
* Keep queued input and explicit chat retries in their original domains
* Preserve reader lifetime across main session interfaces
* Fix static checks after main integration
* Migrate retained recovery Stop contracts to current lifecycle
* test: use typed access for journal population counters
* fix: consolidate history page bounds type imports
* test: exercise legacy snapshot reuse through selected handoff
* Fix attachment draft typing and document test boundary casts
* Repair current journal integration types and contract fixtures
* Keep uncertain send recovery in queue reorder reopen contract
* Repair mobile contract boundaries and exact queue lookup
* Align input finalization contracts with current Stop ordering
* Keep recovery copies independent of queued message barriers
* Reconcile Stop and replay contracts with current lifecycle
* Update recovery tests for current module and draft APIs
* Align Stop recovery contracts with independent queue policy
* Expose independent recoveries to current queue controls
* Cover sending-only queue Resume visibility
* Adapt recovery contracts to shared queue projection
* Use current history and failure-word contracts in recovery tests
* test: retain released reader source contracts through queue settlement
* Keep uncertain sends in ordinary history and preserve saved queue choices
* test: prove mobile draft leaf subscription isolation
* Use typed operation instrumentation in mobile recovery test
* Align lifecycle test boundary typing with static rules
* Repair test module typing and retained runtime copy catalog
* Remove unused chat delivery notice translations
* test(chat): retain legacy handback without uncertainty notice
* fix(chat): omit saved uncertainty card captions
* test: bind released readers to exact queue wire and proof
* Replace archived reader evaluator with exact recorded contracts
* Mark queued edit test hook import as type-only
* Isolate queued card tests from native inline composer imports
* Complete structured mobile resolution in queued edit test
* Align mobile attachment cleanup lifetime test with send outcomes
* fix: restore journal prototype delegation methods
* Keep uncertain sends as ordinary messages in memory
* Release notes admission after uncertain opening completes
* fix: end opening selection operations at uncertain completion
* fix: derive selection admission from live requests and repair integration tests
* test: consolidate send imports and use a supported locale
* Fix CI test types and synchronize retired delivery catalogs
* Type exported test fixture mocks through the public Vitest API
* test: align selection and journal verification contracts
* Reconcile acquired history before journal projection
* Preserve saved invocation facts during history catch-up
* Repair journal recovery consumers and CI contracts
* Keep failed legacy import displays serializable and bounded
* Preserve journal owner boundaries in CI import cleanup
* Fix typed test fixtures for session recovery contracts
* fix: align history and recovery contract types
* test: verify logical rewind across released chats
* Bound retained queue and status reads
* Bound Stop and delivery reads to relevant journal inputs
* Bound subscriber catch-up and stopped-history reads
* test: align journal read fixtures and operation counts
* Bound automatic chat reads to current input evidence
* fix: align journal reader contracts and test fixtures
* test: type historical dispatch rejection fixtures
* fix: restore merged session projection and fixture types
* fix: preserve resumed queues across logical fork rewind
* Preserve queue pause chronology across logical rewinds
Keep original Stop and Resume coordinates while retaining matching warm failure evidence in the journal owner. Keep historical fixture authoring separate from current production writes.
* End historical fixture authoring before current queue actions
* Keep answer operations separate from retained messages
* Fix resting chat attention context
* Fix attention regression fixture provenance
* Validate retained RPC test frame reads
* Fix retained attention and RPC contract fixtures
* Keep send observations on canonical delivery evidence
* feat(editor): add a default Markdown view setting with a switcher right-click menu
Markdown files can now open in Source instead of Rich Editor. The choice
lives in Settings > General > Editor and in a right-click menu on the
editor's view switcher. Changing it keeps already-open tabs on their
current view; the right-clicked tab follows the new default.
Closes#38102
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(editor): switch the right-clicked tab through the normal view toggle
Picking a default from the switcher menu now goes through the same
handler as clicking a segment, so a tab showing Changes or a plugin
editor leaves it for the chosen Markdown view.
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* fix(terminal): a paired-server file link opens in the workspace it was clicked in
Clicking a server terminal link to a file another workspace holds switched to
that workspace: the client asked the host for the holding workspace
(crossWorkspace), and the editor loader moved any absolute-path tab -
including one already holding a grant - to that workspace. Anyone who can
open a tab can already run anything on the host, so the holding workspace
guards nothing. On hosts that save granted files, the link now opens through
a grant in the clicked workspace, and restored tabs re-grant in place. Hosts
that can't (older hosts, their own SSH workspaces) and folders keep opening in
the holding workspace.
* fix(runtime): a desktop client reads and saves a granted file as large as a workspace one
Hosts advertise files.resolve-terminal-path.in-place-files.v1, give desktop
clients a 2 MB grant budget (phones keep 512 KB), and report a grant's size,
so the clicked workspace opens a file another workspace holds in place and
falls back to the holding workspace only where a grant can't serve it.
* fix(terminal): open in place only what a grant can read and save
Review: a grant save must fit one request frame and grants serve no bytes
editors, so the 2 MB budget would open files that can't be saved. Hosts now
report a grant's size and binary content instead, the client opens in place
only text within 512 KB, and only the SSH-workspace refusal moves a restored
tab; other host errors stay retryable.
* docs(terminal): describe the in-place files probe
* fix(terminal): ask the holder first and keep downloads, HTML and missing paths unchanged
Review: the holder answers first, as before, so missing paths, folders, this
computer's files, downloads and HTML previews resolve exactly as they did; only
an existing text file another workspace holds asks again for a grant here.
In-place opens are capped at 256 KB so edits stay within the grant's save limit.
* test(editor): mark the placement test's store stub as a sound cast
* fix(automations): accept Pi model and thinking extra arguments
* fix(automations): keep Pi provider pins and refuse unpinned inline keys
* fix(automations): match Pi value options to its argument parser
* fix(automations): refuse Pi model overrides beside unscoped inline keys
* test(automations): cover Pi's last-provider-wins key pin
* fix(automations): refuse any Pi custom Command text with --api-key
* fix(automations): drop the Pi inline-key pin guard
Pi already sends an unpinned inline key to the chosen model's provider and warns;
the user typed both the key and the model, so Orca does not refuse it.
* Keep local folder creation on the current workspace surface
* Prepare folder workspace tabs before selecting the destination
* Provide inventory boundary in folder creation regression
* Use the composer empty-link contract in re-entry coverage
* Complete the composer re-entry test input
* Preserve sidebar key comparison through creation progress
* fix(automations): say the run's worktree was deleted; document plain run-use recovery
* revert(automations): drop the deleted-worktree wording change (not reproducible)
* docs(orchestration): list Tasks on recovery and note run-use drops other Runs
* docs(orchestration): delete the legacy takeover guide; keep orchestration docs to the normal flow