mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 16:47:34 +00:00
8ef0e99a7cbe69241b6ca56cc963dadbf6f393fc
1477
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
8ef0e99a7c |
fix(ui): use focusable tooltip menu triggers (#9595)
* fix(ui): use focusable tooltip menu triggers * test(tooltips): give each menu surface its own remount check Keep all hover, focus, description, keyboard, pointer and two-remount assertions while avoiding a shared 120-second budget across three surfaces. The failed CI trace passed five full exercises before global teardown. --------- Co-authored-by: Yuris Auzins <zuz666@users.noreply.github.com> Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com> |
||
|
|
359fec1a09 |
fix(terminal): a file link opens in the workspace it was clicked in (#32320)
* fix(terminal): a file link opens in the workspace it was clicked in A terminal or chat link to a file another workspace on the same host holds used to switch to that workspace. It now opens as a plain read-only file in the workspace it was clicked in (local and WSL read on this computer; paired and managed servers read through the owning workspace's scope), with a toast naming the owner and an Open there action, and a Read-only badge in the editor header. HTML on server hosts, server workspaces the client does not know, directories, shift-click download and direct-SSH workspaces keep the switch. The view is watched with its owner through one derived scope (lib/open-file-owner-scope.ts); phones skip plain file tabs they cannot read by workspace-relative path. Fixes #32158 * fix(terminal): a fresh id per other-workspace hint, and owner lookups cached per route inputs * fix(terminal): only this computer's files open in place; SSH and server links keep opening in their workspace Servers read files only by workspace-relative path, so a read-only view of another workspace's server file needed owner rerouting in every read path. Paired and managed servers keep today's switch; the loader moves any restored read-only server tab of another workspace's file there as before. |
||
|
|
d1aef1eee9 |
refactor(worktrees): remove unreachable legacy worktree list IPC (#32942)
* refactor(worktrees): remove unreachable legacy worktree list IPC The desktop and web preloads both always provide `worktrees.listDetected`, so the renderer's fallback to `worktrees.list` never ran, and nothing calls `worktrees.listAll`. Remove both IPC handlers, their preload/web wrappers, the renderer fallback, and helpers only they used. Tests that observed behaviour through the removed handlers now go through `worktrees:listDetected`. * test(worktrees): drop removed list channels from SSH listing matrix Address review: the canonical SSH listing test built channel names dynamically and still invoked worktrees:list/listAll. Also fold the detected-result reader into the shared worktree test fixtures, refresh stale comments, and assert the backfill no-op test listed authoritatively. --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |
||
|
|
c84817e8f9 |
fix(native-chat): preserve accepted command outcomes across agent startup (#31605)
* fix(native-chat): a goal set while the agent rests clears the composer Setting a goal starts the agent when none is running, which moves the session's runtime fence before the host answers. The composer dropped that successful answer as stale, so the objective and Goal chip stayed. Keep the reply for goal changes as for the other writes that may start the agent. * fix(native-chat): settle each command independently of runtime changes * fix(native-chat): scope rewind timeout feedback to its issuing pane * test: use checked native chat command fixtures * test: complete native chat presentation fixtures * fix(native-chat): admit Stop before capability discovery * test(native-chat): consolidate overlapping startup coverage * test(native-chat): preserve IME composition on goal acceptance * fix(native-chat): capture each action before async preparation * fix(native-chat): preserve shared Stop failure reporting * test(native-chat): cover already-sent queue notices after disposal |
||
|
|
d49ad60ead |
feat(chat): add terminal-style pane header actions (#28968)
* Transfer keyboard focus when a sibling chat pane opens * fix(native-chat): hand focus to newly opened chat composer * fix(native-chat): keep tab bar controller within line limit * fix(chat): split into a fresh conversation beside the current chat * test(chat): declare the split fixture store without an assertion * fix(chat): route the split to the source conversation host * test(chat): keep the split regression focused on the launch contract * test(chat): give the command palette split fixture its conversation agent * test(chat): seed the legacy command fixture without expanding its asserted shape * test(chat): isolate pane identity commands from the split launch boundary * Prevent chat splits from starting a terminal on host refusal * Cancel structured launches when their destination pane closes * Model live destination groups in agent launch test fixture * Capture split chat focus intent at the user action * feat(chat): add the terminal pane hover action cluster * fix(chat): keep pane controls above the conversation surface * refactor(chat): share the tab resume launch hook and format pane controls * style(chat): document reuse of the terminal header button styles * refactor(ui): share the pane title button variants between terminals and chats * fix(chat): keep header actions scoped to the displayed chat * fix(chat): abandon Resume when its destination pane closes * test(chat): declare destination fixtures without type assertions * fix(chat): keep Resume visible when its source pane closes * fix(chat): reuse tab menu Resume placement unchanged * test: fix Resume regression harness setup * fix(chat): hand input to the split pane during admission * fix(chat): Split opens a fresh chat beside the current one Split Right/Down on a native chat used to move the chat into a new pane. It now opens an empty pane beside the chat and launches a new chat of the same agent on the same host into it through the New Chat launch, so the new composer takes focus at once. A launch that never opens a tab closes the empty pane. Drops the earlier admission and focus-transfer changes that main now covers. * fix(chat): Split palette search words describe a new chat, not a move * fix: remove misleading chat move shortcut hints * fix(chat): a new chat stays in its pane until its host lists it A host tab list built before a chat's create landed can arrive after the create reply. Once the reply settled the launch, nothing protected the new tab, so that older list dropped it: Split's new pane collapsed and focus moved to another chat, and the next list re-added the chat elsewhere. Keep a small in-memory record for a chat this client created that no host list has shown yet. While it exists, a list that omits the chat keeps its tab. The record ends when an accepted list from that host includes the chat, when the tab closes, on workspace teardown, or 30 seconds after the reply, when the worktree is re-read from its host (including an unchanged version) so a chat the host really closed is dropped then. The launch lifecycle, status, persistence and settlement are unchanged. * test(chat): type the faked create replies without an assertion * Keep new chat protection tied to accepted host lists * Update paired chat close regression to current suppression API * fix(chat): Portuguese Split palette words describe a new chat * test(chat): use the destination group option in split fixture * Avoid History projection in native chat header and currentness checks * fix(native-chat): keep Find below pane controls * fix(ui): keep noninteractive tooltip layers pointer-transparent * Mount native chat actions only for shown panes * Use checked property access in header instrumentation * Read Continue eligibility records through workspace selectors * Cover native header Find clearance and tooltip click passage --------- Co-authored-by: Orca Worker <orca-worker@localhost> |
||
|
|
291e2e807e | feat(browser): open links beside their pane, focus link tabs, honor window.close() (#31844) | ||
|
|
aeb039938f |
refactor(jira): colocate shared contracts and capabilities (#32927)
* refactor(jira): colocate shared contracts and capabilities * test(jira): validate title width against panel budget |
||
|
|
6ed9382c05 |
Allow SSH migration recovery without stopping preserved relay terminals (#32914)
* fix(ssh): preserve running relay panes during migration recovery * fix(ssh): wire pane eligibility through existing relay seams |
||
|
|
4c7991509f | feat(browser): say why a page failed to load and reload it when it can (#31847) | ||
|
|
13ab5239f5 |
fix(terminal): never present a half-built synchronized-output frame on reveal or recovery (#32281)
Settled reveal and every other atlas recovery forced the current xterm buffer onto the screen even while a TUI held DEC 2026 synchronized output. Codex erases rows inside each held redraw, so a reveal mid-redraw showed a blank pane for a frame (#32198). Viewport presents now have one mode: they keep the last coherent canvas and let xterm present when the TUI closes the frame, bounded by xterm's 1s synchronized-output safety timeout. Structural replay keeps its retained frame up until an open synchronized frame closes, bounded by its existing hold cap. Co-authored-by: YEONGUK LEE <contact@abruption.dev> |
||
|
|
551108aea8 | test: finish fixture work before unit-test cleanup (#32285) | ||
|
|
f9e883a4bf |
Show activity status with worktree-style counts (#26443)
* Show prioritized agent counts on the activity bell * Move activity badge into the corner and shrink its count * Use a fixed side column for activity status and counts * Fix activity count spacing and status alignment * Use worktree-style superscript counts for activity * Keep activity count positions and update only number styling * Keep idle layout fixtures out of active agent status updates |
||
|
|
47484b13d6 |
fix(native-chat): keep queued messages in the chat's own record, so a refused one can't be skipped (#30418)
* fix(native-chat): a refused queued message still blocks the queue when its card write fails Every reader of a queued card goes through one derived view of the journal's dispatch facts, so a card whose message was refused reads as returned even when its card-table update and heal both failed. One decision and one writer store the derived state for every action. * refactor(native-chat): keep each queued message in the chat's journal, not a second table A queued message lived in its own SQL table, and a hook that may be skipped copied each refusal from the journal into it. When that write failed, a refused card vanished: the cards behind it sent, and its Send and Delete did nothing. Each queued message is now a `queue` row in the chat's journal beside the submission that hands it off and the dispatch row that answers it. The reducer folds the card from those rows, so a refusal returns the card and holds the cards behind it with nothing written, and replay reads the same. Every action writes its own rows in one transaction. Cards an earlier build stored are read through the same classification until they move: each action moves its own card, and a bulk import runs once per open. A new epoch restates every card still owed an answer in the transaction that deletes the old rows. The settlement hook and its savepoint, the open-time heal, the drain's heal and the derived-card view are gone. The published card list is unchanged. * test(native-chat): adapt the queue tests to cards kept in the journal Cards are written through the journal, so tests queue them with their body alone and hand them off under their real fingerprint. Tests of the removed settlement hook, heal and SQL store are deleted; the text-update spec now covers the edit decision alone, and a test of an earlier build's stored holds seeds the legacy table and reads the imported cards. Rolling over an epoch now keeps a card whose hand-off was never handed over, so that test checks the retried id never sends rather than that nothing sends. * test(native-chat): a refused card holds the queue through rewind, a new epoch and a restart One whole-flow test through the host: the refusal writes no card row, a rewind that retires the refused hand-off and a rolled-over epoch keep the card returned, a restart and Resume send nothing behind it, its own Send refused again still holds the queue, and its Delete releases the next card in order. * test(native-chat): an earlier build's queued cards move into the journal once, each read the same One mixed-import test through the host. The first open's import fails and moves nothing, yet every card reads as it will; Delete and Send each move only their own card. The next open moves the rest in one transaction, writing a refusal only where its hand-off is gone, nothing for answered cards, and leaving an unreadable row on disk. A later command turn spends a moved command card, a third open writes nothing, an old Delete replays from its receipt, and a chat with only answered legacy rows clears them in a delete-only transaction. * test(native-chat): adapt typechecked tests to cards kept in the journal The row-kind table lists a queue row, the clear and legacy-import tests match the current signatures, and the downgrade test keeps only the leftover-send case: a queued card is now a newer-version journal row an older build keeps read-only, and users don't downgrade. * refactor(native-chat): drop the journal row writer's unused transaction hook No caller passes one since the queue's savepoint bookkeeping left. * test(native-chat): downgrade probes write rows at the version older builds read The highest version this build reads is now 4, the queue row's. A newer build's row in these probes is written at version 3 so the older build meets its kind, not a newer version, and the Stop-event probe hands the older build the rows as stored, since a read raises each row's version. * test(native-chat): unit tests follow the queue row's version 4 The legacy-import test opens real SQLite, so it runs in the Node runtime project. The newest version this build reads is now 4, and a read raises a row's version to it, so the turn-lifecycle test no longer pins the in-memory version (the written one stays pinned by journalRowSchemaVersion). * fix(native-chat): one batch echoing two refused queued messages spends both The per-row delivery check returned only the first waiting card an echo proved delivered, so when one settlement carried echoes for two cards the second stayed waiting and was sent again. It now returns every card the row proves delivered, and the fold withdraws each; replay folds the same. * fix(native-chat): a rewind moves an earlier build's cards into the journal first While the open's import had failed, an earlier build's card that read as refused through its hand-off lost that reading when an in-place rewind retired the hand-off: it disappeared and the card behind it sent. The rewind now adopts every legacy card ahead of its tombstones and deletes their rows in its own transaction, as an epoch replacement already does, so the fold latches each card's reading before its hand-off is gone. * fix(native-chat): keep an earlier build's card whose hand-off is still unconfirmed The import dropped a legacy card handed off as a send whose delivery was unknown, though the fold keeps such a card linked and a later refusal returns it. The import now keeps every card whose hand-off a later row can still answer, through the same check the reducer folds dispatch rows by. * fix(native-chat): one delivery echo withdraws at most one queued card One provider echo proves one delivery, yet it withdrew every waiting card with the same text whose hand-off was refused, so a second identical card was lost. The fold now pairs each echo with one card: on its own row the earliest-positioned match no other echo of that row took, and for a card that waits again the earliest applied echo no card has taken. Paired echoes live in the fold's state, so live and replay pair the same; readers of cards not yet imported work on a copy. * fix(native-chat): a queued message handed to the agent is sent again only on proof it never got it Pressing Stop after Orca handed a queued or steered message to the agent, before the agent confirmed it, put the card back in the queue, and Resume or the next send sent it again. A check matching the agent's echo of the text was the only guard against a double send, and it kept trading a resend for a lost card. Whether the card waited again was decided by the rejection's category, not by proof the agent never got the message. A refused hand-off now waits again only where Orca proves the agent never received it: the host still held it, or whoever settled it recorded that. A failure still returns the card and holds the cards behind it. Anything else may have reached the agent, so the card is spent and its message reads as sent, derived on every fold from the stored rejection: with the turn it was answered into, as that turn's unconfirmed message where it was handed over; withdrawn before any turn opened, as a person's own send a Stop took back. The reopen mark ignores a recovered doubt, and the restart reconciler no longer judges a queued hand-off unsent from what history lacks. The echo text matching and its pairing are deleted. A close or quit while the agent starts settles sends with the unrun hold. The host hands nothing to a child before its start answers (structured-agent-session-delivery-loop.ts: a handover needs phase ready), so such a send went to an earlier child and may have run: it now reads as sent instead of waiting again. * fix(native-chat): Claude's report that it removed a queued message unrun lets the card wait again Claude says when it removed a send without running it: the interrupt receipt's cancelled list, or a command_lifecycle cancelled, discarded or refused frame for a command that never started. Every one settles through rejectClaudeDispatchWaiters, which now marks the settlement unrun. For a queued card's hand-off the host records that as proof the agent never got it, so the card waits again under the queue's pause. A direct send keeps the plain refusal: there the proof would owe it as stopped history. * fix(native-chat): an earlier build's card stopped after hand-over follows the same rule on upgrade Earlier builds put a card back to waiting after any refusal without a verdict and cleared its link, so a card the agent may already have would be sent again after the upgrade. The card's own hand-offs in the journal decide now: unchanged and last sent as one that may have reached the agent, it is spent on that hand-off and its message reads as sent; edited or sent again since, it is held for its own Send under a host-only marker that publishes as a plain hold; with no hand-off in doubt, including one that never left Orca, it waits as before. The import writes a card spent on a hand-off in doubt, so its message shows. * fix(native-chat): read a queued row's hand-off before the lookup that uses it The lookup's callback lost the narrowing of the row's change, which the mobile test typecheck rejects. * test(native-chat): a card steered into a turn a Stop ends stays sent in the Stop tests Three tests pinned the old rule, that a card handed into a running turn comes back to waiting when a Stop ends it. Under the new rule nothing proves the agent never got it, so the card is spent: with the turn named, its message reads as that turn's unconfirmed one; without, as a send the Stop took back. Neither is ever sent again. * fix(native-chat): a queued message stopped before Orca handed it to the agent waits again A Stop that aborts a hand-off after its row is written but before the agent call now records the withdrawal as never reaching the agent, for queued hand-offs only, so plan D's rule returns the card to waiting under the Stop's pause instead of spending it. Direct sends keep the plain withdrawal (the flag would owe them as stopped history). The agent's own refusal while aborted is kept rather than overwritten, and an abort thrown during the call stays in doubt. * fix(native-chat): a queued message stopped while the agent prepares it waits again The abort signal reaches only an adapter's preparation (its options, the previous turn opening), never its write, so the signal's own reason thrown back is the adapter saying nothing was written. The handover now reads that, for queued hand-offs only, as never reaching the agent, so the card waits again under the Stop's pause. Claude no longer wraps that abort as an option refusal, which had returned a stopped card as a refused one blocking the queue. A real refusal keeps its cause and its barrier; any other throw stays in doubt and is never resent. * test(native-chat): the usage-limit Continue backlog case writes its card through the journal queue Main's #30396 wrote its backlog card with the stored fingerprint and host-instance stamp the journal queue derives instead; the card the Continue must yield to is the same waiting card. * test(claude): the real-CLI queued Stop expects the unrun mark Claude's cancelled-unrun receipt carries * fix(native-chat): retain spent legacy messages when import fails |
||
|
|
a3512bcd47 | perf(renderer): scope runtime connection and parking subscriptions (#32199) | ||
|
|
ab1563e6f3 |
fix(cli): negotiate agent launches with paired runtimes (#32029)
* fix(cli): negotiate agent launches with paired runtimes * refactor(cli): share paired client capability list across request paths Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
41661e3312 |
feat(sidebar): main runs workspace sleep; the window follows (P3b) (#31564)
* 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 * feat(sidebar): one lifecycle queue per workspace in main, with in-progress hints (switch-gated) Create, delete, terminal.closeAll, terminal.sleep and worktree.sleep now run through one first-in, first-out queue per host-qualified workspace: strict order, no merging, delete cancels what has not started and waits for what has, an unconfirmed outcome pauses until the host's own answer. Hints publish in a new inProgress feed domain (no reader yet); the creations domain is its projection. A finished create or delete keeps its hint until a scan started after it applies, in the same revision. Adds the paired-server metadata serializer (saving domain, no caller yet). Replaces the creation registry. * fix(sidebar): a create never queues behind the previous occupant's delete * feat(sidebar): main runs sleep; the window follows (P3b) * fix(sidebar): keep electron out of the headless server's sleep path The desktop registers how sleep reaches paired servers; a headless serve has none. Co-Authored-By: Claude <noreply@anthropic.com> * test(sidebar): type-check the sleep e2e specs; drop the removed VM suspend stub Co-Authored-By: Claude <noreply@anthropic.com> * fix(sidebar): resume a paused workspace by re-observing, never replaying - A command issued while paused waits behind the pause; when the owner answers again main re-reads terminal liveness and the listing, then carries on. A forced delete runs past it. - Commands keep their arrival order across a workspace lookup; deletes by name key by the resolved id, so they wait for that workspace's running sleep. - A second delete with different options is refused as already in progress. - Close and delete read lost contact as sleep does. - A create always frees its own queue entry, even after a retry took over its id. - Drops the unused opId, and the paired-server write serializer and its feed section, which land with their first caller. Co-Authored-By: Claude <noreply@anthropic.com> * fix(sidebar): main is the only writer of a sleep's wake records; sleep classifies only lost contact as unconfirmed - The window no longer captures wake records for a sleep main runs; it takes main's records from main's answer, which its wake and tab-open resume then read. - A workspace reopened during its sleep keeps its VM running (the window and a phone's activation tell main). - Only lost contact pauses: a definite chat-stop failure or a server's still-live answer is a plain failure with today's toast. - A paired server's chats are asked with the Sleep mark, and again without it when an older server refuses the field, instead of reading an unprobed status. - A phone's worktree.sleep is answered on acceptance and its activation no longer waits on a queued wake, so neither can outlast the phone's request timeout. The web client passes markChatsAsleep and keeps the receipts. - One desktop adapter, passed where the desktop builds the runtime, replaces the two module slots; a headless server gets none. Co-Authored-By: Claude <noreply@anthropic.com> * chore(sidebar): share worktree.sleep's params with the RPC catalog; lint fixes Co-Authored-By: Claude <noreply@anthropic.com> * test(sidebar): test the paired-server sleep where main runs it; drop the window's copies The window no longer sleeps a paired server's terminals, so its convergence and order tests for that path move to main (remote-workspace-terminal-sleep.test.ts) or follow main's answer. Co-Authored-By: Claude <noreply@anthropic.com> * test(sidebar): retry the sleep spec's liveness read while terminals stop Co-Authored-By: Claude <noreply@anthropic.com> * fix(sleep): main resumes every workspace-sleep wake; the window only attaches - Main claims and starts each slept pane through its windowless resume path, with or without a window: the "window present" check is gone. - A phone's open queues main's wake, which finishes only once the resumes have started, and always notes the reopen so a running sleep leaves the VM up. - The window no longer relaunches a workspace sleep's records in fresh tabs; opening a slept pane asks main to resume it and the pane's spawn attaches to main's terminal. - A window save cannot overwrite main's workspace-sleep records. - A VM that would not suspend no longer reads as "kept open": the terminals slept, and the toast says the machine is still running. * test(e2e): main resumes a slept pane once, in place, and keeps its record * chore(i18n): add the VM-still-running sleep message * fix(sleep): a phone's or the CLI's failed sleep shows the desktop toast again * fix: restore main's final P2 test fixes a merge reverted * fix(sleep): window saves never write or clear main's sleep records; headless wake report counts only real sleeps * test: a same-id twin's grace follows the rows' change, not the removed catalog hook * fix(sleep): drop the 1 s settle wait after an observed exit, and wake a slept workspace's mounted agents on phone open A workspace sleep stopped each terminal through main's stop-and-verify, which polled for a further second after the exit it had already seen. And a phone opening a slept workspace no longer reached the window's mounted panes of agents that had finished before the sleep, so they stayed asleep until the window opened their tab. * test: give the workspace-sleep wake record its prompt field * refactor: match slept-agent records to the workspace with worktreeIdsEqual * chore(sleep): one import from sleep-record-pane-claim * test(sleep): route the disconnected-sleep SSH spec, declare the desktop sleep adapter, and open the workspace after its sleep starts * test(sleep): delete a workspace the spec creates, not the seeded one later specs share --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
1ed13cf687 |
fix(chat): split into a fresh chat beside the current conversation (#28950)
* Transfer keyboard focus when a sibling chat pane opens * fix(native-chat): hand focus to newly opened chat composer * fix(native-chat): keep tab bar controller within line limit * fix(chat): split into a fresh conversation beside the current chat * test(chat): declare the split fixture store without an assertion * fix(chat): route the split to the source conversation host * test(chat): keep the split regression focused on the launch contract * test(chat): give the command palette split fixture its conversation agent * test(chat): seed the legacy command fixture without expanding its asserted shape * test(chat): isolate pane identity commands from the split launch boundary * Prevent chat splits from starting a terminal on host refusal * Cancel structured launches when their destination pane closes * Model live destination groups in agent launch test fixture * Capture split chat focus intent at the user action * fix(chat): hand input to the split pane during admission * fix(chat): Split opens a fresh chat beside the current one Split Right/Down on a native chat used to move the chat into a new pane. It now opens an empty pane beside the chat and launches a new chat of the same agent on the same host into it through the New Chat launch, so the new composer takes focus at once. A launch that never opens a tab closes the empty pane. Drops the earlier admission and focus-transfer changes that main now covers. * fix(chat): Split palette search words describe a new chat, not a move * fix: remove misleading chat move shortcut hints * fix(chat): a new chat stays in its pane until its host lists it A host tab list built before a chat's create landed can arrive after the create reply. Once the reply settled the launch, nothing protected the new tab, so that older list dropped it: Split's new pane collapsed and focus moved to another chat, and the next list re-added the chat elsewhere. Keep a small in-memory record for a chat this client created that no host list has shown yet. While it exists, a list that omits the chat keeps its tab. The record ends when an accepted list from that host includes the chat, when the tab closes, on workspace teardown, or 30 seconds after the reply, when the worktree is re-read from its host (including an unchanged version) so a chat the host really closed is dropped then. The launch lifecycle, status, persistence and settlement are unchanged. * test(chat): type the faked create replies without an assertion * Keep new chat protection tied to accepted host lists * Update paired chat close regression to current suppression API * fix(chat): Portuguese Split palette words describe a new chat --------- Co-authored-by: Orca Worker <orca-worker@localhost> |
||
|
|
e61d062ef1 |
Revert HTML preview by default for the release (#31575) (#31893)
* Revert "Preview HTML files by default" (#31575)
Restore source/diff opening for general HTML file-opening routes for the release.
This reverts commit
|
||
|
|
24f5c57030 |
A chat started in a server workspace drawn from the server's layout stays where you opened it (switch-on builds only) (#32003)
* 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. * wip(L270): a host advertising agentSession.create.tab-placement.v1 opens a created chat's tab where the creating client asked, in its own group and tab ids * test(L270): a switch-off windowless host saves a chat opened with no client place at the end of the remembered active group, as before (test only) * wip(L270): a desktop's chat in a frame-drawn server workspace stays put: the frames hold its provisional tab until the server lists it, and the create names its group and the server tab before it (layout-owner build only) * test(L270): a switch-on serve saves a client's chat after the tab it named; the server-layout read moves to one e2e helper (test only) * refactor(L270): the create's tab place keeps its own name, and the layout rule's comment names the client's place first * refactor(L270): the create's tab fields live in their own schema file and the windowless save passes its chat through, so no file passes its line limit; the placement test types its mocks without assertions * refactor(L270): the chat's place names server tabs through H5c's route, which also skips tabs the server's frames do not hold * fix(L270): a chat opened while a reconnected server stream awaits its snapshot still names its group; the layout-owner e2e list runs the placement spec * fix(L270): only a create in a frame-drawn server workspace awaits its tab place, so every other create keeps its timing (the eager History joiner after an early terminal close opens again) * fix(native-chat): read a frame-drawn chat tab's place without a tick, so an eager History joiner's create is still sent * test(native-chat): type the create's params so an async placement cannot compile; name the probe that fills the cache * test: type the placement mock's original through a type import |
||
|
|
e710e89832 |
A paired desktop hands its editor tabs to the server before drawing a server workspace from the server's layout (switch-on builds only) (#32088)
* 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 * 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. * 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 * test(d3a-2): the buffer view index follows a rename main answers * 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) * feat(h5d): hand desktop-only editor tabs to a layout-intents server before its frames draw the workspace A paired desktop dropped an editor tab it opened in a server workspace (with its open file and unsaved draft) on the server's next records frame once that workspace became frame-drawn, and editor opens made after gating hit the same loss. - openEditorTab is now on SERVER_ROUTED_LAYOUT_COMMANDS: the proposed tab id and group are kept, the file id becomes the server's own, and the record loses the desktop's runtimeEnvironmentId. - Before a workspace gates, server-layout-frames hands its desktop-only editor tabs to the server's main (sendToServerMain), recomputes them on each answer, and gates only when all answered ok or tab_exists. Any other refusal or a failed call keeps it on the mirror for that connection; the next one retries. No timer, no hold store. * fix(h5d): only an applied open counts as handed over; tab_exists keeps the tab on the mirror * 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(h5d): a tab closed while its hand-over was in flight is closed on the server before the workspace gates * fix(h5d): an editor tab closed in a workspace a server's frames draw closes on that server by session.tabs.close; a tab kept on the mirror closes here only * fix(h5d): a handed-over tab closed here before the gate is closed on the server first; a server-opened tab's close in a frame-drawn workspace is covered * chore(h5d): a closed stream also closes the hand-over its hand-back started * refactor(h5d): move the frame stream's routing inputs out of server-layout-frames to keep it under the line cap Main's #31869 grew server-layout-frames past 300 lines once merged with H5d; the two helpers move unchanged. |
||
|
|
8fc1698ebb |
Add Resource Manager PTY inventory and checked terminal termination (#31593)
* feat(resources): end local terminals with checked identity checks - Add a PTYs view to the Resource Manager that lists local terminals and can end one. Before stopping, the host confirms the terminal's incarnation, agent ownership, and attachments. This prevents ending a same-ID replacement that started after the list was read. - Bump the daemon protocol to v50 and gate the new fenced kill on it. Older daemons ignore the extra fields, so the client refuses End instead of sending an unchecked kill. - Admit diagnostic reads (PTY listing and memory snapshot) only while the Resources view is visible, so a hidden window does no background scanning. - Report incarnation and host on spawn and exit events so the renderer can match them to the terminal it showed. - Replace the popover's terminal-session list with the new inventory and move memory polling into the Resources view. * Show unrouted daemon PTYs in the Resource Manager * Simplify PTY controls and clarify safe termination * Format local build compatibility protocol lists * Add PTY resource management localization strings * Harden PTY resource manager test fixtures * Use typed IPC event in PTY kill test fixture * Refine local PTY resource view model typing * Refine local PTY resource view model typing * Include local host identity in PTY lifecycle event tests |
||
|
|
f1189be850 |
Offer Resolve with AI for paused merge, rebase, and cherry-pick (#28569)
* Offer Resolve with AI for paused merge, rebase, and cherry-pick A paused operation can need recovery after every conflict is staged, but the Resolve with AI action only appeared when unmerged paths remained. The operation banner now offers it whenever AI actions are visible and an operation is in progress. Starting an AI conflict-resolution agent is also blocked while a Git operation is running. The composer, the banner and card buttons, and the agent launch itself check the running-operation state, so a launch that began during agent detection cannot start while Git is still running. * Block paused-operation recovery only on Orca's own Git work The previous gate scanned the host's process table and refused "Resolve with AI" whenever it could not prove no Git process was running. That refused recovery on older paired hosts, on Windows whenever any Git process ran anywhere on the machine, and while a paged `git reflog` or `git grep` was open in a terminal. - The host tracks Orca's own mutating Git commands per worktree (including ones still queued) and reports them through `git.mutationInFlight`. Reads and fetch/push/ls-remote are not tracked. - Recovery is refused only when the host reports that work. Older hosts without the method keep today's behavior. - The gate applies only to launches from the paused-operation banner and conflict card, not every resolveConflicts launch, so the Checks panel's PR-conflict flow is unchanged. - The agent prompt now tells the agent to stop if index.lock exists or another git command is running, which covers Git started outside Orca on every host. With no unmerged files, the prompt asks the agent to find why Git paused instead of listing zero conflicts. * Preserve Git mutation state until terminated commands exit - Cancel stale recovery checks and agent launches when their dialog or scope closes - Guide rebase recovery agents to amend fixes after failed exec steps * Cover failed rebase exec recovery with real Git * Recognize global Git options in worktree mutation tracking |
||
|
|
199fc7b2c1 |
A Pi chat shows the turns made outside it (#31618)
* feat(native-chat): a Pi chat shows the turns made outside it When a Pi chat's agent opens its conversation, Orca asks Pi for the entries saved after the point the chat last accounted for (get_entries since that entry id) and adds the terminal's turns once, in Pi's order, under the "Continued outside this chat." divider. That point is saved on the conversation handle after each chat turn and at each open. When the chat's own unsaved work and the terminal's share a suffix that can't be split (Orca died mid-turn), that suffix is skipped rather than duplicated, and later terminal turns show normally. A Pi reply over the 8 MiB line limit now fails only the optional read that asked for it instead of ending the chat. * 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. * refactor(pi): catch up through the shared catch-up runner instead of a copy * chore: remove unrelated pnpm executable metadata * refactor(native-chat): the catch-up runner reads the chat's view only when the agent asks * refactor(pi): read the chat's view through the runner's view() * 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(pi): check Pi's session entry ids and get_entries since against the pinned real Pi * test: type the history catch-up sink fixture without assertions * fix(native-chat): bound and revoke history preparation * test: the runner's journal fake answers the cursor Pi's view reads * feat(native-chat): validate expected saved items during history catch-up * fix(pi): preserve outside history and optional read recovery * test(pi): fail RPC smoke promptly when the child stops |
||
|
|
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) |
||
|
|
b342d29cc1 | test: drop obsolete streamparser dependency (#31835) | ||
|
|
aa5594d536 |
fix(native-chat): handle inline image preview edge cases (#31742)
* fix(native-chat): handle inline image preview edge cases * Improve native chat image preview resilience |
||
|
|
b0778082cb |
fix(i18n): recover language selection after rejected catalog loads (#31822)
* fix(i18n): allow retrying rejected language catalog loads * fix(test): return null for unhandled locale asset imports |
||
|
|
99d18f7a2f |
Browser, chat and simulator tabs main-first: the window asks main for each change and draws only main's update (dark, layout-owner build only) (#31516)
* 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)
* wip(d3a-2): a windowless host lists a file main opens on session.tabs at once
* wip(d3a-2): the record key's fields are declared where the key is made
* wip(d3a-2): main publishes an editor tab's view, and a window rebuilds the open file it lacks from it
Main's in-memory editor view now carries the file facts an open file needs; it rides the tab row
(PublishedTab.editorView) and a frame build rebuilds a diff, conflict, preview or check-details tab.
Inert with the switch off: views are set only by layout.apply (switch builds) and read only in frame-sync.
* wip(d3a-2): a switch-on window sends editor opens, closes and promotions to main and draws the tabs from its frames
Layout-owner build only; the switch-off path is unchanged. An open proposes the tab id and sends
openEditorTab with the edit file's record or the tab's view (a reopen also its index); the open
file is written at once and the tab is selected once its frame draws it. Callers that act on the
tab (focus request, link reveal, group focus) wait for that frame through layout-owner-pending-tabs.
A close sends closeTabs; the tab goes with the frame and the file's content after it, and a
refusal keeps both. A promotion sends promotePreviewTab and the frame clears the flag. Deleted:
the drawn editor senders, their before/after captures, the editor drawn-tab holds and the
window-record keep rule (a transient tab is kept by its frame view).
* wip(d3a-2): a switch-on close keeps its selection and a reopen, a link names its group, and main stamps an open's time
Layout-owner build only. After main's frame drops a closed editor tab, the window selects the
group's previous tab and its surface through activateTab and focusGroup, after closeFile's
content cleanup rather than before it; a reopen sent while the close was in flight keeps the
file. openFileForLink's proposed view names the group the open asked for. The window's
openEditorTab sends no createdAt: main stamps the tab's time (ledger L186). Preview promotion
writes no tab itself: the window ends a preview only where a paired server owns the layout.
* wip(d3a-2): editor tests assert switch-on tabs through main's command and frame (test only)
Each store and hook test that assumed an immediate editor-tab draw now runs with main's model
through the T1 transport in apply mode and its frames (tabs-drawn-from-main.test-fixture), and
asserts the tab, its selection and its content after main's frame. The fixture fills the frame
slices a partial editor-and-tabs store lacks.
* wip(d3a-2): main takes a restored move as a command, renames a view tab's path, and keeps one tab per file per group
Layout-owner build only (the switch gates every change; shipped builds unchanged). moveTabToWorkspace no
longer skips a tab "the window's save got there first" (L160): window saves no longer carry the move, so
a repeat is tab_not_found; tab_exists and invalid_params stay. A move or rename may carry a non-plain
tab's new view (its file under the new path or owner), which replaces editorViews[tab] (L180). Main
refuses a moveTab or moveTabToWorkspace that would put a second tab for a file into a group (tab_exists),
and its loader folds a saved session's duplicate into the first, as the window's restore does
(dedupeEditorTabsWithinGroups): the window's frame dedupe hid the second, which no close could reach.
* wip(d3a-2): a switch-on restored move and disk rename are main's commands, and the frame that shows them rekeys the content
Layout-owner build only; the switch-off path is unchanged. A restored editor tab re-owned by its
file's workspace (L157, L158) sends moveTabToWorkspace (a re-own in place sends renameEditorFiles)
under the ids and group the window proposes, with each view tab's new view; a disk rename (L180)
sends renameEditorFiles per workspace and draws nothing first. The window's content rekey (open
file records, drafts, buffers, the editorBuffers echo gate, pendingLiveDiskVerification and
pendingSelfMoveEcho) waits in layout-owner-content-rekeys and the frame flush that first shows main's
change applies it, in the same store update as the frame (a flush now writes one patch). A change no
tab shows is rekeyed on main's answer; a refusal drops it and changes nothing. The move coordinator
and the restored-owner loader wait for that outcome; a refused rename rolls the disk move back.
A rename picks a runtime-owned or floating file's owned id, as main's frames restore it.
Deleted: recordRestoredEditorMoveInLayoutOwner, recordRenamedEditorFilesInLayoutOwner, the
reparent's before/after capture and the drawn-tab holds for moves and renames. Tests that assumed
the window moves or renames first now run through main's T1 transport in apply mode and its frames.
* wip(d3a-2): the static gate's casts and layout-writer ratchet hold for the switch-on move, rename and gap fixes
Layout-owner build only. The content rekey's pure store change stays in rekey-open-files-action
(its writer count drops 4 -> 2, pruned); a twin tab main routes to a paired server ends its
preview through patchTab, the existing tab writer, instead of a new one; a frame-only editor tab
takes its label through the frame build's tab-label facts, read from its view or file record; the
moved file's activation names the moved tab, writing no surface field. Test casts are typed or carry
a SAFETY note.
* wip(d3a-2): the switch-on rename takes the window's pure rekey as an argument, so the two modules do not import each other
Layout-owner build only; no behaviour change. Clears the static gate's no-cycle finding.
* wip(d3b): a switch-on window sends browser, chat and simulator opens, closes and page changes to main and draws them from its frames
Main places the chats its host lists (twin h), a local cancel keeps no tombstone (L155), and the
listing no longer writes chat tab structure where frames draw it (L153).
Co-Authored-By: Claude <noreply@anthropic.com>
* wip(d3b): a switch-on browser, chat or simulator close waits for main's frame, lands an emptied workspace as today, and its tests read main's frames
* wip(d3b): the stale-listing and cross-version chat tests check a switch-on local cancel through main, with a presence precondition and per-test resets
* wip(d3b): main places a tab whose group it lacks in the workspace's first group, not beside the window's anchor (L150)
* wip(d3b): tests for main's chat tab commands from a host's chat changes
* wip(d3b): main's windowless chat tab and chat fork tests apply a switch-on host's chat tab through main's layout
* wip(d3b): the switch-on close and open keep their layout writes in the files the writer ratchet counts them in, and a drawn browser close carries its position instead of a rebuilt layout
* 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.
* wip(d3b): a switch-on close main answers workspace_not_found drops the tab's content as tab_not_found does, and placement reads a missing active-group map safely
* wip(d3b): switch-on browser and chat tests open and close through main's command and draw from its frame (test only)
* wip(d3b): a switch-on chat listing writes no tab structure of a workspace main's frames draw; a host chat title changes only the drawn tab's label
* wip(d3b): main's phone rows show a diff tab and a markdown preview as the window's do, with the preview's source named by the window's open-file id
* wip(d3b): the cleanup close's page-read guard counts its named linear passes, so a per-entry rescan still fails switch-on (test only)
* wip(d3b): the static gate's compat entry, focus-read ratchet and formatting hold for the switch-on test conversions and phone rows
* test(d3a-2): the buffer view index follows a rename main answers
* wip(d3b): placement reads the active-group map the real store always holds; the browser test store seeds it
* wip(d3b): a switch-on background browser open leaves the worktree's remembered browser tab unset, as with the switch off
* wip(d3b): a switch-on chat open selects the chat in its group without moving the focused group, as the window's own open does; its test counts the chats main draws
* wip(d3b): the converted simulator, launch-cancel, cleanup-close and chat-listing tests assert what the window's draws asserted, through main's command and frame (test only)
* wip(d3b): main fences a chat's create on its close even when the close fails for want of the chat host, so a cancelled launch never shows
* wip(d3b): a frame keeps the window's hidden chat launch slot after its neighbour, so a resumed chat keeps its saved place through main's frames
* wip(d3b): a declined chat's terminal is placed after the chat and pinned as it is in one main move, and shown before main closes the chat
* wip(d3b): a switch-on chat close the host refuses asks main to open the chat again, as the window's re-read shows it with the switch off
* wip(d3b): a paired server's workspace, its row naming the server, opens and closes browser and chat tabs without main's layout (test only)
* wip(d3b): a refused chat close comes back only while this machine's host still lists the chat, and a close main answers workspace_not_found removes the window-only tab
* wip(d3b): the twin chat close test names what each build stops: no host off, main's host on (test only)
* wip(d3b): the refused-close reopen takes only a chat agent it can launch
* wip(d3b): a frame's browser selection keeps an unchosen workspace's unset through the restore projection's own rule, not a second write
* wip(d3b): the simulator and paired-server tests type their mock stores without assertions (test only)
* wip(d3b): the switch-off declined-chat placement runs as before, with no wait; only the switch-on move waits for main
* chore(d3b): drop the frame-binding helper the main merge left without a caller
* chore(d3b): drop the import the deleted helper used
* wip(d3b): a chat this machine's window creates is placed only by the window's own open, so a reserved chat slot keeps its place (switch-on)
* wip(d3b): main reads a chat tab change against its layout when its queue reaches it, through the one rule the switch-off save uses, so a close right after an open closes it
* wip(d3b): a declined chat's terminal moves to its index among the tabs main draws, not past a hidden launch slot, so it still replaces the chat (switch-on)
* wip(d3b): main's phone-row comments say which rows the window's own row wins, and a test shows a window-listed diff keeps its dirty state (comments and test only)
* wip(d3b): a switch-on chat open returns only the tab's id and chat, the fields main's frame keeps; no caller reads more
* chore(d3b): the unit-routing census registers the browser suites' drawn-from-main store fixture as a support bridge (test only)
* chore(d3b): the chat tab rule's comment wraps and names its two callers (comment only)
* wip(d3b): main's chat tab queue asks a worktree's host only once it has a saved session, and one failed change no longer stops the ones after it (switch-on)
* wip(d3b): a chat tab batch whose changes cannot be read is logged, and the batches after it still land (switch-on)
* test(d3b): a CLI-chat resume's tab is opened once, by the window through main, after the tab before its hidden slot (test only)
* test(d3b): an SSH worktree has no host to run a chat on, so none of the chat open, close or fix paths reach it (test only)
* chore(d3b): the content tab placement reads folder workspaces through main's workspace-record selector, as the new ratchet requires
* fix(merge): put back main's
|
||
|
|
ed13ca1e6f |
Layout groundwork (switched off): main starts and restores every pane, and the window only attaches and paints the host's history (#31567)
* 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 |
||
|
|
ad0bf9a393 |
fix(native-chat): open inline images directly in preview dialog (#31620)
* fix(native-chat): open inline images directly in preview dialog * fix(i18n): add inline image preview translation keys |
||
|
|
eb9d2f9a06 |
fix(security): update Node runtimes for October security releases (#31687)
* fix(security): update Node runtimes for October security releases * fix(ci): compare normalized web file inventory without ordering artifacts |
||
|
|
7e795b6be6 | feat(browser): show every page load, link clicks included, as a line along the address bar on desktop and mobile (#31597) | ||
|
|
d085427628 |
Keep model choices coherent across Claude updates (#30107)
* Use one model catalog per execution scope * Accept a missing session record in host scope * Merge duplicate launch-resolution import * Fix catalog typecheck errors * Align catalog tests with the one-list design * Type the integration fixture's send without a cast * Give the discovery test complete runtime dependencies * Fix model catalog completion and scoped configured display * Preserve model catalog authority across released clients * Fix mobile first-frame catalog fixture narrowing * Keep startup catalog persistence with one completion owner * fix: preserve chat selections across CLI model updates A newer CLI can omit a model that an existing chat still runs. Keep that chat's selected model and effort instead of replacing them from the available list. Discover one model list per host, account, launch command and folder, refreshing after two minutes. Keep current values and the running model's exact effort choices with its chat. Show only the known effort when Claude supplies no matching menu. Preserve older paired-client delivery and configured launch arguments. Make Codex use independent discovery and retain the established ACP and Pi paths. Tracked in STA-10947. * fix: keep the Pi options read within its file limit after main's growth Tracked in STA-10947. * fix: import the option snapshot's shared module once Tracked in STA-10947. * fix: keep Grok and Pi model menus and fast controls across restarts Restore the established ACP and Pi catalog route: newer listed models and qualified startup defaults stay visible, and genuine retained effort menus survive later reads. Keep exact-child Fast and Speed controls. Update the phone option contracts and the remaining stale expectations to the selection rules. Tracked in STA-10947. * fix: share each workspace's model list and keep live Grok and Pi menus New chats read their workspace's own model list through the one shared request for that workspace, so a second chat in the same folder starts on the model and effort the agent actually runs there, and a slow first answer can no longer overwrite a newer list. A running Grok, OpenCode or Pi chat keeps its live model menu when an older shared list arrives later. Update the tests that still expected the replaced behavior. Tracked in STA-10947. * fix: keep separate agent and chat model lists for Grok, OpenCode and Pi A brand-new Claude chat now offers the selected model's effort levels before the first message, as it did before. Grok, OpenCode, omp and Pi again keep the list read from the installed agent apart from the list a running chat reports, and each newer list replaces only its own source. So a chat shows the model the agent is actually running with that model's own effort menu, and an effort change can no longer act on an older model's menu. Saved model lists on WSL are readable again without starting the agent. Tracked in STA-10947. * fix: keep desktop catalog storage out of the phone's type graph and show Grok's late model answer The phone app's type check no longer pulls in desktop-only catalog storage. The model-list code no longer reaches the saved-catalog file through a type import. When a Grok model change times out and Grok later confirms the model, the chat shows the model Grok actually runs. Tracked in STA-10947. * Remove unrelated merge formatter and package manager churn * Clarify independent catalog ownership and refresh contracts * Declare picker demand callback as an effect dependency * Complete mobile catalog surface and catalog merge types * Complete catalog contract fixtures for current host types * fix: preserve catalog source contracts and account fallback * test: inject independent Codex discovery transport * test: remove accidental client fixture assertion * test: qualify the saved account fallback read * fix(chat): preserve catalog evidence and current child controls * Fix catalog CI types and mobile option readiness * Reconcile session controls after inventory changes * Fix catalog import and deferred test resolver types * Keep model capability observations separate from picker views * Use installed model choices for chat options * Fix model catalog test fixture contracts * Restore model option compatibility and stable settlement * Return a catalog reply in provisional launch fixture * Compose unreported live model defaults in the host * Preserve explicit replacement model on recovery send * fix(chat): use stable scoped catalogs on local desktop * test(chat): preserve generic catalog runner type * fix: scope catalogs to configured environment and keep saved reads passive * fix: satisfy catalog regression and controller CI checks |
||
|
|
98007cb5f5 |
refactor(sidebar): one shared feed client for the desktop window and the phone (#31524)
* 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. * refactor(sidebar): read the sidebar feed through one shared client library on desktop and phone The window and the phone each had their own feed reader. Both now use one reader under src/shared/sidebar-feed-client: paging, notices, retries and the snapshot it installs. A snapshot reuses each unchanged entity's parsed object, so one row's change re-parses one row instead of its whole domain. Adds the gesture and view-report pieces later cuts wire up. Lists are unchanged on both sides. Co-Authored-By: Claude <noreply@anthropic.com> * refactor(sidebar): drop the feed client's unused pieces until their callers land Co-Authored-By: Claude <noreply@anthropic.com> * test(sidebar): key the snapshot test's rows by host identity, as main's feed types now do Co-Authored-By: Claude <noreply@anthropic.com> --------- 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 |
||
|
|
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 |
||
|
|
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) | ||
|
|
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) |
||
|
|
92bbb380a6 |
Sidebar: land the switch-on fixes from the flip branch on main, switch still off (#31466)
* 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. |
||
|
|
c149395c20 |
fix(remote-terminal): repaint an idle full-screen program when a reconnect can't image its pane (#31482)
* fix(remote-terminal): repaint an idle program when a reconnect can't image its pane After an SSH reconnect the pane is repainted from the host's screen snapshot. When the host cannot give one (too large for the replay limit, no buffer, unanswered, overflowed), the pane only printed a loss banner and an idle full-screen program that writes diffs kept its pre-disconnect frame until the user resized. The two places a remote pane declares it has no host image now ask the program to repaint through one choke point: a one-row viewport claim and back, which every TUI honours (Node's tty ignores a same-size SIGWINCH). It respects a phone or another desktop holding the size, skips hidden panes, and puts the row back even if the pane unmounts mid-nudge. Fixes #26887 * test(e2e): idle full-screen program frame after a managed SSH outage * test(e2e): wait for the pane before typing in the idle-frame spec * fix(remote-terminal): pick the redraw height on the host's clamped grid * fix(remote-terminal): wait for the host's grid before putting the redraw row back * test: give transport mocks a connection status for the redraw gate * test(e2e): keep the orcad idle-frame proxy typed for the e2e ratchet * fix(remote-terminal): let a new PTY's redraw supersede a swapped-out nudge * test: keep new transport-mock lines free of type assertions * ci(e2e): route the managed-SSH idle-frame spec to its sources |
||
|
|
b2370f1c6e |
fix(terminal): hidden tabs reconnect to sessions that survived a restart (#31254)
* fix(terminal): hidden tabs reconnect to sessions that survived a restart pty:hasPty answered from the provider's attached-this-run set, so right after a restart every surviving session read as exited and #29459's hidden-agent hold kept live hidden tabs disconnected until shown. On an attached-set miss, ask the host through the provider's existing liveness probe; providers without one (the in-process provider, its own sole owner) keep their answer. * test: remove liveness fixture type assertion |
||
|
|
b3e6705337 |
fix(orcad-migration): a sidebar view setting never blocks moving an SSH host to its server (#31374)
* fix(orcad-migration): a sidebar view preference never blocks moving an SSH host to its server * fix(orcad-migration): keep repo filters on the desktop, keep later acknowledgements; e2e repro |
||
|
|
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> |
||
|
|
018c6e0c42 |
fix(mobile): create and edit notes on headless hosts (#31291)
Create, read, and save Markdown notes on headless hosts through authorized file operations and persisted editor tabs. Addresses the Markdown-note reproduction in #22186, reported by @shota46. Thanks to @Jinwoo-H (#31101), @brennanb2025 (#25436), and @Meapri (#23357) for related community implementations and regression coverage. Those PRs retain broader/distinct scope. Validation: focused tests, paired-headless E2E, iPhone simulator proof, and all CI checks passed. |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |