mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 00:57:45 +00:00
de2700d0fbb9d00a45ad3f3e6f79c9d46c65d081
15758
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
de2700d0fb | refactor: colocate native-chat command envelopes (#32055) | ||
|
|
97d4518d5f |
Show per-agent Session History reading progress (#30954)
* fix(session-history): freeze scan workload before checking candidates * Show per-agent Session History reading progress * Keep reduced-motion behavior in progress primitive * test(session-history): handle native fixture path separators * test(session-history): control bucket fixture enumeration order * test: cover finished empty history progress rows * test(session-history): preserve scoped fixture timestamps * fix(session-history): repair integrated test and locale catalog * ci: refresh history checks against current main * ci: refresh history checks after catalog repair |
||
|
|
6af21633e9 | refactor: colocate native-chat command language rules (#32041) | ||
|
|
ec5eaf96c1 | refactor: colocate provider retry transcript rules (#32024) | ||
|
|
d053ff97ab | refactor: colocate mobile web route and bundle tooling (#32008) | ||
|
|
4dd97f02d5 |
A paired desktop's tab moves, splits, resizes, props and renames in a server workspace go to the server's layout (switch-on builds only) (#31714)
* wip(h5c-1): server workspace layout intents go to the server's main; positions per stream * wip(h5c-1): e2e without a cast * fix(h5c-1): a routed move never moves here; routed callers read the server's stream; a restarted main starts its positions over * fix(h5c-1): layout-apply-send back under its line limit * test(h5c-1): a routed answer reaches the handler with the server's stream key * 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) * feat(h5c-2): a paired desktop's writes to a server workspace its frames draw land in that server's main (layout-owner build) - Routes setTabProps, setChatPane and non-terminal renameTab to the server's main as layout.apply; closes, starts, sleeps and terminal names keep the RPCs that main now applies (session.tabs.close, terminal.close, terminal.split, session.tabs.createTerminal, terminal.rename). A window-style closeTabs is never routed: main takes it as already stopped and stops nothing. - A close the server refuses (a pinned tab) redraws the workspace from the server's frames, so the window's optimistic drop never outlives the answer; same for a refused pane close. - A frame-drawn workspace keeps none of the mirror's ledgers (L240): no tab-id mappings, close or focus intents; they stay only for servers without layout.intents.v1 until the Phase 2 cutoff. Its pane-geometry push is skipped (it goes as intents); pane renames still push. - e2e: paired-desktop-server-layout-writes covers close (layout, session.tabs, terminal stop), rename, split and new tab on a windowless switch-on serve. * fix(h5c-2): an undelivered close of a frame-drawn tab is held for replay; a launch-agent clear stays off the server route * refactor(h5c-2): the undelivered-close test moves to its own module, keeping the lifecycle file under its line cap * fix(h5c): a routed tab move anchors on the server's tabs only, and never sends an anchor the server does not hold (layout-owner build) * fix(h5c): every routed tab move is placed among the server's tabs at the routing point, skipping desktop-only tabs (layout-owner build) |
||
|
|
c30fa6252a | refactor(tooling): group mobile web preview and evidence tools (#31976) | ||
|
|
32370a5e05 |
fix(terminal): settle a restored remote pane whose host session already exited (#31968)
* fix(terminal): settle a restored remote pane whose host session already exited A stale-handle or no_connected_pty refusal kept retrying forever because nothing read the host's own exit record. On those refusals (and the restore attach's stale branch) ask terminal.show once; settle the pane as exited only when the host's verdict is exited, or an older host records an exit cause for a disconnected terminal. Anything else keeps today's retry. Fixes #31539 * fix(terminal): ignore an unverified stop and keep the host's exit code * fix(terminal): settle on a host exit only when the host holds no replacement pane * fix(terminal): settle only while the host pane still holds the exited process * fix(terminal): leave a stopped host surface to materialize instead of settling it |
||
|
|
8556d119bb |
Conflict review shows the file's live unsaved text from any workspace (read-only) (#31990)
* Autosave relies on the guarded write instead of pausing restored or moved tabs Delete the pendingDiskBaselineVerification and pendingLiveDiskVerification pause flags. Every editor save is already a guarded write; a refusal raises the changed-on-disk banner, which is what stops autosave until an explicit save. A guarded write now succeeds when disk changed but already holds exactly the text being saved (local/host compare is byte-exact; client-side fallbacks compare decoded text and refuse when it holds U+FFFD), so a restored draft that matches disk goes clean instead of showing a banner. The restore scan only raises the banner early for tabs that arrive dirty; a watch resync no longer re-checks dirty tabs. Co-Authored-By: Claude <noreply@anthropic.com> * fix(editor): skip guarded saves when disk is already identical Return a compatible optional no-op result from hosts and client fallbacks, clean the tab, and clear its provisional self-write stamp without rewriting identical bytes. Co-Authored-By: Claude <noreply@anthropic.com> * fix(editor): refuse lossy automatic saves, keep identical-skip on older hosts, flag dirty tabs on resync - Autosave and pre-restart saves refuse when disk text can't be decoded losslessly, so a baseline that decoded to U+FFFD can't authorize overwriting different bytes. - New files.write.identical-noop.v1 capability; for hosts with only the v1 guarded write the client compares first, skips identical saves, then delegates to the host's guarded write. - Watch resync compares dirty tabs against disk and shows the changed-on-disk banner. - Local guarded writes check for identical content before the write-only regular-file probe. Co-Authored-By: Claude <noreply@anthropic.com> * fix(editor): tell non-UTF-8 files apart from disk changes, and quiet false resync banners - Local guarded writes judge losslessness by bytes, so a valid UTF-8 file containing U+FFFD autosaves again. A file that really isn't UTF-8 is refused with its own reason, 'not-utf8' (older clients read it as 'changed'), which pauses autosave for that file with one notice until an explicit save converts it. - A watch resync whose disk read fails leaves dirty tabs alone; the guarded save still catches a real conflict. - Resync and restored-tab checks treat disk holding the tab's own just-written save as an echo. - On hosts with only the v1 guarded write, only automatic saves read the file first; explicit saves go straight to the host's guard. Co-Authored-By: Claude <noreply@anthropic.com> * fix(editor): lift the non-UTF-8 autosave pause when its edits end, and report disk changes first - The pause is released once no dirty tab of the file remains (close, reload from disk, discard), so a reopened or reloaded file's next autosave asks the host again and notifies. - Client-side precondition checks compare the signature before looking for U+FFFD, so an external rewrite shows the changed-on-disk banner rather than the non-UTF-8 notice. Co-Authored-By: Claude <noreply@anthropic.com> * Schedule editor autosave per buffer record, and add the join rules the sharing switch uses First of the stacked sharing-switch PRs (G1). Inert while every record has one view, which is all main produces today; tests drive the shared shape through hand-built view indexes. - Autosave timers are decided per record by its first view that may autosave, so a read-only or preview view of a record no longer cancels the record's timer, and a timer survives the view that scheduled it closing (it resolves a live view of the record when it fires). - A view of a detached record gets its own model owner key, so divergent dirty drafts never share text with the file's other record. - Pure join and rejoin rules: rekey / attach / adopt (dirty wins) / merge (identical dirty) / detach (divergent dirty); a detached pair rejoins only once one side is clean. - Mirrored host tabs: a record's dirty flag is the OR of its host-owned views, since an older host publishes it per tab; the dev invariant tolerates exactly that disagreement. Co-Authored-By: Claude <noreply@anthropic.com> * Keep pauses and pending text out of buffer merges, and pick a writable view for autosave - A merge needs equal drafts, baselines, conflict and owner-migration state; anything else detaches, so a paused record never resumes autosave by joining. A side whose text is still pending (no draft) never counts as identical. - A clean-flagged view that still holds a draft counts as dirty in joins. - The invariant tolerates a clean host-owned view on a dirty record only when another host-owned view of that record reads dirty. - Autosave and restart skip CSV-preview and read-only views when picking the view a record writes through. Co-Authored-By: Claude <noreply@anthropic.com> * feat(editor): views of one file share one buffer record Co-Authored-By: Claude <noreply@anthropic.com> * fix(editor): review fixes for shared buffer records A dirty view with no draft no longer pushes disk text into the shared model; merge and deleted-banner joins mirror the record; web-session sync honours detached views. Co-Authored-By: Claude <noreply@anthropic.com> * feat(editor): shared records take every writer, rejoin, outlive no-prompt closes, restore merges - Rich Markdown, notebook and CSV commits also edit the record's models no editor shows, as one undoable edit; a save moves every loaded view's panel text to the written disk text, so a stale dirty check can't mark the record clean over unsaved edits. - A detached pair rejoins once one side is clean (save, Discard, reload) or the partner closes: the clean side attaches, a dirty side wins with its own baseline, so its next save is refused and the banner shows on every view. Rejoin runs on the next microtask, after the caller has finished settling the record; doing it mid-settle let the settle clear the adopted draft. - A dirty record whose last view a no-prompt removal closed is kept with its draft and model; reopening the file joins it, it never autosaves, and dismissing the removal notice drops it. - Restored identical drafts merge; divergent ones detach with their own models and timers. Co-Authored-By: Claude <noreply@anthropic.com> * test(editor): cover shared buffer records end to end, and restart-save a record through its writable view Adds save-queue and store tests for rename and owner-resolution re-keys carrying a conflict, a read-only sibling joining a dirty record, retired-runtime quarantine, runtime-in-SSH identity, the reconnect writer, restart once per record, and a 200-tab index. Fix: restart picked a record's first dirty view, so a read-only view listed first refused the whole restart; it now picks the record's first view that may autosave. Co-Authored-By: Claude <noreply@anthropic.com> * fix(editor): G3 review fixes for kept records, rejoin, undo steps and save events - A no-prompt removal's keep request rides in the write that closes the tabs (editorBufferKeepRequest, consumed by the record middleware), so a removal closing tabs over several writes keeps every dirty record it closes. - Kept records carry their removal notice's token; dismissing a notice, or failing to show it, releases only that notice's records. - A divergent dirty view reopening a kept record stays detached on its own model; a rename onto a kept record's path drops the kept record. - Each rich/notebook/CSV commit into a retained model is its own undo step. - The saved event names the record's views, so the panel updates them even when the saving tab closed mid-save. - A queued save compares the discard epoch of the record it was queued under. - Typing no longer runs the rejoin check; only clearing a side's draft does. Co-Authored-By: Claude <noreply@anthropic.com> * feat(editor): conflict-review overview shows the file's shared record, read-only and live The overview of a conflict review now reads each file through any open view of its shared buffer record (this workspace's first), so it shows the record's current unsaved text and model and follows typing in another workspace. It stays read-only and is never a view itself: it loads as that view would and never pushes text. With no view open it still shows disk text. Co-Authored-By: Claude <noreply@anthropic.com> * fix(editor): conflict-review overview follows its borrowed view and never acts as it - The overview's load reruns when the view it reads through opens, closes, re-keys, detaches or rejoins in any workspace, so it no longer sticks on "Loading...". - Retry reloads through the borrowed view's own workspace, and the editor resolves through that workspace, so this workspace never re-owns the other's tab. - Overview editors are passive mirrors: they don't move the borrowed tab's cursor line or take a reveal meant for it. - This workspace's own tab (even a detached one with its own edits) wins over another workspace's view. - The panel's draft selector re-resolves when tabs or record views change, not only when drafts do; overview lookups are memoized per tabs/views/routes, and the stand-in cache evicts least recently used entries instead of clearing. Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
2583e461b2 |
A browser tab opened in a server workspace drawn from the server's layout stays open (switch-on builds only) (#31898)
* fix(L273-core): tabs this desktop created in a paired server's frame-drawn workspace survive the server's frames (switch-on) A tab the server's layout never lists (a client-hosted browser tab today; any kind recorded the same way) used to vanish on the server's next frame rebuild. This desktop now records such tabs by server and workspace, in its localStorage only (never a session field nor sent), and the server's frame builds keep them as drawn, inside the server's own groups: - A held tab drawn in a group the server lacks moves to the nearest server group; it shows there only if the user was on it, and the user's group follows it. The group tree stays the server's. - The record forgets an id once the server's frame lists it, on close, and on worktree removal, and prunes ids the restored session no longer has before the first frame after a reload. - A held tab's drag moves it here only (no session.tabs.move); it offers no new split, and a server tab sharing its group counts only the server's tabs for split no-ops. - Client-hosted staging records its tab (both ids) in layout-owner builds; restage takes the worktree from its caller instead of scanning the store. * refactor(L273-core): the desktop-created record is inert with the switch off; clearer names - With the switch off the record is never read, kept or written (a single gate in the record). - The frame-drawn check is isDesktopCreatedTabInFrameDrawnServerWorkspace in frame-drawn-desktop-created-tabs.ts: "held" stays the frame hold's word. - The held-tab placement drops its unread activeGroupId; the group follow derives it. * fix(L282): a client-hosted browser tab this desktop creates in a frame-drawn server workspace completes once the server's snapshot lists it, instead of undoing itself after 8 s (switch-on) * fix(L284): a server-hosted browser tab this desktop creates in a frame-drawn server workspace completes when the server's frames take it over, instead of reading as the user's close (switch-on) * chore(L282/L284): adopt only staged client-hosted handles, and name the frame build's handover note (review nits) * fix(L284): a staged tab the server's frames took over completes the create though its pane, unmounted, has no handle yet (layout-owner build only) * fix(browser): a switch-on host answers a paired client's browser create with the new page's id, not its workspace's, before main's frame draws it * fix(browser): the reply names the page id the create asked for first, then the new record's own page (L291) * fix(L284): the frame build scans dropped pages for a handover only when a staged create's page is drawn, so a large close stays one pass |
||
|
|
885cd6ff44 |
Tabs of the same file share one record of unsaved edits (#31974)
* Autosave relies on the guarded write instead of pausing restored or moved tabs Delete the pendingDiskBaselineVerification and pendingLiveDiskVerification pause flags. Every editor save is already a guarded write; a refusal raises the changed-on-disk banner, which is what stops autosave until an explicit save. A guarded write now succeeds when disk changed but already holds exactly the text being saved (local/host compare is byte-exact; client-side fallbacks compare decoded text and refuse when it holds U+FFFD), so a restored draft that matches disk goes clean instead of showing a banner. The restore scan only raises the banner early for tabs that arrive dirty; a watch resync no longer re-checks dirty tabs. Co-Authored-By: Claude <noreply@anthropic.com> * fix(editor): skip guarded saves when disk is already identical Return a compatible optional no-op result from hosts and client fallbacks, clean the tab, and clear its provisional self-write stamp without rewriting identical bytes. Co-Authored-By: Claude <noreply@anthropic.com> * fix(editor): refuse lossy automatic saves, keep identical-skip on older hosts, flag dirty tabs on resync - Autosave and pre-restart saves refuse when disk text can't be decoded losslessly, so a baseline that decoded to U+FFFD can't authorize overwriting different bytes. - New files.write.identical-noop.v1 capability; for hosts with only the v1 guarded write the client compares first, skips identical saves, then delegates to the host's guarded write. - Watch resync compares dirty tabs against disk and shows the changed-on-disk banner. - Local guarded writes check for identical content before the write-only regular-file probe. Co-Authored-By: Claude <noreply@anthropic.com> * fix(editor): tell non-UTF-8 files apart from disk changes, and quiet false resync banners - Local guarded writes judge losslessness by bytes, so a valid UTF-8 file containing U+FFFD autosaves again. A file that really isn't UTF-8 is refused with its own reason, 'not-utf8' (older clients read it as 'changed'), which pauses autosave for that file with one notice until an explicit save converts it. - A watch resync whose disk read fails leaves dirty tabs alone; the guarded save still catches a real conflict. - Resync and restored-tab checks treat disk holding the tab's own just-written save as an echo. - On hosts with only the v1 guarded write, only automatic saves read the file first; explicit saves go straight to the host's guard. Co-Authored-By: Claude <noreply@anthropic.com> * fix(editor): lift the non-UTF-8 autosave pause when its edits end, and report disk changes first - The pause is released once no dirty tab of the file remains (close, reload from disk, discard), so a reopened or reloaded file's next autosave asks the host again and notifies. - Client-side precondition checks compare the signature before looking for U+FFFD, so an external rewrite shows the changed-on-disk banner rather than the non-UTF-8 notice. Co-Authored-By: Claude <noreply@anthropic.com> * Schedule editor autosave per buffer record, and add the join rules the sharing switch uses First of the stacked sharing-switch PRs (G1). Inert while every record has one view, which is all main produces today; tests drive the shared shape through hand-built view indexes. - Autosave timers are decided per record by its first view that may autosave, so a read-only or preview view of a record no longer cancels the record's timer, and a timer survives the view that scheduled it closing (it resolves a live view of the record when it fires). - A view of a detached record gets its own model owner key, so divergent dirty drafts never share text with the file's other record. - Pure join and rejoin rules: rekey / attach / adopt (dirty wins) / merge (identical dirty) / detach (divergent dirty); a detached pair rejoins only once one side is clean. - Mirrored host tabs: a record's dirty flag is the OR of its host-owned views, since an older host publishes it per tab; the dev invariant tolerates exactly that disagreement. Co-Authored-By: Claude <noreply@anthropic.com> * Keep pauses and pending text out of buffer merges, and pick a writable view for autosave - A merge needs equal drafts, baselines, conflict and owner-migration state; anything else detaches, so a paused record never resumes autosave by joining. A side whose text is still pending (no draft) never counts as identical. - A clean-flagged view that still holds a draft counts as dirty in joins. - The invariant tolerates a clean host-owned view on a dirty record only when another host-owned view of that record reads dirty. - Autosave and restart skip CSV-preview and read-only views when picking the view a record writes through. Co-Authored-By: Claude <noreply@anthropic.com> * feat(editor): views of one file share one buffer record Co-Authored-By: Claude <noreply@anthropic.com> * fix(editor): review fixes for shared buffer records A dirty view with no draft no longer pushes disk text into the shared model; merge and deleted-banner joins mirror the record; web-session sync honours detached views. Co-Authored-By: Claude <noreply@anthropic.com> * feat(editor): shared records take every writer, rejoin, outlive no-prompt closes, restore merges - Rich Markdown, notebook and CSV commits also edit the record's models no editor shows, as one undoable edit; a save moves every loaded view's panel text to the written disk text, so a stale dirty check can't mark the record clean over unsaved edits. - A detached pair rejoins once one side is clean (save, Discard, reload) or the partner closes: the clean side attaches, a dirty side wins with its own baseline, so its next save is refused and the banner shows on every view. Rejoin runs on the next microtask, after the caller has finished settling the record; doing it mid-settle let the settle clear the adopted draft. - A dirty record whose last view a no-prompt removal closed is kept with its draft and model; reopening the file joins it, it never autosaves, and dismissing the removal notice drops it. - Restored identical drafts merge; divergent ones detach with their own models and timers. Co-Authored-By: Claude <noreply@anthropic.com> * test(editor): cover shared buffer records end to end, and restart-save a record through its writable view Adds save-queue and store tests for rename and owner-resolution re-keys carrying a conflict, a read-only sibling joining a dirty record, retired-runtime quarantine, runtime-in-SSH identity, the reconnect writer, restart once per record, and a 200-tab index. Fix: restart picked a record's first dirty view, so a read-only view listed first refused the whole restart; it now picks the record's first view that may autosave. Co-Authored-By: Claude <noreply@anthropic.com> * fix(editor): G3 review fixes for kept records, rejoin, undo steps and save events - A no-prompt removal's keep request rides in the write that closes the tabs (editorBufferKeepRequest, consumed by the record middleware), so a removal closing tabs over several writes keeps every dirty record it closes. - Kept records carry their removal notice's token; dismissing a notice, or failing to show it, releases only that notice's records. - A divergent dirty view reopening a kept record stays detached on its own model; a rename onto a kept record's path drops the kept record. - Each rich/notebook/CSV commit into a retained model is its own undo step. - The saved event names the record's views, so the panel updates them even when the saving tab closed mid-save. - A queued save compares the discard epoch of the record it was queued under. - Typing no longer runs the rejoin check; only clearing a side's draft does. Co-Authored-By: Claude <noreply@anthropic.com> * fix(editor): keep open-file mirror writes out of the layout writer ratchet The record middleware now applies reconcile's per-view mirror fields as a membership-preserving map over openFiles, and the mirrored-dirty adoption returns its views for the session-sync patch to write, as it did before. Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
15d663fc3f |
Test runner uses the repo's pinned Bun, and refuses an older one instead of silently skipping tests (#31981)
* chore(test): run-vitest launches the pinned Bun, and refuses a Bun older than config/.bun-version An older Bun silently runs no tests in files that reach newer node builtins, so a local comparison could skip them on both sides. Claude-Session: W5b * chore(test): an older Bun ends run-vitest by exit code, so its message is not cut off Claude-Session: W5b |
||
|
|
48660ae717 |
fix(packaging): keep ssh2's Windows-only Pageant files out of macOS and Linux packages (#31946)
* fix(packaging): keep ssh2's Windows-only Pageant files out of macOS and Linux packages * test: explain the win32 ssh2 filter fixture and format * test: read the excluded ssh2 file names from the exclude table |
||
|
|
54acda1d12 |
Editor buffers ready for sharing: autosave per record, separate models for detached tabs (no behaviour change) (#31970)
* Autosave relies on the guarded write instead of pausing restored or moved tabs Delete the pendingDiskBaselineVerification and pendingLiveDiskVerification pause flags. Every editor save is already a guarded write; a refusal raises the changed-on-disk banner, which is what stops autosave until an explicit save. A guarded write now succeeds when disk changed but already holds exactly the text being saved (local/host compare is byte-exact; client-side fallbacks compare decoded text and refuse when it holds U+FFFD), so a restored draft that matches disk goes clean instead of showing a banner. The restore scan only raises the banner early for tabs that arrive dirty; a watch resync no longer re-checks dirty tabs. Co-Authored-By: Claude <noreply@anthropic.com> * fix(editor): skip guarded saves when disk is already identical Return a compatible optional no-op result from hosts and client fallbacks, clean the tab, and clear its provisional self-write stamp without rewriting identical bytes. Co-Authored-By: Claude <noreply@anthropic.com> * fix(editor): refuse lossy automatic saves, keep identical-skip on older hosts, flag dirty tabs on resync - Autosave and pre-restart saves refuse when disk text can't be decoded losslessly, so a baseline that decoded to U+FFFD can't authorize overwriting different bytes. - New files.write.identical-noop.v1 capability; for hosts with only the v1 guarded write the client compares first, skips identical saves, then delegates to the host's guarded write. - Watch resync compares dirty tabs against disk and shows the changed-on-disk banner. - Local guarded writes check for identical content before the write-only regular-file probe. Co-Authored-By: Claude <noreply@anthropic.com> * fix(editor): tell non-UTF-8 files apart from disk changes, and quiet false resync banners - Local guarded writes judge losslessness by bytes, so a valid UTF-8 file containing U+FFFD autosaves again. A file that really isn't UTF-8 is refused with its own reason, 'not-utf8' (older clients read it as 'changed'), which pauses autosave for that file with one notice until an explicit save converts it. - A watch resync whose disk read fails leaves dirty tabs alone; the guarded save still catches a real conflict. - Resync and restored-tab checks treat disk holding the tab's own just-written save as an echo. - On hosts with only the v1 guarded write, only automatic saves read the file first; explicit saves go straight to the host's guard. Co-Authored-By: Claude <noreply@anthropic.com> * fix(editor): lift the non-UTF-8 autosave pause when its edits end, and report disk changes first - The pause is released once no dirty tab of the file remains (close, reload from disk, discard), so a reopened or reloaded file's next autosave asks the host again and notifies. - Client-side precondition checks compare the signature before looking for U+FFFD, so an external rewrite shows the changed-on-disk banner rather than the non-UTF-8 notice. Co-Authored-By: Claude <noreply@anthropic.com> * Schedule editor autosave per buffer record, and add the join rules the sharing switch uses First of the stacked sharing-switch PRs (G1). Inert while every record has one view, which is all main produces today; tests drive the shared shape through hand-built view indexes. - Autosave timers are decided per record by its first view that may autosave, so a read-only or preview view of a record no longer cancels the record's timer, and a timer survives the view that scheduled it closing (it resolves a live view of the record when it fires). - A view of a detached record gets its own model owner key, so divergent dirty drafts never share text with the file's other record. - Pure join and rejoin rules: rekey / attach / adopt (dirty wins) / merge (identical dirty) / detach (divergent dirty); a detached pair rejoins only once one side is clean. - Mirrored host tabs: a record's dirty flag is the OR of its host-owned views, since an older host publishes it per tab; the dev invariant tolerates exactly that disagreement. Co-Authored-By: Claude <noreply@anthropic.com> * Keep pauses and pending text out of buffer merges, and pick a writable view for autosave - A merge needs equal drafts, baselines, conflict and owner-migration state; anything else detaches, so a paused record never resumes autosave by joining. A side whose text is still pending (no draft) never counts as identical. - A clean-flagged view that still holds a draft counts as dirty in joins. - The invariant tolerates a clean host-owned view on a dirty record only when another host-owned view of that record reads dirty. - Autosave and restart skip CSV-preview and read-only views when picking the view a record writes through. Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
92c8f3cbdc |
fix(ssh): stop floating terminal metadata from blocking SSH access links (#31963)
* fix(ssh): stop local-only workspace metadata from blocking SSH access links Unstamped floating and setup terminal metadata rows were projected onto every execution host, so the dependent-state census refused SSH access links and empty-host claims for every SSH target with orcad_migration_dependent_state. - Local-only workspace ids project only onto the local host, are stamped local on load, and their metadata writes always land on the local host. - The census counts an unstamped legacy row only when its repo is not registered solely on other hosts. - The SSH access error now names the blocking state. Builds on the approach in #31862 by Frank-zhu2000. Fixes #31498 Co-authored-by: Frank-zhu2000 <Frank-zhu2000@users.noreply.github.com> * fix(ssh): rehome remote-stamped local-only metadata, skip orphan rows, and word the SSH access error for a link * fix(persistence): keep a local or unstamped floating row over a remote copy when rehoming * fix(ssh): make the SSH access blocker copy switch exhaustive --------- Co-authored-by: Frank-zhu2000 <Frank-zhu2000@users.noreply.github.com> |
||
|
|
2c937546dd |
A paired desktop keeps the tabs it created in a server workspace drawn from the server's layout (switch-on builds only) (#31853)
* fix(L273-core): tabs this desktop created in a paired server's frame-drawn workspace survive the server's frames (switch-on) A tab the server's layout never lists (a client-hosted browser tab today; any kind recorded the same way) used to vanish on the server's next frame rebuild. This desktop now records such tabs by server and workspace, in its localStorage only (never a session field nor sent), and the server's frame builds keep them as drawn, inside the server's own groups: - A held tab drawn in a group the server lacks moves to the nearest server group; it shows there only if the user was on it, and the user's group follows it. The group tree stays the server's. - The record forgets an id once the server's frame lists it, on close, and on worktree removal, and prunes ids the restored session no longer has before the first frame after a reload. - A held tab's drag moves it here only (no session.tabs.move); it offers no new split, and a server tab sharing its group counts only the server's tabs for split no-ops. - Client-hosted staging records its tab (both ids) in layout-owner builds; restage takes the worktree from its caller instead of scanning the store. * refactor(L273-core): the desktop-created record is inert with the switch off; clearer names - With the switch off the record is never read, kept or written (a single gate in the record). - The frame-drawn check is isDesktopCreatedTabInFrameDrawnServerWorkspace in frame-drawn-desktop-created-tabs.ts: "held" stays the frame hold's word. - The held-tab placement drops its unread activeGroupId; the group follow derives it. * fix(L273-core): a simulator tab opened in a paired server's frame-drawn workspace survives its frames (switch-on) The server never has this desktop's emulator tab, so its frame rebuilds dropped it. The simulator open now records the tab as created here when a server's frames draw the workspace, and opens it in the server group it would have split beside, since that workspace has no group of this desktop's own. L290 (the host runs and streams the simulator, #6701) removes the note. * test(L273-core): the mirrored-workspace simulator test draws the tab and can fail; the side-open test starts from another group Co-Authored-By: Claude <noreply@anthropic.com> * fix(L273-core): list the created-here tab record in the WSL-move storage census It is keyed by a paired server's environment; a local or WSL project never has one, so a move has nothing to carry. --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
a3fa44ad07 |
fix(push): honor "only when desktop is away" on headless orca serve (#31956)
* fix(push): decide orca serve's desktop-away from attached desktops (#31570) * fix(push): report away on sleep and lock, guard late reports and prune stale presence * chore: justify the presence-reporter state slot widening * fix(push): hold away from suspend or lock until resume or unlock * fix(push): key presence sockets by connection instance so a replaced connection reports at once |
||
|
|
abc0c743a2 | fix(push): bound claim transactions before background admission (#31955) | ||
|
|
54b5dbe2cc | refactor(renderer): colocate Linear issue empty-state policy (#31960) | ||
|
|
6a9a80a7fe |
feat(telemetry): daily aggregate WSL usage snapshot on Windows (#31406)
* feat(telemetry): daily aggregate WSL usage snapshot on Windows Adds a main-owned wsl_usage_snapshot event through the existing PostHog track() pipeline (same consent, opt-out, build gate and validator): WSL installed/version, distro counts (installed, running, WSL1/2), WSL-backed project counts (distro fs vs Windows drive, git vs folder), distinct project distros, and the internal WSL-hosts gate. Counts are bucketed; no paths or names leave the machine. At most once per 24h per install, Windows only. * fix(telemetry): bound WSL snapshot retries and name the default distro Stamp before gathering so a failing gather isn't repaid hourly, treat a future stamp as due, resolve 'default distro' to its name for distinct counts, narrow the store type, and cover the running-probe choice and the closed WSL-hosts gate in tests. |
||
|
|
8e9690468a |
fix(orcad): ship computer use with the server and say when it is unavailable (#31958)
* fix(orcad): ship computer use with the server and say when it is unavailable * test(computer): drop the type assertion in the sidecar app-environment mock * fix(cli): carry the server reprovision hint into --json computer failures |
||
|
|
0b4f2d9a86 |
fix(ssh): editing project groups no longer marks a moved host as changed (#31954)
A moved host was marked "changed by an older version of Orca" whenever its retained catalog fingerprint drifted, and that fingerprint includes project group ids. Deleting or creating a group on this desktop therefore unhid the old SSH rows, failed every connect with source_changed, and left folder workspace dialogs checking the fenced ssh target. A host is now changed only when its source holds a project or folder workspace that no move took or linked and the user did not leave behind, which is all a delta move can carry. The fingerprint itself is unchanged, so older builds and existing journals compare exactly as before and no move flips on upgrade. "Keep the server's version" now records the rows it left behind; journals an older build kept get those rows listed once at start. Hosts an earlier build marked for nothing a move could carry are unmarked at start. The banner now says what is actually different. Fixes #31523 Fixes #30969 |
||
|
|
aeb96c580a |
fix(session-tabs): mark re-sent held closes as replays for hosts that accept it (#31951)
A held close re-sent after reconnect went out as reason 'user', so a host's trace could not tell automatic replays from clicks (#31449). Hosts now accept an optional 'replay' reason, advertised by session-tabs.close-replay-reason.v1, and treat it as the user close it re-sends. Clients mark replays only for hosts that advertise it; older hosts keep receiving 'user'. |
||
|
|
d8aeb1f984 |
fix(claude-accounts): restart WSL Claude panes after an account switch (#31945)
* fix(claude-accounts): restart WSL Claude panes on account switch A Windows host cannot see processes inside WSL, so the foreground gate dropped every WSL pane from the restart set. WSL panes now use the guest's hook status and shell marks, the evidence the execution host owns. Reported-by: Kooooojun * fix(claude-accounts): resume WSL Claude panes whose project runs in WSL The account-restart resume builder declined every WSL project runtime, so a WSL worktree or a WSL Windows runtime default left no Claude candidate to restart. Keep that guard for Codex only; a WSL Claude launch reads the guest's selected-account pointer. |
||
|
|
c8d0f99c29 |
fix(orcad): name a missing shared library when the pinned Node runtime can't start (#29829)
A host without libstdc++ (Alpine's default) failed with the loader's raw output. The self-test error now names the library and how to install it, and the missing-library parser also reads musl's loader wording, which node-pty's diagnosis uses too. Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
ca9abc66c5 | refactor(jira): colocate JQL predicate composition (#31933) | ||
|
|
ea60ec5d7c |
Layout-owner builds: a phone's tab move is always the server's own; no ghost editor tabs after restart (#31925)
* Layout groundwork (switched off): main derives agent tab titles from its own hook status With the layout-owner switch on, main gives agent tabs their generated titles from the hook-server status it owns, on the desktop, headless serve and orcad: one renameTabs per status batch, so one write and one frame. It keeps the window's rules (setting, custom and quick-command titles, first title wins, dispatch labels). The window stops sending generated titles for tabs main holds; a paired server's tab, or one main has no tab-bar entry for, is still titled by the window as today. The title rules move to src/shared (agent-row-primary-text, and the new agent-generated-tab-title-rules) so both sides share them; switch-off behaviour is unchanged. * feat(layout): main applies a phone's or paired client's tab move itself, and a tab move names the tab it follows so a stale index can't misplace it (layout-owner build, L193) * fix(layout): a move in a group showing a tab main lacks goes the window's way, a twin's agent tab keeps its window title, and orcad subscribes titles through the store attach (layout-owner build) * chore(layout): main's tab move reaches the old-client translation through its compat entry * chore(layout): mark the window-only-row move fallback as bridge code (ledger L280) * test(layout): attaching the hook store follows its rows for main's tab titles once * chore(layout): the old-client move translation is a compat entry of its own, with no cyclic re-export * fix(layout): every tab a group shows is main's, so a client's tab move never falls back to the window (layout-owner build) - Main moves a client's tab itself in a workspace it owns; a row it holds no tab for (a combined diff) keeps no place, and an order must still name each row shown once (L280). - The window publishes an editor row under its tab id only; a file no group shows gets none. - Main's layout load gives a saved browser record with no tab its tab, as the window's hydration repair did, and the window no longer mints one for a workspace main owns. - Main's layout load drops the saved tabs the window's restore drops (view-less diff, conflict and check tabs, old html previews), through one shared rule; the next save writes it back (L286). - Main lists conflict-review and check-details tabs as the plain file rows the window lists, so older clients keep them once the window's rows are gone (L287). * fix(lint): drop an import the shared restore rule left unused * test: import the move translation through the folder's public path * fix(layout): review follow-ups for the move fallback removal (layout-owner build) - A transient tab stays at load while a view is held for its file, as the window's frame build keeps it, not only for its own tab id. - The repaired browser tab goes to the saved active group only while it holds a tab, as selectHydratedActiveGroupId picks it; tests that the id is stable across loads and restarts, that a window-owned build restores it once, and that a paired server's record keeps the window's repair. - Two switch-on tests keyed editor drafts by path; drafts are keyed by editorFileKey (an owned id in a layout-owner build), so they now use it. * fix(layout): the repaired browser tab skips a saved active group this load empties * test(layout): directly dialed SSH browser record is main's; a server's SSH workspace keeps the window repair * fix(layout): drop view-less editor tabs only in loads that carry main's views, so main's own edits keep a live diff * test(layout): a direct SSH partition gets the same browser repair and drop with no host contact |
||
|
|
041183abae | Use retention indexes before limiting push expiry batches (#31921) | ||
|
|
d33b170a5a |
refactor(ssh): move the reconnect ladder and session channel opening out of SshConnection (#31923)
Move-only: the backoff timer, ladder and wake/network-return redial move into SshConnectionReconnector, and exec/shell/sftp/system-command channel opening into ssh-connection-session-channels, both reading the connection through host getters. |
||
|
|
8967b10d41 |
chore: remove verified dead code and calibrate Knip entries (#31903)
* chore: remove verified dead code and calibrate Knip entries * fix: include mjs consumers in dead-code analysis |
||
|
|
4c80dac0d8 | refactor(tasks): colocate GitLab task-page loading (#31907) | ||
|
|
dc315a4e5a |
refactor(ssh): remove the dead SSH filesystem provider and desktop SSH file watcher (#31922)
* refactor(ssh): remove the dead SSH filesystem provider and desktop SSH file watcher Nothing in production has registered an SSH filesystem provider since the relay removal (#27214/#27215), so every getSshFilesystemProvider read returned undefined. Delete the provider table and the remote watcher subsystem, and collapse the call sites to that always-undefined behaviour: fs:watchWorktree/fs:unwatchWorktree ignore SSH roots, the worktree base watcher only resolves local repos, and worktree removal skips the desktop watcher for SSH paths. * test: drop the nested SSH watcher catalog case that can no longer occur The base-directory watcher never carries a connection id now, so the host-blind-event guard it exercised is gone. Also prune the deleted remote watcher suites from the shard timings and the nested-SSH wording from the worktree catalog reliability gate. |
||
|
|
dcf2795b1d | refactor(ssh): keep the connection step host types private and justify the host key order cast (#31900) | ||
|
|
e5f2123e8d | refactor(shared): delete exports nothing reads (#31917) | ||
|
|
a164f42b8a | fix(ci): exclude inherited job results from runner demand (#31919) | ||
|
|
4165c9545c |
Layout groundwork (switched off): main tells the window whether its start resumed a pane's agent, so the restored banner and reopen count come back (#31908)
* 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 * fix(terminal): a restored pane paints history only from its terminal's host, never the window's copy (layout-owner build only) * fix(terminal): main releases a painted cold restore only for the pane's current terminal (layout-owner build only) * test(terminal): typed ack args in the painted cold-restore test * 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(layout): main starts the panes a client's startPane or wake names, through its one restored-pane starter (layout-owner build only) The model's start and stop effects reach the runtime once the command is written, never the reply. startPane starts a pane whose binding has a start status, and a slept pane from its record. A wake keeps a pane's resume record until its start binds; a failed start keeps it and shows why. With the switch on, main owns a running agent's wake even with a window open. * fix(layout): a window's wake of a pane it resumed itself is record-only, so main does not start it again (layout-owner build only) * test(layout): a client's startPane starts the pane before any other trigger * Revert "fix(terminal): a restored pane paints history only from its terminal's host, never the window's copy (layout-owner build only)" This reverts commit |
||
|
|
8e7b08c617 | refactor(ssh): delete the unused SFTP session late-error latch (#31909) | ||
|
|
f3f12a8b03 | refactor(ssh): stop exporting declarations used only inside their own module (#31897) | ||
|
|
8d6ad5ffe0 | Keep push busy-device lookup correlated to its existing index (#31899) | ||
|
|
6cd2550e61 |
refactor(native-chat): share the catch-up open runner across agents (#31622)
* refactor(native-chat): share the catch-up open runner across agents The Codex catch-up's agent-neutral parts move to one module: the open-time envelope (its own deadline and cancellation, never gating the open), the bounded staged batch, the single settlement commit that re-checks the journal before writing, and the "Continued outside this chat." divider. Codex keeps only how it reads its saved turns and what the chat already holds. No behavior change; the other agents' catch-up builds on this instead of copying it. * chore: remove unrelated pnpm executable metadata * refactor(native-chat): the catch-up runner reads the chat's view only when the agent asks * chore: remove regenerated pnpm executable metadata * fix(native-chat): the catch-up deadline also bounds waiting for its commit to be written A journal write barrier that never settled after the import was admitted held the provider's open past the catch-up deadline. The wait now ends with the deadline or the open's cancellation, like every other step. * test: type the history catch-up sink fixture without assertions * fix(native-chat): bound and revoke history preparation * feat(native-chat): validate expected saved items during history catch-up |
||
|
|
f6585a3916 |
Keep Session History checking totals fixed throughout a scan (#30949)
* fix(session-history): freeze scan workload before checking candidates * test(session-history): handle native fixture path separators * test(session-history): control bucket fixture enumeration order * test(session-history): preserve scoped fixture timestamps * fix(session-history): repair integrated test and locale catalog * ci: refresh history checks against current main * ci: refresh history checks after catalog repair |
||
|
|
33ec2ba9e7 |
fix(claude): close a cancelled approval card when the journal drains (#31735)
A Claude approval card cancelled while the chat journal was briefly full waited for a later provider event to retry the cancellation, so an idle session could leave the card open. The sink's drain wake now retries owed prompt cancellations alongside owed background-task rows, and reading still waits for the next drain if the retry fills the sink again. Tracked in STA-7630. |
||
|
|
4778133a1f | ci: share the mobile bundle across SSH Windows host cells (#31883) | ||
|
|
d5d9170475 | refactor(managed-server): stop exporting declarations used only inside their own module (#31888) | ||
|
|
e4959a6bed | ci: canary a smaller WSL distro matrix for runtime changes (#31873) | ||
|
|
e6ce82f676 | test: isolate Windows tree-kill load fixture startup and cleanup (#31891) | ||
|
|
7c34a48529 |
fix(native-chat): show a prompt hook's block as its own row for every agent (#30898)
* fix(native-chat): show a prompt hook's block as its own row for every agent When a prompt hook refused a message, Codex showed nothing, Claude showed an unmarked warning the turn fold could hide (with a copy of the prompt), and Grok read as the user stopping the turn. Each adapter now writes one warning row, "Blocked by hook: <reason>" or "Hook stopped: <reason>", marked with the hook-outcome presentation that the turn fold never hides: - Codex: a blocked or stopped UserPromptSubmit hook/completed frame - Claude: a warning informational frame that halts the prompt, worded from its structured prompt_submit_outcome when present - Grok: a HookDenied end, from either completion signal under one row identity; the turn ends completed instead of interrupted Nothing new is saved and nothing changes on the wire beyond the new presentation value. * fix(native-chat): end a hook-denied Grok turn on its first signal, and clean older Claude block wording Follow-ups from the size review of the hook-row change: - Grok: a hook-denied turn_completed now ends the turn and writes the row straight away, instead of waiting for the prompt's answer; a later answer changes nothing. - Claude: when an older Claude sends only its sentence, the row keeps the hook's reason and drops the hook path and the copy of the prompt. Whether the row shows still depends only on prevent_continuation. - The turn-ending shape is declared once, the Codex hook payload is read once, and the Codex hook row returns early instead of threading through the generic frame row. * test(native-chat): cover hook-block rows with real Grok frames and a preserved send - Replace the source-derived Grok fixture with frames captured from Grok 1.0.62 (session, prompt ids renamed to the test's). - One whole-flow file, three parameterized cases (Codex, Claude current and older wording, Grok in every completion order), on the shared timeline rig with a per-agent journal identity. - Each case starts with a send on record and checks it is unchanged after the reopen. * fix(native-chat): word every Claude prompt-hook refusal, and draw the Codex hook row like the others - Claude: a plugin module's dropped_by_hook / not_passed_on refusal reads "Blocked by hook" too. - Older Claude sentence: an empty reason no longer turns the prompt copy into the reason; each hook's path label goes per line, while a hook's own [TAG]: stays. - Codex: the prompt-hook row is read once, before classification, and carries no raw-frame disclosure, so it matches the Claude and Grok rows. The hook/completed table entry is back to main's. - Comments the change had made wrong are corrected. * fix(native-chat): strip only the older Claude sentence's first hook label, as before Command labels without a slash were kept and labels inside a hook's own text were removed; the first-line strip is back, with the empty-reason fix kept. * refactor(native-chat): keep a hook's block row visible as a generic turn notice The fold never hides a row presented as 'turn-notice', a host notice about the turn, instead of a hook-specific value; the hook rows write it for every agent. * fix(native-chat): report a Claude prompt hook's exit-2 block once With hook events on, Claude also sends the blocking UserPromptSubmit hook's exit 2 as an error hook_response; that is the block itself, already shown by the halting warning, so it writes no red hook-failed row. Other hook events and exit codes are unchanged. * docs(native-chat): say a prompt hook's exit 2 may also be a missing plugin script * test(native-chat): type the Claude hook-outcome frame as a record * fix(native-chat): keep a blocked Codex prompt's turn on its own message Codex never echoes a prompt its hook refused, so its turn was anchored on the send only while the send read in flight. Once the turn ended the host publishes the send as in doubt, the anchor fell back to the turn record, and the block row moved to a second turn: live, the chat drew two "Worked for" bars; after a reopen the bar sat on the row instead of the message. A send in doubt now keeps anchoring the turn it opened (the first send in flight, else the newest in doubt). * fix(native-chat): keep refused Codex hook notices outside turns * revert(native-chat): keep Claude's exit-2 prompt hook_response as main's red row A missing plugin script also reports exit 2 without blocking, so suppressing it hid a real failure main shows. With a user-added --include-hook-events an exit-2 block shows both rows (known limit). |
||
|
|
43c32582e0 | refactor(orcad): stop exporting declarations used only inside their own module (#31894) | ||
|
|
2078db93ec |
ci: check dependency vulnerabilities on PRs and stable releases (#31858)
* ci: audit dependency security without gating releases * ci: audit stable release tags instead of running daily * ci: isolate release audit data and fix integration checks * ci: require exact audit inputs and ignore candidate archive attributes |
||
|
|
213bc4684d | ci: distinguish indirect unit inputs from comments and data (#31876) |