Commit Graph
454 Commits
Author SHA1 Message Date
Brennan Benson 175f10e32b Run local usage services in a supervised backend process (#40792)
* Move local usage services into a supervised backend process

* Own usage recovery and worker retirement lifetimes

* Preserve answered usage data during background refresh

* Run independent usage scans concurrently in the backend

* Limit backend startup paths to the delegated usage service

* Show usage failures through backend recovery

* Include usage failure strings in the English catalog

* Keep usage update details beside refresh failures

* Resolve usage freshness failure from the final producer snapshot

* Retain the forced refresh completion requirement

* Keep managed backend failures out of application crash reports

* Preserve usage recovery boundaries across backend reconnects

* Match the backend RPC cancellation signal in the usage seam proof

* Avoid serializing backend requests before port delivery

* Keep receiving request validation in the boundary schemas

* test: align usage transport coverage with renderer project

* fix: preserve unknown usage state and renderer retirement

* fix(usage): revalidate tracking before scan admission

* fix(usage): retry failed backend startup on demand

* test(usage): isolate stale backend ownership checks
2026-11-11 16:39:52 -08:00
Kelvin Amoaba b72ef23702 fix(native-chat): refuse a startup card choice when the terminal highlight moved, and add the multi-server MCP card (#41216)
* fix(native-chat): refuse a stale startup-dialog card choice and add a card for the multi-server MCP dialog

* fix(native-chat): drop casts from the startup-dialog guard

* fix(native-chat): refuse a startup card choice until the screen model has applied all received output

* fix(native-chat): refuse a clipped MCP server list and guard restored startup cards

* fix(native-chat): allow one startup card choice per screen so a second client's keys can't hit the next dialog
2026-11-12 00:10:05 +00:00
Brennan Benson 576a6c9342 Update browser navigation state without blocking page attachment (#41045)
* Keep browser chrome metadata current without synchronous guest reads

* Provide the guest Escape subscription in browser pane fixtures

* Provide native navigation state in browser guest fixtures

* Preserve browser navigation and loading across asynchronous metadata reads

* Retire browser metadata when its actual guest is removed

* Explain native identity casts in browser disposal fixtures

* Preserve URL titles for untitled client browser pages

* Preserve current browser navigation and untitled page labels

* Keep merged browser failure type within lint limit

* Keep merged browser failure type within lint limit

* Bound published browser titles to the existing wire limit

* Use template literals in browser title regression
2026-11-11 12:38:19 -08:00
Kelvin Amoaba 04537aabc6 fix(ai-vault): Session History Hand Off gives OpenCode sessions their full history (#41236)
OpenCode, OpenCode 2 and Z Code keep every conversation in one shared
database, so Session History could only hand off the row's preview
messages. The session's own host now copies that one conversation into
the per-chat hand-off snapshot the chat Hand Off already uses, and the
dialog points the new agent at it. Older hosts, SSH rows and failed
reads keep the preview.
2026-11-11 20:06:53 +00:00
Kelvin Amoaba 215197f46d fix(editor): live-update tabs reached through a symlinked folder (#41075)
The workspace watch never follows a symlinked folder, so a tab opened
through one (target outside the root) got no change events. Watch each
such tab's folder directly; main checks for the link with an lstat walk
and already rewrites the folder watch's events to the link spelling.
2026-11-11 08:12:54 +00:00
Jinwoo Hong 5e342d7e1a fix(orchestration): a worker report during an Orca restart is not rejected as a changed identity (#41014)
* fix(orchestration): a worker report during an Orca restart is not rejected as a changed identity

* refactor(orchestration): share the startup bounded wait; skip long-poll checks

* chore(orchestration): drop casts in the restart identity wait

* fix(orchestration): only long-poll checks skip the restart identity wait

* fix(orchestration): ordinary mail never waits for the restart identity restore
2026-11-11 00:59:18 -05:00
Neil 45b433671e fix(recipes): show credentials recipe scripts inherit, add opt-in passEnv allowlist (#40757)
* fix(recipes): warn which credentials recipe scripts inherit, add opt-in passEnv allowlist

Recipe lifecycle scripts run locally with Orca's full environment. Keep that
default (provider CLIs and git auth rely on it) but name the credential-like
variables in Settings and let a recipe opt into passEnv so only listed
variables plus a safe base set reach its scripts.

* fix(recipes): fail closed on a malformed passEnv instead of dropping the recipe

Dropping the recipe let a stored snapshot or a same-id plugin recipe run with
the full environment. Keep it with an empty allowlist and a diagnostic, match
base variable names exactly outside Windows, and make the warning copy apply
to any recipe source.

* fix(recipes): import user-agent-platform from its new app-shell location
2026-11-10 17:58:06 -08:00
Brennan BensonandClaude Opus 5.5 8ac9fb6eee Stop re-reading every linked GitHub issue when the window returns (#40534)
* fix(github): stop re-reading every linked issue when the window returns

Each window reveal re-read the linked GitHub issue of every workspace, shown or not,
through the shared 4-slot gh queue, so PR, Checks and Merge waited behind hundreds of
issue reads, and above 500 linked issues the cache thrashed on every reveal.

Surfaces that show an issue now own its freshness: the reveal handler no longer sweeps
the catalog; workspace cards keep their own visible-only issue poll, now also in the
paired web client and for folder workspaces; the space manager's decision hover polls
its one issue while open.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(space): the open space-manager hover asks the main window to refresh its issue

The desktop Space window shows the main window's published issue cache, so a read done
in the Space window was overwritten by the next snapshot. The decision hover now owns
its issue's freshness only while open, follows the live link, and asks the main window
(the cache owner) to re-read it; the web client reads in place.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(space): route the hover's issue refresh by window and follow owner changes

Choose the main-window relay only in the separate Space window (the paired web client's
fallback API made the relay look available), and refresh when the worktree's owning host
changes, not only its link.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* perf(space): key the open hover's issue refresh off the row projection

The hover re-resolved its worktree owner from the store on every Space-window snapshot,
rebuilding the catalog lookup index each time. Use the cache key the row projection
already computes for the issue it shows.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* test(space): use typed fixtures in the hover freshness test

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-11-10 16:46:16 -08:00
Brennan Benson fd20d34362 Save the issue title on GitHub issue links made without one (#40853)
* fix(github): title GitHub issue links that were saved without one

A workspace linked to a GitHub issue from the CLI (`--issue N`), a phone, the paired web
client or a typed number kept only the number, so search could match its issue title only
while a client happened to hold that issue. The host that owns the workspace now reads each
newly linked untitled issue once, after the write returns, the way its card would, and saves
the title on the link. It keeps the current review selection and never resurrects a removed link.

* fix(github): bound and publish issue-title fills like other metadata writes

Skip writes that leave links untouched before normalizing, check a queued link is still wanted
before reading it, publish filled titles once per repo per 2 s window, refresh the resolved rows
remote clients read, never wake a stopped WSL distro for a background read, keep a selected
review's PR suppression, and keep a failing write observer from failing a saved write.

* fix(github): keep link-title fills off unrelated writes and out of per-title refreshes

Ignore linkedWorkItem in the unchanged-links check (it is rebuilt on every write and always
titled), publish filled titles at once on a quiet host and at most once per repo per 30 s
during a burst on a timer, and only lift a PR suppression when the selected PR changes, so
restating the current PR (as a title fill does) keeps a hidden branch PR hidden.

* perf(github): publish filled link titles as addressed workspace metadata

Instead of a whole-repo worktreesChanged refresh per burst window, the host publishes one
addressed workspaceMetadata set record {hostId, epoch, seq, id, row} for the workspace whose
link was titled: to its desktop window over IPC and to paired clients as a runtime client event.
Clients patch that one row (Cmd-J reads it) and drop replayed records; older clients ignore the
new event and see the title on their next listing.

* fix(github): agreed workspaceMetadata record shape, per-id seqs and late-listing refill

Records name their kind and domain, clients keep the last applied seq per workspace for each
producer epoch (dropped when the epoch changes), and a listing that read metadata before the
title write cannot drop the title: recorded titles refill rows that still hold the same untitled
link for 60 s, never restoring a removed link. The IPC channel is marked as interim transport.

* fix(github): title-only, host-addressed application of workspaceMetadata records

Clients fill the record's issue titles into the named row's still-untitled links and never take
its links over the row's current ones, so a record that arrives after an unlink cannot restore
the issue. A paired host's records reach the row of their own execution host (an SSH twin is a
different row), and go through the bridge's connection-generation check like other host events.

* test(github): type the metadata-record test helper as AppState

* fix(github): keep recorded titles until expiry, refill only changed repos, retire with the pairing

A record that beats the listing adding its link keeps its titles for the full window; the refill
runs only for repos whose listing changed, indexes that repo's rows once and skips rows already
checked; per-workspace seqs live in the expiring entries; a re-pair or reconnect of the producing
environment retires its titles.

* fix(github): read the listing through the workspace record selector

* fix(github): keep link titles until their producer changes; fall back to a listing on eviction

Recorded issue titles no longer expire on a timer (a slow listing could outlast it): they live until
the producer's epoch, pairing or connection changes, refilling only present untitled links. When
more than the cap are in flight the oldest is evicted and its repo is listed afresh, which carries
the saved title. The refill runs in updater form so it never merges a stale store snapshot.

* fix(github): sequenced listings replace the retained-title refill

Per the re-plan ruling: the store keeps one workspaceMetadata cursor per process; a title record takes
the next seq in the same turn as its write, and each listing records the cursor when it reads metadata.
Clients note each record's seq per producer and repo; a listing at or past it ends the obligation, an
older one keeps titles the rows already show and lists the repo afresh (a new invocation, never joining
the stale one), a record whose row or link isn't listed yet asks for a fresh listing, and a seq gap
lists every repo of that producer. The retained-title table, its watcher, TTL and cap are gone.

* test(github): stamp capture, gap, restart and recovery coverage for workspace metadata listings

Also retire an environment's earlier coverage bookkeeping when it is re-paired or reconnects.

* fix(space): keep issue-title coverage per repo and host until a newer listing lands

Coverage now keeps each projection's newest record seq after its covering listing settles, so an older listing that finishes later still keeps shown titles. Projections are keyed by repo and host, paired producers keep their stream cursor, retired pairings and unlisted projections are dropped, every listing served by this host carries its metadata stamp, and a record with any unlisted titled link asks for a listing.


* fix(space): re-list after any stale listing and drop retired metadata producers

A stale listing now always asks for one fresh listing, so a link it dropped comes back and a failed scan can no longer end the wait. Removed environments drop their producer state, the producer's repo list is built only on a gap or a first-seen projection, and recovery listings skip the lineage refresh.


* perf(space): filter current rows only for listings that keep titles


* fix(space): forget removed repos on a metadata gap; keep titles for another producer run's listing


* fix(palette): search a linked issue by its saved title before its card renders

The issue's search entry took its title only from the issue cache, which fills when the card renders, and it shadowed the saved link's titled entry under the same id. It now falls back to the link's saved title.


* perf(palette): read a row's links once per palette document


* fix(palette): give each saved link its own search unit

Saved-link evidence was meant to carry an `attachment:` id but the override set an unused field, so a link shared the `issue:N` unit id with the legacy issue field and with same-number links from other repos, and only the first (often untitled) survived. The id now applies to the unit and its fields, so every saved link's title is searchable. Replaces the cache-title fallback.


* fix(space): keep the title queue draining past a failed link; encode environment ids in coverage keys


---------
2026-11-10 16:38:44 -08:00
Neil 08bb458da4 Remove the per-repo agent folder trust prompt (#40934)
The first-launch 'Trust <repo> for agents?' modal blocked agent launches
to ask a question that offered no real protection: undecided repos, and
every unattended launch, already pre-trusted. Drop the dialog, the launch
hold and the skipAgentWorkspaceTrust plumbing. Repos default to
pre-trust; the per-repo setting remains as an opt-out.
2026-11-11 00:34:24 +00:00
Jinjing 3e86c5ac73 fix(i18n): translate sparse preset validation failures (#40277)
* fix(i18n): translate sparse preset validation failures

* fix: classify sparse directory validation by type
2026-11-09 15:12:50 -08:00
Kelvin Amoaba 98bc1acf16 feat(dictation): pulse three dots in the pill while spoken words are still on their way (#40443)
* feat(dictation): show pending dots while the engine holds speech it hasn't committed

The VAD pause segmenter already knows when a phrase is open. The recognizer
now posts speech-pending when a phrase opens and clears it once that phrase's
text is posted, and the desktop pill shows three dots after the text while it
is on. Only pause-cut on-device streams send it, so cloud, the paired web
client and phones keep today's pill.

* fix(speech): keep a lone click from turning the pending dots on
2026-11-09 20:14:48 +00:00
Jinjing 8ef4680142 Improve project group ordering across hosts (#39816)
* Improve project group ordering across hosts

* fix(project-groups): restore each group order after a failed drop

A deleted group made the aggregate rollback stop early, leaving other groups at
the ranks from the failed drop. Restore each group independently and log the
swallowed single-update failures again.

* refactor(main): give project group RPC methods their own module

Keeps repo.ts under the 300-line cap as the project group surface grows.
2026-11-09 11:40:51 -08:00
Kelvin Amoaba 58733086c9 feat(speech): spell dictated code terms with a personal and per-repo dictionary (#40450)
* feat(speech): spell dictionary terms in dictation, with repo terms and Whisper prompt

* test(speech): pin host repo terms for phone dictation

* fix(speech): keep symbol terms whole, accept null setup params, resolve SSH repos

* refactor(speech): key transcript words once and type phone terms at the reply schema

* refactor(speech): merge speech-types imports in the speech IPC
2026-11-09 19:22:46 +00:00
Kelvin Amoaba 9a45e8bdfb feat(speech): dictate through any OpenAI-compatible transcription service (#40160)
Add an OpenAI-compatible model whose base URL, model and optional key
are set up in a Voice settings dialog and sealed together in ~/.orca,
so a key is never sent to a host it wasn't saved for. It reaches
OpenAI, Groq, Mistral and local servers, and replaces the unused
userModels setting.
2026-11-09 16:57:24 +00:00
Neil 9a21a515d3 fix(vm-recipes): hide an in-progress VM checkout in every window, the phone and the jump palette (#40373)
* fix(vm-recipes): hide an in-progress VM checkout in every window, the phone and the jump palette

A VM create's checkout was hidden only in the sidebar of the window that
started the create. Windows now publish their pending VM checkouts to the
main process, which shares every window's set with all windows and
filters the paired phone's host workspace list and workspace context.
The jump palette and the sidebar read the shared set, and a grouped VM
project no longer draws an empty-project placeholder while its create
is pending. A window's set is dropped when it closes, crashes or reloads.

* test(project-groups): reset leaked runtime environment revisions between files
2026-11-09 06:51:21 -08:00
Neil 6c951c0a9a feat(vm-recipes): show VM lifecycle states in the sidebar, with Suspend/Resume VM (#40046)
* feat(vm-recipes): show VM lifecycle states in the sidebar, with Suspend/Resume VM

* fix(vm-recipes): Suspend VM suspends an idle parent with active children; dedupe repeat suspends

- With active children, Suspend VM sleeps this workspace too, so its VM
  suspends once their sessions stop.
- A repeat Suspend VM while one is pending is dropped in the coordinator
  instead of trusting a possibly stale suspended record.
- After a direct suspend, re-probe the host so its header reads Suspended.
2026-11-09 05:54:03 -08:00
Kelvin Amoaba c323f876b8 feat(dictation): dictate into the focused field of a page in the browser pane (#40191)
* feat(dictation): dictate into the focused field of a page in the browser pane

* test(dictation): cover dictation into a focused browser page

* fix(dictation): type page dictation through the frame that owns the field

* fix(dictation): space page rich-text dictation within its block and offer editors the beforeinput

* test(dictation): simplify the page insertion test mock

* fix(dictation): copy page dictation to the clipboard when an editor blocks the input
2026-11-09 08:52:28 +00:00
Neil 920a83889c refactor: move browser contracts and publication to defining owners (#40209)
* refactor: move atomic publication to its filesystem owner

* refactor: group shared browser definitions by feature

* refactor: bind proxy process and session contracts to defining owners

* refactor: consolidate direct session contract type imports

* refactor: retain atomic publication test timing under moved owner

* fix(benchmark): resolve historical tunnel framing source before loading
2026-11-09 00:45:45 -08:00
Kelvin Amoaba 80deb09aa9 feat(dictation): fixed-size dictation pill with cancel, timer and finish (#40155)
* feat(dictation): fixed-size dictation pill with cancel, timer and finish

* fix(dictation): abort a cloud upload cancelled while finishing; time recorded audio

* refactor(dictation): reuse Esc/IME helpers, keep the pane on the target, isolate meter renders

* fix(dictation): pause the pill timer while hidden; compile under React Compiler
2026-11-09 07:04:54 +00:00
Kelvin Amoaba b41ab35b72 fix(clone): let a clone use its own folder when the repo's name is taken (#40066)
Cloning always went into <parent>/<repo name>, so a same-named repository
from another owner (or any non-empty folder of that name) could not be
cloned into the same parent. Before cloning, Orca now checks the target
on the clone host. If it is taken, the clone forms show a Folder name
field with a free suggestion (<repo>-<owner>, then <repo>-2...) and clone
into that instead. RPC repo.clone and `orca project setup-clone` accept
the folder name too.
2026-11-09 03:14:06 +00:00
Jinjing 74106a0dbc Add protected telemetry milestones (#40043)
* Add protected telemetry milestones

* Fix milestone provenance and acceptance

* Fix telemetry test lint suppression

* Relay remote accepted chat prompts to desktop telemetry

* Honor paired desktop consent when chat host runs telemetry

* Fix mobile typecheck for notification retry timer
2026-11-08 16:58:21 -08:00
Neil 0b1efd9b90 fix(vm-recipes): record each VM before create runs so a quit or crash can't leak it (#39241)
* fix(vm-recipes): record each VM before create runs so a quit or crash can't leak it

Orca now saves a "provisioning" record for a recipe VM before running its
create command, and updates it when create finishes. If Orca quits or
crashes mid-create, or create fails without returning a result, the VM
stays listed in Settings > Cloud VMs as Failed with its instance id, so the
user can run Cleanup (destroy gets the instance id) or remove it from the
list. On startup, provisioning records with no create running are marked
as interrupted.

Records without a recipe result live in a separate file that older builds
never read, so downgrading keeps working. Records also store the repo path
so Cleanup, Copy command, suspend and resume still work after the repo is
removed or re-added under a new id (#17293).

* fix(vm-recipes): keep no runtime row when create is cancelled or rejected before its script starts

* fix(vm-recipes): harden write-ahead records from review

- Never overwrite a provisioning-records file this build cannot read; refuse new records instead.
- Remove the provisioning-records file when empty instead of rewriting it on every store write.
- Only fall back to the saved repo path while it still defines the same recipe.
- Don't let Cleanup or Attach act on a create that is still running or never returned a result.
- Show Provisioning before Cleanup disabled; stamp running records with the save time.

* fix(vm-recipes): keep new store code off direct sync fs calls and render-path translate

* fix(vm-recipes): detect a missing provisioning-records file without a type assertion
2026-11-08 16:31:49 -08:00
Kelvin Amoaba 6c31d92522 fix(native-chat): keep links when pasting rich text into the composer (#39213)
Rich-text pastes now keep their hyperlinks as Markdown links, and pasting a
URL over selected words links them, as other message editors do.

Fixes #39153
Fixes #39150
2026-11-08 23:38:26 +00:00
Neil 0d1220c31f Organize SSH and managed-server modules by concrete owner (#39937)
* refactor(main): organize SSH transport and managed server owners

* Remove orphan deploy readiness fixture and shrink test baselines

* refactor(wsl): import lock records from their defining owner

* refactor(wsl): use the canonical deferred update contract
2026-11-08 12:20:22 -08:00
Brennan Benson 252b427dfd Display private GitHub attachment images in fetched items (#39250)
* Display private GitHub attachment images in fetched items

* Keep attachment event identity aligned with committed renders

* Complete GitHub image response type contracts

* Return private image sources with accepted issue edits

* Keep issue edits successful without optional display data

* Cover unusable issue edit image responses

* Keep optional GitHub image parsing from failing accepted saves

* Fetch GitHub body images with the existing body read
2026-11-08 11:58:15 -08:00
Kelvin Amoaba 9abd095d08 feat(agents): send paired phones the desktop Agents list's verdict per agent (#39243)
While a phone is paired, the desktop window publishes its own Agents-list
classification for each pane (listed, status, unread, clearable, title,
worker parent) and worktree.ps attaches it to each agent row, so the phone
lists agents exactly as the sidebar does. agentActivity.setRead/clear apply
through the sidebar's own Activity actions and resolve once the new
verdicts are stored.
2026-11-08 19:30:27 +00:00
Neil c10881fa8c refactor(shared): organize execution and automation defining owners (#39776)
* refactor: import journal queue state from defining owners

* refactor(shared): organize execution and automation owners

* fix: retain type imports used by surviving test cases

* refactor: finish work item owners and refresh validation metadata

* test: infer reconnect status fixture type

* test(claude): resolve source files beside same-named directories

* test(runtime): group repository creation mock registrations
2026-11-08 06:46:09 -08:00
ae6675d67c fix(window): cancelling a quit prompt clears the quit latch so later closes aren't treated as quits (#39597)
* fix(window): cancelling a quit prompt now cancels the quit

Renderer-owned quit prompts (running-work "Close Window?", Save dialog,
close-guard and shutdown-checkpoint vetoes) never reach
will-prevent-unload, so main kept isQuitting latched after Cancel. A later
plain close was treated as a quit and renderer crashes were not recovered.

Add a window:close-cancelled IPC (preload ui.cancelWindowClose; no-op in the
web client) that the renderer sends on each cancel path. Main accepts it only
from its own renderer, only while quitting, and only once that renderer has
acknowledged the newest close request, then runs the same abort path as a
beforeunload veto.

Fixes #39596

Co-authored-by: Neil <neil@stably.ai>

* test(window): expose webContents.send on the renderer recovery harness type

---------

Co-authored-by: tomarai85 <tomarai85@users.noreply.github.com>
Co-authored-by: Neil <neil@stably.ai>
2026-11-08 06:17:53 -08:00
Brennan Benson d857e09615 Git Graph: commit actions (checkout, cherry-pick, revert) (#39069)
Adds commit actions to the Git Graph: check out a commit or branch, cherry-pick, revert, and create a branch from a commit. Conflicts hand off to Source Control with continue and abort. Works on local, WSL and remote hosts.
2026-11-08 03:43:30 -08:00
Jinjing 5e496520c5 Allow interleaving projects and groups in the sidebar (#39768)
* Allow interleaving projects and groups in sidebar

* Fix drag refs during render
2026-11-08 00:16:23 -08:00
Neil 83f8e6340d refactor(shared): organize file operations and storage owners (#39746) 2026-11-07 22:58:35 -08:00
fb313c4745 perf(terminal): park and defer restored plain-shell tabs whose sessions no longer exist (#39725)
Restored tabs whose local daemon session is gone no longer mount and
respawn a shell at startup; they park and respawn on open.

- Separate pty:getGoneSessionIds probe, started unawaited at startup so
  it never gates reconnect; the authoritative-capability call is unchanged.
- Binding a PTY id (spawn, cold restore, attach) clears its gone mark so a
  revived pane gets parked exit/byte watchers again; late answers for bound
  ids are ignored.
- Agent tabs (launchAgent or sleeping record) and mixed live/gone splits
  keep the mounted restore path.

Replaces #37410.

Co-authored-by: m4air <m4air@Mac.localdomain>
Co-authored-by: thefrikidude <100475418+thefrikidude@users.noreply.github.com>
2026-11-07 21:11:15 -08:00
Neil 009def47a6 feat(app-icon): import a custom app icon from Appearance settings (#39443)
Adds a Custom… option to the App Icon switcher. The picked PNG/JPEG is
header-checked before decoding (square, 64–2048 px, ≤10 MB), re-encoded as
PNG into userData, and applied to the macOS Dock and every window icon. It
persists across restarts and can be removed, which resets to Classic.

Closes #13316
2026-11-07 18:22:37 -08:00
Neil b0dd73e7aa feat(plugins): play a looping video behind app themes (#39450)
App themes may set background.videoPath: a muted, looping MP4 or WebM (H.264
recommended, VP9 or AV1; one video track, no audio; <= 60 s, 8 MB, 2.3 MP)
played over the required static background.path. Main checks it from container
headers without decoding and serves it on the orca-plugin-image slot
theme-<i>-video; images stay static-only. The renderer reveals it on its first
frame, keeps the still on any failure or a decoded size past the limits, plays
it only while main reports the window focused, shown and not minimized and no
dialog is open, and omits it under prefers-reduced-motion.

Animated WebP was measured and declined: software-decoded every frame, it cost
11-23 points of CPU vs 3-10 for the same loop as video.

Implements #38363 proposed by @dohaidang.
2026-11-07 03:32:14 -08:00
Neil ee04233cf2 feat(pairing): show which devices still sign in with their original pairing code (#38949)
* feat(pairing): show which devices still sign in with their original pairing code

Settings lists paired phones and access links that signed in with a pairing
code but never exchanged it (apps from before single-use pairing) with
"Uses an older pairing method. Update ...". Display only; rows from older
builds are never flagged.

* fix(pairing): no update hint for links used by plain-http browsers or still pairing

A link that served a session-only browser key is marked, so the hint stays
off after a host restart drops those keys. Codes still inside the pairing
window are not flagged either: an updated client exchanges within moments.
2026-11-07 02:17:33 -08:00
Jinwoo Hong 228263d566 feat(automations): launch desktop runs from main, not the renderer window (#39247)
Desktop automation runs now launch from the main process through the same launcher orca serve uses. The renderer dispatch path is deleted.
2026-11-07 03:04:56 -05:00
Kelvin Amoaba d3a24e358b perf(dictation): send desktop dictation audio straight to the speech process instead of through main (#39346)
* 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
2026-11-07 07:38:20 +00:00
193dd5726f Git Graph tab: all branches, commit details and file changes (#6746)
* 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>
2026-11-06 19:59:47 -08:00
Brennan Benson f308486115 Show Source Control for folder workspaces (#38831)
* 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
2026-11-06 18:25:57 -08:00
Neil 89fb7c8abe feat(settings): show which paired devices still sign in with older encryption (#39222)
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.
2026-11-06 17:06:25 -08:00
Neil 090945007b Ask once per repo before pre-trusting agent folders (#38946)
* 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
2026-11-06 17:02:46 -08:00
Neil 93589dc6f8 fix(shell): open linked scripts in the editor and reveal programs instead of launching them (#37930)
* 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.
2026-11-06 16:25:12 -08:00
Neil bb5f37c916 feat(pairing): unscanned desktop pairing codes expire (QR 10 min, link 60 min) (#37957)
* feat(pairing): unscanned desktop pairing offers expire (QR 10 min, link 60 min)

Headless serve/orcad mint offers without a TTL, so they never expire.
Paired rows and rows without offerExpiresAt are unaffected.

* feat(pairing): refresh the mobile QR before it expires and show runtime link expiry

The QR re-mints a minute before expiry while visible; an unused runtime
link shows its expiry and, once expired, is replaced by a notice. Web
clients rejected while pairing now suggest asking for a new link.

* feat(mobile): say a refused pairing code expired or was replaced

A first pairing the desktop refuses (direct or Relay) now asks for a new
code instead of showing the raw transport error.

* refactor(pairing): replace type assertions in code-refusal detection and tests

* test(mobile): re-record pairing race golden for refused-code error

* fix(pairing): keep pairing codes valid across sleep and stale registry writers

- QR refresh rechecks the wall clock every 15 s and on window focus, so a
  computer waking from sleep never keeps showing an expired code.
- A registry save from a process with an older copy can no longer reset a
  paired row back to unscanned (which would let its offer expire).
- The QR caption says how long a copied code works.

* test(pairing): annotate the IPC handler stub cast
2026-11-06 16:23:44 -08:00
Kelvin Amoaba 768df33d84 fix(dictation): handle cloud dictation's 10-minute cap, stalled uploads and bad API keys (#39114)
* fix(dictation): bound cloud uploads, stop at the 10-minute cap, check the OpenAI key on save

* refactor(dictation): share the OpenAI error reader
2026-11-07 00:22:24 +00:00
Jinwoo Hong 555bd481bc Let multi-repo folder workspaces parent and nest workspaces, including on other machines (#38904)
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.
2026-11-06 19:03:51 -05:00
Neil 2137a614d9 feat(browser): show when an SSH host is controlling Orca browser tabs, and let people stop it (#39212) 2026-11-06 15:49:24 -08:00
Neil 490a5c05b8 fix(hooks): run a created worktree's setup once its own content is approved (#37978)
* 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
2026-11-06 15:37:32 -08:00
NeilandFlorin Timbuc 239e17f84f feat(cli): add orca host rm, and clean up after every SSH target removal in main (#38774)
* feat(cli): add orca host rm for saved SSH targets

orca host rm --host ssh:<target-id> calls the runtime RPC ssh.removeTarget,
the same removeSshTarget the Settings UI reaches, so the tombstone keeps the
passive ~/.ssh/config import from adding the alias back.

Also document how the import treats Match blocks and Include files.

* fix(ssh): clear a removed target's live state and rename in main for every removal path

Removing an SSH target through ssh.removeTarget (orca host rm, paired clients)
left open tabs with stale reconnect state and kept the host's rename, because
only the Settings dialogs cleaned up after their own IPC call. The shared
removal mutation now drops the display-label override and tells the renderer,
which clears the target's state once for all callers.

Fixes #37949

* fix(ssh): clean up after a removed target in the teardown every removal ends in

A recipe VM's cleanup removes its SSH target through removeRegisteredSshTarget
directly, so the window kept that target's live state. The broadcast and the
rename drop now run there, after the target is gone.

Fixes #37949

* test(cli): keep host rm's retarget-flag refusal next to its other cases

Its old file was removed on main.

Fixes #37949

* refactor(cli): give the host, managed-server and VM handler groups their own module

Keeps the handler manifest under the file-length limit.

Fixes #37949

---------

Co-authored-by: Florin Timbuc <florin@sowild.design>
2026-11-06 06:23:46 -08:00
3704d01615 fix(terminal): a host with a window keeps a pane a paired client moved to a new tab (#38840)
* fix(terminal): a host with a window keeps a pane a client moved to a new tab

Dragging a split pane onto the tab strip in a paired worktree whose host runs
the desktop app with a window flickered and snapped back. The client draws the
new tab and calls session.tabs.movePaneToNewTab, but the host answered
not_held whenever it had a window, because the window owns the layout and only
headless hosts committed the move. The client reverted, and even without the
revert the host window's next snapshot would have rebuilt the old tab.

Main now asks its window to make the move and waits for the answer, the way
other client requests reach a window-owned host. The window moves the pane
with its own drag-out path under the tab id the client already drew, at the
client's drop position, mounting a parked source tab first. A pane the window
cannot reach is refused, so the client puts it back; a host without the
window bridge still answers not_held. Older clients and hosts are unaffected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(web): rely on the optional pane-move bridge instead of no-op stubs

The web client never receives host pane-move requests, and the bridge
already skips registration when these members are absent; dropping the
stubs keeps web-ui-api.ts under the line limit after rebasing on main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(e2e): a window host keeps a pane a paired client moved to a new tab

The window-host case asserted the old refusal, where the client drew the new
tab and put the pane back. It now drags a pane out on a paired client and
checks the pane stays in the tab the client drew: the host window holds the
leaf alone under that tab id, no snapshot pulls it back, and the source keeps
one pane. It fails on main, where the tab count falls back after the drop.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(terminal): refuse a pane move the host window cannot answer

When a paired client asks a host with a window to move a pane to a new
tab, the host relays the request to that window. If the window is gone,
closes, or does not reply within 10 seconds, the relay rejects. The
host returned that rejection as an RPC error instead of a move result.

The host now answers { status: 'refused', reason: 'pane_unavailable' }
in that case. The client already puts the pane back on a refusal, and
this is the reply the relay timeout was sized for.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(terminal): fence a window's pane move once the client heard it timed out

The relay told the client "refused" after 10s but left the window's move
running, so a late commit moved the pane on the host after the client had
put it back. Main now fences the commit: a move the client was told timed
out commits nothing, and a commit answers the client at once, before the
deadline can turn it into a refusal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(terminal): keep the pane-move fence Electron-free and let a claimed commit answer

The leaf-move IPC is shared with the plain-Node runtime, so the fence moves to its own
Electron-free module. A window that goes away mid-commit no longer refuses a move its commit
landed, and a second move cannot replace a fence a timed-out move still holds.

Fixes #37082

* chore(terminal): follow moved shared modules after rebasing the pane-move relay

Refs #37082

* fix(terminal): hold a client's pane move's source tab mounted until the move settles

The timed split-mount lease could expire while the window was still moving the
pane, so cold parking could unmount the source tab after main had already told
the client the move landed. The move now holds the tab mounted for its whole
duration and releases it when it settles.

Fixes #37082

* fix(terminal): don't toast the host window for a pane move another client asked for

Fixes #37082

* fix(terminal): relay a client's pane move only to a window that owns the layout

Where main owns the layout (the dark ORCA_LAYOUT_OWNER=main path), the window's
move goes back through main, which names its own tab, so the client's tab id
could not be kept. Main answers not_held there, as before this change.

Fixes #37082

* fix(terminal): name the renderer a relayed pane move belongs to

Fixes #37082

---------

Co-authored-by: Seunghwan <hyunsh.cycle@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 05:30:17 -08:00