* perf: stop host-status pings rebuilding the title catalog and rereading server config
Host-status updates walked every saved tab for title-refresh support before checking whether any host's support changed, and remote RPC/tunnel/worker checks reread, rehardened and reparsed the saved-server registry files on every call. Gate the title walk on an actual per-host capability flip, and reuse the merged registry read while a stat signature of its three files is unchanged.
* perf: invalidate registry read cache on in-process writes
Coarse-timestamp filesystems can reuse an inode within one tick, so a same-size rewrite could slip past the stat signature; writers in this process now bump a generation that is part of the signature.
* perf: key the registry read cache on file bytes, not stat metadata
A stat signature can miss a same-size rewrite on coarse-timestamp filesystems or a reused inode. Comparing the files' exact bytes (plus mode/ctime and the directory mode, which the uncached read re-hardens) means a hit can never serve data that differs from disk, from any writer.
* perf: only reuse a registry read once hardening holds and the path is unchanged
Reuse now requires the files' permissions to already be restricted (POSIX), so a transient chmod failure is retried on the next read as before; Windows hits keep the ACL verify-and-retry. Snapshots also record each path entry's identity, so a symlink re-pointed and restored mid-read cannot pin the value read through the other target.
* perf: build the cached registry value from the exact bytes it is keyed on
The value was computed by a second, independent read, so a directory or symlink swapped and restored around it could pin data the key's bytes never held. Each reader is split into read + parse-text, and on a miss the cache hardens, snapshots once, and parses that snapshot. Reuse also now requires the exact 0600/0700 modes the readers set, so an incomplete owner mode is repaired as before.
* perf: require the full hardened mode, including special bits, before reusing a registry read
* perf: keep Windows on the plain registry reader
Measured on a Windows host: a cache hit still has to re-verify ACLs, so it cost as much as the read it skipped (0.37 ms vs 0.33 ms). Windows now always uses the original reader, keeping its behavior byte-for-byte unchanged.
* perf(daemon): cache Linux CLK_TCK instead of forking getconf per start-time read
CLK_TCK is fixed for the kernel's lifetime, so the daemon identity check
no longer runs a synchronous getconf process on every read.
* fix(daemon): retry invalid Linux clock tick reads
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* feat(mobile): transcribe dictation on device with SpeechTranscriber on iOS 26
* fix(mobile): fall back to the dictation model, reserve locales and stop a finishing session on cancel
* fix(mobile): resolve on-device speech locales deterministically and report the simulator unsupported
* refactor(mobile): simplify the on-device speech engine and skip conversion when formats match
* feat(pairing): Add server and orca environment add exchange the link for their own token
After verifying the host, Add server (and repair) swap the access link's
token for one generated on this computer when the host advertises
pairing.ticket-exchange.v1, and save that instead. orca environment add does
the same with a short best-effort probe. Any failure saves the link as today;
the host keeps accepting it until the new token is used.
* test(pairing): Add server and CLI leave links unchanged for a released host
* fix(pairing): Add server and orca environment add sign in once after exchanging, and the CLI rejects a refused code
After exchanging a link, sign in once with the new token so the host
retires the link right away instead of at the first later use; a host
downgraded in between would not know the new token.
orca environment add no longer saves a pairing code the host refuses
(for example one another device already used); it asks for a new code.
* feat(mobile): define the on-device speech contract for the native audio module and page bridge
* feat(mobile): dictate on the phone first and replay to the computer when the phone fails
* refactor(mobile): type the on-device speech module without a cast
* fix(mobile): point dictation's finish ref at the committed callback
* test(mobile): wait on the takeover request instead of a fixed sleep
* fix(mobile): release each dictation request's own share of a replayed backlog
* fix(mobile): let the computer's text replace the phone's after a fallback
* fix(crash-reporting): don't report a speech process exit as an Orca crash
* fix(crash-reporting): keep every speech process exit out of crash reports, including OOM
* feat(mobile): recognize dictation on-device on Android with ML Kit
* feat(mobile): fall back to Android's on-device recognizer on phones without AICore
* fix(mobile): keep on-device speech events and finishes tied to the current session
* fix(mobile): let an ended on-device session release the recognizer before the next starts
* refactor(mobile): trim the Android on-device speech engines
* refactor(pairing): move device sighting bookkeeping out of the device registry
* refactor(editor): define plugin Monaco themes outside the hook so it compiles
* fix(diff): keep one tint on wholly new and deleted files
A new file diffs against an empty original, so every line carried both the line tint and the character tint, plus a red row for the empty original line. Drop the stacked character tint and the empty side's lone tint for whole-file additions and deletions in the file diff and combined diff.
* fix(diff): hide the empty side's lone marker and cover the accessible diff viewer
* feat(native-chat): dim the message rail until hover, focus or scroll
The rail drew at full strength the whole time. It now rests at 40% opacity and lights on pointer hover, keyboard focus (focus-visible) or reader scroll input, fading back after about a second idle. Scroll input writes a data attribute on the rail node, so no React render runs per scroll. Reduced motion switches without a fade.
* fix(native-chat): keep the rail lit through drags and its preview, and snap under reduced motion
Chat strong inherited bolder (700), heavier than the chat's own
semibold (600) headings. Use 600 so emphasis stays distinct without
dominating long answers.
Refs #39060
A terminal.send guard with onError: deny that returned an invalid result (e.g. a
deny reason over 500 chars) was logged and skipped, letting the action through.
Treat a malformed result from a fail-closed participant as a failure. (STA-13532)
* fix(antigravity): keep Gemini Enterprise chats off the host's agy
A saved Chat account type other than a personal Google account (or a
malformed or still-saving one) now keeps new Antigravity chats on Orca's
managed runtime, whose sign-in honors it. Before, Enterprise users who had
not yet signed in to Orca's Chat were silently pinned to the host agy's
personal account. The check reads the execution host's own saved settings,
and the health snapshot refreshes when the setting changes.
Refs #39006, #38737
* test(antigravity): drop the settings cast
* feat(pairing): a browser's saved device key retires the pairing link it came from
When a browser signs in for the first time with a saved device key forked
from a ticket row, the row's link token is replaced by a value nobody holds
(the row stays as the fork's anchor; orchestration fingerprint pinned).
Session-only keys re-register from the link on every page load, so they never
retire it. Links from older builds are untouched.
* fix(pairing): a browser key retires a link only during pairing and only if nobody claimed it
A saved browser key now retires its link only when it was registered within
the pairing window of the link's first sign-in, and only when no other
client already proposed its own token for it (that first claim wins, so the
desktop that proposed first is never locked out).
* test(pairing): the web key migration wire test expects a fresh link to be single use
* fix(web): say a refused pairing link may already have been used
A browser opening a single-use link after another device used it is
refused as unauthorized; the message now names that case and asks for a
new pairing link from Orca.
Adds oxc-transform-react as a devDependency and a ratchet that runs React
Compiler in analysis mode over the shipped renderer sources. Files can't gain
bailouts (new files must compile fully), and new code the compiler would run
differently is banned: hooks it memoizes into a cached block, bare `void x;`
reads, member postfix updates used as values, and "use no memo" opt-outs.
Both baselines are generated by --update / --prune.
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Explicit SGR backgrounds equal to the translucent default background, and
style-only cells (dim, italic, extended underline) in WebGL, painted an
opaque fill over the theme wallpaper. Treat them as the default background
in both the WebGL and DOM renderers; the buffer keeps the original color.
Closes#37671
- Keep a bounded history of approved setup hashes per repo; creates send it and the host
runs setup when the created branch matches any approved content.
- Approving a created branch's different setup now saves it to that history.
- Automation runs record skipped setup content; the run page and list offer
"Review and approve setup", and saving re-confirms setup after a skipped run.
- Device registry: if saving a device's first sign-in fails, keep the sign-in in
memory and retry the save every minute until it lands. Before, a restart after
the pairing code's expiry treated the device as an unused, expired code and
refused and removed it.
- Remote runtime client: when a host remembered as E2EE v2 refuses v2 and the
legacy status check gets a sealed "unauthorized" reply, report that refusal
instead of "update Orca on that host" with close code 4001, which managed
orcad read as a different server on its port.
The New workspace and New issue discs on the host and Tasks screens were
a pale #e5e5e5 fill that glared on the dark page. Dark mode now uses the
same accent fill as the Home + button; light mode keeps its dark fill.
Refs #37401
* feat(mobile): exchange the scanned pairing code for a token kept on the phone
When the desktop advertises pairing.ticket-exchange.v1, the phone sends a
token it generated as the last call on the winning pairing socket and saves
that token instead of the scanned one. For Relay pairings the journal is
rewritten first so recovery republishes the same token. Any failure keeps
the scanned code, which the desktop still accepts.
* fix(mobile): say a refused pairing code may already have been used
Single-use pairing codes are refused once another device signs in with
them, so the scan error now mentions that case alongside expiry and
replacement, and still asks for a new code from the desktop.
* feat(pairing): let clients exchange a pairing code for their own token
New offers are marked as tickets. A client signed in with the pairing token
can call pairing.exchangeTicket with a token it generated; the host stores it
durably and keeps accepting the pairing token until the client first signs in
with the new one. That sign-in retires the pairing token and cuts sockets
still using it. Advertised as pairing.ticket-exchange.v1; clients that never
call it keep working exactly as before.
* fix(pairing): never retire a code other clients may already rely on
Exchange is offered only within 10 minutes of a ticket's first sign-in;
after that the code may be in use by several older clients and stays a
plain token. The first client to propose keeps its claim: a different
proposal is refused (that client keeps the code) instead of displacing it,
so two clients pairing at once can't lock out the first. Adds cross-version
tests against the pinned release: downgrade keeps exchanged and unexchanged
phones working, rows from an older host are never single-use, and the older
host doesn't advertise the exchange.
* feat(antigravity): run Chat on the host's agy acp when compatible
New Antigravity chats prefer a compatible host-local `agy acp` (initialize
must answer as "Antigravity ACP Server" >= 1.5.2, protocol 1, loadSession and
session/resume) and fall back to Orca's managed runtime otherwise. The chat's
account home pins its backend, so resume and auth failures never switch it.
Managed chats and their <userData>/antigravity-chat sign-in are unchanged.
Refs #38833, STA-14062
* test(acp): launch an attached host rig with its spec
* fix(antigravity): keep Orca's Chat sign-in, back off a hung agy, and never downgrade permissions
The host's agy takes a new chat only when nobody is signed in to Orca's own
Chat and agy has its own credential. A failed check waits 5 minutes before
retrying (5 s timeout). Full-access chats stay on Orca's runtime, and a host
chat refuses a permission mode agy lacks instead of asking.
* fix(antigravity): check agy's sign-in without a keychain prompt, ignore inherited API keys, allow own mode
agy falls back to an inherited GEMINI_API_KEY, which its models reject, so
host chats drop it. The macOS presence check reads only the keychain item's
attributes. Chats in agents' own permission mode may use the host's agy.
* fix(native-chat): show Orca's install card only for chats on the managed runtime
The status summary marks chats pinned to a host program (hostProgram), so a
chat on the host's agy is no longer blocked when Orca's runtime is missing.
* fix(mobile): show host agy sign-in copy for Antigravity chats
The desktop host now reports antigravitySystemNotSignedIn when a chat on the
host's agy is signed out; give the phone the same copy so its failure table
stays complete.
* 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
Each chunk RPC holds one of the page bridge's 60 desktop request slots until
the host answers, through a reconnect too, so the page drained the microphone
only every 500 ms. Cap chunk sends at four per dictation and merge audio
captured meanwhile into the next request, then drain every 150 ms.
* feat(dictation): pre-warm the speech model and make the unload timeout a setting
The first dictation after launch, or after the idle recognizer unloaded,
waited for the speech host to spawn and load the model. The host now loads
the selected on-device model when voice is on: after the first window (or
at orca serve start), when the voice settings change, and when an Orca
window regains focus after an unload. A dictation started meanwhile binds
to the loading recognizer instead of loading a second one.
Voice settings gain Unload Speech Model: Never, Immediately, 5, 15 (the new
default, was 60) or 60 minutes. Immediately also turns pre-warm off, voice
off frees an idle recognizer, and a model that failed to load is retried
only on a settings change or after a dictation loads it.
* fix(dictation): don't mark a model failed when its warm-up was torn down on purpose, and stop a warm-up that voice-off superseded
* fix(agent-launch): paste Copilot launch drafts when its composer row draws, not after the loading spinner settles
* chore(plugins): regenerate declarative runtime for the Copilot composer signal
* Fit file and browser tabs to their content
Keep terminal tabs at their preferred width and reserve stable editing
space while renaming tabs.
* Remove obsolete tab rename width hook
* fix(automations): send managed SSH host automations to its Orca server
A managed SSH host's automations live on its Orca server (#24863), but the
create dialog, CLI and mobile still saved desktop copies that never ran.
- Desktop dialog: the host's desktop SSH entry is no longer offered; its
key resolves to the host's server entry.
- CLI create routes to the server by project id, or asks to move the
project there first; mobile hands the create to the server's editor.
- Existing desktop copies skip with "{host} now runs automations on its
Orca server" and offer "Move to {host}" through the editor's move save.
STA-13422
* fix(automations): address review on managed SSH host routing
- CLI: refuse a desktop copy when the desktop reports a managed host this
CLI holds no server for; pass the workspace by id to the server; check
field support on the server too.
- Main: the skip follows the owner selector, so local rows pinned to a
managed host's workspace skip too.
- Editor: a destination host whose project table loads after open keeps
the chosen project.
* fix(automations): refuse new desktop rows on a managed SSH host and move leftovers
Per review and the owner's follow-up:
- The desktop's automation create/update refuses a row on a managed SSH
host ("Move this project to {host} first."), covering CLI, mobile, raw
RPC and older clients.
- Existing desktop copies move to the host's Orca server on startup, on
managed connect and on their due tick: created there keyed by the
desktop row id, then removed here. A server without the project leaves
the skip and the Move button.
- Mobile drops the Save-time hand-off; its picker no longer offers a
managed host's desktop SSH setup.
- CLI routes only for a local client, using that client's profile.
* fix(automations): drop the automatic leftover move
A managed host's leftover desktop rows keep the skip reason and the
Move to {host} button; moving them automatically will extend the existing
managed-host migration in a follow-up.
* fix(cli): resolve an automation's SSH destination from the repo's routing id
* chore: prune the repo host read baseline for the CLI destination
* fix(automations): migrate a leftover automation in one click
The detail banner's Migrate runs the editor's own move-to-server save without
opening the dialog. Leftovers show Needs migration instead of Enabled, and
their skipped runs read Needs migration to {host}.
* fix(automations): wrap the Needs migration status instead of cutting it off
* fix(automations): name a leftover's project from its migrated copy
* fix(automations): save a host move against the project table loaded after the editor opened
Migrate loads the server's repo table first, but the save transaction read the
destination captured before that load, so the first click on a cold catalog was
refused. Save now reads the same derived destination the form already shows.
* fix(automations): drop the leftover project-name fallback
The global id lookup could name another host's project. Scoping it to the
leftover's server is not worth the code for a display-only label, so a leftover
whose project moved shows Unknown project again.
* fix(automations): name a leftover's project from its migrated copy
A managed SSH leftover's project always moves to the host's Orca server with
the same id, so only those rows fall back to the id lookup; any other row with a
missing project stays Unknown project.
* feat(speech): let several devices dictate at once on one recognizer
The host ran one dictation at a time, so a phone dictating blocked the
desktop, the web client and every other phone. Each dictation now opens
its own stream on the one loaded recognizer, capped at four, and a start
that needs a different model is refused while others use the loaded one.
* feat(dictation): explain the device limit and a busy model on desktop and phone
* docs(web): say the host has several dictation slots
* fix(speech): free an idle recognizer for cloud dictation and drop canceled starts from the device count
* test(speech): drop a deletion test that only pinned the old single-session limit
* refactor(speech): drop a canceled start from the map instead of flagging it
* refactor(speech): tag stop, discard and feed requests with their stream on the new base
* fix(speech): satisfy the type-aware lint on waiting for stopping sessions
* fix(speech): copy only the fed view of a sub-buffer, and call it a speech model in the busy copy
* fix(speech): bind a starting session to its recognizer in the step that picks it