QuickCommandsPane, useAddRepoCloneFlow, UntitledFileRenameDialog and the
workspace board column resize hook guarded a render-phase setState with a
ref written in the same render. When a sibling suspends, React discards
that render and its queued updates but keeps the ref write, so the retry
skipped the adjustment (no Add dialog, empty clone destination, stale
save-as name, stale column width). Track each guard in state instead, and
sync the column-resize handler refs in a layout effect.
The other two sites from the issue (WorktreeMetaDialog prevIsOpenRef and
use-native-chat-draft lastScopeKey) no longer exist on main: the dialog
remounts a keyed draft and the chat draft reads via useSyncExternalStore.
Fixes#25527
Co-authored-by: Matheus Breguêz <matbrgz@gmail.com>
Jcode accepts a string or an array of strings per hook event, but Orca's
[hooks] parser only understood single-line strings, so any array made
status and install report error and no hook was written.
Parse string arrays, append Orca's command as the last element (after the
user's pre_tool gates), repair a stale Orca element in place, collapse
duplicate managed elements on reinstall, and remove only Orca's element on
uninstall. Edits are offset splices on the original text, so user entries,
comments and layout survive; malformed or non-string arrays still fail
closed.
Fixes#33998
Co-authored-by: Neil <neil@stably.ai>
Codex paginated rollouts persist an item_completed ContextCompaction item
with no content, and legacy rollouts persist an event_msg context_compacted.
The Codex decoder dropped both, so the native Chat timeline never showed
that context was compacted. Emit a system row with the shared compaction
copy and presentation so it renders as the compaction divider.
Fixes#14188
Co-authored-by: Neil <neil@stably.ai>
The details tokenizer's start hook matched any `<details` substring, so
marked cut a paragraph at a `<details>` mention inside inline code and its
HTML-block rule swallowed the rest, rewriting the file on save. HTML blocks
open only at a line start, so anchor the hook there.
Fixes#19799
Co-authored-by: Neil <neil@stably.ai>
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>
Codex can render a separate `? for shortcuts` row below its status row. The
composer classifier returned on the first nonblank bottom row, so it never
examined the real status row above the hint and the IME placeholder mask
never activated, leaving the placeholder beside composing text.
Fixes#24337
Co-authored-by: Neil <neil@stably.ai>
Co-authored-by: Andy Zhang <16486422+v01dstar@users.noreply.github.com>
Local Setup named only Node, pnpm, and Bun. A first pnpm install also
compiles node-pty and the native addons with node-gyp and loads them in
Electron, and *.electron.test.ts fall back to xvfb-run on headless Linux.
Document the per-platform build toolchain, the Linux Electron runtime
libraries plus xvfb, and the Corepack/pnpm version caveats.
Fixes#31589
Co-authored-by: Neil <neil@stably.ai>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Automatic hibernation checks and stops each agent pane on its own and only
protects terminal tabs that are visible on screen, so other tabs in the
active worktree can still sleep. Update the hibernation page to match:
drop the "hibernate together as a unit" claim, scope the foreground rule
to visible tabs (active tab, other split groups, Activity page), separate
manual whole-worktree sleep from automatic per-pane reclaim, and describe
resume as happening when you return to the agent's tab.
Fixes#33940
Co-authored-by: Neil <neil@stably.ai>
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
Main now tags each Orca-authored PR/MR create failure with a reason code
(the English error is unchanged for mobile, older clients and logs), and
the renderer maps the reason to translated copy in the Source Control
notice, the Checks panel and the pre-create blocked notice. Sign-in steps
are single-sourced across providers.
Fixes#33321
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Hardened-runtime TCC refused EventKit and Contacts requests from terminal-launched
tools without a prompt: Orca and its terminal host lacked the addressbook and
calendars entitlements and the matching purpose strings. Declare them the same
way camera, microphone and location already are; the terminal host inherits both.
Fixes#12933
Co-authored-by: Claude <noreply@anthropic.com>
Japanese IMEs convert one clause at a time, but the terminal's preedit overlay
painted the whole composition uniformly, so the clause the candidate window
targets was invisible. Read the IME's selection from xterm's textarea on the
insertCompositionText input event (Chromium only updates it there) and paint
that range of the preedit in the theme's selection colors. Cell-grid advance
corrections are unchanged and nothing reaches the PTY; a collapsed caret or a
range outside the preedit highlights nothing.
Fixes#27873
Co-authored-by: Neil <neil@stably.ai>
The store-driven Agents drawer has no Radix trigger, so dismissing it with
Escape or the close button dropped focus to document.body. Capture the
focused element on each open transition and return focus to it on close,
unless focus already moved elsewhere or the close hands focus off (outside
pointer dismissal, agent reveal, switching to the pop-out).
Co-authored-by: kambarakun <kambarakun@gmail.com>
`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
kiro-cli agent upgrade (and Kiro CLI 3.0) turns an agent's hooks into a
list of {name, trigger, matcher, action, timeout}. Orca rejected that shape,
so status reported an error and on/off left the files untouched. Orca now
refreshes its list entries in place, appends missing ones, removes only its
own on off, and writes the list form into upgraded agents that have no hooks
yet so they stay in the universal format. The remote installer shares the
same path.
Fixes#34194
* fix(wsl): start no running-distro read inside the stop settle window
settledStartedAfter slept with one setTimeout until the settle anchor. Node truncates timer
delays to whole ms against a ms loop clock, so the wake can land up to ~2ms before the anchor
by performance.now(), and the probe then started inside the window and could still see the
stopping distro as running. That is the hook relay stop-settle flake ("expected true to be
false"). Sleep until performance.now() has passed the anchor.
* test(wsl): spy on setTimeout without a type assertion
* test(wsl): fire the spied timer far early so a single sleep always fails
* fix(wsl): re-read a settled exit only from a probe started after the window
After a watch or holder exit, the liveness supervisor slept EXIT_SETTLE_MS and re-read the
running list with no bound, so it could join a list probe that started inside the settle
window and still listed the stopping distro. The re-read now passes the exit time, and the
wiring reads through the gate's settledStartedAfter with exit + EXIT_SETTLE_MS as the bound.
* feat(quit): add Quit and Stop All Sessions to the app menu and tray
Quit keeps daemon sessions alive for warm reattach. The new item, placed
directly above Quit in the macOS app menu and menu bar icon, the Windows
File menu and tray, and the Linux File menu, asks for confirmation, runs
Settings' Kill all sessions in the renderer, and quits only when every
session stopped. Closing the tabs first means nothing reopens or resumes
on the next launch. Serve hosts do not offer the item.
* fix(quit): keep Orca open when Kill all cannot prove every session stopped
- Kill all no longer reads a daemon generation that failed to list as
"no sessions": a failed poll keeps the last confirmed remaining set,
and a failed initial listing sets listingIncomplete.
- Quit and Stop All Sessions stays open on an incomplete listing or a
terminal tab opened during the sweep, logs why it stayed open, and
reveals the window before the final quit.
- Share the main-window message box helper, stub the request on the web
client, and add Portuguese strings.
- Cover the windowless path, request expiry, the in-flight guard, and
failed listings in tests; bound the E2E exit wait.
* fix(terminal): count Kill all per daemon generation within a time window
- A poll updates only the daemon generations that answered, so one
failing generation no longer hides the kills of the others.
- Poll for 6.5 s of wall-clock time instead of 65 attempts, because a
wedged generation fails each listing only at the request timeout.
- Flag a sweep with no daemon provider as listingIncomplete.
- Log the leftover-tab exit, share the one-shot window intent with
Open Settings, and cover the sender check, the reveal before the final
quit, and retries in tests.
* fix(quit): stay open while a newer build's daemon is held
- Set listingIncomplete while this build holds a newer protocol's
daemon, whose sessions it can neither list nor stop.
- Reuse getDaemonAdapters in Kill all, type the shared one-shot helper
by its flag factory, and move the app-menu quit handlers next to the
tray options so both serve/desktop splits are tested.
- Cover a quit that outlasts the request expiry and a failed request
pull, and fix the docs lead-in for the new quit-time case.
* fix(quit): explain, without stopping anything, while a newer version runs terminals
- Check the newer-version daemon hold before the confirmation and show
an explanation instead, so Kill all no longer closes every tab and
then refuses. listingIncomplete stays as the backstop.
- Translate the explanation, describe listingIncomplete generically,
and assert the single timed-out poll in the Kill all test.
* fix(quit): explain before asking whenever Kill all cannot list every session
- Show the explanation instead of the confirmation while the daemon
provider is missing (still starting or failed) as well as while a
newer version's daemon is held.
- Name only the steps that work (wait for terminals to open, reopen a
terminal opened in a newer version), avoid stating a hold as fact, and
label the dialog's button through the catalog.
* fix(quit): add the relaunch step to the explanation and align the ja wording
* test(pty): keep a confirmed stop when its generation fails a later poll
* test(quit): give the E2E timeout bump its reason
* fix(quit): stay open when the daemon still lists a session after Kill all
Kill all counts only the sessions of its first listing, so a session
started since then, for example from the CLI or an automation before its
tab reaches the renderer, would outlive the quit. Main now lists every
daemon generation again before quitting, and quits only after a listing
in which every generation answers and none lists a session. It polls
within Kill all's 6.5 s window so the tab sweep's PTY shutdowns can
finish; otherwise the renderer shows the existing error and Orca stays
open.
ui:quitAfterStoppingAllSessions becomes an invoke that answers whether
main quits.
* feat(quit): rename to Quit Completely with a macOS hover hint, and explain plain Quit in the confirmation
* fix(quit): show progress while Quit Completely runs, and refuse at once when the daemon can't be fully listed
---------
Co-authored-by: Neil <neil@stably.ai>
* fix(wsl): start no running-distro read inside the stop settle window
settledStartedAfter slept with one setTimeout until the settle anchor. Node truncates timer
delays to whole ms against a ms loop clock, so the wake can land up to ~2ms before the anchor
by performance.now(), and the probe then started inside the window and could still see the
stopping distro as running. That is the hook relay stop-settle flake ("expected true to be
false"). Sleep until performance.now() has passed the anchor.
* test(wsl): spy on setTimeout without a type assertion
* test(wsl): fire the spied timer far early so a single sleep always fails
wsl-hook-relay-stop-settle.test.ts imports the locale catalogs and failed in
the same shard on both CI attempts of #34361, and on none of 22 other failed
shards. Revert until the timing interaction is understood.
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
The unit-test ?url loader embedded six ~1.5 MB base64 catalogs as string
literals, so every test file loading i18n.ts fetched and parsed ~11 MB over
the module runner. Emit a readFileSync-built data: URL instead; fetch()
still parses the real catalog.
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* fix(workspace-board): paired hosts keep their own custom status columns
A desktop client grouped a paired host's workspaces with its local column
list, so cards in host-only columns (e.g. status-6) fell back to the default
column. Fetch each host's workspaceStatuses via the existing ui.get RPC and
merge them after the local columns for grouping, without persisting them.
Fixes#34227
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(workspace-board): keep paired host columns safe from local column edits
- Removing a local column resolves cards against the merged board list, so a
host card in a host-only column is never rewritten.
- New column ids are unique against the merged list, not just local ones.
- The context menu status submenu and the closed-sidebar order use the merged
columns too; the shared-status helper moves to the menu policy module.
- A host that sends no columns adds none instead of the defaults; identical
replies keep the stored identity and out-of-order replies are dropped.
- The merged list is named boardStatuses and tolerates stores without the
host fields, so the earlier WorktreeList fixture edits are reverted.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(workspace-board): keep host cards whose host still defines a removed id
Removing a local column whose id a paired host also defines rewrote that
host's cards to the local fallback. Skip runtime-host cards when their own
host still has the column.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(workspace-board): keep host column ids in the composer and drop a retired host's columns
Creating from a host-only lane now keeps that column's status, a host that is
removed or re-paired to another machine no longer leaves its columns behind,
and the new tests use checked fixtures instead of casts.
Co-authored-by: Kooooojun <edisonhuang1122@gmail.com>
* fix(workspace-board): deliver a host's workspaceViewChanged so its columns refresh
Co-authored-by: Kooooojun <edisonhuang1122@gmail.com>
* fix(workspace-board): show and offer a paired host's columns only for that host's cards
The board adds a host's column only when one of that host's own cards uses it,
never the host's copy of the default column, so local cards keep falling back
to the local list. The context menu offers host columns only when every card
is on that host, the composer applies a lane's host column only when creating
on that host, and status writes drop another host's column id.
Co-authored-by: Kooooojun <edisonhuang1122@gmail.com>
* fix(workspace-board): read cards through the record selectors and skip foreign-lane drops
Board lanes and the context menu now leave out a host's copy of the default
column, so a host card's menu matches its lane. Lanes skip the card scan when
no host adds a column. A drop into another paired host's lane is ignored
instead of re-ranking the lane.
Co-authored-by: Kooooojun <edisonhuang1122@gmail.com>
* fix(workspace-board): sync tasks against every offered column and test host-lane drops
Task-status sync now finds a paired host's column that no card uses yet, as the
card's menu offers it. A write left empty after dropping another host's column
is skipped. New tests cover board and sidebar drops into a host-only lane.
Co-authored-by: Kooooojun <edisonhuang1122@gmail.com>
* fix(workspace-board): resolve host columns by runtime owner and compare mixed selections per card
A paired host's SSH workspace, which the host's runtime owns, now gets that
host's columns like its other cards. A mixed selection marks and moves each
card against its own host's columns. A status write for a card the store does
not know yet passes through unchanged.
Co-authored-by: Kooooojun <edisonhuang1122@gmail.com>
---------
Co-authored-by: Kooooojun <edisonhuang1122@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* 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
* test(wsl): hand the reap test's fake cat its real stdin
The fake cat backgrounded the real cat with `&`, which a non-interactive sh starts on
/dev/null (dash even under `<&0`). The socket reader therefore ended at once, before orcad
hung up, and the relay killed its partner, sometimes before that cat's TERM trap was set:
on Linux under load the trap count stayed at 1 even after #34329 (3 of 6 rounds of 50
failed). The fake cat now passes stdin on fd 5, as the relay itself does, so the socket
reader ends only when orcad hangs up and the stdin reader blocks until killed.
* test(wsl): mark the fake cat armed only once its child is known; tighten the comment
Only the automations and client-hosted-browser steps had an id-keyed
entry in the localized step copy map, so every other contextual tour
(workspace board, agent sessions, browser, tasks, floating workspace,
workspace creation) rendered English titles, bodies, and action labels
next to localized Back/Next/Done controls.
Give every tour step a required stable id and move the copy lookup into
contextual-tour-step-copy.ts. The map `satisfies` a record over all step
ids, so a new step without localized copy is a type error. Primary and
secondary action labels (Split terminal, Got it, Browser settings) are
localized through the same entry. Existing automations and
client-hosted-browser keys are unchanged so shipped translations keep
resolving.
Add the new strings to en and translate them for ko, es, ja, zh, fr,
and pt.
Co-authored-by: Neil <neil@stably.ai>
The free windows-11-arm and macos-15-intel lanes already set the matrix's wall
time; the paid 8-vCPU Blacksmith Windows lane finished minutes earlier for ~$48/day.
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Bell, test, task-complete, tool-preview, permission-probe and startup
registration notifications now read their sentences from the i18n
catalogs instead of English literals. The notification translator
accepts interpolation values; the English default fills them so
headless hosts produce unchanged English copy. The startup permission
prompt waits for main-process i18n before it is shown.
Fixes#27068
Co-authored-by: outof-place <contact@outofplace.space>
check:changed --base=<ref> resolved a merge base for the gates it drives
directly, but the flaky-test ratchet subprocess re-derived its scope from
origin/main. On a checkout whose origin is a stale fork that pulled in
unrelated files and could fail the run. check:changed now forwards its
merge base via ORCA_CODE_QUALITY_BASE (the env the other changed-code
gates already honor), and the ratchet uses it before falling back to
origin/main.
Fixes#33763
Co-authored-by: Neil <neil@stably.ai>
Both arch jobs overlapped ~2.5 min of setup with qualification, then polled
for the template for 400-560 s on the most expensive runner we use.
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
The queued-message test rig deleted its temp state directory while the
shared journal SQLite handle was still open, so teardown failed with
EBUSY on Windows. Close the rig root's journal database after the host
drains, then remove the directory.
Pin commit.gpgsign=false and tag.gpgsign=false in the throwaway repos
built by the release script tests so a global signing config cannot turn
lightweight fixture tags into annotated tags that abort with
"fatal: no tag message?".
Vitest's reporter checks existsSync(file) and then reads the file to find a
source map. When an error stack names a directory, readFileSync throws EISDIR
as an unhandled error, so the shard fails with every test passing. Read only
regular files.
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>