mirror of
https://github.com/stablyai/orca.git
synced 2026-11-02 01:57:57 +00:00
a57e027cb3894d2d84a7dfd2d32211699caffaf6
3453
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
a57e027cb3 |
fix(orcad): one update decision for SSH and WSL, with connect, Update and Force intents (#31682)
* fix(wsl-host): apply the SSH update rules to a WSL connect A WSL connect deployed this app's orcad whenever the active build differed, so a distro another, newer Orca had activated ended in an unconnectable deferral, and a build the host had rolled back from was re-activated on every connect. It now runs the same planManagedOrcadAutoUpdate decision SSH uses and serves the active build when that plan skips. * fix(wsl-host): serve the skipped-update incumbent only if it is still active * fix(orcad): one update decision for SSH and WSL, applied again under the fence The connect's update plan moves to its own module, and the fenced admission every first deploy and WSL connect uses now applies it, so a transient readiness failure or a race cannot reactivate a rolled-back build. * refactor(wsl-host): adopt the serving build through the SSH first-deploy adoption * fix(wsl-host): do not retry a candidate rejected earlier this launch A rejected candidate is restored without recording rolledBackFrom, so WSL retried it on every connect. SSH's launch-scoped failure memory moves to a shared helper keyed per host, and WSL feeds it to the shared update decision. A null adoption now re-reads the record before the deploy, as SSH does. * fix(wsl-host): keep Force offered when a connect skips this app's build The WSL connect's update admission moves to its own module, which returns a forceable hint when it serves the incumbent over a rolled-back or failed build. * fix(orcad): connect, Update and Force are the three update intents on SSH and WSL Only a connect applies the skips; an explicit Update retries a skipped or failed build (clearing the failure memory) behind the terminal census; only Force passes the census. WSL's Update button now sends the update intent instead of a plain connect. A connect that records a rejection serves the restored incumbent at once. * fix(orcad): an explicit Update keeps the newer-host skip on SSH and WSL Update lifts only a host's own skips (rolled back, failed before); Force lifts all. SSH's explicit update now applies the same fenced admission, so stable cannot replace a host a newer Orca activated without Force. A rejection retries adoption only once the incumbent is back and no journal awaits recovery. * fix(wsl-host): keep Update and Force offered on a deferred cold connect A connect the host deferred with nothing serving now publishes its deferral, so a host stopped after a rollback can still be retried from the UI. SSH's explicit Update forgets a remembered failure only once it lands, and a restore that could not be read keeps the rejection as reported. * fix(wsl-host): offer only Force over a host a newer Orca runs, and keep failure memory until an Update lands A host-newer refusal gets its own deferral code so the WSL row hides Update, which can never pass it. An explicit Update no longer clears the launch failure memory up front; only a landed deploy does, as on SSH. * refactor(wsl-host): merge the intent type into the existing shared imports |
||
|
|
10d410455e |
Repair only the damaged parts of a profile instead of rolling back the whole profile (#30931)
* fix(profile-state): repair only the damaged sections instead of rolling back the whole profile One profile-state row whose checksum no longer matches made Orca refuse to open the whole profile, and the only recovery swapped in a whole backup, losing every newer change in every section. Startup now checks each section on its own. When only some sections are damaged, the dialog offers "Repair and Restart": Orca copies the damaged database aside, then replaces just those sections with the newest backup copy that passes its checks, keeping every intact section's current value, in one checked write. Headless starts name the damaged sections and `orca profile state rollback --repair`. Whole-file damage keeps the existing backup rollback. Also turns on checkpoint_fullfsync for the profile database: on macOS plain fsync leaves the drive cache unflushed, which can leave a long row's pages from two versions after a power loss or forced shutdown. Tracked in STA-10893. * test(profile-state): pin the repair dialog's text and buttons * fix(profile-state): list the repair module for the CLI typecheck; keep section labels out of the layout-writer count * fix: validate section repair admission before decoding payloads * fix(profile-state): share backup handles during section repair |
||
|
|
fcac5cf0f0 |
refactor(native-chat): one shared parser for the approval envelope (#30848)
The phone and the desktop each parsed the status row's
{ approval: { tool, summary } } envelope with identical validation.
Move that validation and the answer keys into
src/shared/native-chat-approval-envelope.ts; each caller still builds
its own card (phone English labels, desktop translated labels) and
keeps its own precedence and heuristics. No behaviour change.
Co-authored-by: E2E Test <e2e@test.local>
Co-authored-by: Claude <noreply@anthropic.com>
|
||
|
|
7df3ff76d5 |
fix(sidebar): keep each host's activity, PR lane and retention apart for a same-id workspace (dark) (#31517)
* fix(sidebar): land flip-a's switch-on fixes on main, still dark (no flip) The fixes found while the sidebar ran from main's feed, without turning that on: ORCA_SIDEBAR_INPUTS_IS_MAIN stays false, and none of flip-a's stop-gaps come along. - Main stops counting a closed tab's terminal once it was asked to stop and no pane shows it; a failed or refused stop takes the request back, and a host out of contact stays unverifiable. - Main fetches a paired server's rows with the transport's normal bound, so a server slower than 5 s still lists. - A deferred unread raise flips the folder row it was decided for, and a read while the owner check is out cancels it. - Phone: no worktree.ps flash before the feed; a host's last installed feed stands in while a fresh read is out; it highlights its own last-opened workspace and a fold never scrolls; projects file under their project group with the desktop's indent; the collapse spinner shows only after 500 ms; "Reopen to update" keeps the rows on screen. - e2e helpers and journeys for the switch-on suite. * fix(sidebar): keep an owed relay stop marked, clear a thrown stop, and prune pending unread raises - A stop-and-wait that throws takes its stop request back, like one that fails. - A final stop the legacy relay still owes stays marked, since the relay retries it. - Pending unread raises are tracked only while one waits, so the map no longer grows. - The paired-server fallback poll test gives its third fetch a reply and asserts live. * fix(sidebar): key activity, review, retention and repo lookups by host, not bare id The feed published activity and PR facts by bare workspace id, so a same-id workspace on two hosts (a paired server and this desktop, or two servers) shared one host's facts. Activity and review are now keyed by row identity, retention sweeps per listed row, and the builder resolves each row's repo by its own host's registration. Bumps the feed semantics version to 3. Claude-Session: e1fdb3d9-6985-4693-84aa-9a9fad732103 * test(e2e): read a review fact by its row key Claude-Session: e1fdb3d9-6985-4693-84aa-9a9fad732103 * test(mobile): key fixture rows by row identity Claude-Session: e1fdb3d9-6985-4693-84aa-9a9fad732103 * fix(sidebar): count and label rows by their own repo registration; keep activity keyed through a failed listing Host header counts and mixed-host labels read the row's own registration, as the host filter and row now do, so one render cannot file a row under one host and count it under another. A failed listing keys activity by the last rows. Claude-Session: e1fdb3d9-6985-4693-84aa-9a9fad732103 * fix(sidebar): a slower older listing never overwrites a newer one's rows Claude-Session: e1fdb3d9-6985-4693-84aa-9a9fad732103 * test(sidebar): read the quiet-desktop PR fact by its row key Claude-Session: e1fdb3d9-6985-4693-84aa-9a9fad732103 * refactor(repos): the renderer re-exports the shared getRepoHostIdentity Claude-Session: e1fdb3d9-6985-4693-84aa-9a9fad732103 |
||
|
|
2b716f601d |
fix(mobile): attach Codex images when agent identity is missing (#31370)
Use the shared desktop image-path formatter for unidentified mobile terminals and identified Codex, preserving owned agent identity, bracketed paste, upload routing and acknowledged-input ordering. Adapted from community PR #30310 by Wooseong Kim (@innocarpe). Fixes #30298. Validation includes exact-head CI and aggregate gate, native macOS and Windows consumer checks, actual paired Windows uploads with byte-integrity checks, and combined mobile/desktop tests, types, lint and bundle checks. Co-authored-by: Wooseong Kim <innocarpe@gmail.com> |
||
|
|
4a671db940 |
fix(native-chat): retire ghost queued continuation bubbles
Retire queued continuation echoes when transcript rows represent the sends. Preserve literal backslashes, occurrence boundaries, and image barriers on mobile and desktop. Closes #15288. Adapted from #15283. Co-authored-by: grboy6770 <2000grboy@gmail.com> |
||
|
|
524fa0039b |
Preview HTML files by default (#31575)
* Preview HTML files by default across file-opening flows * Centralize workspace file opening and HTML preview routing - Preserve source, diff, and host-safety behavior across all entry points - Reuse previews within the requested editor group and support branch HTML files * Respect working tree state when opening HTML previews * Route HTML links through owning workspaces and editors * Preserve HTML preview state and refresh changed local files * Resolve HTML preview hosts from workspace ownership * Update file-opening tests for HTML preview routing |
||
|
|
87ca5dad43 |
refactor(repo-host): one helper for the hosts registering a repo id (#31634)
Four twin checks (sort stamps, sidebar unread, sort-order notify, worktree-meta owner) each rebuilt new Set(repos.filter(id).map(getRepoExecutionHostId)). Name it once as getRepoRegistrationHostIds in shared/repo-host-identity. |
||
|
|
553a704403 |
refactor(execution-host): one resolver for the SSH target a repo row may dial (#31623)
getStoredRepoSshConnectionId and seven inline getSshTargetIdForExecutionHost(getRepoExecutionHostId(repo)) spellings answered the same question. Name it once in shared (getRepoDialableSshTargetId), build getRepoRoutingConnectionId on it, and drop the getRepoHostedReviewExecutionHostId re-export alias in favour of a direct import of getStoredRepoExecutionHostId. |
||
|
|
7d43ed9024 |
refactor(orcad): share one unverifiable terminal census (#31633)
Five modules each declared the same all-null census for a daemon that could not answer; they now use one frozen constant from the shared census module. |
||
|
+4 |
ae64a215a3 |
Add Brazilian Portuguese UI localization (#31627)
* feat(i18n): add Brazilian Portuguese * feat(i18n): migrate legacy Portuguese plugin preferences once Co-authored-by: Jackson Florencio <120382485+Jackson-Florencio@users.noreply.github.com> Co-authored-by: joaoguilhermesimon-collab <245094637+joaoguilhermesimon-collab@users.noreply.github.com> Co-authored-by: heslanx <167841364+heslanx@users.noreply.github.com> Co-authored-by: Janderson Fagner <135186779+janderson-fagner@users.noreply.github.com> Co-authored-by: Carlos <141033701+eusoucarlosmedeiros@users.noreply.github.com> Co-authored-by: João Messias Lima Pereira <41351492+joaorura@users.noreply.github.com> Co-authored-by: oliveiraleandro1981 <46854234+oliveiraleandro1981@users.noreply.github.com> Co-authored-by: hugolrf <12834009+hugolrf@users.noreply.github.com> Co-authored-by: Jonathan Oliveira <50607380+eojonathan@users.noreply.github.com> Co-authored-by: Antenor e Filhos <278213917+antenorefilhos@users.noreply.github.com> Co-authored-by: Daniel Dias <85655597+DeVFirmino@users.noreply.github.com> Co-authored-by: Sergio Alexandre de Carvalho <65230046+salexcarvalho@users.noreply.github.com> Co-authored-by: “Melostack” MeloX <229196278+Melostack@users.noreply.github.com> * rm design doc * fix(i18n): complete Brazilian Portuguese catalog --------- Co-authored-by: Jackson Florencio <120382485+Jackson-Florencio@users.noreply.github.com> Co-authored-by: joaoguilhermesimon-collab <245094637+joaoguilhermesimon-collab@users.noreply.github.com> Co-authored-by: heslanx <167841364+heslanx@users.noreply.github.com> Co-authored-by: Janderson Fagner <135186779+janderson-fagner@users.noreply.github.com> Co-authored-by: Carlos <141033701+eusoucarlosmedeiros@users.noreply.github.com> Co-authored-by: João Messias Lima Pereira <41351492+joaorura@users.noreply.github.com> Co-authored-by: oliveiraleandro1981 <46854234+oliveiraleandro1981@users.noreply.github.com> Co-authored-by: hugolrf <12834009+hugolrf@users.noreply.github.com> Co-authored-by: Jonathan Oliveira <50607380+eojonathan@users.noreply.github.com> Co-authored-by: Antenor e Filhos <278213917+antenorefilhos@users.noreply.github.com> Co-authored-by: Daniel Dias <85655597+DeVFirmino@users.noreply.github.com> Co-authored-by: Sergio Alexandre de Carvalho <65230046+salexcarvalho@users.noreply.github.com> Co-authored-by: “Melostack” MeloX <229196278+Melostack@users.noreply.github.com> |
||
|
|
b128cbbd6a |
fix(chat): route OMP question cards to terminal (#31440)
Route terminal-backed OMP question cards to the owning terminal and refuse automated ask answer/cancel writes. Preserve structured semantic answers and recognize OMP multi-select metadata. Verified against the real OMP selector source; automatic semantic card answering remains tracked in #26692. Co-authored-by: jairuspace <jairusbpace@gmail.com> Co-authored-by: Daniel Lopez <dafeloru@gmail.com> |
||
|
|
b3f1607c84 |
Remove directional tab-close actions and tracking (#31599)
* Remove directional tab-close menu actions * Remove retired directional-close translations * Remove retired directional-close PostHog tracking * Preserve legacy app-open schema compatibility without emitting samples |
||
|
|
2b1da76d70 |
fix(mobile): a windowless server warns about unwakeable agents only when the workspace really has slept agents (#31607)
* fix(mobile): warn about unwakeable agents on a windowless server only when the workspace really has slept agents
A running ('live') or quit agent's resume record made a windowless server tell the phone
'Open Orca on the host to wake sleeping agents.' Count only real sleep records, using the
shared isPaneSleepRecord predicate.
* refactor: move the headless wake check into a shared hasSleptAgentToWake predicate
* docs: say a live or quit record is resumed by its own pane
* test: build the headless wake report store from the shared partitioned fixture, without casts
* test: make a host-less session read miss the record, so the SSH partition test proves the named-host read
|
||
|
|
52f6960751 | fix(mobile): closing the tab a phone shows returns it to the tab it viewed before (#31365) | ||
|
|
22154de422 | feat(mobile): tab strip shows terminal and chat agent state, reveals the next tab, renames on double-tap (#31372) | ||
|
|
ba8f237ade | fix(mobile): phone terminal and chat tab names match the desktop (#31384) | ||
|
|
3d6a945b54 |
Mobile chat composer uses desktop's placeholder (#31425)
* Mobile chat composer uses desktop's placeholder The phone composer said "Message Codex" (or "Message, @files, /commands") while desktop says "Ask anything, @ to mention files, / for commands". Both now read the one string from the shared native-chat copy table, so they can't drift again. * Ship the shared composer placeholder in the runtime English catalog * Keep the composer placeholder beside, not inside, the turn-status copy The turn-status table maps one-to-one onto the status.* catalog keys; the placeholder is composer.placeholder, so it gets its own constant and its own catalog check. * Read the composer catalog without a type assertion * Read the composer placeholder from the typed catalog directly |
||
|
|
d4a7494c47 |
A Codex chat shows the turns made outside it (#30883)
* feat(native-chat): a Codex chat shows the turns made outside it When a Codex chat's agent opens its conversation, Orca reads Codex's own saved turns and adds the ones the chat doesn't have, after the chat's rows, behind a quiet "Continued outside this chat." divider. Turns are matched by Codex's own turn ids, read whole before anything is written, and never re-imported; a chat imported from a transcript gets only turns started after the import. A failed or partial read adds nothing and the next open tries again. * chore: remove accidental package manager lockfile churn * test(codex): expect restored turns with optional timestamps * test(codex): run catch-up SQLite fixtures under Node * Make Codex history catch-up atomic and preserve adoption origin * Keep imported Codex history separate from send delivery * Derive Codex history reconciliation from saved journal facts * Validate saved Codex turn identity before planning its notice * Align Codex catch-up fixtures with current journal contracts * Check oversized item echo declarations by their saved identity * test: retain Codex catch-up flow and concrete regressions |
||
|
|
8174208436 |
fix(terminal): every start in a deleted folder opens at the workspace root and says so (#30747)
* fix(terminal): start every terminal in the workspace root when its saved folder is gone, and say so A terminal whose start folder was deleted opened at the workspace root only when the window started it; the CLI, phone restore, orchestration and other host starts failed with the raw "Working directory ... does not exist" error, because the renderer had to opt in with a cwdFallback flag. The execution host now makes that decision for every start that may create a process (anything but a known live pane), with no caller flag. The fallback is reported only when a process was proven started at the requested folder, so a reattach or a crash restore in its recovered folder never claims "workspace root", and only then does the saved start folder heal. A recovered folder that was itself deleted yields to the request's resolved folder. The notice reaches each caller through surfaces it already has: the window's terminal line (now also written when a host-created tab attaches, and on a restored reattach), the agent.launch warning (also for callers that show the tab themselves), and a toast when a phone tap restores the tab. * fix: recover terminal startup folders without healing unreadable paths * fix: preserve saved start folder when revealing fallback terminals * Reuse saved-tab lookup in cwd persistence test * Import mobile tab activation result from its contract * Check startup fallback response without a test cast * Update expectation to catch up with accepted saved-folder metadata * Preserve inconclusive WSL directory evidence when saving startup folders * fix: project restored terminal ids for handle consumers * test: return void from launch parity cleanup * test: retain saved folder in shared spawn expectation |
||
|
|
0eb2403ef8 |
Main closes, renames, sleeps and starts new tabs' terminals itself, with or without a window (dark, layout-owner build only) (#31571)
* fix(terminal): a CLI or phone close through main keeps a pinned tab, as the window-era close did * feat(terminal): main applies phone closes, renames, pane titles, sleeps and agent-launch tabs itself, with or without a window (layout-owner build) * fix(terminal): a tab close through main also ends the tab's dormant SSH relay session; agent-launch tabs main writes keep the window's focus rule, prompt and outcome (layout-owner build) * feat(terminal): main starts a new tab's first terminal under the window's ids, and the pane attaches to it (layout-owner build) * test(terminal): tests that counted the window's create calls or publications see main's start of a new tab * fix(terminal): an account-switch restart binds only into a pane main's layout holds, with no window tree (layout-owner build, L197) * test(terminal): the new-tab start tests hold no type assertions; the start's ledger row is L252 * fix(terminal): a new tab's pane attaches to any terminal main binds it to, and a failed start keeps the tab's startup * test(terminal): a Claude account-switch restart's tests draw the pane main holds * fix(terminal): a launch pane main takes back still tells the window why, which drops its hold and prompt; a rename through main answers the name it stored (layout-owner build) * fix(terminal): a new tab another owner's terminal won attaches to it, and a failed start's startup never runs twice * fix(terminal): main's pane close reads its stop result as a type only, so no runtime module loads before its base class * fix(terminal): a phone's close through main drops the closed rows from what phones list at once, not at the window's next publish (layout-owner build) * chore(terminal): mark the phone-close row drop interim (ledger L260) * test(worktree-create): a new local tab's startup counts as run when main starts it (layout-owner build) * test(worktree-create): record main's starts without a type assertion * fix(terminal): a tab closed before main declines its start gets no startup back (layout-owner build) * fix(terminal): the window's new tab starts in main when its own window asks over IPC, which labels itself a client (layout-owner build) * fix(terminal): a refused close of a pinned tab leaves its running agent launch alone (layout-owner build) |
||
|
|
84a9f995da |
Sidebar P2: main publishes without waiting on git, and every source announces its changes (inert) (#31513)
* fix(sidebar): land flip-a's switch-on fixes on main, still dark (no flip) The fixes found while the sidebar ran from main's feed, without turning that on: ORCA_SIDEBAR_INPUTS_IS_MAIN stays false, and none of flip-a's stop-gaps come along. - Main stops counting a closed tab's terminal once it was asked to stop and no pane shows it; a failed or refused stop takes the request back, and a host out of contact stays unverifiable. - Main fetches a paired server's rows with the transport's normal bound, so a server slower than 5 s still lists. - A deferred unread raise flips the folder row it was decided for, and a read while the owner check is out cancels it. - Phone: no worktree.ps flash before the feed; a host's last installed feed stands in while a fresh read is out; it highlights its own last-opened workspace and a fold never scrolls; projects file under their project group with the desktop's indent; the collapse spinner shows only after 500 ms; "Reopen to update" keeps the rows on screen. - e2e helpers and journeys for the switch-on suite. * fix(sidebar): keep an owed relay stop marked, clear a thrown stop, and prune pending unread raises - A stop-and-wait that throws takes its stop request back, like one that fails. - A final stop the legacy relay still owes stays marked, since the relay retries it. - Pending unread raises are tracked only while one waits, so the map no longer grows. - The paired-server fallback poll test gives its third fetch a reply and asserts live. * feat(sidebar): main publishes cached snapshots and announces its own stores; detected, view and live-content domains (inert) * fix(sidebar): keep links confirmed only by a new git run, no rescan on metadata writes, no phone wait on detected checkouts * fix(sidebar): rescan the workspace rows when the Windows runtime default changes * test(sidebar): gate the 1000-workspace publish cost on its median * test(sidebar): name the git-run token type in the identity test |
||
|
|
cfd9e9c82c |
Editor tabs main-first: the window asks main for every editor tab change and draws only main's update (dark, layout-owner build only) (#31438)
* Editor tabs main-first: the window sends each editor action to main and draws editor tabs only from main's frames (dark, layout-owner build only) With ORCA_LAYOUT_OWNER_IS_MAIN on, editor open (preview replace, open to the side), reopen at its position, close, close-all, promote, the resolved conflict placeholder, a restored-editor move and a disk rename are layout commands the window sends before it draws anything. Window content (open file records, drafts, buffers, the autosave gate) is written at once for an open, and rekeyed in the same store update as the frame for a move or rename. Main publishes each non-edit tab's view, keeps views in memory across applies and partition changes, stamps the tab's time, folds same-file duplicates, and republishes a windowless host's session.tabs after its own file open. The switch-off path is unchanged. * test(d3a-2): the buffer view index follows a rename main answers * test(e2e): the old-phone baseline records a windowless server opening the file, as main does since #31291 (test only) |
||
|
|
6970cad0d3 | feat(browser): ask before sites use camera, mic or location (#31158) | ||
|
|
5dd5294294 |
perf(orcad): keep the server bundle ASCII so V8 stores it at half the size (#31495)
* perf(orcad): keep the server bundle ASCII so V8 stores it one byte per character 129 non-ASCII characters in regex literals (and three in a String.raw plugin template) made V8 hold the whole ~11 MB orcad-server.js source as a two-byte string. Write them as \uXXXX escapes and fail the build if any return. Co-Authored-By: Claude <noreply@anthropic.com> * fix(orcad): keep license comments ASCII and name the module the guard trips on esbuild gathers legal comments at the end of the bundle; escape that comment so a dependency's license text cannot fail the ASCII guard, while licenses still ship with the code. The guard now reports the offset and, from an unminified rebuild, the module the character came from. A dark-switch build is covered. Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
abce679f88 |
feat(accounts): add Claude and Codex accounts on a remote server from a paired client (#31461)
* feat(accounts): add Claude and Codex accounts to a remote server from a paired client The server runs the login (Claude's pasted-code sign-in, Codex's device code) and keeps the credentials; the client only opens the page and relays the code. New capability-negotiated accounts.hostSignIn* methods; older servers degrade to an update message. Fixes #20009 * fix(accounts): settle host sign-ins by the login's outcome, pin pairing, guard stdin Review fixes: a cancel the login refused still reports the added account; the client pins each flow to its pairing revision; Claude's stdin EPIPE no longer crashes the host; both sides accept only the provider's sign-in host; pasted codes reject whitespace; a host-side supersede says so; Codex cancels its own login handle; opt-in real-CLI test. * fix(accounts): keep an early Cancel, retry polls through the host's window, scan Claude stdout only A Cancel clicked before the host answers the start is applied once it does; the client keeps polling through a short outage until the host would have ended the flow; Claude's link is read from stdout only; a timeout after the code was sent keeps its own message. * test(accounts): name the stdin sender and allow its stream write in the persistence ratchet * fix(accounts): register each Add Account attempt before its first await One attempt per click owns the pairing pin, the cancel flag and the host's flow, so a Cancel during the capability check or the start is never lost; polls retry only lost answers; the stdout forwarder is removed on cleanup. * refactor(accounts): drive the client sign-in as one explicit state machine per server and provider checking -> starting -> following -> settled, with Cancel as a flag the run loop delivers once the host names its flow and re-asks after any answer until the host takes it. A second Add joins the running attempt; a Cancel the server could not be told about is surfaced; retry policy tested. * fix(accounts): retry a lost start answer, and report a cancelled sign-in as cancelled however it ends * fix(accounts): Cancel reaches the server a sign-in runs on, and a typed code stays with its flow The host picker can move while a sign-in runs: Cancel now targets the running server sign-in (or the desktop's own login when none runs), and the pasted-code form is keyed by server and flow. * fix(accounts): run at most one server sign-in per provider, so Cancel has one meaning * fix(accounts): the server sign-in prompt owns its Cancel Cancel for a server sign-in now lives in the prompt bound to that server, which also reappears with its Cancel when Settings reopens mid-sign-in; the header Cancel is only for this desktop's own login. * fix(accounts): hide the desktop-login Cancel while a server sign-in runs; narrow the prompt's status * fix(accounts): keep the desktop-login Cancel in local scope even while a server sign-in runs * fix(accounts): the header Cancel follows the running addition, wherever Settings now points Each Add Account records where it is signing in. Cancel stops that sign-in, on a server or this desktop, and is hidden only where that server's own prompt already offers Cancel. |
||
|
|
bbc8303b47 |
A running agent's resume note is not a sleep, so switch-on builds report it connected (dark, layout-owner build only) (#31485)
* fix(layout): a running agent's resume record is not a sleep in switch-on builds The shared layout load put every pane resume record into leaf.sleeping, so main's terminal index read a live or quit capture as a slept pane and terminal.list reported running agents as disconnected and unverifiable. Records of slept panes (isPaneSleepRecord) now load to leaf.sleeping and running agents' records to leaf.resume; both save to the same sleepingAgentSessionsByPaneKey entry, so the disk format is unchanged. The disk-field census names both holders, and a model rule refuses a pane holding both. * fix(layout): a waking start wakes only slept panes; a recordOnly wake drops either record * fix(layout): main's shutdown reconnect facts read a running agent's resume record as awake |
||
|
|
9577c531c7 |
fix(ssh): a managed host's 'no desktop is sharing its browser' error names the browser routing setting too (#31444)
* fix(ssh): the host's 'no desktop is sharing its browser' error names the browser routing setting too The desktop withholds the browser bridge from a managed SSH host whose pages browse from the desktop's own network, but the host's CLI was told only to turn on the per-host CLI control switch, even when it already was on (#31421). * fix(ssh): name the per-host routing opt-out too, and guard the labels against UI copy drift |
||
|
|
cfdc347bd8 |
Layout-owner build: main shows live terminals that lost their tab, at the same moments the window did (dark) (#31430)
* feat(layout): main places a swept workspace's live terminals; the window sweep only reads (switch-on builds only) The window's activation sweep (first visit, an empty or sleeping activation, a phone wake) now asks main, through its existing terminal.list call, to place that workspace's unshown live terminals before listing them. With the switch on the sweep writes nothing to the window store and counts a pane main names as shown, so it no longer seeds a fresh shell beside a terminal main just placed. Main's "still binding" check also covers a registration in flight. * fix(layout): a sweep's placement pass holds back only its own workspace's first surface (switch-on builds only) * test(layout): switch-on activation-gate and census-call tests read main's listing, not window binds |
||
|
|
fc8439e05f |
fix(notifications): a pane with hooks gets its finished banner from its hook turn only (#30620)
* fix(notifications): tie a completion banner to the hook turn it announces, not a 10 s window A title-only working->idle edge (Claude's /btw side question fires no hook) raised a generic "Task complete" banner because the pane's hook row was older than 10 s. On a hook-reporting pane the hook row now decides: a row not yet announced supplies the banner content however old it is; an already-announced row means no turn ended, so no banner. Hookless agents keep the generic title fallback. * fix(notifications): a pane with hooks gets its completion banner from its hook turn only The title lanes (mounted pane and parked watcher) announced a finished task on every working->idle title edge, even on panes whose agent reports turns through hooks. A title spin that ends no hook turn (Claude's /btw side question, Codex's /compact) raised an extra banner that could only be filled with the workspace name and tab title. On a pane the hook lane is tracking, with a settled Done row from the same agent, the title lane now defers before it claims the turn, so the hook lane's banner (with the last message) is the only one. A BEL in the same burst waits on the hook lane's pending Done. Hookless agents, restored rows, hooks switched off and the post-restart re-arm keep the title banner. This replaces the previous commit's announcement memory in the dispatcher. * fix(notifications): hook-owned turn ends settle pane bookkeeping and announce re-entered Stops - A title idle edge that defers to the hook lane now settles the pane's turn, so quitting the agent after a /btw no longer raises a generic "Task complete" banner. - A BEL held while the hook lane's Done is pending is superseded by that Done at fire time instead of being held until the next turn start. - A Stop re-entered after a blocking Stop hook carries its new final message in its identity, so the hook lane announces it instead of deduping it against the first Stop. - Title lanes ask the hook lane whether it can announce the turn end (live, agent evidence, not re-arming) instead of copying its conditions, and fall back when the agent's own hooks are switched off. - The hook lane's non-quiet Done forwards its own snapshot, so its banner content no longer depends on the stored row's age. - The whole-flow test now drives the real pane wiring, the real hook lane and the real dispatcher. * fix(notifications): the host marks each real turn end, so a repeated status can't raise a second banner The renderer told a Stop re-entered after a blocking Stop hook from a restatement by its final message. The message also changes when the listener's cache resets (a session reset, then an idle reminder), so a restated finished turn raised a second, empty "finished" banner. The execution host now classifies each provider's own turn-end event by name and stamps an optional `turnEndedAt` on the status row. Idle reminders, session events, replays, child re-emits and result retries restate it; a turn-end event on an already finished row is a new end only when the agent says it re-entered (`stop_hook_active`). Local hosts and SSH hosts (orcad) mint it in `attachStatusTiming`; the WSL relay mints it at admission and sends it on its envelope. The field is persisted, forwarded over IPC and the paired-server session projection, and the renderer's done identity is `turnEndedAt ?? stateStartedAt` (after the existing `turnCompletedAt` precedence). `stateStartedAt` and every other clock are unchanged. Paired servers that predate the stamp advertise no `agent-status.turn-ended-at.v1` capability; their panes keep the terminal-title completion fallback exactly as before. * test(notifications): type the old-server pane setup in the completion flow test * fix(notifications): a blocking Stop's final banner reaches the user; /compact and quitting an idle agent stay quiet Real-agent QA on the hook-only completion design found three gaps: - A Stop re-entered after a blocking Stop hook could be dropped twice over: inside 1 s of the first banner by the replay guard (a new host turn-end stamp is now a real end), and by main's 5 s per-workspace desktop cooldown. A banner with the same notification id and changed text now replaces the one already shown instead of being dropped; every other cooldown rule is unchanged. - On a mounted pane, a hook-owned idle edge (Codex /compact) no longer claimed a completion, so the burst's BEL went out as a Bell banner. The deferred idle edge now absorbs that BEL like a claimed completion did. - Quitting an idle agent after a title spin raised a generic process-exit banner. On a pane whose hook lane owns turn ends and whose last turn is done, an exit ends no turn; a crash mid-turn still gets its banner. * test(runtime): the projection reference keys on the host turn-end stamp too * test(relay): restore the Copilot retry test while CI isolates a runner error * test: key the hot-path projection reference on the turn-end stamp; relay retry restates the end * fix(notifications): host turn identity per agent session; keep later blocking-Stop banners past main's cooldown - The execution host (local main and the WSL relay, one shared reducer) now tracks which turn an agent session is in: Codex by its own turn id, Command Code by its transcript prompt position, every other agent by its prompt-opening hook. A turn end mints a new end when its turn differs from the last ended one, so a Codex turn woken by agent mail and a text-only Command Code turn get their banner; duplicates, replays, tool reopens and result retries restate it. The record is saved with the status row (validated on restore) and never published. - Main's desktop and mobile notification cooldowns are back to main's code exactly. - A later end in the same done segment (a Stop re-entered after a blocking Stop hook) that lands inside main's cooldown is held, latest only, and sent once after that cooldown through main's unchanged gates; the renderer learns main's answer to anchor it. - One pane check gates every changed pane behavior: the pane's bound PTY runs on a host that stamps turn ends and its agent's continuation signal is verified (Claude, Codex). Every other pane, and an unbound PTY, keeps main's behavior. - A hook-owned idle edge absorbs its burst's BEL for main's full 1.5 s; exits and replacements ask the owner check with the exiting agent; a title spin on a hook-owned pane opens no turn. * test(notifications): run the whole-pane harness through main's shared desktop cooldown, not main's service module * fix(notifications): decide hook ownership from how the host identified each turn end The execution host now labels every classified turn end with one optional disposition beside its end stamp, by the same rule for every agent: identified (an unused prompt opening, a new native turn id, or a verified re-entered Stop) mints a new end; a same-turn repeat is a proven duplicate only when the agent supplies both a turn id and a verified continuation flag; anything else is ambiguous and never mints. Replays and retries restate the decided answer, and local and relay hosts carry an end only within a done segment. The renderer replaces the Claude/Codex pane gate with one evidence decision: identified ends are hook-owned, ambiguous ends keep the title lane's role, and rows without a disposition (older hosts, unclassified agents) keep the previous behavior exactly. A title working edge on a hook-owned pane keeps its turn bookkeeping and only the hook receipt; an owned idle edge settles after the existing max wait, or announces through the title path if the end turned out ambiguous. Idle and working title edges resolve the same owner (the title's agent, else the foreground one). The held later banner for identified continuations is unchanged. * test(notifications): give the whole-pane harness's host event its local connection id * fix(notifications): a settled cancellation ends the open turn, so its late Stop folds as on main A cancel Orca inferred from Esc or Ctrl+C writes a done row with no turn-end event. The host reducer now completes the current turn key on that settled cancellation, so the same turn's late Stop is a repeat (ambiguous, no new stamp) instead of a second identified end that requested a second banner. * fix(notifications): a settled cancellation ends the open turn on every lane, so a relay's late cancel end restates none The desktop restates a WSL relay's end as decided. The relay never learns a cancel the desktop inferred for a non-Claude agent, so Grok's own late stop_cancelled reached the desktop as an identified end and gave a second banner. One predicate now decides both lanes: after a settled cancellation that carried no end, a restated end keeps the shown one, and a child event carrying the cancellation also ends the open turn. The relay's own cancel row is the main agent's, never a child's. * fix(notifications): a relayed done segment shown without its own end keeps showing none The restatement fold tested the previous row's cancel verdict, so the row it left behind (a late plain stop) no longer matched and the relay's next restatement brought its end back. Fold on the row's turn-end fact instead, which the folded row satisfies again. |
||
|
|
ae9bc8d120 |
A slept native-chat workspace reads asleep and hides (#28457)
* fix(sidebar): a slept chat workspace reads asleep and hides * fix(sidebar): a wake during Sleep's teardown keeps the workspace awake * test(sidebar): give the activation-race mock store the chat tab map Sleep reads * feat(native-chat): the host marks the chats an explicit Sleep stopped The fact "the user's Sleep put this chat to rest" now lives on the host that runs the chat, on its session record, instead of in the window. - New runtime capability agent-session.workspace-sleep-mark.v1 and an optional markAsleep on agentSession.sleepWorkspace (strict params, so only hosts advertising the capability are sent it). - The Sleep close decides each chat's receipt inside the same per-session step that stopped it; with markAsleep it writes sleptAt in that step's record write, so a chat is marked exactly when it is in the receipt. - The next send the host hands over clears sleptAt, whichever client sent it; the idle sweep and plain stops never mark. - sleptAt rides the status summary (optional field), its equality, and the retained summary of a closed chat, so it survives restart through the persisted tab index. * refactor(sidebar): read a slept chat from its host's mark, not a window record The window no longer keeps its own saved record of which chats Sleep stopped (explicitlySleptChatsByWorkspace). That record had to mirror every host event that should end it (sends, replaced conversations, renames, profile and WSL moves, removal, wakes during teardown), and each missed mirror was a bug. The host now owns the fact (previous commit). - Delete the record, its schema, store slice, workspace-session field registrations, moves, renames, purges, migrations and hydration, and the renderer clearing hooks. Those files are back to main. - Opening a workspace no longer wakes its slept chats (product ruling): drop the chat Sleep-attempt tokens and their set-active-worktree hook. A slept chat wakes only when a send reaches its host, from any client. - The status bridge mirrors each chat tab's published sleptAt into a store map keyed by the chat's pane key, dropped when its projection unmounts and never saved; a chat whose host runs it again is not slept. - The asleep derivation, hide filter, palette, board and context menu read that map. Explicit Sleep sends markAsleep only to the local host and to hosts last probed with the new capability; a project move never does. * fix(native-chat): preserve host-owned Sleep across fallback and legacy tabs * fix(native-chat): clear Sleep when a new send is admitted * chore: restore incoming documentation after merge hook formatting * fix(native-chat): let admitted sends supersede pending Sleep receipts * fix(sidebar): wait for host Sleep state before showing restored chats * fix(sidebar): retain host Sleep answers when chat projections remount * fix(native-chat): clear Sleep when queued drafts become sends * test(sidebar): cover unknown and known chat Sleep state on the board * test(vault): expect both host status capability checks * test(native-chat): consolidate Sleep behavior coverage * fix(native-chat): commit Sleep wake with send admission * test: pin visibility while chat host is unidentified * test: consolidate workspace sleep regression flows * test: keep status feed regression within renderer boundary |
||
|
|
d1ed73ba9b |
fix(orcad): refuse new terminals while an update stops the server (R32) (#29834)
* fix(orcad): refuse new terminals between an update's last census and the stop The last census before an SSH or WSL update stops the server now closes the server's terminal admission first. Every spawn lease (acquireWorktreeTerminalSpawn, which both spawn paths hold across the provider spawn) passes the fence, the census waits for leases taken before the close, and an update that leaves the incumbent serving reopens it. A reattach to a session the daemon still runs is still admitted. * fix(orcad): fence-exempt reattaches spawn attach-only; reopen admission when no stop was sent * fix(orcad): stop nothing once the recount's admission fence may have lapsed |
||
|
|
ce71535bbf |
Layout groundwork (switched off): main starts every split and new pane, the window only attaches (#31258)
* feat(terminal): the window's store shows main's pane start status (layout-owner build) A status-only frame changes one store entry and no pane tree, so the pane's view stays mounted. * refactor(terminal): keep the frame sync within its line budget * feat(terminal): every pane main starts shows 'starting' from before it is written (layout-owner build) * feat(terminal): a pane main is starting waits and attaches; a failed start shows why (layout-owner build) Fixes owner-visible row 15: a failed agent or setup start no longer opens a plain shell. * test(terminal): iterate store listeners directly * fix(terminal): an exit that reports no code keeps its pane, as lost contact (layout-owner build) * test(terminal): a pane main started attaches to main's terminal at its own grid * test(terminal): detach census tests assert main's move and frame with the switch on * test(terminal): exited-pane tests assert main's close with the switch on These 8 have failed switch-on since #31130 moved the exit close to main. * feat(terminal): main writes and starts every split of a pane it holds; the window attaches (layout-owner build) The window's split, a startup split with its command, and a CLI split all go through main's terminal.split; main's frame mounts the pane, with or without a window intent. Deletes the window's split wait and withdraw, the split appear intents, and the queued-split skip and cancel. The e2e helper uses the real split. A startup split leaves focus on its source pane. * fix(terminal): the attach-only wait leaves sleeping, resuming and held panes to their restore path * fix(terminal): a split pane attaches to the terminal main bound it to; a taken pane id is refused Also restores the proof that a split closed while it starts gets one answer and leaves no pane or start status behind. * fix(terminal): a window split main cannot start still shows its pane, failed, with why * fix(terminal): check a taken pane id first, keep the start's own error, clear a gone pane's status Three test stores gain the pane-start field the window's store always has. * test(terminal): the admission stub answers whether its pane is still written; a closed pane's failed start clears * refactor(terminal): the rebound workspaces' bindings show through one call in the bindings module * test(terminal): a pane main wrote and the user closed mid-start drops its failed status * fix(terminal): a split main starts is listed to phones and session.tabs without a window's report * fix(terminal): a reconnect shows its pane starting and why it failed; a lost owner race shows no failure; a split no one asked to see keeps the phones' tab * fix(terminal): a kept create another owner won shows no failure; the window attaches to the winner |
||
|
|
b99552cabc |
fix(runtime): a phone reads and saves Markdown tabs on a headless server (#31101)
* fix(runtime): a phone reads and saves Markdown tabs on a headless server With no window to relay through, markdown.readTab and markdown.saveTab answered renderer_unavailable. Main now reads the tab's file (or its saved draft) from disk and saves with a version compare plus the disk write precondition, in the shapes the window's editor relay returns. With a window attached the relay is unchanged. * Headless Markdown save: keep the draft's disk baseline, honour read-only tabs, re-find the record to clear * Use the disk baseline only for a draft * ci: run the phone Markdown spec's orcad case in the orca serve mode-switch job * Headless Markdown: answer from disk only; a window's saved draft stays its view state Clearing a saved draft wrote workspace layout from main, which no layout command carries. Main now never shows or edits a draft; the desktop's restore check reports a disk change. * Phone Markdown: one path through main and the disk, window or not A paired server's workspace is read and saved on that server with files.read / files.writeIfUnchanged, so it answers from its own disk and keeps the precondition. The window relay (ui:mobileMarkdownRequest and the renderer bridge) is removed. * e2e: a phone saves a Markdown tab the desktop holds unsaved edits in The phone reads the disk with the tab marked dirty; its save lands on disk, and the desktop keeps its draft and shows the changed-on-disk banner. * Group the Markdown disk host's imports * Drop an unneeded store cast from the dirty-readers test * Phone Markdown: refuse a save on a server too old to check the disk; cover the untitled tab A server before #30605 has no files.writeIfUnchanged, so the save is refused with the existing update-required message rather than written unchecked. * Phone Markdown: refuse a file an SSH host holds outside the workspace; pin the untitled answer * Phone Markdown: untitled tabs stay read-only; older servers save like the desktop editor; a closed-source preview still reads - The desktop lists an untitled document's tab with isUntitled (optional, additive); main answers it read-only with unsupported_untitled and refuses its save before touching the disk. - A server without the files.write.disk-precondition capability gets the desktop editor's guarded save: compare from here, then a plain files.write, or a conflict. Every call of a save is pinned to the pairing it probed. - A preview whose source tab was closed has no saved record; it reads its own file, read-only. * e2e: read the untitled tab's answer without a cast * Phone Markdown: pin a save's pairing before its first read A save captures the server's pairing before it reads, and every call of the save, the read included, is pinned to it, so a re-pair between the read and the write is refused. * Test: with no window, a phone saves an existing untitled.md and never creates a deleted one --------- Co-authored-by: E2E Test <e2e@test.local> |
||
|
|
dda855e837 |
test(layout): pin the layout-owner save format for downgrade, and keep main's touched-records save with an exhaustive guard (dark, layout-owner build only) (#31341)
* test(layout): downgrade probe for the layout-owner session format, and the touched-records patch's guard Downgrade probe (ledger L8): a checked-in golden of the session main writes in a layout-owner build (layout commands through the Store plus a window save), matched byte for byte; a switch-off Store hands it on unchanged and a switch-off window restores its tabs, panes, groups, editor file and browser tab. Regenerate with ORCA_UPDATE_LAYOUT_OWNER_GOLDEN=1. L104: for each writer of the touched-records patch (layout.apply, the cross-partition move, the window save), the patch equals a full save on every partition the load leaves alone, and differs only on records the load normalizes otherwise. Two concrete legacy cases are pinned, and a switch-off window restores each differently after a full save, so the patch stays. * test(layout): type the guard's partition ids and locate the golden without import.meta * test(layout): pin every legacy fixture's patch difference, assert restored panes and group selection, keep the golden deterministic |
||
|
|
fece03b5ac |
fix(orcad): voice dictation on headless orca serve hosts (#31217)
* fix(orcad): voice dictation on headless orca serve hosts
Mobile dictation against an orca serve host answered speech_unavailable
since serve moved to orcad: orcad never installed the speech service
factories, and the model manager and download imported Electron.
The model download now takes a host request opener (Chromium net on the
desktop, Node https on orcad, with redirects mapped to the same event),
the STT worker ships in the orcad slot, and orca serve hands orcad the
installed app's on-device recognizer. A host without a recognizer (an
SSH or WSL managed slot) lists only cloud models and sends a phone that
picked an on-device model to the setup sheet. Mobile names the fix when
an older server still has no speech at all.
The parity test now requires both roots to install speech.
Reported-by: delorean2020 (@delorean2020)
* fix(orcad): retry Node download drops, stage the recognizer on Windows, review fixes
- Node https errors (ECONNRESET, ETIMEDOUT, ...) are retryable, so an
interrupted orcad model download resumes (Codex P2).
- On Windows orca serve hands orcad a per-version copy of the recognizer
in the profile, so a loaded addon never locks the app's files against
an update (Opus).
- Drop a recognizer candidate path that could never match; one SAFETY
helper per test file instead of repeated casts; comments.
* fix(orcad): cloud speech is ready only with a key this host can decrypt; prune superseded recognizer copies
Codex round 2: a desktop-sealed OpenAI key exists on disk but orcad's
secret store cannot open it, so the catalog reported cloud models ready
and dictation failed at the end. orcad's model manager now proves the
key decrypts. The desktop keeps its existence check (no keychain prompt).
* fix(orcad): never prune staged recognizers before the profile lock; copy linked packages as real files
Codex round 3. ref-oss check: versioned copies are cleaned only by the
process that owns them, so the pre-lock prune is dropped rather than
patched.
* fix(orcad): remove a partial recognizer copy when staging fails (Codex round 4)
* fix(speech): never hand out another store's ciphertext as the OpenAI key (Codex round 5)
* refactor(orcad): no recognizer copy on Windows; Windows orcad keeps cloud dictation only
Design check after review round 5: every remaining finding came from the
Windows copy that dodges addon file locks. Dropping it shrinks the
surface to a path lookup; Linux and macOS serve keep on-device models.
* fix(speech): name the fix when an old server refuses a mic tap; always set the recognizer env; retry ENOTFOUND (final Opus review)
* fix(managed-server): probe install completeness relative to the slot dir
CI: shipping stt-worker.js added one more absolute slot path to the
install-complete probe, pushing it further past the exec-size budget.
Testing each artifact relative to the slot (inside a subshell cd) cuts
the probe from ~12.2 KB to ~3.6 KB, so it drops off the over-budget
list instead of raising a ceiling.
* fix(mobile): pin host.platform's read to a platform type, independent of which zod copy resolves
With mobile's own dependencies installed (which CI does for any PR that
touches mobile/), the e2e program resolves two zod copies, and the
salvaged platform arm from src/shared inferred as {}. The schema now
narrows through an explicit platform guard.
* fix(mobile): keep the host.platform read line intact for its RPC mutant; narrow in a second step
Mobile verify: the recorded-RPC mutant anchors on the read line, which
the previous guard rewrote, so the mutant could not apply.
* refactor(cli): app-executable resolution in its own module; drop the platform guard now zod is aligned
launch.ts went one line past max-lines with main; resolveAppRoot,
resolveForegroundOrcaExecutable, getExecutableAppArgs and
stripElectronRunAsNode move to orca-app-executable.ts (re-exported, so
callers are unchanged). With mobile and root on one zod (#31260), the
host.platform schema no longer needs the extra narrowing step.
* refactor(cli): import app-executable helpers from their module; no re-exports from launch.ts
|
||
|
|
356bda10d1 |
fix(github): repo cache eviction clears every host's entries, keeping a live twin's (#31312)
* fix(github): repo cache eviction drops every host scope's entries, keeping a twin's own Eviction and issue-source changes matched only unscoped key prefixes, so SSH-, runtime- and source-scoped entries survived repo removal. Keys are now composed by one builder and read back by its inverse, so eviction classifies each key by host and keeps only a surviving same-id twin's bucket. Hosted review (GitLab MR) entries are evicted too. Co-Authored-By: Claude <noreply@anthropic.com> * fix(github): read task-source scopes with empty parts back whole Co-Authored-By: Claude <noreply@anthropic.com> * fix(github): an issue-source change evicts only the changed twin's buckets Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
065e6873f6 |
A paired desktop shows the held-terminal banner and Reopen for a server's held terminals (switch-on builds only) (#31275)
* A paired desktop shows a layout-owner server's held panes on the existing banner; Reopen asks the server (switch-on builds only) The server's frames carry status 'held' on a pane a newer build's terminal service may still run. The desktop now lists those panes on its existing newer-build banner, with the same wording and states. Reopen calls the server's new `layout.reopenHeldPanes` under the server's ids: the server stops its newer service, as the desktop's own Reopen does, and starts each pane still held. The desktop starts nothing; the server's next frames clear the status and bind the pane. A refusal or failure shows the banner's failed state. Served and advertised (`layout.reopenHeldPanes.v1`) only by a layout-owner build, to paired desktops (not phones). A server without the capability, or that publishes no status, shows no banner, as today. * test(layout): held server pane stays Reopening until the reattach frame, stays failed after a refusal, and shows nothing without the reopen capability * fix(layout): a held pane's Reopen acts only on its own host and on main's published status (switch-on builds only) - Reopen needs the clicked pane's key; a pane that already left the banner's store does nothing, so a server banner clicked as its frame drops the pane never stops this machine's newer service. - Local versus server is decided by the pane's host, not by whether it has a release. - The banner portal for a pane with no terminal handle exists only in a layout-owner build. - The server reopens only panes its published layout shows held, the status the desktop saw. * test(layout): Reopen ignores a pane rebound to a new terminal whose held entry is still stored * test(layout): the dropped-pane Reopen test checks for the reopen method, since the view report shares the RPC client |
||
|
|
6e589bb4ba |
Layout-owner build: phones see main's terminal and editor rows (A2, dark) (#31276)
* wip(layout): applyDefaultTabs command in the layout model (dark) * wip(layout): applyDefaultTabs takes the window's proposed ids; main refuses a taken one * wip(layout): main closes or keeps an exited split pane by the window's rule (dark) * wip(layout): a create or split sent again after its close is refused (dark) * wip: typed test doubles * wip(layout): main writes a worktree's default tabs in one command and starts them, window or none (dark; twin e) * wip(layout): main writes a worktree's default tabs in one command and starts them, window or none (dark; twin e) * wip(layout): main provisions setup and default tabs even when the agent fails to start; no window takes over (dark) * wip(layout): pane start status in frames; a failed start keeps its pane; a terminal close stands after a failed write (dark) * wip: lint fixes (max-lines, test throws) * test(layout): a terminal close stands after a real failed profile write; switch off still refuses * fix(layout): checked types in the close-failure rule and its tests * fix(layout): a failed start keeps its pane whatever failed; default tabs start once; a tab's last pane keeps its card (dark) * test(layout): the agent's kept pane counts as the workspace's terminal; main starts it whatever failed * fix(layout): the switch-off create path gains no await * fix(layout): a failed start never writes back a pane the user closed (dark) * fix(layout): a failed start's pane goes through main's create rules, so a closed tab stays closed (dark) * test(layout): D1a's switch-on close tests assert the intent main receives (both builds) * test(layout): the close fixture answers main's command without naming the method * fix(layout): a re-created pane binds as designed; the id-only ratchet reads a shared import (dark) * fix(layout): a pane's start status may sit on a pane still bound to its old terminal (dark) * fix(layout): a pane's start status ends when its pane binds again by any path (dark) * test(layout): same-id restore ends a status; a replayed split brings back a closed pane by design * test(layout): terminal reply fixtures captured from the window-owned build, and main's switch-on proofs * wip(layout): main's terminal surface index feeds the runtime graph (dark) * wip(layout): main's panes stand in for the window's terminal list (dark) * test(layout): the window sends main-listed panes as mount facts (dark) * test(layout): surface proofs use public runtime calls * chore: lint * feat(layout): close by handle closes in main's layout; the close-time relist goes (dark) * feat(layout): main's binding outranks a caller's pane claim on the terminal record (dark) * test(layout): window-list tests stay on the rollback build; switch-on mount-fact cases (dark) * wip(layout): main's terminal index reads E1's shared projection and follows its operation ends (dark) * refactor(layout): the main-surfaces host lives beside the session-tabs readers, keeping the graph file under its line cap (dark) * fix(layout): main's panes skip the window-era membership fence, follow workspace routing, and list a terminal when it registers; window close only hands the graph back (dark) * refactor(runtime): explicit-close PTY stops move unchanged to their own module, for the file's line cap * test(layout): the cold-restore table runs in a layout-owner build too: one push per registration * test(layout): the parked and automation pane tests assert the same window report in both builds (mount facts with the switch on) * chore: one layout-owner switch import after the restack on D1a * feat(layout): main's terminal.list rows carry the host's verdict; a pane with a status is never live (dark) * chore(layout): fit main's surfaces under the lint gates after the D1a restack * feat(layout): every pane status D1a publishes keeps a row from reading live; remote host answers pass through (dark) * feat(layout): a drag-out moves main's own binding and refuses the home copy's last pane; the window's terminal claim is not consulted (L127, dark) * fix(layout): a tab close by a starting pane's handle stops the tab's terminals; a starting pane is never exited on the inventory alone; terminal.list lists every saved pane main owns; the pane claim lookup leaves the output path (dark) * fix(terminal): keep shipped exit and default-tab behaviour with the layout switch on An exited pane closes through the legacy retirement unless the keep rule holds; the default tab's title and color still reach the window and the phone's live tabs. * fix(terminal): one id guard for every pane write; keep panes on an unvouched -1 Main's taken and closed-id guard now runs where every pane write goes, so a runtime create replayed after its tab closed is refused before it spawns. An exit code nothing vouches for keeps the pane, as the window does; a kept pane sends the phone no retirement. Converts the switch-on tests that asserted the deleted window hand-off and title bridge. * fix(terminal): keep the closed-tab guard on every pane write, held-id checks on client commands A paired host starts each pane of a client's tab as a create under the tab's id, so a held tab id is refused only for a client's own layout command. * fix(terminal): a provider-reported -1 keeps the pane, as the window does Only a requested stop or a host-confirmed death closes a pane on a negative exit code. Pins that the closed-tab guard never fires in the shipped build. * chore(terminal): satisfy the exhaustive-switch and test typing checks * test(terminal): give the replay test's live process a cwd * fix(layout): main's index reads routing only for a workspace two partitions hold, the fence reads ownership once, a legacy pane keeps its claim; switch-on tests assert main's answers after a window closes (dark) * refactor(runtime): a worktree stop's terminal collection moves unchanged to its own module, for the file's line cap * fix(layout): main's index routes every workspace as the layout stream does, re-reading routing only on a write or a terminal registration; a worktree stop names every terminal main binds; a starting pane is unverifiable (dark) * feat(layout): a workspace main owns publishes its terminal and editor session.tabs rows from main's layout; the window still supplies browser, chat and view-only rows until D3b (A2, dark) * chore: lint * test: type the row fixtures * chore(layout): main's session.tabs projection is an old-client entry, imported only by main's terminal surface index * fix(layout): main's session.tabs rows carry the tab's name, the window's agent status and markdown file id, list rows in tab order, and keep groups and selection consistent (A2, dark) * refactor(layout): the projection's group order moves unchanged to its own old-client module, for the file's line cap; lint * feat(layout): every pane status main publishes keeps a row from reading live; a resuming pane is unverifiable like a starting one (dark) * test(layout): a windowless server's layout change reaches a subscribed phone's session.tabs rows (dark) * fix(layout): a window-only row keeps its place in the rows, the window's selected row lands in a group that selects it, and a workspace with no main group keeps the window's groups (A2, dark) * fix(layout): a pane main has no binding for yet keeps the window's terminal in its phone row, so an SSH restore lists it pending (A2, dark; ledger L243) * fix(session-tabs): phones show the window's live selection, focused pane, dirty state and titles over main's saved view; exited terminals stay gone; stray rows join the active group (switch-on builds only) * fix(session-tabs): main's groups stand with main's selection; certified exits only; a selected stray row joins main's active group --------- Co-authored-by: E2E Test <e2e@test.local> |
||
|
|
1c76c71409 | fix(mobile): list session tabs in the desktop tab bar order (#31269) | ||
|
|
5c666d2974 |
fix(native-chat): retry a failed chat name, show the failure on the tab's hover card, and add Regenerate name (#30646)
* fix(native-chat): name a chat from its first message at once; the generated name replaces it A native chat's one stored name was written only by a best-effort, one-shot generation, so any failure left every surface on the 'Codex Chat' placeholder. The first prompt now names the chat when its first turn starts, and the generated name replaces it only while the field still holds that seed. * fix(native-chat): the chat names setting says a chat shows its first message until it is named * fix(native-chat): retry a failed first chat name twice before keeping the first message A failed name generation was final for the chat's lifetime. The first name now gets two retries, after 2 and 4 seconds, and stops early once the first-message name was replaced. * fix(native-chat): show a failed chat name on the tab hover card, with Regenerate name A chat whose name generation failed after every retry gave no sign of it, and nothing could run it again. The host now keeps the last failure in memory and publishes it with the chat's status while the chat still has the name it failed to replace; the tab hover card shows it with the error dialog, and a new agentSession.regenerateName (behind a capability) makes one more try. * fix: derive chat naming progress from host jobs * fix(native-chat): merge duplicate import CI caught * fix(native-chat): declare agentSession.regenerateName in the cross-version surface manifest * fix(native-chat): stub naming-failure hook in SortableTab rename shortcut test |
||
|
|
70b2197c97 |
The window shows tab, title, move and divider changes only from main's update, never drawing them itself (dark, layout-owner build only) (#31257)
* The window shows each layout records frame record by record, without reading its session back (dark, layout-owner build only) A records frame no longer re-serializes the window's whole session and runs the Loader. Panes' terminal bindings and live titles apply alone, to the tabs the frame names; any other record builds only its own workspace's slices, with the view and facts read from the store. Only a workspace the store does not show from frames yet (startup, a reopened stream, a first visit, one sent whole) takes the full build. Tests install an apply check that, after every flush, compares the store with the full build for the same cache and pre-flush store; the shared layout-apply transport now sends main's records frames and fails a flush that differs. * A frame of only tab rows shows without reading the window's session back or running the Loader (test, layout-owner build only) * The account-restart test's fake binding runtime carries per-pane binding generations, so it passes with the switch on (test only) * The account-restart test types its store writes, so its five old e2e type errors are gone (test only) * A workspace build reads the editor selection and groups through the save's own writers, as the full build does (dark, layout-owner build only) * A tab released from the window's hold shows the binding main wrote meanwhile (test, layout-owner build only) * The frame-apply check still checks a flush's other workspaces when it builds one in full, and the shared transport keeps a test's own check (test only) * The frame-apply check is proven to catch a mismatch beside a full build, and the shared transport restores only the check it installed (test only) * A tab move shows from main's frame alone; the window no longer re-applies it or adopts main's group id (dark, layout-owner build only) On main's answer the window only selects the moved tab in its new group, a view choice the frame does not carry. While main's stream is down it still moves the tab itself, as the frame sync's closed-stream rule says, but under its own group id: the reopened stream's snapshot redraws main's copy. The three switch-on census files for tab moves now pass without a switch mock: the context menu's move routes to the tab bar's move, the drag hook's pane-column split goes to main in a layout-owner build, and the tab bar's own move is pinned on a paired server's workspace. A new frame test pins each move through main and its frame. * A tab's rename, color, pin, view and a pane's title show from main's frame alone; the window no longer applies them again (dark, layout-owner build only) * A divider drag sends main the ratios it moved, not the window's tree, and an in-tab pane move shows from main's frame; the ratio hold is gone (dark, layout-owner build only) * Divider ratio params and their command tests get their own modules (file size) * Layout-writer baseline pruned: the tab move no longer writes the layout from the window * The window never draws a tab prop, pane title or in-tab move itself; while main's stream is down the reconnect's snapshot shows them (dark, layout-owner build only) * View-mode test seeds its paired-server workspace without a type assertion * A moved tab is selected in the frame flush that shows the move, unless the user selected since; main's answer no longer selects it (L120, dark, layout-owner build only) * Frame sync stays within its line budget * Frame sync writes its patches through the moved-tab selection, within its line budget * The moved tab's selection reuses activateTab's own store update, so no new layout writer is added (L120) * Format the tab-move frame test * A tab moved while main's stream is down is selected by the reconnect's snapshot that shows the move (L120) * A generated tab title main gave is drawn by its frame alone; the window writes only titles left to it as today, and the rename test checks the phone graph republishes (dark, layout-owner build only) * A divider ratio names the split it means, and main refuses it where that path holds another, so the window snaps back to main's ratios; a drag back to main's ratio before it answers is sent too (dark, layout-owner build only) * test(layout-owner): tab moves drawn from frames make no window tab or group writes * test(layout-owner): a reopened tab shows its title and color from main's frame * test(layout-owner): the tab bar's preview pin through main ends the preview and counts a use * test(layout-owner): read main's generated-title renames without a type assertion * test(layout-owner): type the sent renames so the title helper reads them without an assertion * The frame-apply check selects the moved tabs the flush selected, so the tab-move tests exit clean and a wrong selection still fails (dark, layout-owner build only) * test(layout-owner): the title batch's live labels are checked in both builds; only the generated title differs * Main keeps a tab's first agent-generated title unless the window's title replaces it, so a second prompt while main's stream is down no longer renames the tab (dark, layout-owner build only) * test(layout-owner): a revealed terminal's tab name is drawn from main's frame, not its reply (dark, layout-owner build only) --------- Co-authored-by: E2E Test <e2e@test.local> |
||
|
|
d6bc6cb3b3 |
fix(process-host): let a slow Windows taskkill finish instead of orphaning the grandchild (#29501)
* fix(process-host): let a slow Windows taskkill finish instead of orphaning the grandchild Co-Authored-By: Claude <noreply@anthropic.com> * fix(process-host): abandon taskkill shortly after the root exits so it cannot walk a reissued pid Co-Authored-By: Claude <noreply@anthropic.com> * test(process-host): log the taskkill outcome and caller settle time under load Co-Authored-By: Claude <noreply@anthropic.com> * ci: register the late-taskkill Windows test in the package-windows path list Co-Authored-By: Claude <noreply@anthropic.com> * fix(process-host): release the git admission slot on root close again; cap the background taskkill at 30 s Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
d1aeba939f |
Native chat: remove unused journalCheckpoint and setConversationCommand (#30645)
* refactor(native-chat): remove three chat mechanisms nothing drives - The adapter's optional startAnswered hook lost its only implementer in #27925; it was still forwarded through four layers and always read as answered. A start that never answered is read from the child's phase, as the exit already does. - The saved lease's journalCheckpoint is only ever written as null and read only by validation. Records an older build saved with it still load: it joins the retired lease fields dropped at load, so the next write sheds it. - The record store's setConversationCommand had no production caller. The tests that stood in for an older build with it now seed the saved row and reopen the store; three that only re-pinned another test are removed. * test(native-chat): an older build's null journal checkpoint also loads * Keep startAnswered cleanup in its owning PR * Keep historical chat fixtures aligned with their readers |
||
|
|
c93514db5b |
refactor(protocol): each area owns its published runtime capability list (#30641)
* refactor(protocol): each area owns its published runtime capability list RUNTIME_CAPABILITIES now spreads one array per area instead of a flat hand-appended list, and runtime-capability-constants.ts is split into the areas that own each constant. The published set is unchanged and pinned by an exact sorted test. * test: preserve capability baseline while allowing additions |
||
|
|
9af1ac8d28 |
A screenshot dragged into a chat lives as long as the chat (#30419)
* fix(native-chat): a chat's visuals folder lives as long as the chat A chat's scratch folder was deleted once the chat had gone 30 days unused, so an old chat's inline visuals turned into "Visualization unavailable" while the chat and its history stayed. The common pattern keeps a chat's files for as long as the chat and never deletes them on an "unused for N days" timer. The sweep now removes only folders no chat record maps to, plus leftover .expired folders older builds parked. Everything that existed only to make the age rule safe goes with it: the per-folder save lock and its expiry candidates, the session-lane idle check, the journal last-activity query, and the migration's fresh-window timestamp. The visuals skill no longer tells agents the folder is temporary. * fix(native-chat): an image sent in a local chat lives as long as the chat A local chat's pasted images lived in one paste folder that deleted every file older than 30 days, sent or not, so an old chat's images turned into label chips. Paired-server chats already keep pastes in the host's attachment store, where the send that names a file claims it for its chat and only unclaimed files are ever swept: the common pattern. A local composer paste now goes into this machine's own attachment store (starting the local chat host first), so its send claims it the same way. The old paste folder is no longer written or swept: messages sent before this change may name its files. Restored drafts still read it. An unclaimed upload now waits 30 days instead of one before the sweep takes it. A draft names its image by this path, so the window is how long an unsent draft keeps its image; 30 days is what local drafts had, and it outlasts the host's send-id window so a resent draft still finds its image. A chat folder an older build set aside as unused (<hash>.expired) goes back to its chat instead of being deleted. * fix(native-chat): a terminal-backed chat's pastes are never swept A terminal-backed chat has no journal, so no send claims its pastes and nothing can tell a sent one from an abandoned one. Its pastes therefore stay in the paste folder, which is no longer swept, rather than in the attachment store, whose sweep would take them after 30 days unclaimed. Only a local structured chat's paste (forStructuredChat) goes to the store, where its send claims it. * fix(native-chat): keep the attachment owner's literal kind for structured local chats * fix(native-chat): a screenshot dragged into a chat lives as long as the chat On macOS a screenshot dragged from its thumbnail is copied into $TMPDIR/orca-drops, and that copy was deleted 7 days after the drop, so a sent dropped screenshot turned into a label chip after a week. The common pattern keeps a file sent in a chat as long as the chat. A local structured chat's composer now moves each such copy into this machine's chat attachment store, where the send that names it claims it for the chat. Every other copy (terminal panes, terminal-backed chats, editors, startup prompts) has no owner Orca can tell, so copies are no longer swept at all. The copy budget now bounds one drop instead of every copy still on disk: without a sweep, counting earlier copies would refuse every later drop. * style: wrap the drop-copy call * test: drop an unused import * test(ipc): expect the local chat store option on the clipboard registrar * test(ipc): mock the drop-copy registrar in the core handler test * Store fresh chat drops before publishing their paths * Type the drop preparation fixture with its request contract * Capture immutable drop batches before attachment publication * Set explicit buffers for cross-volume attachment imports * Control the deadline in the drop abandonment test * Return void from the drop test timer cleanup * Use the captured attachment destination for queued drops * Register the paired runtime in the queued drop test |
||
|
|
a9310986cd |
A CLI or phone file on a layout-owner server opens in the tab group a paired desktop shows (switch-on builds only) (#31264)
* feat(layout-owner): a CLI or phone file on a layout-owner server opens in the group a paired desktop shows Each paired desktop reports, through a new layout.reportView (served only by layout.intents.v1 servers, never to phones), the group it shows in each workspace the server's frames draw. The server keeps reports per connection beside the window's, opens files in the latest one's group, and drops a connection's reports when it closes, falling back to the saved view. * test(e2e): a CLI file open on a switch-on server lands in the group a paired desktop shows * fix(layout-owner): layout.reportView refuses a caller with no connection, as sidebarInputs.reportView does * feat(layout-owner): a desktop's re-sent view after a reconnect never outranks another desktop's live report layout.reportView takes an optional refresh flag. The desktop's first report of each workspace after a stream snapshot carries it; the server ranks such entries below every live report, while they still replace the connection's own earlier entries and beat the saved view. * feat(layout-owner): a paired desktop re-sends its view, as a refresh, when its control connection to the server comes up on a new socket The server drops a connection's view reports when it closes, and the layout stream can stay up across that, so the desktop watches the shared control diagnostics for a new socket. * fix(layout-owner): a desktop's refresh keeps its own live rank; stop reporting to a server without layout.reportView A same-connection resubscribe re-sends its view as a refresh, which demoted the connection's own live report below an older desktop's. Ranks are now an explicit live/refresh tier. A pre-PR switch-on server answers method_not_found; the desktop then stops sending for that stream. * fix(layout-owner): a group the server's own frames move is re-sent as a refresh, never a live report The snapshot resend read the store before the sync applied the snapshot, so the flush's group change reached the store subscription as the user's. Writes made by the frame sync now report as refreshes, and the snapshot resend runs after the flush. * test(layout-owner): a change frame's group move goes out as a refresh; the frame-write mark survives nested writes |
||
|
|
ae58ca06d6 |
fix(tasks): GitLab items and project-view dialogs act on the twin host that returned them (#31271)
GitLab task items now carry the host of the repo row that fetched them, and the project-view dialog stamps the row its slug matched. Both resolve through one provider-neutral helper (getWorkItemRepo, generalized from #31252's GitHub-only getGitHubWorkItemRepo), so a repo id published by two hosts never routes an action to the other host's row. |
||
|
|
6e973fa6d4 |
A launch can name chat or terminal: agent.launch gets a surface pick and the CLI gets --surface chat|terminal (#31191)
* feat(agent.launch): a launch can name chat or terminal, and the CLI gets --surface chat|terminal agent.launch gains an optional `surface` field, read by hosts advertising agent.launch.surface.v1. `terminal` always runs the agent's CLI; `chat` runs a chat even when the user's default is a terminal, and one the host can prove it cannot open is refused with a typed no-effects error before anything is created, so a retry answers with the same refusal. The pick is part of the replay fingerprint. `orca agent launch` and `orca worktree create --agent` accept --surface chat|terminal and refuse it on a host that would drop it, including the legacy worktree.create fallback. * test(agent.launch): pin the surface pick beside the other pre-start failures * fix(agent.launch): settle requested surfaces from resolved workspace facts * fix(agent.launch): describe fallback boundaries and correct test types |
||
|
|
ab8e12c3cb |
fix(ai-vault): keep sessions recorded under a symlinked workspace's real path in Workspace scope (#31244)
* fix(ai-vault): keep sessions recorded under a symlinked workspace's real path in Workspace scope (#31139) A workspace registered through a symlink (~/work/api -> /data/src/api, or macOS /var -> /private/var) lost every session whose agent recorded the resolved cwd: scope matching compared strings, and those rows also counted against the out-of-scope depth cap. The host that owns the files (the desktop for local scans, orcad for SSH and server hosts) now resolves each scope path's real path, scans both spellings, and gives matching sessions optional matching-only cwdAliases in the registered spelling. Workspace scope, the depth cap and project attribution read cwd plus aliases; resume still uses the recorded cwd. Older hosts send no aliases and older clients strip the field. Reported, with the proposed approach, by @quzard in #31139. * fix(ai-vault): rank aliased attribution by closest root; never resolve recorded cwds Review feedback: a longer-spelled parent link could take a nested project's session, and resolving every out-of-scope cwd could stall a listing on a hung network mount or raise macOS privacy prompts. |