Commit Graph
2665 Commits
Author SHA1 Message Date
Brennan Benson 8d908764dc Revert "Keep same-machine folders with matching Git remotes in separate projects (#32272)" (#34662)
* Revert "Keep same-machine folders with matching Git remotes in separate projects (#32272)"

This reverts commit 29a5f89b8a.

On a machine with several clones of one repo, #32272 moved the oldest clone
into the shared cross-host project. Shared Windows QA machines have several
orca clones, so everyone's sidebar "orca" project suddenly gained that
machine's main checkout and its worktrees. Revert until the intended
grouping is decided.

Conflicts resolved against later main: Settings files moved into
settings/repository (imports adjusted), and two test files later deleted on
main stay deleted.

* Use checked types in restored test fixtures

* Combine restored fixture type imports
2026-10-27 20:38:35 -07:00
OrcaWinandOrcaWin 0323d28498 test: remove 49 low-value test cases (#34678)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-27 20:13:46 -07:00
ef6e661449 fix(runtime): accept Muse's composer when the voice-input label is absent (#25695)
* fix(runtime): accept Muse's composer when the voice-input label is absent

Muse labels its composer's top rule "Voice input" only where voice is
available, so on Linux the readiness predicate never matched and
worker-start timed out at agent_readiness. Keep the voice-label match and
also accept the composer itself: a rule (two or more rule chars) directly
before the prompt glyph after the latest Muse banner, so a shell prompt like
'~/app ❯' or '╰─❯' under a printed banner is still not ready.

Fixes #25005

Co-authored-by: j33pia <242526320+j33pia@users.noreply.github.com>
Co-authored-by: Nawapat Buakoet <nawapat.b@covest.finance>

* fix(runtime): escape Muse composer regex so the orcad bundle stays ASCII

---------

Co-authored-by: Neil <neil@stably.ai>
Co-authored-by: j33pia <242526320+j33pia@users.noreply.github.com>
Co-authored-by: Nawapat Buakoet <nawapat.b@covest.finance>
2026-10-27 19:12:13 -07:00
Neil 3929371709 fix(wsl): background repo probes, PR refresh and worktree scans never start a stopped distro (E9) (#34632)
* fix(wsl): background repo probes, PR refresh and worktree scans never start a stopped distro (E9)

Any Windows touch of a \\wsl.localhost path, or Git/gh run for it, boots a
stopped distro. These background paths ran without asking the stopped-distro
gate: the worktree scan refresh (Git-admin fingerprint reads plus git worktree
list), the remote-identity and username enrichment sweeps, the startup
upstream backfill, the quiet-period ref maintenance, and queued PR refreshes.
Each now asks blockedWslDistroForPath first and skips, keeping known data,
until the distro runs; a host path never asks the gate.

* fix(wsl): a user act's wake allowance ends with the act

The allowance lived in an AsyncLocalStorage store, so any timer, listener or
queue created during a user act (opening a workspace, a wake read, the first
git status) kept it forever and could start the distro later, after a
terminate. The scope now carries an active flag cleared when the act settles.

* fix(wsl): skill discovery and automatic refreshPRNow never start a stopped distro (E9)

- Skill discovery (mounted by passive UI) leaves out roots on a stopped or
  unverifiable distro, and skips plugin discovery for such a cwd, instead of
  reading \\wsl.localhost; they are not reported missing, so the next scan
  picks them up.
- refreshPRNow skips automatic reasons (visible, active, post-push) on a
  blocked distro like the drainer does; only a manual refresh may start it.
- Ref maintenance's opt-out probe takes the same sync wslDistroForPath
  early-out as the other paths.
- Tests: stopped-with-no-cache worktree scans answer from stored rows, and
  'unverifiable' is treated as blocked.

* fix(wsl): skill discovery for a WSL target never starts a stopped distro (E9)

Passive UI discovers skills for projects whose agents run in WSL. The target
resolution probed the distro home (wsl.exe) and the scan ran in the distro,
so either booted a stopped one. Discovery now refuses a stopped or
unverifiable WSL target before the home probe and answers no skills and no
sources, so nothing reads as missing and the next scan retries. Delete and
share keep resolving without the gate.

* fix(wsl): a settled user act marks its distro woken; host skill scans never await the gate

- runAsWslUserAct (the gate's wrapper) notes the distro woken inside the act
  when it settles, success or throw, so a follow-up is never answered by a
  'stopped' read taken before the act.
- Skill discovery takes sync early-outs: a host-only root set and a host cwd
  never await the WSL gate.

* fix(wsl): Share discovers as a user act; a stopped distro's skills read as unknown, not absent

- skills:prepareShare (and the skills.share RPC) discover as a user act on the
  target's distro, so an explicit Share on a stopped distro finds its skills.
- A blocked WSL target answers an 'unavailable' source per requested kind, and
  roots (or a workspace plugin folder) on a stopped distro are reported
  'unavailable' instead of dropped, so the installed-skill verdict says the
  status may be incomplete rather than offering Install.

* test(wsl): use a fixed fetchedAt in the stopped-distro PR refresh test

* fix(wsl): Share scans stopped distros' skill folders, with or without a target

The desktop Share dialog sends no target, so the user-act scope had no distro
and the gated scan reported a stopped distro's repo folders unavailable; Share
then failed with skill-share-source-not-found. discoverSkills takes
gateStoppedWslDistros: false for both share paths (IPC and RPC), skipping the
stopped-root split and the plugin-cwd check; passive discovery keeps the gate.
Gated and ungated scans of one target no longer share a coalesced answer.
2026-10-27 18:56:02 -07:00
Younghwan ChaeandNeil e8d57136a2 fix(runtime): re-list a repo's worktrees when repo.update changes visibility (#32783)
* fix(runtime): re-list a repo's worktrees when repo.update changes visibility

`orca repo set --external-worktree-visibility show` (and any other repo.update
client) only emitted repos:changed, so the host renderer kept stale
`visible: false` worktree rows and the newly shown worktree vanished from the
sidebar. The settings controller now also emits worktrees:changed for the repo
when a visibility input changes, without invalidating cached Git scans.

Fixes #32764

Co-authored-by: Younghwan Chae <chyohw97@gmail.com>

* fix(runtime): declare notifyWorktreesChanged on RuntimeConstructionServices

The repository settings controller is built in OrcaRuntimeWithPreservedBranchCleanup,
but notifyWorktreesChanged is implemented further down the chain in
OrcaRuntimeWithGetStatus, so the base class needs the abstract member to typecheck.

---------

Co-authored-by: Neil <neil@stably.ai>
2026-10-27 18:30:12 -07:00
YEONGUK LEEandOrcaWin 7efd2c4b7a fix(i18n): localize desktop Setup terminal titles (#28602)
* fix(i18n): localize desktop setup terminal titles

* test(runtime): declare orcad's English Setup terminal title in composition parity

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-27 17:51:39 -07:00
Neil ea532813b1 refactor: group native chat turn presentation (#34629) 2026-10-27 17:33:03 -07:00
Pandu dwi PutraandBrennan Benson ec49e9fac0 fix(native-chat): list OMP skills in the / menu as /skill:<name> (#31999)
* fix(native-chat): list OMP skills in the / menu as /skill:<name>

* test(native-chat): OMP now carries the Pi-shaped skill profile

* Publish native OMP skills with compatible command catalogs

* Index native skill descriptions once per picker build

* Combine catalog test composer imports

* Correct native catalog test types and project inputs

* Run cross-platform picker scaling contract with mobile tests

* fix: discover native commands before the first chat message

* test: isolate native command discovery from mobile typechecking

* fix: release command discovery ownership after managed root exit

* refactor: reuse the ACP child owner during shutdown drain

* test(acp): load filesystem implementation before test execution

* test: drive discovery close completion explicitly

* test: await discovery admission cleanup between demand waves

* test: control discovery close ordering in demand waves

---------

Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
2026-10-27 17:14:20 -07:00
Neil 90c713af1f fix(wsl): refuse Windows drive folders on a WSL host, offer Add on this computer (#34530)
* fix(wsl): refuse Windows drive folders on a WSL host and offer to add them on this computer

A /mnt/<drive> project on a WSL host shows false line-ending changes, misses
edits made from Windows, and searches slowly. Add Project refuses typed,
picked and dropped Windows drive folders for a WSL host and shows the refusal
inline with Add on this computer, which switches the open dialog to this
computer. The WSL-host orcad re-checks by /proc/self/mountinfo (after
resolving symlinks) in repo.add, repo.create, repo.clone, explicit
folderWorkspace.create and the nested scan/import, answering with the
existing runtime_error code plus optional data, so older clients still show a
readable message. Everything is behind the WSL hosts gate; SSH is untouched.

* test(wsl): hold the e2e's this-computer add instead of racing its failure

On the Linux CI host the switched local add of a Windows path fails its nested
scan and hands off to the Open as Folder modal, closing Add Project before the
test looked. The refusal itself already came before any scan. The e2e now
records the local add at its scan step and asserts it went to this computer
with the Windows path; a unit test pins that a refused pick never reaches the
host path step.

* test(wsl): type the e2e's held-scan window hooks instead of Reflect
2026-10-27 16:29:54 -07:00
Neil f3adb8f85a refactor: group native chat work-run boundaries (#34528) 2026-10-27 15:18:29 -07:00
Kelvin Amoaba 917ef9c737 feat(browser): install Chrome extensions from the Web Store into Orca's browser (#30940) 2026-10-27 22:04:12 +00:00
OrcaWinandOrcaWin 649dc1a6fb test: remove 113 low-value tests (#34541)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-27 14:45:01 -07:00
Brennan Benson e71d4358c1 Native chat tabs can hand off work from their right-click menu (#34504)
* Add native chat hand-off menu on main

* Fix saved chat hand-off observations and RPC exports

* Reuse indexed chat tabs for hand-off subscriptions

* Match saved-chat clear fixture to current journal API
2026-10-27 14:06:57 -07:00
Brennan BensonandWooseong Kim 287a34c0e5 fix(github-project): show linked PRs and sub-issue progress (#34185)
* fix(github-project): render linked PR and sub-issue progress values

* fix(github-project): probe schema support for project column values

* fix(github-project): preserve independently supported project fields

* Preserve nullable project values and unsupported fetch concurrency

* Exercise project relation payloads through unchanged consumers

* Combine project coordination test cache imports

* Use checked project payload types in consumer tests

* Place project provider column contract in cross-layer tests

---------

Co-authored-by: Wooseong Kim <innocarpe@gmail.com>
2026-10-27 13:48:54 -07:00
Brennan BensonandHotragn 8f3d8c8583 fix(gitlab): preserve MR pagination when totals are missing (#34171)
* fix(gitlab): keep paging merge requests when GitLab omits the totals

When total headers are absent, a full response advertises another page to probe and a short response ends pagination. Keep the existing header precedence and unknown total count.

Tracked in #33800

* test(gitlab): cover MR pagination exhaustion and header precedence

* test(gitlab): verify unknown-total metadata across list consumers

* test(gitlab): cover remaining screen consumers of list metadata

* test: use checked GitLab RPC runtime fixtures

---------

Co-authored-by: Hotragn <103170876+Hotragn@users.noreply.github.com>
2026-10-27 13:46:07 -07:00
Brennan Benson be619433be fix: keep startup prompts usable during agent restore (#30978)
* refactor(terminal): separate startup commands from live input

* fix(terminal): run POSIX startup commands from shell prompt hooks

* fix(terminal): preserve staged commands in shell-owned startup

* fix(terminal): correct startup types and release spawn environment

* fix: keep startup prompts usable during agent restore

* test: run startup prompt regression in the native runtime

* fix(zsh): retire deferred startup callback safely

* test: make shell readiness fixtures satisfy timing checks
2026-10-27 13:11:31 -07:00
Sojiro.OandBrennan Benson 9802866ed5 fix(editor): Copy Remote URL links to the code the workspace is on, with the selected line range (#33597)
* fix(editor): Copy Remote URL links to the code the workspace is on, with the selected line range

The gutter menu's Copy Remote URL always built the link on the default
branch and dropped any multi-line selection. Prefer the HEAD commit when
origin already has it, then the origin branch the current branch tracks,
then the default branch; carry an optional end line through IPC, RPC and
the git provider so a selection becomes a line range.

* fix: pair remote file links with published repository refs

* fix: prepare exact remote file fallback before publication scan

* test: consolidate remote file URL integration coverage

* test: document real-clock cancellation deadline proof

* test: handle optional cancellation signal in assertion

* test: reuse spawned child fixture across runtimes

---------

Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
2026-10-27 13:00:23 -07:00
Brennan Benson b46fb04487 fix: finish admitted terminal prompts after sender disconnects (#34373)
* fix: finish admitted terminal prompts after sender disconnects

* test: consolidate prompt input contract assertions

* Keep first-submit ownership independent of sender observation

* Recheck prompt ownership after awaited guards

* Cover abandoned prompt admission during restored pane startup

* Use typed provider write spy in prompt socket regression
2026-10-27 12:55:42 -07:00
Brennan BensonandOrca Worker f0eb0949d0 fix(windows): delete again succeeds after a held file is released (#34058)
* fix(windows): stop recursive deletes from retry-storming when a file is held open

Node's async recursive rm retries every nested entry separately and orphans
sibling walkers when it rejects, so deleting a workspace holding a live
browser profile multiplied filesystem work per directory level until the
main process hung. Delete Windows host trees with a walker that retries
whole passes from one shared budget and leaves nothing running on failure.

Also bound and log detached worktree removal, and fail a file transaction
whose proper-lockfile lock is compromised instead of throwing from its timer.

* fix(windows): retry a locked delete as whole passes instead of a custom walker

Replace the hand-rolled tree walker with Node's own recursive rm and no
per-entry retries. Async rm gives each nested entry its own retry counter and
never stops siblings, which is what multiplied the work; with retries off each
pass is one bounded walk, and a transient Windows lock repeats the whole pass
from the existing 250/500/1000/2000 ms budget, checking the job's signal
between passes.

A failed delete now names the file another program holds open instead of
Node's EBUSY/EPERM text. Drop the 30-minute background deadline. The lost-lock
error no longer embeds the library's message and says the write may or may not
have been saved.

* test: cover bounded deletion failure and recovery contracts

* test: prune unchanged cleanup coverage

* fix(worktrees): give truthful failed-delete cleanup advice

* test: document fake-clock retry adapter

* fix(windows): preserve checkout ownership until files are removed

* fix: preserve worktree ownership checks across metadata aliases

* test: make checkout retry clock completion explicit

* Keep failed Windows workspace deletions retryable

* test: correct removal fixture imports and path formatting

* fix: retain failed removal until a retry is accepted

* test: identify the mocked backoff clock

* test: preserve failed removal records and recreate marker fixtures

* test(worktrees): preserve exact checkout bytes in refusal contract

* test(worktrees): exercise current native deletion contracts

* test(windows): prove long-path deletion through native checkout removal

* test(windows): preserve real Git runner exports in removal fixture

---------

Co-authored-by: Orca Worker <orca-worker@localhost>
2026-10-27 12:31:10 -07:00
Brennan Benson a588c8ce66 fix(orchestration): settled workers stop reading resting slash lists (#34342)
* fix(orchestration): observe journal publications for worker redrive

* test(orchestration): remove redrive fixture type assertion

* test(orchestration): cover CLI redrive and worker publication scale

* test(orchestration): respect CLI project boundary and type worker fixtures
2026-10-27 12:24:27 -07:00
Brennan Benson 7cb23565c9 fix(native-chat): resting chat skill checks share one capped Codex process per account (#34349)
* fix(native-chat): resting chat skill checks share one capped Codex process per account

A resting chat's slash list started one hidden codex app-server per
folder, each kept alive 70 s outside the two-check limit, and a scan
waiting in line for 30 s was treated as hung and doubled. With many
resting folders this kept about a hundred Codex processes alive.

- The catalog runs one scan per folder at a time; readers join it.
  Failures retry three times, then wait for the TTL. Entries unread for
  an hour are dropped.
- Codex folders that share an account, binary, arguments and
  environment are answered by one app-server, batched into skills/list,
  with at most two probe processes alive until their teardown ends.
  A probe that cannot start backs off, and a refresh does not reset it;
  an explicit refresh asks a live probe to reload.
- Batched replies are matched to their own folder.

Tracked in #34141.

* test: use fake timers for resting Codex probe cache checks

* fix(native-chat): bound resting discovery and retain process ownership

* fix(native-chat): close cancelled late directory acquisitions

* test(native-chat): complete cancellation discovery fixture

* test(native-chat): align fixtures with resolved workspace contracts
2026-10-27 10:33:48 -07:00
Brennan Bensonandm4air 4a39d186da Extract conversation switch foundations without activating them (#34384)
Co-authored-by: m4air <m4air@Mac.localdomain>
2026-10-27 10:27:58 -07:00
Neil 2a08315494 refactor: group Claude structured session lifecycle owners (#34449)
* refactor: group Claude structured session lifecycle owners

* refactor: consolidate retargeted Claude state type imports
2026-10-27 09:17:40 -07:00
Kelvin Amoaba e310a63624 fix(mobile): use desktop's rules for queued messages, turn status, Stop and the chat composer (#34000)
* refactor(mobile): share the queued-card projection with desktop

Image-only queued drafts showed a blank card on the phone.

* fix(mobile): honor the host's Queue follow-ups setting

With it off, a mid-turn send from the phone still queued while desktop sent it into the running turn.

* fix(mobile): keep a queued-card edit when the card leaves the queue

The phone closed the editor and dropped the typing; it now goes to the chat box as on desktop.

* fix(mobile): hold /clear and /compact for background work and sends

The phone fired the command where desktop holds it, showed start failures twice, and left the agent unnamed in failures.

* fix(mobile): hand a queued edit back to the chat it came from

* fix(mobile): scope a command's send hold to its own chat

* fix(mobile): read a published null queue as empty

* refactor(mobile): share the returned queued-card caption rule

* test(mobile): drop queued-card tests the shared rules already own

* refactor(native-chat): share the needs-attention rule and drop leftover wrappers

* fix(mobile): type the structured sends mock, drop the moved queued-cards path and ship queued edit copy at boot

* fix(native-chat): drop the queued card's unused image-label import after the rebase

* fix(mobile): order the phone's queue reorder preview by position, as the shared cards sort

* test(mobile): keep the queued-messages test within its line budget

* fix(mobile): settle terminal chat working state and turn timing with desktop's rules

Move desktop's live-status merge, Stop suppression, hook-state resolver and
terminal turn timing to src/shared/native-chat. Mobile reads the transcript
lifecycle the host already sends, so a provider turn end settles Working the
same way desktop does. Terminal chats now get the turn bar, timer and folding,
and the streaming preview uses the shared deriveNativeChatStreamingText.

* fix(mobile): send desktop's terminal Stop keys, a second Escape only for OpenCode

Desktop sends one Escape on Stop. It adds a paced second Escape only for
OpenCode, which confirms an interrupt that way. Claude Code and Codex read
Esc-Esc at an idle prompt as rewind. The rule moves to
native-chat-agent-support, and the phone now paces the second Escape at
desktop's answer step instead of 80ms.

* fix(mobile): word Orca-stop and usage-limit notices like desktop

Move desktop's Orca-stop sentences to src/shared with an English table that
desktop translates. The phone names the Orca host it pairs with, and when
that host is unnamed it says "the remote server". Usage-limit rows rebuild
desktop's sentence from the failure fact. The usageLimitLineText fallback,
which only the phone used, is deleted.

* test(mobile): pass the overlay's host name in the prompt controller harness

* test(mobile): re-record Stop goldens at desktop's step, split status-notice tests, pin the turn clock and ship notice copy at boot

* fix(mobile): offer structured Stop while a send is out and read queue-sends-next as working

Move desktop's structured Stop rule, its Stop-joining helpers and the
queue-sends-next rule to src/shared. Both sides now use them. The phone
counts its in-flight sends, so a conversation-stop host offers Stop before
the turn opens. Between queued turns the phone reads as working, and
commands that can't wait in line hold like they do on desktop. Untargeted
Stops are no longer joined on legacy hosts.

* fix(mobile): offer terminal Stop again when its Escape went nowhere

Stop hides Working on the phone like it does on desktop. The phone's Escape
can fail outright, though, while desktop writes to a local PTY. When no Escape
could have landed, the phone now drops that suppression. The streaming preview
also treats a pending echo as the transcript's tail, as desktop does, so an
earlier reply can't hide a new preview.

* fix(mobile): hold structured Stop while a send it covers is in flight

On desktop, Stop's held and Stopping state depends on whether the chat can
stop, and that includes a send still in flight. The phone now does the same.
Once Stop is pressed before the turn opens, the button holds and repeated
presses send no more untargeted cancels.

* fix(mobile): end the terminal Stop hold when a new message is sent

Desktop clears Stop's working suppression on an optimistic send, so a new turn
that keeps the same hook epoch (Claude with working subagents) still offers
Stop. The phone now does the same when a new pending echo appears.

* refactor(mobile): trim chat turn wiring after the desktop rule move

Drop the controller's unread nativeChatAgentWorking field. Re-render only when
a chat starts or stops having a send out. Keep the structured turn indicator
stable across stream frames. Type the Stop test table instead of casting.

* refactor(mobile): track structured sends in flight once for Stop and command holds

* test(mobile): re-record Stop goldens now that Stop settles with its Escape delivery

* fix(mobile): escape terminal image paths like desktop drops

* fix(mobile): keep launch-draft prefill over older user turns

* fix(mobile): rank @ file matches with desktop's fuzzy Quick Open search

* fix(mobile): use desktop's composer triggers for mid-prompt / and $ picks

Picked @ files are now quoted when the path has spaces.

* fix(mobile): send picked skill paths on structured chats

* fix(mobile): list the host's disk-scanned skills in the / and $ menus

The phone names the workspace and the host resolves its folder, refusing SSH
workspaces as desktop does; hosts without skills.discover-workspace.v1 list none.

* fix(mobile): forget a picked skill once its token is deleted

* fix(mobile): drop a picked skill as soon as its token is deleted, and retry failed skill scans

* fix(mobile): treat a picked leading skill as chat and stop failed skill scans from refiring

* test(mobile): drop the menu ranking case the shared picker tests own

* refactor(mobile): keep skill picks on the draft record and reuse shared send rules

A rejected send now keeps its picks and leading-skill origin on both send paths.

* fix(mobile): replace a re-picked skill's file and restore picks with a rejected send

* fix(mobile): read the chat agent for the skill catalog from the active resolution

* style(native-chat): format the composer autocomplete files

* fix(native-chat): import the picker's apply and send-classify rules from the shared composer module

* refactor(mobile): import the composer's draft-store hooks once

* style(mobile): format the notice row's style import
2026-10-27 15:34:56 +00:00
Kelvin Amoaba cb511d769e fix(native-chat): list an idle Claude chat's / commands from the account it will start on (#34429)
* fix(native-chat): list a resting Claude chat's / commands from the account it will start on

A resting chat's next start follows the selected Claude account, but its / menu was read from the account saved when the chat was created. Read it from the same account a new chat would be created on, and rescan when the selection changes.

* refactor(native-chat): resolve the at-rest account in the shared catalog so its key matches the scan

* test(native-chat): build the second at-rest chat record from a lease fixture
2026-10-27 14:21:12 +00:00
OrcaWinandOrcaWin 89dea213df test: remove 43 low-value tests and five replay suites (#34417)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-27 06:16:34 -07:00
mmarabelandNeil ad4b814018 fix(orchestration): drop the subshell parens from typed setup commands (#22537)
The observed (`worker-start --setup run`) and sequenced (`wait-for-setup`) setup
commands are typed into the user's interactive shell. Pair-inserting line
editors such as zsh-autopair rewrite the `( ` of the `( bash runner )` wrapper
into `(  )`, so bash -lc receives a stray `)` and rejects the whole line: setup
never runs, no completion marker is printed, and the worker stays at
setup_state: running (or the agent gate waits out its timeout).

The runner command is always a single external command (bash/cmd.exe/
powershell.exe), so the subshell added nothing; run it directly.

Fixes #18059

Co-authored-by: Neil <neil@stably.ai>
2026-10-27 05:41:00 -07:00
Neil b35fb1ccfb fix(orchestration): route dispatched workers to the skill and stop heartbeat loops before worker_done (#34404)
The orchestration skill description only advertised coordinator work, so a
dispatched worker never loaded it and built its heartbeat loop from the
preamble alone. Neither the preamble nor the guide said to stop a background
heartbeat loop before worker_done, so the loop outlived the task.

Add worker-role triggers (heartbeats, ask, worker_done) to the description and
stub, and tell workers in the preamble and guide to restart an expired
heartbeat loop mid-task and stop it before worker_done.

Fixes #26605
2026-10-27 05:12:22 -07:00
Neil bd88b2acea fix(orchestration): resolve worker-start new-child/new-top-level targets without requiring a managed caller worktree (#34388)
`orca worker start --worktree new-top-level --repo <selector>` no longer
requires the dispatching terminal to sit inside a managed git worktree; the
explicit repo is all a top-level worktree needs. new-child (and
new-top-level without --repo) still need a caller worktree, and now fail
with an actionable invalid_argument error before any effects are recorded
instead of a bare selector_not_found.

Fixes #22345
2026-10-27 04:28:58 -07:00
Neil 295ed991dd refactor: group shared native-chat session options (#34356) 2026-10-27 04:14:07 -07:00
Neil 325ab0d886 fix(projects): register a folder once when two adds race on a server (#34303)
* fix(projects): register a folder once when two adds race on a server

A paired server's repo.add looked for an existing project, then awaited icon
and upstream detection before saving. Two adds of the same new folder both
passed the check and saved it twice. Re-check right before saving, as the
desktop's local add already does.

Part of STA-12331.

* refactor(projects): reuse the add's store when adopting an existing project
2026-10-27 03:01:27 -07:00
OrcaWinandOrcaWin 4378e2fd3b perf(native-chat): load the chat host and agent adapters on first use (#34318)
Startup evaluated every structured-agent adapter (claude, codex, acp, pi)
even when no chat was opened. The host install already loaded the
registrations with a dynamic import, but structured-chat-cli-fork imported
the same module statically to read sessionHistory, which the lightweight
metadata already exposes.

- structured-chat-cli-fork reads structuredAgentRuntimeMetadata (the same
  objects, so behavior is unchanged).
- installOnJournal moves unchanged into structured-agent-session-host-assembly,
  which install() loads on first host install; the existing installing slot
  already single-flights and retries it.

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-27 01:14:23 -07:00
Neil 16ea126b4e fix(repos): remove a paired-server project while its server is unreachable (#34079)
* fix(repos): let a project be removed while its paired server is unreachable

An unreachable server's repo.rm timeout aborted the whole removal. The error toast
now offers 'Remove from this desktop now', which purges this desktop's rows, records
a pending repo.rm (persisted UI state, optional), fences the server's catalog so it
cannot re-add the project, and replays the removal once the server is ready again.
Re-adds and replays for one server are serialized so neither undoes the other.

Fixes STA-9705

* fix(repos): replay owed removals whenever the server is ready, and lift the fence on refusal

Main publishes status only on change, so a record written or seeded after the ready
snapshot now replays at once. A refused replay refreshes the server's catalog, a
retired server drops its records, a user disconnect also offers local removal, and
the field is main-owned so paired clients cannot overwrite it.

* fix(repos): keep owed removals across disconnects and re-pairs; drop them only for forgotten servers

Also picks up records written during an in-flight replay, classifies the new UI field for
WSL moves, and fixes the test's projection shape.

* fix(repos): say 'disconnected' when the user disconnected the server, and test records surviving re-pair

* fix(repos): retry a replay when the server reconnects while it is still failing

* test(repos): narrow the toast action instead of asserting its type

* test(repos): run the toast action's removal directly instead of dispatching its handler
2026-10-27 00:51:44 -07:00
Neil 2c6dd27276 refactor: group shared native chat shell environment policy (#34301) 2026-10-27 00:30:13 -07:00
Brennan Benson a0e7fa0619 Keep Claude approval choices across model changes without freezing delivery (#34031)
* fix(native-chat): keep Claude approval choices across model changes without freezing delivery

A Claude chat saved as "Approve for me" could reopen asking for every approval,
because Orca read the folder's saved model list as launch policy, and a model
change could keep running in a weaker approval mode Claude opened with. The
first fix for this was reverted after it froze the app: restored history left
a turn reading as running, a send was refused against it, and delivery retried
the same send with nothing changed.

- The saved approval choice goes to Claude as-is; the menu offers every mode.
- Orca records the mode Claude actually opened in. A send that picks a new
  model reopens Claude first when that mode differs from the one requested,
  only when the chat owes no work; Orca mail notices never reopen it.
- A message sent while a Claude turn is running joins that turn on its current
  choices; new choices apply at the next new turn. Nothing is refused.
- History catch-up closes every turn it opens, in the thread it opened in.
- At startup, a turn reading as running under the new agent that the agent is
  not running is ended as interrupted.
- Delivery goes round again only after a step shows what it changed (a commit,
  a different child, a start it joined ending, or a wake); otherwise it waits
  for the next change instead of repeating the same step.

Tracked in STA-10953.

* fix(native-chat): preserve execution evidence at dispatch

* Fix delivery iteration ownership and progress contracts

* Correct journal fixture types in delivery regressions

* Separate Claude input choices from error correlation
2026-10-27 00:01:09 -07:00
Brennan Benson 1c4d4a03bc feat: drag to reorder queued messages on desktop and phone (#34026)
* feat: drag to reorder queued messages on desktop and phone

* fix: use the queue grip button padding from its primitive

* fix: preserve the queue grip touch width with hit slop

* test: carry queue animation mocks into the chat view harness

* test: complete the shared chat view harness registrations

* test: carry the queue move cross-version wire contract

* Scope queue move feedback to the issuing chat and preserve phone touch width

* Keep queue reorder presentation stable during measurement and pending moves

* Keep measured row handoff tokens constant size

* Avoid rebuilding queue arrival IDs on unchanged renders

* Fix mobile queue regression test fixture boundaries

* Share the queued message leading slot with its reorder grip

* Fix queue regression fixture quality findings

* Check queue icons within the leading slot

* Allow every unsettled queued message to reorder

* Supply stable scope in queued row test mock
2026-10-26 23:57:36 -07:00
OrcaWinandOrcaWin 136721fcb9 test: remove53 low-value cases and8 suites (#34264)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-26 23:45:26 -07:00
Kelvin Amoaba cd757f4b19 fix(browser): pass orca wait's timeout through to agent-browser (#34283)
Condition waits never forwarded --timeout, so agent-browser always stopped at its own 25s default and --timeout 60000 still failed at 25s. orcad's external Chromium run and Electron sidecar socket also capped waits at a fixed 90s. One builder now passes --timeout (explicit or the documented 25000 default) on the local and orcad paths, process kills sit 5s above it, and every forwarding hop waits max(60s, T+30s).
2026-10-27 06:17:21 +00:00
Wooseong KimandNeil 845e4668e3 fix(orchestration): stop calling unobserved worker input "submitted" (#21453)
When a worker's turn start was not observed, the dispatch receipt claimed
the input was "written and submitted". A swallowed Enter leaves the prompt
in the composer, so say the text and Enter were written, keep the wait bound
and possible causes, and point at `terminal read` before resending.

Co-authored-by: Neil <neil@stably.ai>
2026-10-26 22:59:09 -07:00
OrcaWinandOrcaWin 84494ce394 test: remove 338 low-value cases and 28 suites (#34234)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-26 22:58:55 -07:00
Kelvin Amoaba 9d2e441947 fix(mobile): compare branches and show diffs with desktop's base-ref and whitespace settings (#34038)
* fix(mobile): compare branches and show diffs with desktop's base-ref and whitespace settings

Base-ref resolution moves to src/shared and the host publishes the compare-against-upstream
and show-whitespace settings, so the phone's branch compare and diffs match desktop.

* fix(mobile): use the upstream compare policy on the review screen and rebuild diffs when whitespace changes

* fix(mobile): reread Source Control settings on Refresh and restart diffs loading under the old whitespace setting

* test(mobile): cover diffs dropped when desktop's whitespace setting arrives

* refactor(mobile): share one Source Control settings read per host across screens
2026-10-27 05:29:46 +00:00
Brennan Benson 2e181b1b78 Reuse chat history snapshots across readers (#34215)
* Reuse rendered chat history across readers

* test: cover current journal reads across source changes

* test: align journal assertions with current snapshot reuse
2026-10-26 22:19:15 -07:00
9dd44715ba fix(mobile): keep a renamed tab's name on mobile after the host restarts (#33743)
* fix(mobile): keep a renamed tab's name on mobile after the host restarts

After a restart, the reconnecting client restarts each saved tab's
terminal (restored-pane-materialize -> createRuntimeOwnedMobileSessionTerminal),
and the mobile row built from the new terminal carried no customTitle, so
the phone showed the terminal's own title although the saved session still
held the name. publishPtyBackedMobileSessionTerminal kept a name only from
an earlier mobile row, which a restart does not have.

Both builders now resolve the name through one helper: a value already on
the mobile row wins, including a cleared null; only a row with no value
falls back to the tab's saved name.

Fixes #33737

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

* refactor(mobile): resolve a restarted tab's saved name through getPtyManualTitle

getPtyManualTitle already owns a terminal's manual name (the saved tab's,
else the live rename), so the row builders use it instead of a second
saved-session lookup. A cleared name reads as cleared from the saved
session, so the test now covers that instead of a row-only clear.

* fix(mobile): keep a row's cleared name instead of falling back to the saved one

A null customTitle on a row is a clear: paired desktops need it to drop a
cached name, and a desktop clear lands on the row before the debounced
session write. Only a row with no value falls back to getPtyManualTitle.

* test(mobile): pin that a row's cleared name survives a terminal restart

---------

Co-authored-by: clayphi <193966884+clayphi@users.noreply.github.com>
Co-authored-by: Claude Code <noreply@anthropic.com>
Co-authored-by: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com>
2026-10-27 05:18:58 +00:00
Neil bff2843fca refactor(shared): organize native chat ask input owners (#34251)
* refactor(shared): group native chat ask input owners

* refactor(shared): keep split ask type imports erased
2026-10-26 22:18:52 -07:00
Kelvin Amoaba e79388e3ab fix(agents): keep OMP session titles when a turn finishes (#34266)
Orca wrote a synthetic "OMP ready" / "OMP - action required" title over
the session title that OMP and Orca's titlebar extension already paint.
OMP's hooks are authoritative and its own rest title reads as idle, so
stop synthesizing terminal titles for OMP, as for OpenCode.

Linear: STA-11986
2026-10-27 05:15:58 +00:00
Brennan Benson 192f5783ab fix(runtime): keep terminal.send under the file-length limit with one refusal helper (#34256)
#33968 pushed terminal-send-method.ts to 301 counted lines against the 300-line rule, failing Lint on main and every PR that merges it. Eight refusal branches spelled out the same zero-byte result; one helper builds it.
2026-10-26 22:01:45 -07:00
Kelvin Amoaba 75bcd328cf fix(mobile): answer multi-question asks like desktop and clear Claude's wait on phone answers (#33968)
* fix(runtime): clear Claude's question wait when a phone keystroke answers it

Claude sends no hook after AskUserQuestion is answered; desktop infers it, phone answers left the pane waiting.

* fix(mobile): answer multi-question cards like desktop, with Skip and back-stepping

Mobile required every answer and locked grouped structured questions once tapped; the card rules and structured mapping now live in shared code.

* fix(mobile): announce ask card options as checkboxes or radios

* fix(mobile): answer a grouped question only on the prompt it was picked on

* test(runtime): drop the multi-question case the shared answer rule already covers

* refactor(native-chat): share the typed-answer rules and fold the host answer check into terminal.send
2026-10-27 04:27:06 +00:00
Kelvin Amoaba a9f90e7a71 fix(mobile): use desktop's terminal paste, key and query-reply rules (#33957)
* fix(mobile): send terminal paste, keys and query replies like desktop

* fix(mobile): encode Shift+Enter for the pane's negotiated keyboard modes

The phone's xterm now tracks Kitty flags and modifyOtherKeys with desktop's tracker, seeded from the host snapshot.

* fix(mobile): keep keyboard modes across terminal snapshots

* fix(mobile): adopt snapshot keyboard modes with desktop's rule

* refactor(terminal): share snapshot keyboard-mode adoption and mode mirroring

* fix(mobile): compare terminal modes field by field instead of a dynamic read

* refactor(mobile): share terminal display-mode adoption between snapshot and resize

* fix(mobile): accept only known terminal display modes from the host
2026-10-27 04:26:16 +00:00
Kelvin Amoaba 7e5f9a48df fix(mobile): use desktop's sort, colour, count, agent-row and delete rules in the workspace list (#33995)
* fix(mobile): use desktop's sort, colour, count and agent-row rules in the workspace list

Recent ignores terminal output, repo colours fall back to desktop's neutral,
Home counts skip archived rows, folder workspaces trail each lane, and a stale
agent row on a live terminal reads "No update in …" like desktop.

* fix(mobile): keep Delete disabled on a primary checkout, as desktop does

* fix(mobile): keep the task text on a silent agent row

* fix(mobile): show a silent agent row's time since its last update

* test(mobile): drop agent-row tests that replay the shared verdict

* refactor(mobile): check host PTY liveness once and keep the delete guard on the row flag

* test(mobile): re-record the home catalog goldens for the list's active count
2026-10-27 04:25:59 +00:00
Kelvin Amoaba 6c2423d25d fix(mobile): list Source Control changes with desktop's sections, order and row rules (#34017)
* fix(mobile): list Source Control changes with desktop's sections, sort and row rules

Conflicts get their own section in the user's group order, submodule rows lose stage and
discard like desktop, paths sort with the shared collator, and review notes go out as desktop formats them.

* fix(mobile): read the group order the host now publishes and keep submodule rows read-only

The host's paired settings projection now carries sourceControlGroupOrder, the
normalized status projection keeps submodule metadata, and conflict rows reuse the
shared conflict-kind labels.

* refactor(mobile): share Source Control grouping and row rules with desktop

Desktop and mobile group rows through one shared helper, mobile's unstage button
follows desktop's rule, and the group order resets when the host changes.
2026-10-27 04:14:56 +00:00