Repository navigation
chore: sync main into phase35 (sync 1) - #26419
Merged
Merged
Conversation
…ne row (#26048) * fix(native-chat): fold thoughts and tool calls between replies into one row A model that thinks before every tool call drew a 'Thought for Ns' row between each call, and each thought also stopped the calls from merging, so a turn read as dozens of alternating rows. The transcript now draws an unbroken stretch of tool calls and thoughts within a turn as one tool run; thoughts read in order inside it when opened and are not counted in its label. * fix(native-chat): keep work runs whole while a turn edits files A live turn that had edited a file kept splitting its newest call or thought off the run (the row carrying the turn's diff rollup could not join), so a lone "Thought for Ns" row came back at the bottom. - Run boundaries are now one pure pass in src/shared (nativeChatWorkRunSpans), applied over the built transcript slots. Section heads end runs by sitting between rows, and rows inside an expanded subagent section are grouped the same way. - The row carrying the turn's diff rollup joins as the run's last member; the run takes the rollup from it and the bar from its head. - A collapsed run reserves one tool row plus a lead's words, not every thought's text. - Lone rows and runs draw through the same MessageRow, with one flat keyed line list, so a row becoming a run no longer remounts (a revealed diff card no longer re-fires its scroll). - A run opens by default while the reader holds one of its thoughts open; the run's own choice still wins. - A drawn thought with no visible text joins a run instead of splitting it. * fix(native-chat): pair each row's result with its own call; keep open thoughts out of runs - A structured journal row's call and result now share one id (the provider's call id, else the row's own id), so a diff or output can no longer pair with an earlier unanswered call in the same work run or folded message. A quiet command followed by edits now shows each diff card under its own call, and the rollup's reveal lands on it. - A thought still being thought, while its turn or subagent works, keeps its own row after the run and joins once it ends. - Removed the rule that opened a run because a thought inside it was open; a thought keeps its own open state inside its run. * fix(native-chat): an open thought the live line lets go of joins its run Only a subagent section keeps a still-running thought out of its run. At the top level the live line owns the open thought; when it lets go (a Stop in flight, a prompt waiting on the reader) the thought now joins the run at once instead of flashing as its own row until the turn ends. * test(native-chat): guard the run row memo; name the slot post-pass test after it Settled work runs must not rebuild their lines and thoughts on every stream frame of the live run. Renames the slot-level run test so it no longer shares a name with the shared span test.
Play workspace video and music files on mobile using bounded authenticated downloads and native media controls. Adversarial review fixes cover rewritten files and Android policy tests. Includes iOS simulator screenshots and a playback recording in PR #26148. Co-authored-by: ChangJun Park <40492343+ckdwns9121@users.noreply.github.com> Co-authored-by: Lirone Levy <lirone88@outlook.fr> Co-authored-by: dupi <david.li.du@gmail.com> Co-authored-by: John Cusack <5961784+John-Cusack@users.noreply.github.com>
…a.yaml default tab, as the desktop does (#26106) * fix(worktrees): the host's create puts its agent in the first default tab, as the window does With orca.yaml default tabs, the window's create starts the agent in the first default tab: that tab takes the template's title and color, and the template's command does not run beside the agent. The host's create started the agent in its own untitled tab and then created every default tab, the first one running its command too, so the workspace had one tab more than the window's create. Now the agent's tab takes the first template's place. * fix(worktrees): title the agent's default tab as a tab, so the phone keeps the agent's status The host's create titled the agent's tab by renaming the terminal, which writes the pane's own title stamped to outrank every title the agent sets later. On a headless host the phone reads status from those titles, so the agent tab showed the template title forever and lost its working/idle status. The window's create only sets the tab's custom title. The host now titles the tab the same way: the window gets the tab's custom title (not counted as a user rename), and a headless host saves it on the tab and shows it on the phone until the agent titles itself. The pane's title is left to the agent. The host also finds a terminal's tab itself from its handle, so the create over SSH, which never passed the tab, colors the agent's tab too. Every default tab is dressed by the same step, so the other tabs' titles are now kept on a headless host as well, and a failed title no longer skips the color. * fix(worktrees): find a default tab through the window's leaf once the window adopts its handle The host found a provisioned terminal's tab only through the runtime's own pty record. Once the window's graph sync adopts the handle, that lookup misses, and the default tab's title and color would be dropped with only a log line. No create reaches it today (the tab is dressed before the window's first graph sync), but any wait added before provisioning would. It now falls back to the window's leaf, as renaming a terminal already does. Tests: the restart case now saves the title to a real session and rebuilds the phone's tabs from it; a create through createManagedWorktree with an agent and default tabs spawns the agent and the second template only and titles the agent's tab "Dev" without counting it as a user rename. * test(worktrees): drop type assertions from the default-tab tests Use a runtime subclass for the protected launch scope and provisioning host, a typed store and notifier, and typed mocks in place of casts, so the changed-code quality gate passes.
… its folders match (#26104) * fix(worktrees): the host's create keeps a sparse preset only when its directories match The window's create records a sparse preset on the new worktree only when the checked-out directories are exactly that preset's, so an edited selection is not shown as the preset it began as. The host's create wrote whatever preset id it was sent. Both now share one check (moved out of the two copies in the window's create path). * fix(worktrees): read sparse presets only when a preset was chosen, and never fail the create on them The shared preset check took the preset list up front, so every create read the presets even with no preset chosen (the window's create used to skip the read), and a read that threw failed the create - on the host after `git worktree add` had already run. The check now takes a reader, returns early when no preset was sent, and reads inside its try so unreadable presets record no preset instead of blocking the create. Repeated checked-out directories no longer match a preset with a different set of directories.
… and follows the chat's next turn (#25960) * fix(native-chat): drop the queue-paused header and Resume button A Stop, a restart or /clear holds the queued cards. The hold stays; only the header row naming why, and its Resume button, go. A held card shows no caption, and its own Steer, or any new message, releases the queue. * test(native-chat): type the unknown hold reason a newer host may publish * fix(native-chat): a held card offers Send, not Steer, when no turn runs Steer vs Send now follows whether a turn is running, not the card's hold, so a card held after a Stop, a restart or /clear reads Send. * fix(native-chat): the queue sends past held cards instead of stalling behind them A card queued after a Stop (or written after a restart or /clear) sent only once the cards held before it were released; with no header to explain or release the hold, it sat silently. The next sendable card now skips held cards; a returned card still blocks what is behind it. * fix(native-chat): the queue's send of a card is the person's turn, so held cards follow it After a Stop, a card queued later sent past the held cards, but the queue recorded that send as Orca's own turn. It never ended the Stop's pause, so the held cards then waited forever with nothing on the card saying why. A queued card is always something the person wrote: only the client send RPC may now create one. The queue's send of it is therefore recorded as the person's turn, which ends the Stop's pause once the agent takes it, and the held cards then drain in order. * fix(native-chat): a queued card carries its author, so the queue's send of it is that author's turn Main now lets Orca's own sends ask to queue (sendAgentTurn's 'queue' delivery), so "every card is a person's" no longer holds by refusing host sends. Each card records who wrote it (the submission's client/host vocabulary) in a new nullable column; the drain records that origin, so a person's card ends a Stop's pause and Orca's does not. /clear carries the author. Rows from before the column read as a person's. The userSend-only admission gate is removed. * docs(native-chat): state why an unrecorded card author reads as a person's * fix(native-chat): a restart holds only cards written before it, and an idle held queue offers Resume A restart's pause held every waiting card, including one a person typed after the restart while Orca's own continuation ran, and nothing released it except a per-card Send. It now holds only cards another host process wrote, the same way a Stop holds only cards queued before it. The composer's primary button becomes Resume (Play) while nothing is typed, no turn runs and the host holds a card Resume would send, whatever held it (Stop, restart or /clear). It calls the existing agentSession.queuedMessagesResume, guarded against a second press in flight. A card nothing holds keeps the run going between a turn's end and the queue's send of it, so its Steer no longer flips to Send for the frame in between. * fix(native-chat): the host publishes which pause holds each queued card The host published one pause for the whole queue, so a client held every waiting card while it was set. Between a turn's end and the queue's send of a card queued after a Stop or restart, the composer could flash Resume and the cards Send, and a card queued after a Stop lost its "Waiting for your answer" caption. Each published card now carries an optional `heldBy`: the pause holding it, or null, derived from the same rule the drain reads. A client holds only those cards; against a host without the field it falls back to the queue-level pause. * test(native-chat): Resume needs the queue capability and is disabled whenever Send is * docs(native-chat): describe per-card holds in the queue contract and table comments * fix(native-chat): the composer goes from Resume straight to Stop, and Resume returns focus After Resume, the host lifts the hold in one update and sends the first card in a later one. In between nothing was running, so the composer's button flashed a disabled Send. A card nothing holds now keeps the queue's run going for the button too: an empty composer shows Stop, disabled until the turn starts. Not when the host refuses every send (a rewind whose outcome is unknown, read from its status), where nothing is coming. The same fix removes the Stop, Send, Stop flip between queued turns. Resume disables the button, which dropped keyboard focus; focus now returns to the composer. * fix(native-chat): the host names the card its queue sends next, so the chat stays working across the gap A turn's end, or a Resume, and the queue's send of the next card commit as two host updates. In between nothing was running, so the working status, timer, pickers and composer button flipped for one update. The client guessed the drain from its own copy of the host's gates, which missed a /clear-replaced source and covered only the button. The queue publication now carries `nextQueuedMessageId`: the drain's own next card through the drain's own gate (`nextStructuredQueuedMessage`, which the drain step now calls), null whenever the host would refuse the send. The client derives one fact, the queue is about to send, and every working reader follows it; Stop stays disabled until a turn can be stopped. The client-side copy of the gates and the status-feed rewind read are removed. * test(native-chat): the queue's next card survives the coalescer, the reducer and a history page * test(native-chat): build the snapshot that names the next card through its helper * feat(native-chat): a held queue keeps its header row, and a new message asks before passing it The queue's header row ("Queue paused because you interrupted", or Orca restarted, or you cleared the conversation) comes back above the cards it holds, with Resume; it names the oldest held card's pause, as the host publishes it per card, and hides over cards held only on their own or returned. The header's Resume and the composer's share one in-flight guard. A held card reads Steer again whether or not a turn runs; a card held on its own or returned keeps Send. Sending a message while the header shows (Enter or the button) first asks "Send message?": Clear queue deletes every card and then sends (a failed delete sends nothing), Send message sends and keeps the cards, which follow the new turn, and dismissing sends nothing and keeps the draft. Host commands send as they are. * fix(native-chat): the paused row goes while your own message is on its way to lift it After "Send message" over a held queue, the row kept saying "Queue paused…" until the agent accepted the new turn. The chat now reads that gap from the outbox: while this composer's direct send is recorded by the host and not yet accepted, the controller shows no paused row (and so no Resume or confirmation). A refusal settles the entry and the row comes back, since the hold did not lift. Orca's own sends never enter this outbox, and the queue's send of a card goes under a fresh id, so neither hides it. Nothing is stored. * fix(native-chat): a "Send message?" choice is taken once, and a failed Clear queue is one toast The closing dialog stays mounted and clickable through its exit animation, and a double-click or a held Enter lands twice before any re-render, so Send message (or Clear queue) could send the captured message twice. The pending send now lives in a ref that the first choice takes; a second one finds nothing. Clear queue deletes one card at a time and stops at the first failure, so a failed press shows one toast instead of one per card. The dialog keeps its compact width at desktop sizes and the primitive's narrow-window gutter (`max-w-sm sm:max-w-sm`, as the other compact confirmations). * fix(native-chat): Clear queue's message goes out once, and keeps text typed while it waits After Clear queue, the message waited in the composer while the cards were deleted one by one. A second Enter in that window sent it again, and text typed meanwhile was wiped when the chained send was accepted. From the Clear queue choice until its message has gone out, the composer's structured send does nothing. The chained send (and Send message's) now carries the composition it was taken from, and the composer is cleared on acceptance only if it still holds exactly that, as host commands already do. Also: the v1 contract comment names `nextQueuedMessageId` and its absent- means-null fallback, and the own-send check returns at once on an empty outbox. * fix(native-chat): the queue carries on after any turn, in order, and a restart sends nothing by itself - Any accepted turn ends a Stop's or a /clear's pause, whoever sent it (a person, Orca's own messages, or the queue), and so does Resume. The card and submission author fields that only fed the old person-only rule are gone. - The queue sends strictly in order: a card never overtakes a held one. - After a restart nothing sends by itself and no paused row shows: the chat's next turn (the carry-on, or the person's own message) runs first, then the cards. - Resume and "Send message?" are offered only while nothing runs and no prompt waits. * fix(native-chat): after a restart no queue pause shows, and a card written before the next turn waits for it too * fix(native-chat): a quit hands no queued card off, and the paused row goes while any turn that will lift it is on its way - The queue stops handing cards off when the host tears down. A card sent during a quit was refused at close, and that refused send withdrew the chat's restart offer, so resuming after the relaunch sent nothing. - The host publishes no pause while a turn sent after it (your message, Steer, or Orca's own) waits for the agent; a refusal shows it again. This replaces the client's own-send check. - A card written after a restart is an ordinary card again: it waits while any card from before the restart still waits. * refactor(native-chat): the host's paused-row-while-a-turn-is-on-its-way check in one expression * refactor(native-chat): the composer's queue Resume rides the structured transport beside the held queue * fix(native-chat): the "Send message?" choice ends with the pause it asked about; tests follow main's draft props - The open dialog closes when the queue's pause lifts under it (Orca's mail, another client's Resume, any accepted turn): nothing is sent, the draft stays, and the next Enter sends as usual. The pending choice records the hold it was asked under; nothing new is stored. - The composer-field Resume test passes main's dropScopeKey/draftScopeKey. - The dialog test expects main's rule: only the sent text leaves the composer. * fix(native-chat): a message kept after a quit or close waits like every other card, and follows the chat's next turn A message Orca accepted but never handed to the agent before a quit, crash or close came back as a card held on its own ("Not sent yet — press Send"): the queue skipped past it, so later cards sent first, and only the person's own Send released it. It is now an ordinary card at the head of the queue that waits, with every card the chat closed with, for the chat's next accepted turn. One rule for a chat that was not running, derived from the journal: when this host first opens a chat (after a restart or crash), or a person closes it, and cards are waiting, a reopen mark is written (a tombstone carrier key, like the Stop and Resume marks). The cards queued before it wait until a turn is accepted or Resume comes after it; nothing sends by itself, and no paused row shows, a Stop's included. The idle sweep's own eviction writes nothing and changes nothing the person sees. A mark that cannot be written leaves the open working and holds from the open itself until the next turn; the next open marks again. A rewind restates the mark. /clear's carried cards also wait unshown. This replaces the host-instance comparison and its adoption write, and the 'kept' hold (stored 'kept' and legacy 'stopped' holds now read as none). * fix(native-chat): mark the reopen in the one open path, and keep an idle chat with waiting cards open Review round 1: the startup restore opened chats past the per-host first-open mark, so their cards could send by themselves after a quit or crash; a card mid-hand-off at the open got no mark; a close that left the chat open re-marked after every new send. - Every open marks when a card waits, or is mid-hand-off with its send unanswered. - The idle sweep keeps a chat's handle while cards wait, so its eviction never reopens one and stays invisible; it drops it on the next sweep once they leave. - A person's close marks once; its delivery re-check marks only when it settled a send. - A failed mark holds from where the mark would have gone. - A Stop made after a reopen shows its row. - The rig's restart is a real quit and relaunch. * fix(native-chat): review round 2: restore the dropped Resume and failed-Stop tests; a late mark starts where the chat stopped - Restores eight tests the previous commit dropped by mistake. - A mark the delivery loop or a close's re-check writes after a later send starts where the chat stopped, so that send still lifts it; a later mark never narrows an earlier, wider one. - Only the idle sweep's own close keeps a chat with a card waiting (or mid-hand-off) open, and it still releases an ended child's lease first; a person's close drops it as before. * test(mobile): a host-kept card's test stands in a Stop's pause, as this host publishes no restart pause Main's #24660 test published queuePause 'restarted', which this branch's wire type no longer lists, so the mobile tests typecheck ratchet failed. * refactor(native-chat): settle a restart's leftovers and mark them in one host-lifetime step Keeps the delivery loop under its line limit after main's Stopping change; no behaviour change. * test(native-chat): a kept card's Resume and failed-mark tests quit through the held start's release Main's #25152 holds the start these tests send into; quitting without releasing it left the quit waiting.
…ats (#26103) * feat(native-chat): visual directive grammar and host read for a chat's visuals folder A shared grammar for the ::orca-visual{file="..." title="..."} reply line, the per-chat visuals folder location on the owning host, and the agentSession.readVisual runtime method that reads one visual with lexical and canonical containment, a 512 KiB bounded read and UTF-8 refusal. * feat(native-chat): render chat visuals inline and in the right sidebar Native-chat assistant replies render a ::orca-visual{...} line as the chat's HTML visual in an opaque, scripts-only sandboxed frame: CSP first, the host frame navigation guard registered before content runs, live theme without a reload, fitted height, links opened in the viewer's browser only from a real gesture, lazy mount, and one muted line when the visual cannot be shown. Open in sidebar shows the same frame in the right sidebar, widened while it is open and restored after. * fix(native-chat): visual CI fixes, shared height governor, live-turn streaming hold Registers agentSession.readVisual from the methods index so the structured method file stays under its line budget, replaces reflective reads with checked narrowing, moves the pure height governor to src/shared for mobile, and holds a half-written directive tail while the turn works (structured text rows carry no running state). * fix(native-chat): harden the visual read and link opening Re-checks after the open that the chat's visuals folder is still the real directory at Orca's path, reports unexpected filesystem faults by code without host paths, and lets one click in a visual open at most one page. * fix(native-chat): keep visual lines out of plain-text reply surfaces; review fixes One shared helper drops visual lines (outside fenced code) from reply text where it becomes plain text: the structured status summary that feeds the sidebar row, dashboard, notifications, phone rows and handoffs, and AI Vault reply previews. Review fixes: height also counts a pinned body's overflow, only the live frontier row holds a half-written visual line, the runaway-height stop needs the same step repeated, and any host refusal evicts the cached revision. * fix(native-chat): resolve the visuals folder without the removed journal-paths helper Main removed the per-chat journal paths and the journal database's state directory; the visuals folder keeps the same sha256 layout on its own and the read method uses the profile state directory the chat host is opened in. * fix(native-chat): review round 2 fixes; copy a reply without its visual lines Reply previews in Agent Session History drop visual lines per text part before lines are folded; the frame adds a body's overflow only when the body really overflows; fence tracking follows CommonMark closers and openers; the copy button copies a reply without visual lines; a coded read fault keeps its cause. * fix(native-chat): update the frame's theme ref after render; read the visuals folder pair at once * test(native-chat): declare agentSession.readVisual on the cross-version agent-session surface * fix(native-chat): copying a reply keeps its code blocks and indentation Removing visual lines now closes only the gap each removal leaves, instead of collapsing blank lines across the whole reply and trimming its indentation; the visuals folder is checked parent first again so a broken path answers the same way every time.
…e) (#26078) * refactor(terminal): renderer senders attach placement to pty spawn, report-only The window now says where each fresh terminal goes: the first pane of a tab sends new-tab with the tab's creation fields, a split sends the parent leaf, direction, ratio and the tree after the split, background launches send the same, and a Codex restart that rebuilds a rootless layout sends root. A reattach sends nothing. Main still only records whether placement names the tab its binding write picks; its 'absent' value now counts only new leaves without placement, so it reads the fallback mint and graft directly. Extends the shared placement type with optional row, ratio and proposedRoot, each dropped alone if malformed, so older and newer peers keep working. * fix(terminal): background agent placement row claims no launchAgent the tab lacks The adopted tab is created without launchAgent, so a row naming one would diverge from the renderer's tab once main applies placement. * fix(terminal): a before-split publishes its pane with the final tree order wrapInSplit now places the new pane first itself, so the pane-created handler's proposedRoot sees the real post-split tree instead of the order before the subtree split moved it. * test(terminal): type the background-terminal tab patch precisely
…tition (#26088) * fix(ssh): bind reattached SSH panes into the target's own session partition A relay reattach persisted the pane binding into the local partition, minting a minimal copy of every reattached SSH tab there. When nothing saved over it (a reconnect with no window open), the next startup kept that copy and skipped the SSH partition's rows for the workspace: its open editor tabs were missing for a launch and its agent-resume records were dropped (STA-9544). Bind into ssh:<target>, where the spawn bound the same pane. * test(e2e): run the reattach home-partition spec only in the Docker SSH lane * test(e2e): keep Electron running after its last window closes on Linux in the reattach spec * test(e2e): sync the SSH reattach spec on state, not sleeps Waits for the SSH partition to persist the open file and for main's SSH state to report the reattached connection, instead of fixed 3s/30s sleeps that could pass vacuously on a slow reconnect.
…er (#25146) * feat(native-chat): a paired server keeps a store for chat attachments A structured chat on a paired Orca server had nowhere to put a file the client attached. The server now keeps a per-chat attachment store under its userData, filled through agentSessionAttachment.uploadStart/Append/Commit/Abort and read back for previews through agentSessionAttachment.read, behind the structured session gate and advertised as agent-session.attachments.v1. Nothing records cleanup as owed: a sweep re-derives what may go from the host's chat records and journal (unfinished uploads after an hour, uploads for a chat that never existed or that its journal never mentions after a day). The clipboard RPC's in-flight bookkeeping moves into a shared ChunkedUploadRegistry both use. * feat(native-chat): upload attached files into a paired server's chat store Main streams dropped or picked files (fs:uploadPathsToAgentSessionAttachments) and pasted images (clipboard:saveImageAsTempFile with agentSessionAttachment) into the chat's store on its paired server, reusing the file-import slice streamer and pinning every call to the pairing revision and server process. The browser client's paste takes the same route. * feat(native-chat): attach files to a structured chat on a paired server Pasted images, files dropped from Finder/Explorer and the file picker no longer refuse with "Local attachments are not available for remote sessions." in a structured chat on a paired server: they upload into that server's chat store and the chat gets only the stored path. Dropped files show as pending chips at once so Send waits for them; every attached item carries the server, pairing and chat it was stored for, and a send to anywhere else drops it with the existing "changed hosts" notice. Chips and transcript images read stored files back through the server, never from this machine's disk. An older server gets an update notice instead of an upload. The terminal-backed chat keeps refusing. * test(native-chat): type the attachment test doubles without bare casts * refactor(native-chat): keep the clipboard RPC on its own upload bookkeeping The chunked upload registry stays for the chat attachment store only, beside it. * feat(native-chat): claim chat attachments when the host admits a message A message's references into the host's attachment store are claimed in the same journal transaction that makes it durable (a direct send, a queued draft, a /clear carry). The sweep deletes an upload only after marking it in that database while no claim exists, so a send and a sweep can never both win: a client's send that names an expired or foreign upload is refused whole with the new attachmentExpired reason, before anything is recorded. This replaces scanning chat transcripts for paths. The store is now <root>/<upload id>/<name>, refuses uploads for chats the host does not hold, caps stored names in UTF-8 bytes and avoids Windows device names. The preview read goes through the protected bounded read and the request's reply budget, so an image too large for one reply is refused instead of closing the connection. * feat(native-chat): let structured Claude read the host's chat attachment store Attached non-image files live outside the workspace; the store root is passed as an added directory so the agent reads them without asking, the common pattern. * fix(native-chat): skip a dropped folder before staging walks into it * fix(native-chat): pin the browser client's chat paste to its server Every upload call goes to the environment the paste was meant for and checks its pairing and server process, as the desktop upload does; a re-pair ends the upload instead of storing the image on another server. * refactor(native-chat): let the host's claim be the only attachment check The composer no longer records which server and pairing each attachment came from, and Send no longer strips attachments it judges foreign: the host refuses an expired or unknown stored path when it admits the message, which also covers retries, Stop-restores and queued edits the client check missed. Previews of stored files route through the chat's own server by path. Removing a file's chip while it uploads now keeps its @path out of the draft. A dropped or picked file shows its name and kind on its chip while it uploads, with no separate progress toast, and files that did not attach are named in one notice. A rich-text paste into a chat on an older server no longer shows the update notice beside the pasted text. * chore(native-chat): keep the claim hook beside the draft consume and fit the line budgets The submission's claim runs in the same append hook as a draft's consume, owned by the queued-message collaborator, so the journal store stays within its size limit. Formats the new files and regenerates the runtime-required English catalog. * test(native-chat): pin the attachment store grant in the Claude launch, restore upload result types * fix(native-chat): create the attachment store at install and claim only exact store paths Claude drops an added directory that does not exist when it starts, so the store root is created when the host installs it, best effort. A commit keeps its upload in flight until the rename lands, so a sweep never takes a slow upload's part file. Only a path under this host's exact store root is claimed and required; any other mention of a store path is plain text and never refuses the message. * refactor(native-chat): reuse the newer-Orca notice and drop leftover attach options An older server's refusal to store attachments uses the notice every other write it refuses already shows, so one string fewer in every catalog; the attach callbacks lose an options parameter no caller passes since the provenance check went. * fix(native-chat): give a message refused for an expired attachment back to the composer Sending the same message again can never bring the attachment back, so instead of a Retry that always fails the message's text and images return to the composer, as a Stop's withdrawn message does, and the notice says what to remove. * fix(native-chat): keep uploads across a prompt, say why a file did not attach An upload that finishes while a prompt card has replaced the composer lands in the scope's attachment and draft caches, which the composer reads back when it returns, instead of the unmounted composer. The single failure notice names the cause the files share, such as the size limit. Rich text pasted into a chat on an older server no longer flashes an image chip: the chip waits for the server to take the image. A picked command waits, as Send does, while an attachment is still uploading. * fix(native-chat): keep a paste's upload across a prompt; pin the Claude grant hand-off A paste uploading into a paired server's store keeps its chip live when a prompt card unmounts the composer, and its result is filed for the composer's return, as a drop's is. A failure cause ending in full-width punctuation gets no extra full stop. A runtime test pins that the store root reaches Claude's launch resolver through the adapter. * test(native-chat): type the grant hand-off captures without casts * test(native-chat): pin that the composer files a paste's upload across a prompt * fix(native-chat): retry a held rename at commit and keep cut names Windows-safe A commit renames the part file with the Windows retry the app uses elsewhere, so an antivirus or indexer holding it briefly no longer loses the upload. A name cut to the byte limit is stripped of trailing dots and spaces again. The upload pump's result cast states why it holds. * fix(native-chat): keep attachments still on their way in the pane's attachment cache A prompt card unmounts the composer, and an attachment still saving or uploading lived only in that composer: one that came back showed no pending chip, so Send went out without the file and the file then landed in the next draft. Pending chips now live in the pane's attachment cache beside settled ones and settle there whichever composer is showing, so Send waits for them after a remount too. A stored file's @path goes into the pane's draft cache, which keeps it through an input-method composition and a remount. A rich-text paste's image is registered as a hidden pending chip before the server is asked, so Send waits for it from the start, and it shows only once the server takes it. * test(native-chat): a dropped file still uploading stays pending across a remount * fix(native-chat): reveal a rich-text image in a composer that came back; keep caret inserts A rich-text paste's held image is revealed through the pane's attachment cache, so a composer a prompt card remounted during the server's answer shows it rather than holding Send on an invisible chip. A stored file's @path goes in at the caret while the composer is showing and not composing, as every other attach does; only mid-composition or after a remount does it go to the pane's draft. * fix(native-chat): never evict a pane's attachments while a composer shows them or one is on its way The pane attachment cache is now where chips live, so its 128-scope bound passes over a scope a mounted composer subscribes to or that still holds a pending attachment, and evicts the oldest scope nobody uses instead. Protected scopes are bounded by mounted composers and attachments in flight. * fix(native-chat): never evict the scope just written when every older one is in use * fix(native-chat): keep the attachment store out of the RPC method table's imports The method table is imported far and wide (the SSH relay's CLI included), and the attachment RPCs pulled in the store, whose preview read loads modules that read fs constants at load: any test that mocks fs/promises without them failed to import. The installed store now lives in a small registry module; only the host wiring loads the store itself. * refactor(native-chat): the submission hook gets its own module Merging main added the operation receipt to the queued-message insert, which put journal-queued-messages.ts over the 300-line limit. The submission hook only uses the queued-messages object's public methods, so it moves out. * refactor(native-chat): fit the attachment sweep and paste upload into main's line budgets After merging main, the record store and the clipboard handlers each ran a few lines over the 300-line budget. The sweep now asks the record store whether a chat is recorded through the two lookups it already has (readable or unreadable) instead of a new id-listing method, and the paired-server paste upload lives beside the other attachment uploads. No behavior changes. * fix(native-chat): pending chips sit beside main's saved draft, which keeps server-stored images Main now saves a composer's draft (text and settled images) in one store that survives a reload or quit. This branch kept every chip, settled or not, in its own pane cache. After the merge the saved draft owns settled images and the pane cache holds only chips still on their way: they come back pending in a composer a prompt card remounted, hold Send, settle into the saved draft whichever composer is showing, and are never saved themselves, so a restored draft cannot bring back an upload as if it were attached. An image a paired server stored for the chat is saved with the draft as the real image (a pasted one is no longer turned into a "Not kept" placeholder), and the restore check leaves it to that server, whose claim at Send refuses one it no longer holds, instead of asking this machine's disk for a path that only exists on the server. Also: previews and the pending subscription move into their own small hooks to fit the line budget, the "local attachments" notice moves to the composer-target module so the attachments hook no longer pulls in the upload module's imports, and tests follow main's new mocks. * test(native-chat): give the claims test main's provider handle and message source * fix(native-chat): a paste still uploading dies with its tab or workspace A pasted image still uploading to a paired server sits in the pending chip cache, which by design outlives the composer and its tab. Removing the workspace deleted its saved drafts but left those chips, so the upload finishing afterwards recreated and saved the deleted draft. With the workspace's tabs gone it had no owner either, so no later workspace cleanup could ever remove it. A pending chip now records the owner its draft would have, resolved the way the draft store resolves one when the chip is added. Workspace removal and a user's tab close drop pending chips by the same owner, conversation and tab matches they delete drafts by, and a chip that settles after its chat's tab closed writes its draft under the owner it was begun in. * fix(native-chat): the claim type uses the journal's own SQLite type * refactor(native-chat): read the pending-image handlers off the composer's attachments Main's multi-file picker (#23956) and this branch together took NativeChatComposer over the 400-line limit; reading the two pending-image handlers the way the neighbouring ones already are keeps it at the limit. * test(native-chat): pass the launch-args resolver main now requires in the attachment grant test * fix(native-chat): grant the attachment store beside folders the saved Claude Arguments add Main (#25721) now builds additionalDirectories from the user's --add-dir arguments; this branch's attachment-store grant replaced that list instead of adding to it. Both are granted now. Moves the Claude session-id derivation to its own module to keep the resolver under the line limit. * test(native-chat): run the attachment store's SQLite tests in the Node runtime project * test(native-chat): follow main's attach ownership recheck (#25749) in the attachment tests * test(native-chat): a named upload chip still shows when the agent takes no images * fix(native-chat): a paired-server image paste failure keeps its error apart, as main's notice card does * fix(native-chat): the attachment sweep reads the chat list directly now that records have no import still owed
…er (#26044) * fix(native-chat): recall every sent prompt with Up/Down in the composer Recall read a list kept in the composer, so it forgot everything on reload or remount. It now reads the prompts the chat shows. * fix(native-chat): put the caret at the end of a recalled prompt It stayed where the last prompt had it, so Up skipped past a recalled multi-line prompt. * test(native-chat): read the editor in the recall spec through a type guard
…ly (#26207) Copy was always listed, greyed out or copying text selected earlier. Paste was offered on messages, and did nothing in a structured question's answer field.
* ci: run full pull-request unit tests across ten shards * Bound required Linux package tooling setup
…26315) * test: keep native filesystem watcher contract under Node * test: reset canonical repository keys with runtime mocks
* Stop unchanged GitLab task detail replies from refetching * Reuse approved RPC fixture for task detail tests
* Release native chat clipboard previews when the composer unmounts * Assert late previews retire before explicit cleanup
…-only sidebar card (#25652) * feat(native-chat): show each interrupted chat's workspace as its read-only sidebar card The resume-on-restart dialog listed workspaces as a bare name and glyph, so users could not tell their sessions apart. It now renders each workspace with the sidebar's own WorktreeCard in a new read-only mode, with the offered chats in place of its live agent rows, and nests child workspaces under a listed parent the way the sidebar does. The dialog is wider and the chat rows use the sidebar's compact agent row layout. Co-Authored-By: Claude <noreply@anthropic.com> * test(native-chat): name the nesting helper for what it returns Co-Authored-By: Claude <noreply@anthropic.com> * fix(native-chat): resume dialog cards show no status The dialog's cards stand in for the chats before the restart, so the sidebar's live status (Failed, now Interrupted) read as a verdict on chats the dialog offers to resume. A read-only card shows no status, and in the new card style its PR joins the badges because the status lane that normally holds it is gone. Co-Authored-By: Claude <noreply@anthropic.com> * fix(sidebar): a read-only card is inert and shows no live state, whatever its caller passes A read-only card stayed inert only because each control checked `readOnly` on its own and the caller left out the handlers that would turn the rest on: passing `onLineageToggle` put a live child-workspaces toggle on it. It also still painted live state over the resume dialog's chat rows: the sleep dim, the "Queued for deletion" overlay (which blocked unticking a pre-ticked chat) and the prompt-cache countdown. WorktreeCard now reduces a read-only card's props to what it pictures (every handler, selection and active flag dropped), and the controller resolves `readOnly` once into what the views already read: card properties without status, ports or live agents (and with the host), no sleep or delete state, no cache countdown, and one `interactive` flag for its own controls. A contract test renders a read-only card with every feature and handler on in all three card styles and fails on any control outside the caller's rows or any live state. * fix(native-chat): nest and indent the resume dialog's child workspaces as the sidebar does A child workspace whose parent has older metadata with no host id stayed at the top of the dialog, though the sidebar nests it: the dialog copied a strict same-host filter, while the sidebar treats a hostless row as belonging to the host. The nesting rule now lives in one sidebar lineage helper, getWorktreeLineageAncestorsOnHost, which scopes by host the way the sidebar's lineage inputs do. In the legacy card style a nested child was inset for its parent's depth instead of its own, so it sat one sidebar step too far left (0px instead of 14px), outdented into the parent's padding. Each child is now inset for its own depth, as a sidebar row is. A groups-level test renders real sidebar cards from the store, with the child nested under a hostless parent, and checks every offered chat's checkbox renders and the legacy inset. * fix(native-chat): the resume dialog places every workspace even if lineage loops Nesting followed each workspace's parent chain, so two workspaces listed as each other's ancestors (possible only when two hosts report opposite lineage for the same ids) were placed under no top-level entry and vanished, while their pre-ticked chats would still be resumed. The loop is now cut where the walk first comes back, so every workspace renders. * fix(native-chat): keep "Don't ask again" above the resume dialog's buttons when narrow Below the small breakpoint the footer stacks bottom-up, so the option moved into it rendered under Resume and Dismiss all. It now reads top to bottom: the option, then the actions; wider windows keep the single row. * test(sidebar): import the card statically in the read-only contract test Its dynamic import ran in a beforeAll with a 20 s timeout, below the suite's 60 s hook timeout, and timed out under parallel load, which skipped all six contract tests. * fix(sidebar): legacy lineage children step in as far without a status lane Legacy cards outdent their nested child list by 1.125rem to pull it back over the 22px status lane. A card without that lane (a read-only card in the resume dialog, or the sidebar with the Status property off) kept the outdent, so a child's title sat 3px right of its parent's instead of 25px. Without a lane the list now sits at ml-1, which gives the same 25px step (39px at depth 2). Cards with a status lane, the default sidebar, render exactly as before. * fix(native-chat): nest resume dialog children by the sidebar's own row rule Round one nested a child under a parent whenever either had no host id, but the sidebar's row builder looks a parent up on the child's own host, so a row with no host id nests only under a parent with none either. The dialog showed a parent/child pair nested that the sidebar shows side by side. The row builder's parent lookup is now one function, getSidebarLineageParent, and the dialog walks ancestors with it (getSidebarLineageAncestors) over the same non-archived rows and cycle set. The chat's workspace is still found on its host, falling back to a row with no host id. A test checks the four host pairings against the real row builder, and the dialog test renders each pairing with every chat's checkbox. * test(sidebar): pin child-workspace indent with and without a status lane Covers legacy, compact and new card styles with the Status property on and off. With Status on, the legacy and compact sidebar keep their outdent unchanged; with it off they now step children in by the same amount; the new card style is identical either way. The file now imports the card statically, since its 15 s per-test dynamic import timed out under load. * fix(sidebar): a read-only card's child workspaces step in as the sidebar's do The legacy outdent on a card's child list cancels its status lane, which in the sidebar is the unread button plus the row gap: 1.125rem, exactly the outdent, not the 22px the previous fix assumed. Its ml-1 for a card with no lane put the resume dialog's children 4px deeper per level than the sidebar's (25px against 21px). With no lane there is now nothing to cancel and no margin, which gives the sidebar's 21px (35px at depth 2). Status is a fixed card property, so every live card has the lane: only a read-only card takes this branch, and the live sidebar is unchanged. The tests now pin the outdent for live legacy and compact cards and no margin for a read-only one, instead of an unreachable Status-off state. * test(sidebar): a read-only card never carries main's multi-selected marker Main now styles a multi-selected card through data-worktree-card-selected. The contract test passes isMultiSelected, so it now asserts the marker stays off a read-only card, and drops the context-menu event constant main removed from that module. * fix(sidebar): preserve quiet read-only card geometry * fix(native-chat): match resume cards to sidebar rows * fix(sidebar): preserve passive SSH identity on read-only cards * fix(native-chat): keep resume focus and folder host identity * fix(native-chat): remove resume dialog list border * fix(sidebar): keep passive SSH pills static * fix(native-chat): align resume card vertical spacing * fix(native-chat): preserve cache-only card header spacing * fix(native-chat): lay the resume dialog out as a checkbox-column list Every row of the resume list (Select all, workspace, chat) now keeps its checkbox in one left column; nesting indents only the content after it, and a divider separates every row. A workspace checkbox is tri-state over its own chats and those of the workspaces nested under it; Select all heads the list with an "N of M selected" count. Failures a retry cannot fix stay out of both. Up/Down move between the list's checkboxes. The dialog no longer renders the sidebar's WorktreeCard, so the read-only card mode and the sidebar changes that only served it are reverted to main. The sidebar lineage helpers stay: the dialog still nests child workspaces by the sidebar's own rule. * fix(native-chat): name SSH hosts by their saved label in the resume dialog The workspace row's host chip spelled an SSH host with its raw target id (e.g. ssh-1728291234567-abc12d) because it never got the sidebar's host labels. It now takes them from useSidebarHostScopeOptions, as the delete dialog does, so the chip reads "devbox" like the sidebar. Also translates the dialog's five new strings in es, fr, ja, ko and zh. * fix(native-chat): group the resume list in bands and let a project select its chats The resume list now follows the common grouped-list pattern: each workspace heads its chats with a full-width tinted band (lighter for a nested child, at its own indent), chats are split by plain hairlines, and groups meet without gaps or rounded row corners. The project row gets a checkbox in the same left column, tri-state over every eligible chat in the project (nested workspaces included, failures a retry cannot fix left out) with an "x of y" count, on a band a step stronger than a workspace's. The list takes arrow keys, so it is now a named group (role="group"). * fix(native-chat): no project row for workspaces the resume list cannot place Workspaces the store cannot place in a repo were grouped under a header with an empty name, so the new project checkbox was announced as "Select all chats in ". Main and the earlier heads showed that header as a bare folder glyph with no name, and there is no id to fall back to. Those workspaces now get no project row; their own rows and Select all still cover their chats. * fix(native-chat): lay the resume list out as nested workspace boxes Brennan chose the nested-box layout without row dividers. A project is a tinted band; each workspace is a bordered, rounded box one step in from its project, holding its header band and its chats, and a child workspace's box sits inside its parent's, after the parent's chats, at the parent's chat indent. The boxes are drawn in the content area only, so every checkbox stays in the one left column outside them. Rows carry no dividers; the box border is the only line. * fix(native-chat): show the resume list as a tree Brennan chose the Resume Tree. The list is now a tree: machine, then project, then workspace (a child workspace is a node inside its parent, after the parent's chats), then the chats. Every row keeps its checkbox in one left column, then an 18px indent per level, a disclosure arrow (an empty slot on a leaf), the icon and label, and its count. Levels read apart by icon and weight; there are no boxes, bands or row dividers, only the divider under Select all. The machine level shows only when the machine is not obvious (a remote host, or chats on more than one), named with the sidebar's host labels and an SSH chip; it replaces the per-workspace host chip. Chats are grouped per machine first, so one workspace id on two hosts stays two nodes. Every node is tri-state over the eligible chats under it, through the existing per-chat onToggle. Nodes start expanded; collapsing is local to this opening and keeps the selection. The list is a role="tree" of leveled treeitems: Up/Down move between checkboxes, Left collapses and Right expands the focused node. * refactor(native-chat): read each resume chat through one key helper Every per-chat read in the resume tree (its tick, its toggle, its failure, and each group's coverage) now goes through chatState, so a later change of chat key stays in one function. Tests pin the machine level's host rules: a runtime host gets a machine node but no SSH chip. * fix(native-chat): make the resume tree's keys work from its Tab stop and after a click Tab lands on the tree itself, where the arrow keys did nothing. Down and Home now enter at the first checkbox, Up and End at the last; Home and End also jump to the ends from a row. Clicking an arrow with the mouse left focus on the arrow, where no tree key works. After a collapse or expand click, focus now moves to that row's checkbox, or to the tree when the checkbox is disabled. --------- Co-authored-by: Claude <noreply@anthropic.com>
* feat(opencode): run OpenCode 2.x in the structured chat too `opencode acp` on stable 2.x starts its own private `opencode serve --stdio` child with the chat's environment and ends it when stdin closes, so the chat's account pin reaches it just as it does on 1.x. Admit stable 2.x from 2.0.14 beside stable 1.x from 1.18.31; pre-releases, older releases, and other major lines keep the terminal chat. The `opencode2` agent is unchanged. Restart recovery already reads a 2.x session's own tables first; tests now cover a database an upgrade left with both generations, and one whose 2.x message table has an unknown shape (nothing found, no error). * fix(opencode): show OpenCode's own permission option names * test(opencode): run the 2.x recovery tests with the real-SQLite Node project
* feat(omp): open OMP in the structured chat over ACP
OMP joins Grok and OpenCode on the ACP lane: `omp acp`, with the user's
environment passed through and the chat pinned to PI_CODING_AGENT_DIR
(default ~/.omp/agent), the directory OMP's terminal chats read. A create
and every launch ask `omp --version` of the binary the Command setting
resolves to; stable releases from 17.0.5 run the chat, older ones keep the
terminal-backed chat. Like every ACP agent it runs only with the updated
structured native chat setting on.
* fix(omp): show a shell command's output and exit code in its row
OMP's ACP server sends a command's result as rawOutput {content, details}
with a non-zero exit at details.exitCode, and repeats it as content behind
a "$ <command>" echo. The shared reader takes content text first, so the
row's output began with the command and carried no exit code. The OMP
dialect drops the echo, moves the result text to stdout without OMP's
"Wall time" and "Command exited with code N" notice lines, and maps
details.exitCode. Recordings of omp acp 17.0.5 (exit 3, exit 0, Stop
mid-command) replay through the adapter.
* feat(file-drop): add element owner preparation plumbing * Fix terminal and chat file drop destination ownership * fix runtime terminal drop ownership and queued chat retries * fix(file-drop): preserve feedback acceptance and destination ordering * fix(file-drop): attach chat and composer files at the drop surface * fix(file-drop): preserve project destinations and live chat availability * fix(file-drop): keep path resolution in filesystem namespace * test(file-drop): use filesystem path bridge in PR3 fixtures * fix(file-drop): bind terminal drops to pane elements * fix(file-drop): compare terminal pane identity across public views * test(file-drop): align composer lifecycle with element-owned drops * Fix interrupted chat composition and refuse unused quick-create drops * Type the quick-create drop regression transfer * fix(file-drop): let the explorer, project sidebar, tab strip and editor own OS drops STA-6940 PR5. The file explorer tree, the project sidebar, each editor group's tab strip and its editor area now take OS file drops on their own element through the shared owner hook, with identity from their own render (the explorer's shown workspace and target row, the tab strip's and editor group's worktree and group) instead of the active worktree. The explorer and sidebar broadcast subscribers and their legacy drop markers are deleted; editor-open moves out of useGlobalFileDrop into editor-dropped-file-open, which the legacy unmarked-chrome route still uses until PR6. * fix(file-drop): keep editor delivery tied to its destination group * fix(file-drop): guard delayed opens and share floating ownership * fix(file-drop): remove legacy OS drop routing (STA-6940 PR6/6) * test(file-drop): finish owner test and comment cleanup * Clarify dropped-file default editor destination * Retain UI test namespace for terminal resume coverage
6 of 9 tasks
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Merges current
main(4f4f021, 79 commits; then 00a8250, 4 more, clean) intoOrcaWin/phase35(bb27aad). Real merge commit; both sides are kept, and main-owned files stay identical to main except where a Phase 3.5 rule had to be applied to main's new code (listed below).Conflicts and resolutions
src/main/runtime/rpc/methods/structured-agent-session.ts: Phase 3.5 had moved the approval/question/option answers intostructured-agent-session-prompt-response.ts(to stay under max-lines) and given each a declared permission. Main (Pi structured chat, Add Pi structured native chat through its RPC mode #25851) routed those answers to the session's own host (requireStructuredSessionHost(ctx, sessionId)) and movedhandoffStatus/commandsintostructured-agent-session-options-read.ts. Kept the Phase 3.5 split and ported main's routing into it; the prompt-response file now holds only the three answer methods, andhandoffStatus/commandslive where main put them.config/scripts/build-orcad.mjs,config/scripts/orcad-entry-build.mjs: Phase 3.5 (fix(remote-runtime): stop arm64 Linux servers dropping clients every heartbeat (empty WebSocket write EFAULT) #26200) builds every forked child generically fromORCAD_CHILD_ENTRY_POINTSviaorcadChildOutputFilename(which is how the session scanner service ships). Main (feat(runtime): isolate the server behind a compatibility launcher #26374) split the bundle into a compatibility launcher (orcad.js) and the server (orcad-server.js). Kept the generic child loop and took main's launcher/server split, so children still land beside both files.config/scripts/vitest-sqlite-runtime-files.mjs: kept both sides' new entries (Phase 3.5's serve-selection/serve-profile tests and main's Pi launch-resolution test).docs/reference/headless-linux-server.md: main deleted it as stale (docs: remove stale internal reference documentation #26328); Phase 3.5 had added a paragraph about the daemon scope needing linger. Took main's deletion; nothing links to the file any more.Semantic ports (no textual conflict)
RPC caller permissions (fix(rpc): deny-by-default caller permissions and a scoped SSH bridge credential #26337): every method now has to declare a permission, so main's new methods were classified:
accounts.beginClaudeSignIn,accounts.finishClaudeSignIn,accounts.cancelClaudeSignIn(fix(claude): activate account profiles and remove credential replay (Step 4 of 4) #24434):accounts-admin, like the other account mutations.agentSessionAttachment.uploadStart/uploadAppend/uploadCommit/uploadAbort/read(feat(native-chat): attach files to a structured chat on a paired server #25146),agentSession.continueInterrupted,agentSession.readVisual:workspace, like the rest ofagentSession.*.agentSession.handoffStatusandagentSession.commandslost theirworkspacepermission when main moved them tooptions-read; restored there.max-lines: main's
placementfield (fix(ssh): bind reattached SSH panes into the target's own session partition #26088) pushedpty-input-recovery.tsto 301 lines. Split the transport-options builder intopty-connection/pane-transport-options.ts(no logic change) instead of disabling the rule.Serve selection: main's switch of the native preflight entry to
orcad-server.jsmerged cleanly into Phase 3.5'sorcad-local-serve-selection.ts; remote launch and stop paths keep pointing at the launcherorcad.js, which is main's intent.SSH pane partition (fix(ssh): bind reattached SSH panes into the target's own session partition #26088, fix(session): at startup, a local copy never overrides an SSH-owned workspace's own copy #26098): main binds reattached SSH panes into the target's own partition; this auto-merged into
ssh-relay-session.tsalongside Phase 3.5's remote pane binding, and the SSH suites pass.Localization catalog:
mainat 8b20d21 (Remove local branch deletion success notification #26422) failsverify:localization-catalogson its own, becauseen-runtime-required.jsonno longer coversen.json. After merging 00a8250, I regenerated the catalog withpnpm run sync:localization-runtime-catalog(no hand edits). This is the only file here that differs frommainfor a reason outside Phase 3.5.Phase 3.5 fix merged in after the sync
fix(serve): serve on the profile the CLI selected, never the default profile's lock #26424 (serve on the profile the CLI selected): merged into phase35 after this sync started, and merged here cleanly. It changes
src/cli/runtime/launch*.tsandconfigure-process.ts. Main's server/launcher split (feat(runtime): isolate the server behind a compatibility launcher #26374) loads the server in the same process withargvintact, so the--user-datapath that fix(serve): serve on the profile the CLI selected, never the default profile's lock #26424 pins still reaches the orcad server. Main's switch of serve selection toorcad-server.jsfor the native preflight composes with fix(serve): serve on the profile the CLI selected, never the default profile's lock #26424's use of the selected profile.src/cli/runtime,src/main/startupandsrc/main/orcadpass (1283 tests; one orcad lifecycle integration test was flaky under load and passes alone).fix(remote-runtime): open terminal tabs reattach after a managed server update re-pairs the host #26433 (open terminal tabs reattach after a managed server update): merged into phase35 later and merged here cleanly, together with main up to e4cd14d (31 more commits, including Abort failed runtime downloads after cleanup #26269). Main's new RPC methods typecheck with declared permissions, and the runtime-required catalog needed no further change.
CI note
The base is temporarily
main. AgainstOrcaWin/phase35, the changed-lines React Doctor gate counts every merged main commit as new code. It reports 38 findings, all in main-owned files (mostlycloud/), and the failed preflight then skips every test job. Run againstorigin/main, the same gate is clean. This head already containsmain, so CI tests the same tree that phase35 fast-forwards to.Verification
pnpm install --frozen-lockfile: clean, no lockfile change neededpnpm tc: passoxlinton every file the merge changed,check:code-quality:changedagainstorigin/mainandorigin/OrcaWin/phase35: 0 findingsaudit:anti-slop,check:max-lines-ratchet,check:runtime-electron-ratchet,check:reliability-gates: passsrc/main/runtime/rpc,src/cli,src/main/orcad,src/main/startup,src/main/ssh, the terminal-pane and renderer runtime suites, plus the runtime-boundary, session-scanner smoke and orcad slot script tests: 1727 files, 17328 tests passed. The React Doctor changed-lines gate againstorigin/mainis clean.shell-readytests fail under the bun project on this Mac, and fail the same way on a pre-sync tree, so that is the local environment, not this merge.