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(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
* 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
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.
* 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
* 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>
* refactor: organize remaining terminal pane features by owner
* refactor(terminal): consolidate replay guard type imports
* refactor(terminal): retarget notification mocks and generated consumer import
* refactor(terminal): repair benchmark owner lookup and stale mock export
* perf(mobile): trim the page bundle back under its old budget
* build(mobile): simplify the route entry anchors and zod table guard
* fix(mobile): import zod as a type in the RPC result reader
* 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
* fix(mobile): Cmd/Ctrl+Enter on an empty chat composer steers the first queued message
Matches desktop (#38732) in the paired web page: with nothing typed, the
chord steers the head waiting card through that card's own Steer, only
where its other way is steer.
* fix(mobile): empty Cmd/Ctrl+Enter sends the first queued card after a Stop, as desktop does
Desktop's rule (#38877) also sends the head card when no turn runs, and
goal mode keeps the empty chord inert. The queue list's handle now runs the
head card's own Send when idle, and steers mid-turn only where the chord's
other way is steer.
* test(mobile): type the queued-card handle refusal cases
* feat(mobile): holding Cmd/Ctrl in the phone chat composer shows what Cmd/Ctrl+Enter will do, as desktop does
Desktop's held-modifier preview moves into src/shared so the phone web
composer runs the same rule: Send becomes Queue, Steer or Steer queued
and the head queued card lights while the chord's modifier is held
alone on a hardware keyboard. The queue's empty-chord rule is one
function the preview and the press both read.
* fix(mobile): build the held-chord preview with the phone's own React, and offer only presses that act
The shared hook now takes its React hooks from the caller, as the shared
attachment preview does: the phone bundle resolves a different React copy
than the repo root, so importing react from src/shared broke every phone
web composer (Invalid hook call).
The preview also matches what a press does: one pill press per hold (a
double-click can't send a card after the draft went), a picked /plan-style
skill counts as a message, a head card whose own action is on its way shows
no Steer queued, and the lit state lives in the tray so a chat switch keeps it.
* fix(mobile): held-chord pill takes one click like desktop, keeps busy state per chat, lights only the head row
- The pill ignores a click whose detail is above 1, as desktop does, so a
deliberate later click in the same hold still acts.
- In-flight card ids are kept per chat with functional updates, so a chat
settling after a switch can't clear the current chat's record.
- The lit state is a small store only the head row subscribes to, so a hold
re-renders one row, not the whole queue.
- The desktop preview test moves back beside the desktop wrapper it drives.
* refactor(mobile): one owner for queued-card actions, read by both the list and the held-chord cue
The queue list owned its in-flight guard per mount and the tray mirrored it
for the composer's Steer queued cue; each mirror leaked (cross-chat and
same-chat remount overwrites, an all-chats copy per change). As desktop's
queued-messages controller does, the tray now owns one action store: pending
keys per chat, admitted synchronously, released per action, published by
copying only the changed chat. The list takes those actions as a prop (it
keeps its own only when mounted alone), and the empty chord presses the head
through the same run, so the cue and the press can't disagree. The list's
imperative handle goes away.
Also replaces a type assertion on the pill's click detail with an in guard.
* test(native-chat): desktop chord-preview wrapper re-exports the hold delay its test reads
* feat(dictation): stop desktop dictation after a minute of silence
A toggle dictation left running keeps the mic open indefinitely. Stop it
a minute after the last speech, with a toast, like the phone's tap mode.
A held push-to-talk key is never stopped; its release ends the session.
Mobile's timer moves into src/shared so both apps use the same rule, and
the shared meter now records when it last heard speech.
* fix(dictation): gate the silence stop on hold mode, as the phone does
* refactor(dictation): let the desktop silence hook own the hold-mode rule, as mobile's does
* refactor(dictation): move the capture meter into its own hook
* refactor(dictation): initialise the capture meter ref without a render write
* fix(mobile): keep speech said while dictation starts and fail fast on a dead host
* fix(mobile): let a tap or long-press end dictation while it starts
* fix(mobile): wait for the host's cancel before the next dictation starts
* fix(mobile): let a start wait out the previous start on the host too
* refactor(mobile): track host dictation work per client, not per screen
* test(mobile): read the start's dictation id without a cast
* fix(mobile): finish instead of cancel when a held mic is released while starting
* fix(dictation): space dictated text from its neighbours and keep text that can't be inserted
* fix(dictation): don't report a contentEditable insert as landed when nothing was inserted
* refactor(dictation): read caret neighbours per block and trim the spacing helpers
* feat(remote-runtime): use E2EE v2 for desktop and CLI connections to remote runtimes
Desktop connections to remote runtimes (orca serve, orcad, managed SSH,
browser bridge) and the orca CLI now use E2EE v2 framing for hosts that
support it, and fall back to the legacy framing for hosts that do not.
- One-shot requests try v2 first. A host that refuses the v2 hello
(hosts from before v2) or refuses capabilities on v2 token auth gets
one transparent legacy redial; nothing but the handshake is sent before
authentication, so the redial is safe.
- After a v2 sign-in succeeds, the host's pinned key is remembered in the
profile (shared by the app and the CLI). Every connection to that host,
including long-lived sockets, then uses v2 and never falls back.
- Long-lived sockets keep legacy framing until a host has proven v2, so
they never see an older host refuse a v2 hello.
- Hosts accept clientCapabilities on direct v2 token auth, so desktop and
CLI clients keep their capability negotiation on v2. Relay token auth
stays exact.
* feat(remote-runtime): recover a latched host that was downgraded, without a legacy downgrade path
A host this profile had signed in to over v2 was refused for good if it
was later downgraded below capability-carrying v2 token auth. Hosts now
advertise pairing.e2ee-v2-token-capabilities.v1. When a latched host
refuses v2, a one-shot request asks the host over legacy framing (keyed
to its pinned key, so only the real host can answer) for status.get:
- no capability: the host itself says it was downgraded; the latch is
dropped and the request goes ahead over legacy;
- capability present, or no answer: the request fails with an error
that says to update the host or check the network path. Only the
read-only status check ever ran over legacy.
A refusal faked on the path cannot produce the host's answer, so it
cannot force legacy framing for a latched host.
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
* feat(serve): refuse public source addresses on orca serve unless allowed
`orca serve` listens on every interface. It now accepts connections from
loopback, private LAN (10/8, 172.16/12, 192.168/16, 169.254/16, fc00::/7,
fe80::/10) and Tailscale (100.64/10, fd7a:115c:a1e0::/48) sources, and drops
public-source TCP connections before HTTP parsing or the WebSocket upgrade.
Public sources stay accepted when:
- --allow-public is passed (orca serve, orcad, serve service install);
- a paired device has authenticated from a public address (persisted flag);
- --pairing-address is a hostname or a public IP literal;
- any device paired before this server first ran the check is still paired.
The startup log states which rule applies; the first refused public address
each hour is logged with the flag name. Desktop listeners and Orca Relay are
unaffected.
* feat(serve): accept on-link public addresses and add --refuse-public
A public source inside a prefix of one of this host's own interfaces (os.networkInterfaces, re-read at most every 5 s) is a same-network peer, such as a LAN client using global IPv6, and is now admitted. Prefixes wider than /16 (IPv4) or /48 (IPv6) are ignored.
--refuse-public (orca serve, orcad, serve service install) refuses public addresses regardless of grandfathered pairings, a remembered public connection or the pairing address; it cannot be combined with --allow-public.
* feat(cli): pair clients with a running Orca via `orca pair`
A headless Orca could only print a pairing offer at startup, so pairing a
phone with `orca serve service install` meant stopping the service. `orca
pair` asks the running runtime for an offer over the owner's local socket
through a new `pairing.createOffer` method (pairing-admin, never granted to
paired clients or phones). It reuses the desktop's offer logic, now shared
by the Settings IPC handlers: mobile offers stay LAN-only unless --relay,
and the first network pairing widens a loopback bind as the desktop does.
Also honor an explicit ORCA_SERVE_RUNTIME=orcad on packaged macOS. Electron
serve needs a desktop login and aborts in an SSH session with nobody logged
in; orcad is plain Node and serves there, at the cost of no in-app updater.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* refactor(cli): move the serve-restore and pair handler groups out of the manifest
handler-group-manifest.ts was already at the 300-line limit on main, so
adding the pair group failed lint. Group the two serve/pairing entries in
their own file, as jira, linear and reference already are.
* test(mobile): pin the desktop pairing IPC handlers' replies and runtime calls
Characterizes mobile:getPairingQR and mobile:getRuntimePairingUrl across
every branch (explicit and default address, LAN-only with no address,
Relay without an address, relay failures, this-computer reach, widening
and its failure, unavailable offers). The same file passes unchanged on
main and on this branch, so moving the offer logic into
pairing-offer-requests.ts did not change the desktop path.
* test(mobile): type the QR stub instead of casting it
---------
Co-authored-by: Office exitnote <h@Offices-MBP.lan>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Neil <neil@stably.ai>
Device registry saves re-read the file, merge and rename. A save that read
the file just before another writer removed a device (an admin script, the
desktop, orcad) and renamed just after would write the revoked row back, and
its token would keep working. Saves now publish only if the file is still
exactly what they read (compare-and-swap via the staged write's last check),
and otherwise re-read and re-merge. A file caught half-written by a
non-atomic writer is re-read after a short wait instead of being treated as
corrupt and overwritten.
* fix(native-chat): list Claude and Codex models per account, not per folder
Opening the chat model picker rebuilt every worktree row to work out which
folder the read was for, because Claude and Codex model lists were keyed by
folder. Only the default model/effort depends on the project, so lists are now
keyed by account, WSL and the resolved CLI invocation and listed from the
account folder; the CLI is still resolved where the chat would launch. The
selected workspace is derived directly from its selector, and its own project
settings decide whether the account's default model and effort are shown.
The worktree projection and its cache are removed.
* test: merge duplicate node:os import
* fix(native-chat): list from Orca's probe folder, keep cwd-relative CLIs in their folder, one project-settings pass
- List Claude/Codex models from Orca's empty probe folder so a home folder's settings never read as another account's project settings.
- A command line naming files relative to the chat's folder lists there and is keyed to it.
- Read project settings once per binding for both the model and effort answers; per-model effort-only Claude settings keep the account model.
- A running chat's catalog withholds the account's configured choice where its project may pick its own.
- Bound the picker's workspace resolution so a stalled floating folder cannot hold it.
* fix(native-chat): catch quoted ./ arguments, list prewarm from Orca's probe folder, fake-timer the shared-listing test
* perf(native-chat): parse each project settings file once for both model and effort
* test: catalog e2e expectations list from Orca's probe folder; phone saturation keys per folder
* fix(native-chat): share a listing while any reader wants it; list custom command lines in the chat's folder; resolve a floating folder within the read's own wait
- A model listing shared by several folders keeps running while any reader still waits on it, queued or running, and stops (never starts) once the last one gives up.
- A Claude or Codex command line with user arguments or a relative command lists in the chat's folder; Claude reads only user settings there so project settings never narrow the account's list.
- A waiting read resolves a floating folder within its own deadline; a read that doesn't wait returns at once and starts the listing when the folder resolves.
* fix(native-chat): every reader of a shared model listing owns it until its own deadline; floating reads list their folder within their own budget
- Readers of the same request share one lookup that lives until the last reader's deadline, so later readers neither repeat CLI/settings setup nor lose the listing when an earlier one ends or is cancelled.
- A shared listing's requests carry no single reader's deadline: the provider timeout bounds them and the last reader leaving stops them.
- A floating picker read that doesn't wait starts its folder's listing under its own deadline and retirement, and says the listing is in progress so the picker follows with a waiting read.
* fix(native-chat): a shared model listing survives the chat that started it; unreadable workspaces list as the account
- Each reader of a shared lookup fences its own request, so one chat closing never ends another chat's listing; the lookup's budget is the latest deadline among readers still waiting, and its listeners are released when it ends.
- A picker for a workspace this host can't inspect (remote, WSL, ambiguous, stalled) lists as the account does from Orca's probe folder, never from wherever Orca itself runs.
* refactor(native-chat): readers of one model request share an ownerless setup and each own only their claim and the listing
- Same-request readers share one setup (folder, account, CLI scope and the folder's own default settings) that belongs to no reader: its budget is fixed when it starts, it ends with the service or the agent's retirement, and it is reused while it runs and while the listing it found runs.
- Each reader keeps its own claim, deadline and cancellation; a binding holds scope facts only and never cancels another reader's claim or a shared listing, so one chat closing never ends another chat's listing.
- A floating picker read that doesn't wait no longer asks to list the unresolved folder.
* fix(native-chat): shared catalog setups end on their budget or a workspace change, readers settle on cancel, and defaults and account seeding happen once
- A shared setup that runs out of its budget, or whose workspace or agent is retired, answers nothing at once and leaves the cache, so a reader with time left resolves afresh (a moved folder never reuses the old path).
- Cancelling a reader, or stopping the service, ends its wait at once.
- A folder's own default settings apply to its binding whenever they arrive; the account list is seeded once per shared setup, not per reader.
- A reader shares a setup only with readers whose own chat agrees on whether it may list.
* fix(native-chat): a settled catalog setup serves its whole listing, and a folder's default settings stay bounded and reach every reader
- A setup that has answered stays reusable while the listing it found runs, past its first reader's deadline, until it is ended or its agent retired, so later readers don't redo CLI and project-settings work.
- The setup's budget holds until the folder's default settings have answered too; settings that outlive it are read again while a reader still has time, and apply to the request's current binding whichever reader asked.
* refactor(native-chat): the catalog service owns each model refresh within one fixed budget; pickers only wait
A refresh (account, folder and CLI resolution, the folder's default settings,
admission and the listing) now belongs to the catalog service with one 30s
budget fixed before setup. Readers join it and wait only until their own
deadline or cancellation; none extends, shortens or ends it. Expiry withdraws
queued listings, stops the process and drops late answers. Defaults apply only
to the refresh's own binding, so a moved folder's late settings never describe
the new folder. A waiting read that used its whole budget is final.
Replaces per-reader claims, shared setups and reader-counted listing demand.
* fix(native-chat): retiring an agent or a chat's folder ends its catalog refresh at once, and a shared setup never rechecks the chat that started it
- Retiring an agent ends a refresh still stalled in setup, so a waiting picker
answers at once instead of after the refresh's budget.
- A listing that runs in a chat's own folder stops, and its answer is dropped,
when that folder's command context retires; listings shared across folders
stay independent.
- Scope resolution uses only the passivity its readers had when they asked,
so another chat's record going away mid-setup no longer turns it saved-only.
- A reader that stops waiting unsubscribes from the listing at once.
* fix(native-chat): one workspace's retirement no longer stops a catalog listing another workspace shares
A listing is keyed by the folder it lists in and the CLI invocation, so moving
one workspace never makes it describe the wrong folder; stopping it there
cancelled a listing another workspace on the same folder still needed. Agent
retirement and shutdown still stop listings at once.
* test(native-chat): a folder change keeps another workspace's shared catalog listing, and never answers the new folder with the old one's
* test(native-chat): a folder agent's listing survives another workspace on its folder moving
* test(native-chat): type the shared-setup harness's held stages without casts
* fix(native-chat): Claude and Codex pickers show the account's default in every folder
The picker no longer reads a repo's own model or effort settings for Claude and
Codex: they show the account's list and default everywhere, and the CLI still
applies the repo's settings at launch. OpenCode, OMP and Pi keep their project
checks. The model/effort split and the peek withholding go back to main's shapes.
* fix(native-chat): a Codex listing in the chat's folder never loads that folder's project config
A Codex command line that may name files in the chat's folder still lists there,
but now marks that folder and its parents untrusted for the listing, so a repo's
own model catalog can't replace the account's. The exported project-settings
check also answers no for Claude and Codex.
* fix(native-chat): Codex lists models only from Orca's probe folder
A Codex listing no longer starts in the chat's folder for custom or relative
command lines, so a repo's own config can never change the picker's list, as
other Codex hosts do. Chats still launch with the user's command line and folder.
Drops the untrusted-folder override.
* fix(native-chat): Codex listings never read project config above Orca's folder
With no project-root markers, Codex reads project config only from the listing's
own folder, so a trusted repo that contains Orca's profile can't supply the
account's model list.
* docs(native-chat): say what Codex listing isolation covers, and what managed config can still restore
* feat(settings): add Antigravity IDE to the Open In Apps presets
Launch Google's Antigravity IDE through its bundled `antigravity-ide`
launcher (`agy` is the separate Antigravity CLI agent), and make the
setting searchable by "antigravity".
Supersedes #5636.
Co-authored-by: Anddy Sahir Agudelo <AnddyAgudelo@users.noreply.github.com>
* chore(i18n): add German Antigravity IDE entries for parity
---------
Co-authored-by: Anddy Sahir Agudelo <AnddyAgudelo@users.noreply.github.com>
Fresh desktop profiles start in an ordinary folder workspace at the OS home directory, with the initial repo group named ~. Onboarding Continue saves the selected default agent and launches it in that workspace through the normal activation path. Existing profiles and user-renamed groups remain unchanged.
Independent reviews and prior real Claude/Codex QA are complete. Refreshed-head CI is green, including all unit shards, changed E2E, static/typecheck, packages, persistence and WSL. All 54 focused regression tests passed; three bounded local check timeouts are documented in the PR.
* 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.
* fix(automations): store the approved setup hash and skip setup when a run's branch differs
Saving an automation with setup run records the hash of the content the user
approved; headless, chat and desktop-dispatched runs send it with the create,
so a branch whose setup differs skips setup (logged on the host). Writes that
set the decision without a hash (CLI --setup run) and older automations keep
today's behaviour.
* fix(automations): show on the run when setup was skipped because the branch differs
Runs record setupSkippedReason 'setup-changed' when the new workspace's
orca.yaml setup differs from the hash saved with the automation (headless,
chat and desktop-dispatched runs), and the run page says so with what to do.
The note survives the run's completion update.
* test(automations): pass run history to the setup-skipped run details test
* fix(packaging): require macOS 13.4, which the bundled speech engine needs
* fix(updater): keep the macOS requirement title clear of the close button
* fix(packaging): check out the Darwin floor table for adhoc builds and raise the cask floor
* refactor(updater): reuse the parsed Darwin floor for the macOS requirement
* fix(updater): use a margin to keep the macOS requirement title clear of the close button
* fix(updater): say the macOS requirement comes from Electron and the speech engine
* fix(speech): send the dictation language to Whisper, SenseVoice and OpenAI
The voice language setting was stored but nothing read it, and its 'en' default never came from the user. Replace it with dictationLanguage (default auto), pass it to every engine that accepts one, and add a picker that says when the selected model ignores it.
* test(settings): add dictation language fixtures
* refactor(speech): keep each model's dictation languages on its catalog entry
Language support belongs to the model, not its engine type, so an English-only Whisper build can't be sent another language. Also reuse SettingsRow and cache Intl.DisplayNames.
* fix(speech): let SenseVoice users pick Cantonese
* test(speech): drop type assertions from the new language tests
* fix(clipboard): shrink re-encoded paste images to fit size limits instead of rejecting
Pasted images are re-encoded as PNG before saving, and the size cap was applied to
that output, so a retina screenshot or copied photo that grew during re-encoding
failed with "Clipboard image is too large". Downscale until it fits at every
re-encode site (desktop clipboard image, Windows copied files, renderer conversion).
Fixes#37855
Co-authored-by: Wooseong Kim <innocarpe@gmail.com>
* fix(clipboard): keep image copies full size; only pastes shrink to fit
Copy image shares the PNG conversion with paste. Copies keep rejecting an
oversized PNG instead of silently copying a smaller image, and canvas
downscales use high smoothing quality.
* test(clipboard): match macOS paste tests to downscale-to-fit
Image mocks gain resize; the over-pixel-cap rejection case is removed since
those pastes now downscale (covered by the handler regression test).
* fix(clipboard): let pasted non-PNG images above the pixel cap downscale in the renderer
* test(clipboard): drop web paste test for the removed over-pixel-cap rejection
* fix(clipboard): let pasted PNGs above the pixel cap downscale instead of rejecting
* fix(clipboard): route web PNG pastes through the fit-to-limit converter
---------
Co-authored-by: Wooseong Kim <innocarpe@gmail.com>
* Offer terminal sign-in from native chats and retain refused worker tasks
* Handle sign-in terminal failures and complete failed-start cleanup callers
* Read retry task identity from its dispatch context
* Assert retained transcript identity through the bounded worker read
* Remove obsolete private structured retry teardown assertion
* Use real signed-out Pi and OMP error frames in regression coverage
* Route chat sign-in through explicit host terminal creation
* Keep paired agent launch owner explicit in the tightened terminal contract
* Keep focus baseline pruning limited to the removed reads
* Keep terminal login recovery available for unresolved model notices
* Keep the Grok diagnostic assertion scoped to its refused delivery
* Limit chat sign-in subscriptions to eligible failure rows
* Pass the execution host in terminal creation fixtures
* Scan Pi OAuth failures linearly and verify shared sign-in bounds
* Assert failure metadata and diagnostic routing without translated copy
* Bound diagnostic matcher steps at the Pi failure boundary
* Type the diagnostic matcher receiver explicitly
* Check sign-in button elements with a DOM type guard
* Recognize Pi credential failures and keep one recovery action
* Keep Pi sign-in diagnostics behind failure details
* Verify recovery action changes while refusal wording stays stable
* Use the recorded refusal sentence in the recovery fixture
* Type the recovery fixture as a submission rejection
* Make chat sign-in actions large primary controls
* Reuse primary action styles without duplicate notice subscriptions
* Keep translated primary chat actions within narrow notices
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.
* Upload GitHub images when pasted or dropped
Keep image bytes out of issue and review bodies by uploading from the owning composer and inserting hosted attachment URLs.
Co-authored-by: lydiassssz <60514233+lydiassssz@users.noreply.github.com>
* Preserve GitHub image intake across editor and thread states
* Keep image placeholder boundaries and ordinary HTML paste intact
* Match the native replace overload in the image projection fixture
* Use typed element queries in the image intake fixture
* Narrow textarea elements at the fixture boundary
* Show failed GitHub draft images as filename links
* Keep GitHub draft attachment links current across updates
* Supply the repository address in the attachment preview fixture
---------
Co-authored-by: lydiassssz <60514233+lydiassssz@users.noreply.github.com>
* feat(serve): add bind address option
* fix(serve): validate service bind address
* fix(serve): validate bind flag values
* fix(serve): reject scoped bind addresses
* docs(serve): document bind option paths
* docs(serve): note that --mobile-pairing needs an all-interfaces bind
* fix(serve): move bind address parsing to shared so the packaged CLI can load it
The packaged CLI ships out/cli and out/shared but not out/main/orcad, so
cli/runtime/serve-bind-flag.js failed the packaged runtime-import check
(and would fail to load) when it imported main/orcad/orcad-bind-address.
Move the module, unchanged, to src/shared and point orcad, Electron serve
and the CLI at it.
* test(serve): pin down the default listener and the --bind startup paths
- Extract the Electron serve listener pin into resolveServePinnedBindHost
and test that serve without --bind stays unpinned (all interfaces, as
before), an explicit --bind pins it from either argv form, and the orcad
browser sidecar stays on loopback.
- Golden the orcad and Electron serve argv without --bind against the
pre---bind output.
- Test that the CLI passes bind only when given and refuses --bind with
--mobile-pairing before launching.
- Guard that serve-bind-flag and the shared serve validation load only CLI
and shared modules, which is what the packaged CLI ships.
---------
Co-authored-by: lw <13579959+bertramlongwuyan@user.noreply.gitee.com>
Co-authored-by: Neil <neil@stably.ai>
Co-authored-by: xuanchangfeng <210147232+xuanchangfeng@users.noreply.github.com>
* 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): attach existing worktrees to folder workspaces
worktree set accepts --parent-worktree folder:<id> (hosts advertise
worktree.set.folder-parent.v1), keeping each worktree to one parent in the
store. --parent-worktree active/current resolves to a folder workspace from
its terminal or enclosing directory, and worktree create without --repo under
a folder workspace lists the folder's Git repositories with a ready command.
* fix(cli): report folder parents and keep typed flags in folder repo choices
worktree show/set report a folder parent; the missing-repo error lists --repo
choices to add to the user's command and names the Attached worktrees panel.
Load and import repairs skip malformed rows and only touch imported worktrees,
an import keeps a worktree's existing parent, a twin-owned folder parent warns,
and folder ambiguity keeps its choices.
* fix(persistence): settle one parent per imported worktree before writing
An Orca server migration import now decides each touched worktree's parent
before any write: an explicit folder parent replaces an inferred worktree
parent, any other disagreement refuses the import. --parent-worktree active
in a folder terminal still lists the folder's repositories.
* fix(persistence): let a folder parent beat hydrate's inferred worktree parent
Load repair and import reconciliation share one rule: an inferred worktree parent loses
to a folder parent when the folder was chosen explicitly or the worktree parent carries
hydrate's orchestration stamp. Import tests now run payloads through the manifest parser
and cover a conflict that appears after staging.
* fix(cli): list folder repos when active resolves to the enclosing folder
worktree create without --repo used only the terminal's env to find the folder, so
--parent-worktree active run outside an Orca terminal fell back to the generic
missing-repo error. Use the parent create already resolved.
* fix(hooks): run worktree setup only when its content matches the approved script
Clients may send approvedSetupHash with a create. After git worktree add the
host hashes the created branch's shared setup and default-tab commands with
the prompt's function; on a difference it skips them and returns
setupTrustMismatch with the branch content. Creates without a hash (older
clients, CLI, mobile, orchestration) and older hosts behave as before.
* fix(hooks): send the approved setup hash and re-prompt on a changed branch
The composer and direct work-item creates send the hash of the setup content
the user approved (empty content included; none under repo-wide trust or
local-only policy). When the host reports different branch content, show a
setup-skipped toast and the existing trust prompt with that content.
* test(hooks): hash the prompt's and the host's orca.yaml from the same fixture
Covers CRLF and BOM primary checkouts, the plain-HTTP digest, the hash a
runtime host reports to remote and mobile clients, run-both and local-only
policies, creates without a hash, and pins the digest so a format change is
deliberate.
* style(worktrees): keep the creation flow under the line limit
* fix(plugins): drop a plugin's approval when its catalog moves it to another repository
* plugins: revokePluginConsent refuses when the plugin service is not running
* cli: orca plugin install/update ask again when a plugin moves to another repository, for every plugin kind
* Support Gemini Enterprise authentication for Antigravity Chat
* Keep Antigravity account settings current across clients
* Type the mutable account snapshot fixture
* Keep account settings readable during Antigravity sign-in
* Use standard account form controls and localized placeholders
* Refresh Antigravity account settings after host reconnect
* Annotate the account form fixture without a cast
* Keep Antigravity auth settings at the ACP boundary
* Exercise host settings in the Enterprise sign-in regression
* Save visible Antigravity account settings before sign-in
* Remove obsolete account Apply copy and correct renderer test types
* Put Antigravity sign-in choices before the action
* Keep unchanged account fields editable on blur
* Preserve Antigravity sign-in status beside account choices
* Fix sign-in status predicate type narrowing
* Reject unconfirmed Antigravity account settings
* Show a shared Antigravity account failure once
* Type the Antigravity publication candidate
* Pair Antigravity settings failures with meaningful recovery
* Register account failure copy and refresh the schema catalog
* Keep the latest Antigravity save outcome ahead of delayed reads
* Type the retained account normalization fixture directly
* Keep RPC recovery coverage within its project boundary
* Keep Antigravity settings recovery in the Sign in action
* Keep unavailable Antigravity choices out of the recovery form
* Keep Antigravity settings reads stable across renders
* Retain the ACP task fixture deletion option type
* Hide unavailable Antigravity sign-in choices after a load failure
Orca already drops protected strings when it loads a language pack and keeps
its own English text for trust, permission and destructive dialogs.
validate:plugin and orca plugin validate still rejected the whole pack, so the
community directory refused otherwise-good packs. They now report how many
protected strings are ignored as a warning; every other pack rule stays strict.
* Redesign plugin detail pages with publisher discovery
- Show publisher provenance, related plugins, and clearer source links
- Preserve installed release attribution and README links across catalog changes
- Support navigation between plugin pages while preserving review queues
* Fix publisher link button styling
* Format resolved installer test
* Remove unrelated scratch files