* 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
* feat(ai-vault): reveal an Antigravity session's artifacts folder
* fix(ai-vault): detect agy artifacts from the discovery walk and open inside the folder
* test(e2e): pass onRevealArtifacts in the OMP child history fixture
When an agy parent ends its turn waiting on a backgrounded run_command,
agy sends Stop with fullyIdle=false and OSC 7501 done, and sends that
command's PostToolUse only when it exits. Orca held the row open only for
invoke_subagent helpers, so after two minutes of hook silence the
program's done ended automation runs while the command still ran.
Track parent run_command steps whose PostToolUse has not arrived
(daemons excluded) and hold a busy Stop as monitoring. The program's done
settles that hold only after 30 minutes of hook silence, so a command
that never exits cannot pin a run until the 6h watch limit.
Fixes#35234 (STA-12678)
* refactor(renderer): organize runtime clients and feature state owners
* fix(refactor): preserve incoming consumers and validation routing
* fix(refactor): consolidate defining-owner type imports
* fix(speech): run the on-device recognizer in its own process
A native crash in sherpa-onnx took Orca down with it, and a decode stuck
in native code could not be stopped. The recognizer now runs in an
Electron utility process (a forked child under orcad), is killed when a
stop or cancel goes unanswered, and a dead process ends the dictation
with an error instead of a silent stop.
* fix(speech): let a loading model finish when its dictation is canceled
* fix(speech): never leave a speech child behind or crash main on its fatal error
* refactor(speech): drop the teardown message now that terminate kills the process
* fix(speech): fork the orcad speech child through process-host with a typed request
A draft such as `dark mode: fix the toggle`, or agy's plan placeholder typed
verbatim, matches the composer caret row's edit-mode alternative. agy hides
`? for shortcuts` while the composer holds text, so composer_ready refused,
but composer_ready_custom_status_line (#38930) took agy's own
`[mode · ]model · effort` footer for a custom status row and answered idle.
Orca would then paste into the operator's unsent text.
Status rows may no longer be agy's built-in footer. The captured agy 1.2.7
screen of the plan placeholder typed as a draft comes from #23402.
Split from #23402.
Co-authored-by: seonghobae <8172694+seonghobae@users.noreply.github.com>
An unconfirmed send settled as posted when the refreshed thread had any new
comment with the same text, so another person's identical comment cleared the
draft. The host now passes GitHub's viewerDidAuthor, and the phone settles only
on a comment the viewer wrote. Without it (GitLab, REST fallbacks, older hosts)
the draft stays with the check-the-thread notice.
* feat(dictation): capture desktop audio in an AudioWorklet at 16 kHz
Desktop dictation captured with a ScriptProcessorNode on the renderer's
main thread and shipped hardware-rate buffers, which main then decimated
to 16 kHz with linear interpolation and no low-pass, folding everything
above 8 kHz back into the speech band.
An AudioWorklet now resamples to 16 kHz with a windowed-sinc low-pass,
posts 100 ms frames, and sends a level every 50 ms for the meter and
silence auto-stop. Desktop and the web client share it.
* refactor(dictation): share the sinc resampler and meter window, one frame sink
Move the resampler to src/shared beside the meter it feeds, share the
50 ms meter window with the phone's, drop the constant per-chunk sample
rate from buffered audio, and send frames through one deliverFrame so
the speech-host port can replace it later.
* Show the machine on folder workspace cards, as other workspace cards do
* Build folder fixture rows without passing the array index
* Add a fixture for a folder card on another machine
* Drop the folder card screenshot fixture
* Put an unstamped folder card on the host it runs on
A folder workspace with no host stamp resolved to its project group's host for
labels and sections but to local for its card row. Stamp it the way catalog
adoption does so the card, label and section agree with runtime dispatch.
* perf(mobile): return to LAN soon after joining Wi-Fi
* perf(mobile): allow one arrival per dwell and keep 12 s for Tailscale probes
* perf(mobile): defer a second arrival to the dwell and keep 12 s for non-private hosts
* refactor(mobile): share one network read across hosts and build the fingerprint once
* perf(runtime): resume a phone terminal from what it already received
* fix(runtime): carry a missed cwd change on resume and keep points the ring covers
* fix(runtime): resume only onto the grid the missed output was written for
* test(runtime): type the resume token without a cast
* perf(mobile): ack terminal output after xterm parses it
* fix(mobile): retry refused terminal acks and share one ack across echoes
* fix(mobile): retry a refused terminal ack on the timer, never inline
* test(mobile): keep only terminal credit tests that fail without it
* refactor(mobile): key terminal ack settles by stream token
* test(mobile): type the credit test timer steps
* test(mobile): use T[] for the parsed callback list
React Native Web repeats the last scroll event 100 ms after scrolling
stops. The page list cleared its record of its own scroll write on the
first matching event, so the repeat after each place-keeping correction
counted as the reader scrolling. For a parked reader that froze the
streaming reply below for 300 ms about every 0.7 s and released it in
one step, which showed as lurches of 20-40 px.
* perf(mobile): show the model picker's models as soon as a chat opens
* fix(mobile): retry skill scans and trailing command reads through the query
* fix(mobile): keep menu-read commands across a re-probe and rescan skills on demand
* refactor(mobile): read chat query state from the client and give the model catalog query its own module
* test(mobile): give the reopen seed catalog its fetchedAt
* fix(mobile): keep the query layer out of the phone options hook's module graph
* perf(mobile): load the new-tab agent list with the session and share it with "Fix with" labels
* fix(mobile): wait for host status before detecting agents
* fix(mobile): read the agent list again on each open after a failed read
* refactor(mobile): name the source control agent through the shared label hook
* chore(mobile): type the new-tab recording adapter with a type import
* feat(usage): show Muse subscription quota in the status bar
Reads `muse usage --json` (Muse Code 1.4.13+), which reports the
subscription allowance without a model turn, and maps `allowance.window`
to the five-hour session meter and `allowance.weekly` to the weekly meter.
`rateLimits.muse` now appears in `orca account list --json`.
- Gate on `muse --help` listing `usage`: older builds treat an unknown
subcommand as a prompt, so Orca never sends `usage` to them.
- Run with MUSE_NO_AUTO_UPDATE=1 and MUSE_LOGIN=0 so a background poll
never self-updates the launcher or starts a device sign-in.
- Missing CLI, signed out, API-key login / no allowance, non-JSON output,
and timeouts each map to an explicit unavailable/error snapshot.
- Hidden meters skip the spawn, and Muse publishes after the sibling batch
so a slow CLI cannot delay Grok/Cursor/ZCode/Antigravity.
- Default-on status-bar item with a one-shot migration, CLI-detection
gating, visibility menu and Settings toggle, remote-owner mapping.
Based on the design in #34232 by ShemYu.
Co-authored-by: ShemYu <11490047+ShemYu@users.noreply.github.com>
* fix(usage): cap Muse usedPercent and date readings by fetch time
Muse can report over-quota usage above 100; clamp to the 0-100 meter
contract like the other providers. Use the fetch time for updatedAt so a
cached older asOfMs cannot make every window focus trigger a refresh.
* fix(usage): prevent Muse launcher installation during polling
* fix(usage): gate Muse on its version file and run the active build directly
- Read the launcher's `.muse-version` and require 1.4.13 or newer (the
release that added `muse usage`); only installs without that file fall
back to `muse --help`, cached by launcher path, mtime and size.
- Execute `muse-bin-<version>` directly when it exists, so the launcher's
update, sign-in and install paths never run; the env guards stay for
the launcher fallback. A version change during the call discards the
reading as stale.
- Start the Muse fetch at the beginning of the full cycle and cap it near
8s (6s usage, 2s help), so a slow CLI holds the shared queue far less.
---------
Co-authored-by: ShemYu <11490047+ShemYu@users.noreply.github.com>
* refactor(dictation): run the desktop controller on a shared session state machine
* test(dictation): assert session machine behaviour, not run numbering
* refactor(dictation): allow a new start from the error phase
* test(dictation): drop the error-restart case no desktop path depends on yet
* refactor(dictation): derive stop-during-start from the phase and share the start teardown
This reverts commit 88fa7ed2a9.
Also drops the agyInstall strings a later locale sync added to the
non-English locale files, so no orphaned keys remain.
* perf(mobile): reopen Files folders from memory
* test(mobile): re-record Files explorer goldens for query reads
* fix(mobile): paint cached Files rows before the host client attaches
* refactor(mobile): share query observer options and Files' empty-state rules
* test(mobile): type the Files explorer test host
* perf(mobile): open Review with the Changes data already loaded
* fix(mobile): keep the open diff through re-keys and wait for Review rebuilds
* fix(mobile): release Review writes once their files show, retry only fixed diffs
* fix(mobile): follow language switches in Review's diff states
* fix(mobile): keep Changes' compare key out of render and type the Review adapters
Hold the lead Stop through the shared child-work fold while UUID children
are live between SubagentStart and SubagentStop. A child that sends no hook
for 10 minutes stops holding the row. Interrupt (subscribed async, as Muse
requires), SessionStart and SessionEnd clear tracked children, and the
existing transcript-poll replay of the held Stop then reaches done.
Fixes#28090
* fix(agent-hooks): bound Muse transcript polling when the session goes silent
A Muse session that ended without Stop kept its 1s session-log poll alive
until the pane closed or another hook arrived. Idle polls now probe the
owner's process after 60s without log growth (an exited owner is reconciled,
which clears the poll) and stop after 30 min of silence; the next hook
re-arms polling.
Linear: STA-8313
* fix(agent-hooks): re-arm the Muse poll after a presence probe restates the row
A live owner's suspension flip (Ctrl-Z/fg) makes the idle presence probe write
a new row; the next poll tick saw a superseded row and stopped, and no hook
re-armed it. Re-arm on the restated row, keeping the idle clock, unless a newer
hook already owns the pending poll.
Linear: STA-8313
* fix(muse): back off quiet transcript polling safely
* fix(muse): replace the presence probe with a 5s quiet-log back-off
Back a quiet Muse log off from 1s to a 5s cadence instead of probing
presence; log growth, a status change, or any hook for the session
(including ignored ones) restores 1s. Removes the idle presence probe and
the row-restatement re-arm, which Muse rows never needed: Muse hooks carry
no trusted process identity, so the probe always returned early.
* fix(mobile): reread Source Control settings when a screen opens or the app returns
* test(mobile): use the shared hook harness for the settings reread test
* feat(mobile): show a thin refresh line under the header while cached data refreshes
* fix(mobile): start the refresh line still under unknown motion and key it by execution host
* fix(mobile): draw the refresh line only once the motion preference is known
* fix(mobile): start the refresh sweep at the left edge on each reveal
* refactor(mobile): drive the refresh line from the screen's own refetch and keep its timers in the header
* test(mobile): give the Changes content fixture the new refreshing field
* perf(mobile): stop a hidden host list refetching on every change
The host list's change-event path fetched worktree.ps (and the server rows read) on every worktreesChanged or reposChanged with no throttle, even while the page was buried under a session or the app was backgrounded. Hidden pages now note what they owe and the foreground refresh catches up once; visible pages read at most once per 3 s.
* perf(mobile): count poll reads in the change throttle and queue the catch-up read
* perf(mobile): leave the poll out of the change throttle and queue the catch-up only after skipped notices
* test(mobile): drop the throttle timing test
* refactor(mobile): one owed flag and one full catch-up read for a hidden host list
* perf(mobile): skip the Suspense fallback on page boot
* fix(mobile): keep open-target boots on the lazy path
* test(mobile): pin that the page waits for its held route chunk
* refactor(mobile): preload every page layout and keep screens apart
* perf(mobile): render the cached page build first and talk only after the connection's verdict
* test(mobile): give ready shell states their page window
* feat(mobile): let pages read and write the device store
* fix(mobile): import zod as a namespace in the page store verb
The page bundle check rejects `import { z }`: it ships zod's locale tables.