* perf(browser): throttle idle guests between active operations
* perf(browser): lease only the guest for non-capture agent commands
Keep the renderer paint hold, host unthrottle, and host re-hide readback on
capture paths only, and never let the one-way paint-hint send fail the capture.
* style: format bridge paint-hold test
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* fix(native-chat): open sandbox: images and files on the chat's host
Agents sometimes cite files they wrote as sandbox:/abs/path. Markdown
sanitizing dropped those addresses, so the chat showed only alt text and
an inert link. Chat Markdown now rewrites sandbox: links and sandbox:/file:
images to the chat's own file hrefs before sanitizing, and the shared href
router reads sandbox:/abs as the absolute path on the chat's host.
A paired host now grants a chat client a read-only exact-path grant for
any host file the chat links, instead of searching the chat transcript for
the path. The search missed structured chats, Markdown link targets and
older replies, and guarded nothing: a paired client can already run
commands on that host. The grant still refuses network shares, device
paths, hardlinks, pipes and sockets.
Desktop chat links and images now send their chat to the host, so files
outside every workspace on a paired host open and render. A chat link the
host does not have is reported missing there and never read from this
computer.
* Fix chat file reference and granted file actions
* Keep granted chat files on existing read-only tab route
* Remove accidental package manager lock metadata
* Match WSL artifact descriptor fixture to regular file stats
* Fix canonical host image grant acquisition
* Fix host image test transport request typing
* Drop sandbox: address handling from native chat links
sandbox:/mnt/data placeholders belong to a hosted code tool that swaps them
for its own download links; they never name a file on the chat's host.
* fix(wsl-mover): settle an interrupted move automatically, and hold the host's server update during a move
An app update or crash between sending a move's copy and the host's commit leaves a
`blobs-transferred` record. Startup recovery has no host to ask, so the projects stay fenced, and
the automatic mover only previewed, which refuses an in-flight move, retrying forever. The
projects stayed read-only until the user started the move from the dialog. The automatic mover's
preview now settles that record first (through the host: back to zero, or forward when the host
committed), holding the distro's run while it does so it never races a move the user starts. The
dialog's preview stays read-only. The preview moves to its own module.
A Settings Update or Force, or any reconnect, could also redeploy the host's server in the middle
of a move, cutting it off (safe, but the move is lost). Once a running move has journaled, a
version-changing redeploy now keeps the running server and reports the update as waiting for the
move, mirroring the SSH path's skip while migrating. The move's own first connect, before it
journals, can still update an outdated server.
* fix(wsl-mover): an automatic settle never reloads the window, and never holds its own host update
Review of #34823:
- A settle that finds the host committed ran the desktop rewrite, which reloads the window past
its unload veto. From the automatic mover it now records the commit only (the projects stay
fenced), and the next start's rewrite pass finishes it before any window opens. A move the user
starts still finishes at once.
- The server hold counted any unfinished record of the distro, so an automatic settle of an old
record with an outdated server was held, refused for an update, and retried the same way. The
hold now counts only the cutover the running move journaled itself.
* fix(wsl-mover): let the user finish a committed move in-session; park the automatic mover meanwhile
After an automatic settle records a commit for the next start, nothing could finish it in-session:
the user's Move refused as already in flight and the orchestrator retried for nothing.
- A settle that finds the head committed but not yet rewritten on this desktop finishes it now when
the user asked (their Move, or the status line's Retry), returning the existing "finished an
earlier move" answer; the automatic mover still leaves it for the next start.
- The automatic refusal carries detail `finish-pending`, which parks the distro (new reason
`finish-pending`) instead of retrying; the next start, whose rewrite pass finishes the move before
any window opens, puts it back to pending.
- The park rules move to wsl-move-orchestrator-park.ts.
* fix(wsl-mover): give the finish-pending park its own status line
Main's move status line (#34676) names every park reason; the finish-pending reason this PR adds
needs one: the earlier move is done on the host, and finishes here at restart or with Check again.
* fix(native-chat): a refused queued message still blocks the queue when its card write fails
Every reader of a queued card goes through one derived view of the journal's dispatch facts,
so a card whose message was refused reads as returned even when its card-table update and heal
both failed. One decision and one writer store the derived state for every action.
* refactor(native-chat): keep each queued message in the chat's journal, not a second table
A queued message lived in its own SQL table, and a hook that may be skipped copied
each refusal from the journal into it. When that write failed, a refused card
vanished: the cards behind it sent, and its Send and Delete did nothing.
Each queued message is now a `queue` row in the chat's journal beside the
submission that hands it off and the dispatch row that answers it. The reducer
folds the card from those rows, so a refusal returns the card and holds the
cards behind it with nothing written, and replay reads the same. Every action
writes its own rows in one transaction. Cards an earlier build stored are read
through the same classification until they move: each action moves its own
card, and a bulk import runs once per open. A new epoch restates every card
still owed an answer in the transaction that deletes the old rows.
The settlement hook and its savepoint, the open-time heal, the drain's heal and
the derived-card view are gone. The published card list is unchanged.
* test(native-chat): adapt the queue tests to cards kept in the journal
Cards are written through the journal, so tests queue them with their body
alone and hand them off under their real fingerprint. Tests of the removed
settlement hook, heal and SQL store are deleted; the text-update spec now
covers the edit decision alone, and a test of an earlier build's stored holds
seeds the legacy table and reads the imported cards. Rolling over an epoch now
keeps a card whose hand-off was never handed over, so that test checks the
retried id never sends rather than that nothing sends.
* test(native-chat): a refused card holds the queue through rewind, a new epoch and a restart
One whole-flow test through the host: the refusal writes no card row, a rewind
that retires the refused hand-off and a rolled-over epoch keep the card
returned, a restart and Resume send nothing behind it, its own Send refused
again still holds the queue, and its Delete releases the next card in order.
* test(native-chat): an earlier build's queued cards move into the journal once, each read the same
One mixed-import test through the host. The first open's import fails and
moves nothing, yet every card reads as it will; Delete and Send each move only
their own card. The next open moves the rest in one transaction, writing a
refusal only where its hand-off is gone, nothing for answered cards, and leaving
an unreadable row on disk. A later command turn spends a moved command card, a
third open writes nothing, an old Delete replays from its receipt, and a chat
with only answered legacy rows clears them in a delete-only transaction.
* test(native-chat): adapt typechecked tests to cards kept in the journal
The row-kind table lists a queue row, the clear and legacy-import tests
match the current signatures, and the downgrade test keeps only the
leftover-send case: a queued card is now a newer-version journal row an
older build keeps read-only, and users don't downgrade.
* refactor(native-chat): drop the journal row writer's unused transaction hook
No caller passes one since the queue's savepoint bookkeeping left.
* test(native-chat): downgrade probes write rows at the version older builds read
The highest version this build reads is now 4, the queue row's. A newer
build's row in these probes is written at version 3 so the older build
meets its kind, not a newer version, and the Stop-event probe hands the
older build the rows as stored, since a read raises each row's version.
* test(native-chat): unit tests follow the queue row's version 4
The legacy-import test opens real SQLite, so it runs in the Node runtime
project. The newest version this build reads is now 4, and a read raises a
row's version to it, so the turn-lifecycle test no longer pins the
in-memory version (the written one stays pinned by journalRowSchemaVersion).
* fix(native-chat): one batch echoing two refused queued messages spends both
The per-row delivery check returned only the first waiting card an echo
proved delivered, so when one settlement carried echoes for two cards the
second stayed waiting and was sent again. It now returns every card the
row proves delivered, and the fold withdraws each; replay folds the same.
* fix(native-chat): a rewind moves an earlier build's cards into the journal first
While the open's import had failed, an earlier build's card that read as
refused through its hand-off lost that reading when an in-place rewind
retired the hand-off: it disappeared and the card behind it sent. The
rewind now adopts every legacy card ahead of its tombstones and deletes
their rows in its own transaction, as an epoch replacement already does,
so the fold latches each card's reading before its hand-off is gone.
* fix(native-chat): keep an earlier build's card whose hand-off is still unconfirmed
The import dropped a legacy card handed off as a send whose delivery was
unknown, though the fold keeps such a card linked and a later refusal
returns it. The import now keeps every card whose hand-off a later row can
still answer, through the same check the reducer folds dispatch rows by.
* fix(native-chat): one delivery echo withdraws at most one queued card
One provider echo proves one delivery, yet it withdrew every waiting card
with the same text whose hand-off was refused, so a second identical card
was lost. The fold now pairs each echo with one card: on its own row the
earliest-positioned match no other echo of that row took, and for a card
that waits again the earliest applied echo no card has taken. Paired echoes
live in the fold's state, so live and replay pair the same; readers of
cards not yet imported work on a copy.
* fix(native-chat): a queued message handed to the agent is sent again only on proof it never got it
Pressing Stop after Orca handed a queued or steered message to the agent,
before the agent confirmed it, put the card back in the queue, and Resume
or the next send sent it again. A check matching the agent's echo of the
text was the only guard against a double send, and it kept trading a
resend for a lost card. Whether the card waited again was decided by the
rejection's category, not by proof the agent never got the message.
A refused hand-off now waits again only where Orca proves the agent never
received it: the host still held it, or whoever settled it recorded that.
A failure still returns the card and holds the cards behind it. Anything
else may have reached the agent, so the card is spent and its message
reads as sent, derived on every fold from the stored rejection: with the
turn it was answered into, as that turn's unconfirmed message where it was
handed over; withdrawn before any turn opened, as a person's own send a
Stop took back. The reopen mark ignores a recovered doubt, and the restart
reconciler no longer judges a queued hand-off unsent from what history
lacks. The echo text matching and its pairing are deleted.
A close or quit while the agent starts settles sends with the unrun hold.
The host hands nothing to a child before its start answers
(structured-agent-session-delivery-loop.ts: a handover needs phase
ready), so such a send went to an earlier child and may have run: it now
reads as sent instead of waiting again.
* fix(native-chat): Claude's report that it removed a queued message unrun lets the card wait again
Claude says when it removed a send without running it: the interrupt
receipt's cancelled list, or a command_lifecycle cancelled, discarded or
refused frame for a command that never started. Every one settles through
rejectClaudeDispatchWaiters, which now marks the settlement unrun. For a
queued card's hand-off the host records that as proof the agent never got
it, so the card waits again under the queue's pause. A direct send keeps
the plain refusal: there the proof would owe it as stopped history.
* fix(native-chat): an earlier build's card stopped after hand-over follows the same rule on upgrade
Earlier builds put a card back to waiting after any refusal without a
verdict and cleared its link, so a card the agent may already have would
be sent again after the upgrade. The card's own hand-offs in the journal
decide now: unchanged and last sent as one that may have reached the
agent, it is spent on that hand-off and its message reads as sent; edited
or sent again since, it is held for its own Send under a host-only marker
that publishes as a plain hold; with no hand-off in doubt, including one
that never left Orca, it waits as before. The import writes a card spent
on a hand-off in doubt, so its message shows.
* fix(native-chat): read a queued row's hand-off before the lookup that uses it
The lookup's callback lost the narrowing of the row's change, which the
mobile test typecheck rejects.
* test(native-chat): a card steered into a turn a Stop ends stays sent in the Stop tests
Three tests pinned the old rule, that a card handed into a running turn
comes back to waiting when a Stop ends it. Under the new rule nothing
proves the agent never got it, so the card is spent: with the turn named,
its message reads as that turn's unconfirmed one; without, as a send the
Stop took back. Neither is ever sent again.
* fix(native-chat): a queued message stopped before Orca handed it to the agent waits again
A Stop that aborts a hand-off after its row is written but before the agent call now records
the withdrawal as never reaching the agent, for queued hand-offs only, so plan D's rule returns
the card to waiting under the Stop's pause instead of spending it. Direct sends keep the plain
withdrawal (the flag would owe them as stopped history). The agent's own refusal while aborted
is kept rather than overwritten, and an abort thrown during the call stays in doubt.
* fix(native-chat): a queued message stopped while the agent prepares it waits again
The abort signal reaches only an adapter's preparation (its options, the previous turn opening),
never its write, so the signal's own reason thrown back is the adapter saying nothing was
written. The handover now reads that, for queued hand-offs only, as never reaching the agent,
so the card waits again under the Stop's pause. Claude no longer wraps that abort as an option
refusal, which had returned a stopped card as a refused one blocking the queue. A real refusal
keeps its cause and its barrier; any other throw stays in doubt and is never resent.
* test(native-chat): the usage-limit Continue backlog case writes its card through the journal queue
Main's #30396 wrote its backlog card with the stored fingerprint and host-instance stamp the
journal queue derives instead; the card the Continue must yield to is the same waiting card.
* test(claude): the real-CLI queued Stop expects the unrun mark Claude's cancelled-unrun receipt carries
* feat(mobile): edit a queued message's images on the phone: remove or attach
* fix(mobile): gate queued image edits on the host reader verdict
* test(mobile): declare the upload fixture chat mode
* fix(native-chat): retain spent legacy messages when import fails
* fix(mobile): reveal the full queued image editor on open
* fix(mobile): invalidate queued editor geometry without a render ref write
* refactor(chat): prepare composer drafts and measured queue dragging
* feat(native-chat): add conditional queue removal and authoritative reordering
* feat(native-chat): move queued edits into the normal composer on desktop and phone
* fix(native-chat): own complete phone drafts before authoring
* fix: preserve submitted composer drafts across storage replacement
* fix composer validation and mobile CI diagnostics
* fix mobile composer test contracts and diagnostic output
* Capture mobile test compiler diagnostics in CI
* Fix queued message contract typing and English strings
* test: complete queued extraction transport fixtures
* refactor: separate composer skill binding from send mutations
* fix(mobile): grant draft storage within route limits
* fix(native-chat): settle each draft save on its own and publish complete composer drafts
Phone: a queued Edit copy no longer consumes the save that later typing
owns. Each ordinary save and the exact copy settle only their own result;
an explicit save waits for its own pending write, and a failed or retired
write never schedules another or revives the copy.
Desktop: a pasted-text leaf no longer shifts which selected file binds
where, and a hand-back held during an IME composition reaches the store
only merged with the visible editor document, including writes the editor
admits without an update and the composition settle.
* fix(native-chat): import the draft type once in the draft cache
* test(mobile): type the held-write state without an assertion
* test(native-chat): assert the handed-back file on Send without reading a typed transport as a mock
* test(mobile): pass main's agent-confirmed flag to the submitted-files harness
* fix: reveal queued reorder grip on touch screens
* fix: save reopened phone drafts and batch reorder measurements
* test: use an explicit React module type
* Preserve queued message sortable identities on unchanged renders
* Prevent stale queued drag completion after unmount
* fix(mobile): hand off navigation across session workspaces
* test(mobile): use resolved session identity in dictation fixture
* feat: edit queued messages inline with the chat composer
* fix: normalize queued edit skill ownership
* fix: retire phone skill selections with the sent draft
* fix: consolidate queued edit module imports
* fix: separate edit save action and stabilize image callbacks
* fix: complete phone inline composer integration
* fix: retain same-name queued skill selections
* fix: complete desktop queued composer integration
* test: use typed phone composer host lookups
* fix: include queued edit localization keys
* Preserve inline edit input across save and attachment completion
* Preserve complete queued edits across delayed saves
* Preserve queued edit input across delayed completion
* Complete queued edit lease loss and pending cancellation
* Use recognized queued edit test fixtures
* Consolidate queued edit imports and type host test spy
* Print mobile test compiler diagnostics on ratchet failure
* Correct queued edit integration fixture types
* fix: keep queued edit placeholder independent of readiness
* Fix queued edit compiler fixture boundaries
* Align upload completion test with ordinary insertion
* Combine mobile composer draft imports
* Load queued edit host fixture during test collection
* Size inline phone queue editor to available space
* Use typed scroll components in phone presentation contract
* Save typed text in a queued edit as written
The inline editor is the chat composer, so it keeps the composer's own identity model and no longer refuses Save for text that looks like a command. Command rows picked from the menu are still refused, Enter still means Save, and Edit never sends or runs anything.
Regression tests cover Undo after deleting an accepted command or skill pill, a scanned skill named like a command, two saved files behind one token, and typed command text saving literally. Also fixes two merge leftovers in tests: a missing import and a fixture that stored the whole menu row as a skill.
* Keep ordinary sends on the main composer contract
* Fix mobile composer imports and fixture loading
* Consolidate queued edit imports and duplicate regression
* Restore ordinary send catalog short circuit and repair test boundaries
* Preserve returned rich drafts through send acceptance
* Remove obsolete queued editor strings from new locales
Follow-ups from the large-file audit (E7):
- files.search had no byte budget: a result past the remote reply cap failed outright with
reply_too_large. It now keeps the whole files that fit and marks the result truncated, as
files.listAll and searchPaths already do. Local callers stay uncapped.
- A diff side past the 10 MiB read cap came back as binary, so the diff said "Binary file
changed" for a large text file. Blob reads mark it `tooLarge`, the binary diff result carries an
optional `tooLarge`, and both diff views say "File too large to diff". Older clients ignore the
field and keep the binary view. buildDiffResult builds its binary sides without a cast.
- The real-WSL lane pages a 100 MB file over the plain distro's shared control socket in the
editor's 384 KiB chunks and records the time, chunk count and orcad's VmHWM. Record-only: it
asserts nothing, and a failure is recorded in the receipt.
The terminal context menu only showed a close item when the tab had split
panes. A single-pane terminal now offers "Close tab", which sends the same
close request as Cmd/Ctrl+W. Pinned tabs and one-off command terminals keep
hiding it, matching the pane header's close button via a shared
canCloseTerminalPane/terminalPaneCloseLabel.
Fixes#20598
Co-authored-by: Neil <neil@stably.ai>
Fold every forge SSH host alias (ssh.github.com, altssh.gitlab.com,
ssh.bitbucket.org) through one shared normalizeForgeWebHost so main-process
provider detection and renderer manual review links agree. Remotes on
ssh.bitbucket.org now open bitbucket.org pull request forms instead of
ssh.bitbucket.org URLs, and Gitea no longer claims the host.
Co-authored-by: Eric Junker <4758+ejunker@users.noreply.github.com>
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
* fix(wsl-mover): a moved project's chat opens and answers here; a fenced terminal names its host
New e2e: a moved project's existing chat still answers after the move, run by this computer's
agent, with no host involved. Writing it surfaced two product gaps:
- Opening the chat from the moved project's sidebar entry asked its composer for focus before
the prompt editor mounted; tiptap threw and the terminal workbench crashed. The editor handle
now focuses (and answers contains) only once its view is mounted.
- A new terminal in a project its WSL host now owns (one a chat's agent might ask for) was
refused with "is moving ... try again when the move finishes". It now names the host and the
way back: use the project on this computer again.
* test(wsl-mover): wait for the moved chat's composer before typing and sending
Like #34573: the composer must be editable before typing, and hold the typed text before Send.
The statusline payload carries only the 5-hour and 7-day windows, so each
live post rebuilt Claude's usage without extraUsage and cleared the credit
balance until the next OAuth poll. Retain the last OAuth extraUsage alongside
fableWeekly. Automated Claude polls on main are gated only by Retry-After, so
credits keep refreshing on cadence.
Fixes#29755
Co-authored-by: Neil <neil@stably.ai>
File links in the terminal link popover now offer a "Copy file path"
button alongside the existing "Copy link" for URLs. The copied value is
the resolved filesystem path, so OSC 8 file:// links show the raw URI but
copy the decoded local path.
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Register Groovy as a lazily loaded TextMate language using Shiki's bundled
grammar, and map .groovy/.gvy/.gy/.gsh/.gradle, Jenkinsfile, Jenkinsfile.*
and *.Jenkinsfile (plus the groovy shebang) to it. Token scopes are mapped
onto ids the stock Monaco themes already style.
Fixes#25635
Co-authored-by: Neil <neil@stably.ai>
Double-click selected across spaces when a custom Word Separators value
omitted the space. The field was empty with the default (which starts with
an invisible space) only as a placeholder, so users rebuilt the list
without it. Like xterm.js, VS Code and Ghostty, Orca keeps respecting the
configured value exactly; the field now shows the default as its value,
offers Reset when customized, and warns when no space is included.
Fixes#28335
Co-authored-by: Neil <neil@stably.ai>
The terminal census collapsed sessions spread over more than one daemon
generation to a null protocol, so a host that kept a legacy daemon alive
across an earlier update refused every forced update and rollback while any
terminal ran. The census now reports every live protocol, and the crossing
check requires the incoming build to attach each one. The single-protocol
field stays for older desktops.
* fix(agents): open existing chat and retained pane destinations
* fix: keep agent navigation scoped to the current destination owner
* test: initialize dashboard tabs without a type assertion
* fix: complete navigation test and English catalog contracts
Adds kimi-code/k3 (Kimi K3) to the static Kimi model list, which is also
the allowlist, so planning with K3 no longer fails with "not available".
Drops the test that only restated the catalog.
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
A host decided files.read binaries by extension only. A binary with an extension it did not list
(.so, .o, .wasm, .class, ...) came back as text: under 512 KiB the editor opened it as text, where
a save writes the mangled text back; up to 50 MiB the desktop paged the whole file in before its
NUL check called it binary. Applies to WSL and SSH hosts and paired servers alike.
- The host checks the prefix it already reads for a NUL byte (git's own heuristic) and answers the
existing `binary_file` refusal, which every client handles.
- The desktop shows any binary that has nothing to preview (not an image or PDF) as binary at once,
without fetching a preview or paging (readRemoteBinaryFile); and treats text from an older host
whose first 8 KiB holds a NUL the same way.
Detect Vivaldi profiles on macOS, Windows, and Linux and import their
cookies like other Chromium browsers. On Linux, Vivaldi stores its
cookie key under Chrome's libsecret application name, so the browser
definition can override the keyring application used for the lookup.
Co-authored-by: Neil <neil@stably.ai>
* fix(ai-vault): keep post-delete list reads out of pre-delete scans
The scan coordinator shares an in-flight scan between callers with the same
key, but the key did not include the cache generation. A list read issued
after a session delete invalidated the cache therefore joined the scan that
started before the delete and was handed its stale result, so the deleted
session reappeared. Keep cache generations in the internal coordinator key
while retaining shared progress keys. Cover both completion orders,
same-generation sharing, late cache writes and progress ownership.
* fix(ai-vault): keep post-delete reads out of pre-delete multi-host scans
The IPC list handler coalesces through its own scan coordinator, so a read
after a delete could still join a pre-delete local, runtime or all-hosts scan
and get the deleted session back. Add both cache generations to that key;
remote-client deletes only bump the shared session-list generation.
---------
Co-authored-by: Neil <neil@stably.ai>
Explain the worker-stop receipt's processAction values in the recovery
guide and command notes: closed_agent_terminal means Orca already issued
the close (no follow-up terminal close), and stop_unknown exits 1 while
possibly still reporting closed_agent_terminal.
Fixes#13197
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Clarify that `terminal create --title`/`terminal rename` set the tab
title (reported under visualLayouts), that `set-pane-title` sets
`terminals[].paneTitle`, and that `terminals[].title` is not a stable
key. Document that `agentIdentity` is only the agent type and that the
`worktree ps` `agents[].paneKey` join locates a specific agent's pane.
Fixes#12674
Co-authored-by: Neil <neil@stably.ai>
Add a Devin Desktop preset to the Open In Apps catalog, launched via its
documented `devin-desktop` command (`devin` is the separate Devin CLI agent),
and make the setting searchable by "windsurf", "devin" and "devin desktop".
Co-authored-by: Anton Kuligin <anton.kuligin@notraffic.tech>
Co-authored-by: Neil <neil@stably.ai>
* fix(browser): restart Find when its native guest changes
* fix(browser): keep Find counter blank while the current search is pending
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* fix(remote): reconcile stale paired agent status and pane titles
Adapt the remote completion repair from #25205 while preserving sparse runtime pane slots through existing PTY bindings.
Co-authored-by: Manan Chataut <mananchataut@gmail.com>
* fix(remote): preserve pane identity when reconciling moved titles
Use mounted manager identities and retained PTY bindings ahead of replay order; keep fresh byte-stream ownership on the correct moved pane.
* fix(remote): release byte status fence after host approval
* fix(remote): tighten paired title removal and test the timing fence
- Share the byte-write timing decision (resolveRendererOwnedAgentStatusTiming) between pty-connection and the flap test so reverting it fails a test.
- Only delete a saved pane title when its durable leaf is absent from both the host layout and local panes; unresolved or surface-less slots are kept.
- Share the client-fence predicate (clientAgentStatusFencesHost) between status and title reconciliation.
- Cover removed-leaf spinner fallback in worktree status and a client split absent from a pre-split host snapshot.
* fix(remote): correct fence predicate and title test types
---------
Co-authored-by: Manan Chataut <mananchataut@gmail.com>
Expose the runtime's existing branchNameOverride on `orca worktree create`
so callers can name the Git branch (skipping the configured branch prefix)
while --name keeps setting the workspace name. A valueless or empty
--branch fails before any runtime request; omitting it leaves the request
unchanged. Document the flag in help, the spec notes, and the CLI
reference, including the numeric suffix applied when the name is taken.
Fixes#19541
Co-authored-by: Neil <neil@stably.ai>
Hosts filter with `updated_at >= ?`, comparing text against UTC
toISOString() values, so a `+08:00` cutoff was read as if UTC and a
whole-second `...00Z` cutoff dropped sessions in that same second
(`Z` sorts after `.`). The cursor key also hashed the raw spelling.
Normalize `since` to toISOString() in AiVaultSearchFiltersSchema. Every
client and host path parses this schema, so new clients send a cutoff
older hosts already compare correctly, and new hosts normalize whatever
older clients send before filtering or building the cursor key.
`Date` keeps milliseconds only, so a cutoff like `...00.0005Z` rounds up
to the next millisecond rather than letting in a session from before it.
Fixes#28253
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Register glimmer-js and glimmer-ts Monaco languages that embed
JavaScript/TypeScript as the background script and tokenize each
<template> block as a Handlebars island. Embed entry is bounded by the
shared line budget so long lines cannot blow the Monarch recursion
limit. Mobile maps the new ids to javascript/typescript highlighting.
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
* feat(wsl-mover): restore the host's browser link before a move, else say logins stay behind
A WSL host without its deployment link has no stable browser partition, so the move's cookie
copy carries nothing. Connecting a move's host now re-saves the live connection once when the
link is missing, which restores it whenever the pairing or ports were what broke it. When the
host still cannot keep logins, the move preview lists "browser sign-ins won't carry over";
the move is never blocked for it. The move result also names the reason the copy recorded.
* refactor(wsl): share the save-and-notify of a host connection; keep the service under its line cap
Rebased on main, wsl-host-service.ts went over 300 lines. The persist-then-retire-transports
step now lives in wsl-host-connection-record.ts (saveWslHostConnection), used by both the
connection-change handler and resaveConnection.
--peek and --all return no deliveryId, so a coordinator that loops on them
never acknowledges anything. Document that, note the 100-row cap for --all
and Run --peek, and give the drain recipe: repeat plain check with
--ack <previous deliveryId> until deliveryId is null.
Co-authored-by: 서정화 <seojeonghwa@Mac.mshome.net>
Co-authored-by: Neil <neil@stably.ai>
* fix(agent-status): preserve WSL Codex questions and child completion
* test(agent-status): route remote Codex question test through relay envelopes
* fix(agent-status): scope remote Codex roster removals to the reporting relay session
- Unlabelled relayed polls retire only children the current relay link listed;
each WSL link starts a new session, so a restarted guest's partial roster
cannot delete children an earlier guest reported.
- One removal rule: an explicit list, a done aggregate, or an unshed absent
roster proves removals; a shed roster with live work keeps the children.
Drops the old done-main-agent clear that wiped shed rosters.
- An unshed poll with no roster now retires the last child while the root works.
- Share the Codex hook event-to-state rule between local and relayed paths.
* feat(wsl-mover): D3a-2 per-distro move status line on WSL host rows, with decline causes and stale-record pruning (dark)
* fix(wsl-mover): key D3a-2 status records without the node-only journal module in the renderer
* fix(wsl-mover): D3a-2 pre-review — never prune user choices or on an unreadable host file; calm paused line; one-key blocker sentence; Show terminal feedback
* refactor(wsl-mover): D3a-2 distro-in-use check in its own module
* test(wsl-mover): the orchestrator host-connect test gives the new in-use port
* fix(wsl-mover): D3a-2 Check again runs as the user's act on the distro (stopped-distro gates)
* fix(wsl-mover): D3a-2 user steps use main's runAsWslUserAct gate; wiring test
The README misspelled "sponsored" in the Signed Builds section, and
CONTRIBUTING.md still pointed at a scheduled release-rc.yml workflow that
no longer exists (release-cut.yml is dispatch-only).
Fixes#14568
* fix(terminal): forward copies from Codex started in remote shells
* test(terminal): capture PowerShell copy probe exit status
* fix(terminal): preserve configured shell arguments for remote profiles
* test(terminal): isolate profile result from system startup output
* test(terminal): run native daemon shell probes with Node
* fix(terminal): keep an inherited remote-viewer marker out of local panes
An Orca started from a remote viewer's pane inherits ORCA_REMOTE_VIEWER_CLIPBOARD,
which would make every local Codex fake an SSH session. Treat the marker as
per-spawn identity so only explicit launch env carries it.
Geist 1.800 mapped U+03C9 (ω) to the capital Ω glyph. Upstream fixed the
cmap in v1.8.1 (vercel/geist-font#248); v1.8.2 drops the bad mapping so ω
falls back to the system font while Geist keeps its Λ λ μ π Ω glyphs.
Vertical metrics are unchanged.
Fixes#23152
Co-authored-by: Neil <neil@stably.ai>
* fix(worktrees): finish a delete Git left half-done on macOS and Linux
git worktree remove drops the registration even when its recursive
delete fails partway, on every OS. The in-operation recovery that
finishes that delete was gated to win32, so on macOS and Linux the
leftover folder was recorded as failed and every retry was refused.
Run the recovery on all platforms with its existing gates (Git lock,
strict listing without the path, watchers closed first). Later retries
and the startup finish still never delete an unregistered folder.
Fixes#33023
* fix(worktrees): skip partial-delete recovery over a nested worktree
* fix(worktrees): leave orphan errors to the proven orphan cleanup
* test(worktrees): fail Orca's follow-up delete where a partial delete must stay listed
Git-deregistered partial deletes are now finished by Orca on every OS, so the
mocked IPC and runtime leftover tests make that follow-up delete fail too.
Co-authored-by: Anmol Bhardwaj <anmolbhardwajinv@gmail.com>
* fix(worktrees): finish a recovered partial delete like a normal one
Skip the repo-wide git worktree prune (Git already dropped the row; prune
can unregister checkouts on unmounted drives) and verify the row is gone
instead. Delete the branch with the normal -d policy rather than always
preserving it, so merged branches go and unmerged ones are kept.
* refactor(worktrees): drop the dead force arg and run the live partial-delete test on macOS
Co-authored-by: Anmol Bhardwaj <anmolbhardwajinv@gmail.com>
* test(worktrees): match main's signal arg and locked-file delete error in partial-delete tests
Co-authored-by: Anmol Bhardwaj <anmolbhardwajinv@gmail.com>
* fix(worktrees): surface the nested-worktree refusal and keep the live race test Windows-only
The recovery swallowed the nested-worktree guard into 'not recoverable', so users saw
Git's 'Directory not empty' instead of the nested-worktree message. The live churn race
flaked on macOS when Git won the race; real-Git darwin runtime tests already cover it.
* refactor(worktrees): drop the redundant post-delete registration re-check in partial-delete recovery
The predicate already proved Git dropped the row; re-listing after the rm
only turned a listing failure into a misleading stale-registration error
after the folder was gone. The force argument is now always true for the
remaining stale-registration caller.
---------
Co-authored-by: Neil <neil@stably.ai>
* fix: keep setup command short so it survives shell startup
The POSIX setup wrapper inlined the runner path six times. On long
worktree paths it exceeded macOS's 1024-byte canonical PTY input limit
when typed while the shell was still sourcing rc files, so the tail and
submit byte were dropped, setup never ran, and a gated agent waited
forever.
Carry the setup script in ORCA_SEQUENCED_SETUP_SCRIPT and submit only
`bash -lc 'eval "$ORCA_SEQUENCED_SETUP_SCRIPT"'`, mirroring the
existing startup gate. The env is merged into setup.envVars wherever the
gate is built so provisioning, fallback retries and renderer tabs/splits
all receive it, and WSL imports it untranslated.
Closes#20935
* fix(wsl): forward the setup script WSLENV entry before the daemon boundary
Same-protocol daemons from older builds rebuild WSLENV without
ORCA_SEQUENCED_SETUP_SCRIPT, so WSL setup would eval an empty script and
the gated agent would wait for its timeout. Also swap new spec casts for
the spawnedPtyEnv fixture.
* fix: fail loudly when the sequenced setup script env is missing
A dropped ORCA_SEQUENCED_SETUP_SCRIPT made eval run an empty string and
exit 0, silently skipping setup. ${VAR:?missing} reports it in the Setup pane.
---------
Co-authored-by: Neil <neil@stably.ai>
* test(wsl): cover a WSL-host orcad and the desktop at different versions
The desktop and a WSL distro's orcad update independently, and nothing pinned how they meet.
- wsl-orcad-update-admission.test.ts: an older desktop keeps the newer orcad another desktop
deployed serving, with no Update hint, and deploys over it only on Force; a policy refusal over
that host carries orcad_host_newer. A newer desktop updates an older orcad on connect, keeps it
serving with a Force hint after a failed update, and treats an orcad from before app provenance
as updatable.
- cross-version-wire/wsl-host-desktop-downgrade: against the baseline release, a newer desktop
withholds the agent-session account env from a WSL orcad that never advertised it (the release's
strict schema accepts the call), sends agent.launch's account env the release accepts, and reads
the release's errors with no drive refusal; an older desktop reads the Windows-drive refusal as
the readable runtime_error it already shows.
* test(wsl): type the unused Claude preparation, and read the older orcad's error with the newer desktop's unwrap