* test(wsl): measure reboot to first prompt for the WSL host and the old bridge
The real-WSL lane's phase 3 now terminates its distro and times, median of 3 each, how long a
WSL-host project takes to a working terminal (holder boot, orcad start, terminal spawn and an
answered command) against one wsl.exe into the login shell, as the old bridge does. Test-only.
* test(wsl): relabel the restart baseline honestly and raise the WSL2 job cap
The baseline measures distro restart (VM warm) to first command output; the old-bridge figure is a
non-PTY wsl.exe --exec lower bound, not ConPTY first paint. The job cap rises to 40 minutes so it
no longer undercuts phase 3's own 30-minute timeout.
* fix(wsl-mover): ask before quitting while a WSL project move runs
A plain quit (Cmd/Ctrl+Q, closing the window) during a move gave no warning: the next launch
recovers safely, but the move is lost. While main reports a move running, the window's close
guards now ask "A project is moving to its WSL host. Quit anyway? The move will be undone at next
launch." Behind the internal WSL hosts gate: the guard registers only with it open, and main
answers false with it closed. An intentional restart and an unanswered question never prompt.
* fix(wsl-mover): the quit prompt's running check gives up after 2 s
The close request is acknowledged before the guards run, so a hung wslMove:isRunning would block
quitting for good. The guard now treats no answer within 2 s as no move running, like a failed
ask.
* test(wsl-mover): give the flag-off host test the confirmation dialog the quit guard reads
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* fix(wsl-move): undo connects the WSL host after a relaunch, and startup heals a crash mid-pairing save
Undo is the user's act, so it connects the host the way Connect would before asking it anything;
without that, a relaunched host with no open work refused every undo as unreachable.
A crash between refreshWslHostPairing's two writes leaves the record's bridge port off the saved
pairing, and the overlay then drops the WSL link. Startup realigns the record to the pairing.
Co-Authored-By: Claude <noreply@anthropic.com>
* test(wsl-hosts): startup realigns bridge ports before any bridge connects, and not while gated off
Co-Authored-By: Claude <noreply@anthropic.com>
* test(wsl-move): undo's terminal stop never connects a host whose Remove is pending
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* feat(i18n): add German UI language
Register German (de) as a built-in UI language alongside en, zh, zh-Hant,
ko, ja, es, fr, pt, ru and tr: language setting, system-locale detection
(de, de-DE, de-AT, de-CH, de_DE.UTF-8), renderer and main-process lazy
catalogs, Settings picker label in every catalog, the "Sprache" settings
search keyword, onboarding picker, docs and a German glossary and style
guide (config/i18n-german.md).
de.json covers every en.json key except the sparse-policy entries that must
fall back to English (status-bar credit balance copy and the incomplete goal
change notice). Tests that used de-DE as the unsupported system language now
use it-IT.
The catalog is machine-translated, reviewed by two independent passes with
arbitrated fixes, and swept for terminology consistency.
* feat(i18n): translate German strings added on main
Translate the 158 keys main added since the branch point (WSL project move,
diff size limits and others), reviewed by two passes like the rest of the
catalog, and drop the 5 keys main retired.
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* fix: release dictation when renderer documents retire
* fix(speech): give each renderer document its own dictation owner
A reload during a pending local startup let the successor document's first
run reuse the retired owner key and silently join the canceled startup.
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* feat(wsl): say WSL hosts are paused in serve-only
Settings → WSL hosts explains that saved hosts and their projects keep working
while adding hosts and moving projects is paused, and the add-host menu says
where Add WSL distro went. Shown only after main answers serve-only, so open
and closed gates never show it.
* style(wsl): use the text-2xs token for the paused add-host line
* test(wsl): give the move status-line test's hosts mock the paused hook
* fix(i18n): drop queued-message keys #31180 removed from en
#31180 removed components.native-chat.queuedMessages.cancelEditing, .pasteOrDropImages and
.saveNeedsContent from en.json; tr.json, added since, still carried them, so the locale catalog
check failed on main. They are the only stray keys in any locale.
* fix(lint): split runtime-file-read-client under the max-lines cap
Move the remote chunked-download helpers into runtime-remote-file-download.ts so
runtime-file-read-client.ts is back under the 300-line lint cap. No behaviour change.
* fix(terminal): preserve Codex exit queries across snapshot restore
* fix(terminal): replay owed queries from discarded output and superseded replays
- Mark yielded queries discarded when ACK overflow trimming, the recovery
cutoff, or snapshot-buffer overflow drops their output unsent, and replay
them after the covering snapshot (including the ACK recovery snapshot).
- Purge snapshot query replays and escape-tail frames still queued behind the
ACK window when a newer snapshot is published, and put new replays at the
head of the ACK queue so they follow their snapshot.
- Share isPrimaryDeviceAttributesQuerySequence across DA1 call sites.
- Move replayed-query settlement into settleReplayedYieldedQuery.
* fix(terminal): replay DA1 still batched when an ACK recovery snapshot covers it
* test(terminal): type the overflowed initial-snapshot stream fixture
* test(terminal): give the host-chain recovery stream fixture its required query and batcher state
#34650 removed dashboardPopout.terminal.closed from en.json, but ru.json,
tr.json and zh-Hant.json kept it, so verify:localization-catalogs fails on
main ("extra: dashboardPopout.terminal.closed"). Drop it from those three. No
other locale has extra keys; ru's _few/_many plural forms are expected.
* feat(wsl): admit no new WSL host or move in serve-only
Main refuses wslHosts:add while the internal gate is closed and reports
acceptsNewWork in the WSL host capability; moves already need the open gate.
The renderer hides Add WSL distro and every Move entry point once main says
serve-only, while saved hosts keep their Connect and Remove.
* fix(wsl): offer no new WSL host or move until main answers the capability
The renderer's acceptsNewWork now starts false, so Add WSL distro and the Move
entries never flash in serve-only before the capability reply.
* fix(wsl): point the WSL-bridge chat refusal at Add WSL distro only when it is offered
In serve-only Add WSL distro is hidden, so the refusal keeps its terminal-only
words there.
* test(wsl): give the move status-line test's hosts mock the new-work hook
#34676's test mocks use-wsl-hosts-enabled; WslHostsSection now also reads
useWslHostsAcceptNewWork, which follows the same gate there.
* 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>