mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 09:07:40 +00:00
d371e2d8b6bb3ba4f4ae4f3db2b4af4e752d73cc
1345
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
d371e2d8b6 | ci: reduce runner occupancy in release and contract jobs (#28930) | ||
|
|
6837a4c180 |
fix(ssh): a relay-era desktop can join the server another desktop on the same login runs (#28886)
* fix(ssh): a relay-era desktop can join the server another desktop on the same login runs A desktop that still had relay-era projects on a host whose managed server another Orca version ran could never connect: its migration skipped the running-version adoption, so it deferred on a terminal count only a paired server can give. Once the versions matched it failed instead because the server already held the same repository path, added by the other desktop. The migration now pairs with the running server like a fresh desktop does, before any upload. Projects the server already holds at the same host path are linked to the server's project instead of imported twice: the journal is re-cut without them, under the same migration id, only after the server proves it holds nothing of the earlier manifest. A conflict the server will keep refusing now says what conflicts and how to get past it. * fix(ssh): never link a project a bridged relay terminal still runs in The bridge reattaches a relay pane only into its own tab, which a linked project never sends to the server, so linking would leave that terminal running out of sight. Refuse the link instead, naming the project and how to end the terminals. * test(ssh): run the link test on Node's SQLite runtime and read the host's activation record * test(ssh): establish desktop B's profile before seeding its relay-era state * test(ssh): connect desktop B's host explicitly in the shared-login spec * fix(ssh): say what a linked project left behind, and never blame it for an unowned relay terminal A project this desktop moved that the host's server already held is linked to the server's copy, so its tabs and automations from this desktop stay behind. The connect now carries those linked projects and the SSH host card says so, with the automation count, instead of dropping them silently. A bridged relay terminal of unknown or removed project still blocks a link, but its guidance no longer names the linked project as where it runs. Both terminal messages now offer removing the project from this desktop as the way out. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: m4air <m4air@Mac.localdomain> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
f5c50642de | refactor(runtime): check terminal creation and splitting (#27708) | ||
|
|
1bdbc23648 | refactor(runtime): check session graphs and tab lifecycle (#27694) | ||
|
|
7cf09809f6 |
fix(mobile): group by Project with the desktop sidebar's own code (#28850)
* fix(mobile): phone server rows follow the desktop's other-client and sleeping filters A phone created through a desktop now records whom it acted for, so the desktop's Hide other-client workspaces filter keeps the user's own phone workspaces (locally and on servers) and hides only other people's. The desktop relay marks each server row its sidebar would hide, and the rows its own browser or chat tabs keep awake; the phone applies both. * test(e2e): dedupe sidebar row ids in the visibility spec * fix(mobile): address review: owner naming, old-server relayed phones, scope trim * refactor: move the sidebar's project section grouping into shared The project sections the desktop sidebar draws under Group by Project are built from the project/setup projection, the per-host catalog fold, the endpoint host stamp, and the section labelling and ordering rules. Move the pure parts of each into src/shared (no behaviour change) so the paired phone can call the same code instead of guessing the grouping. * fix(mobile): group by Project with the desktop sidebar's own sections The phone grouped Project sections by repo display name, so it merged checkouts the desktop keeps apart (several local stablyai/orca checkouts, or a server's copy) and split what the desktop merges. The phone now reads each host's repo.list, project.list and projectHostSetup.list (the desktop's own and each server's through the relay), folds them with the renderer's shared catalog fold and projection merge, and builds sections with the desktop's shared grouping, labelling and ordering code. Keys, labels and Recent order equal the sidebar's; a host that predates the project lists gets the repo-derived projection, as the desktop does. Header colour and icon come from the section's anchor repo and rows from their own repo by id, as on the desktop. The repo filter lists every catalog repo by id per host instead of merging same-named repos. Group-by labels read Project and None. project.list and projectHostSetup.list are routed for paired phones (read-only, workspace permission). * refactor(mobile): record a phone's owner as creator and fold desktop tab activity into the host flag A phone now records its owner as the creator (this host for a directly paired phone, the relaying desktop for a delegated one), so the shared rule is unchanged and onBehalfOf is gone. The relay ORs the desktop's saved browser/chat tabs into hasHostSidebarActivity, so the phone's status dot follows too, and local worktree.ps counts the same signals. The old-server fallback is removed. * test(e2e): poll the phone and the sidebar together in the visibility spec * fix(mobile): one Project-section builder for desktop and phone, with the desktop's repo order Review fixes. The sidebar's buildOrderedGroups now calls the shared buildProjectSections for Group by Project (notice and pending repos are optional inputs), so desktop and phone run one function. The phone ranks manual header order by its folded catalog order, which is each host's repo.list order with the desktop's own repos first, as the renderer loads them; only the desktop's cross-host manualRepoOrder (pairing-local) is unknown to it. The projection selector is now a memo over the shared resolver. A transient project-list failure keeps that host's last catalog; only forbidden or method_not_found falls back to the repo-derived projection. Catalog rows are checked on every member grouping reads, and the parity test answers the renderer's real catalog fetch, includes a server's own project rows, and fixes Manual and Recent orders that differ. * fix(mobile): use the repo-derived catalog until a host's project lists first answer * fix(mobile): the desktop serves its sidebar's Project catalog instead of the phone fetching it The phone fetched repo.list, project.list and projectHostSetup.list from the desktop and every relay-ready server each minute and folded them itself, so rows from offline or older servers fell back to repo sections and keys moved on cold start. The desktop renderer now publishes the inputs its own Project headers use (state.repos in sidebar order, the projection selector's projects and setups, projectOrderBy) on the runtime window graph it already sends to main, only when they change. Main keeps the last one and answers it on a new paired-desktop read, mobileRelay.projectSections. The phone reads it with its repo metadata and groups with the shared builder; a desktop that predates the read (forbidden or method_not_found) leaves the phone grouping rows by repo from the desktop's repo.list, and a transient failure keeps the last catalog. The payload carries only grouping and label fields, no hooks, credentials or uploaded icons. The phone's own catalog fetching and its project.list/projectHostSetup.list routes are removed. * fix(mobile): keep uploaded repo icons on the phone, and isolate the snapshot tests * test(e2e): the desktop window serves its sidebar's Project catalog to the phone * chore(i18n): regenerate the runtime English catalog (drift already on main) * fix(mobile): read the Project catalog as its own request, outside the recorded repo-metadata flow The extra read inside the repo-metadata refresh stalled its recorded RPC sequence. It now runs beside it with its own throttle. The repo-metadata goldens are re-recorded for the intended state change: the name-keyed colour, icon and id maps became desktopRepos. * fix(mobile): fetch the Project catalog at once when the screen moves to another host The refresh window is now kept per client, so a host screen reused for another desktop doesn't wait out the previous host's 60s window before showing its Project headers. |
||
|
|
8df0f67c82 | ci: skip redundant PR platform warmers (#28917) | ||
|
|
d1bb7fec80 |
[CI] Reduce repeated test imports and fixture overhead (#28627)
Co-authored-by: Claude <claude@anthropic.com> |
||
|
|
e2fa611916 |
feat(native-chat): chat scratch folders expire after 30 days unused (#28394)
* fix(files): never write through a dangling symlink A path that doesn't exist yet was authorized by walking up to the nearest existing folder. A dangling symlink fails realpath with ENOENT, so the walk stepped over it and approved the link's own path; the write then followed the link and created its target outside the allowed folders. The walk now continues from the link's target, and returns that real destination. * refactor(native-chat): rename the chat visuals folder to the chat scratch folder The per-chat folder agents write into is the chat's scratch space, not only a visuals folder. Its root becomes <state>/chat-scratch/<hash> (the hash is unchanged) and the agent variable becomes ORCA_CHAT_SCRATCH_DIR. The old ORCA_CHAT_VISUALS_DIR is never set and is still stripped from what every agent child inherits, Pi included. The visuals skill names the new variable and says the folder is temporary. * test(native-chat): keep the delivery test to the rename The launch parity snapshot already pins that every agent drops the pre-rename variable. * feat(native-chat): chat scratch folders expire after 30 days unused A chat's scratch folder was removed only when its record was gone or its local workspace was proven deleted, so the folder of a long-idle chat lived forever. The sweep now expires a chat's folder once its newest journal row and the newest entry anywhere in the folder (complete, no-follow scan) are older than 30 days, unless the host holds a running agent, a delivery or an open dispatch for the chat. The final recheck and a rename to <hash>.expired run in the chat's session lane and then a per-folder lock that Orca scratch saves (desktop IPC and host RPC) also take; the delete runs outside both. A save that settles after the age scan marks that expiry stale. Leftover .expired folders and the pre-rename native-chat-visuals root are removed. The workspace-removed rule and its catalog verdict are deleted. * fix(files): refuse any dangling symlink in the missing-path walk Following a dangling link by hand resolved relative targets against the lexical parent, not the link's real folder: through a folder link the walk could loop forever or authorize the wrong file. Refuse instead: realpath can't say where a dangling link leads, but a write would follow it. * test(files): drop the loop case that duplicated the dangling-link refusal * refactor(native-chat): trim the scratch expiry and test the real live-work veto - Remove the other-profile catalog options (running profile id, never-written profiles) that only the deleted workspace verdict used; history cleanup's reader is unchanged. - The sweep keeps its original remove-and-warn block; an unused chat's folder is parked by one function. Drop the unused `now` option and the candidate identity check. - The live-agent case runs against the host's real runWhileExecutionIdle. * style(native-chat): keep the session host within its line limit after the main sync * fix(native-chat): every scratch save and every overlapping sweep share the folder lock Binary (base64) runtime saves now take the scratch folder lock like text saves, and each folder keeps a set of expiry candidates so one sweep finishing no longer hides a save from another. |
||
|
|
f23fa8ca76 |
Prepare Windows natives with scoped compiler settings and measure servicing (#28881)
* Scope Windows native compiler setup and measure ARM inbox preparation * Add a controlled Windows native preparation comparison * Test native preparation through an injected process runner * Guard reviewed Windows native batch runners |
||
|
|
3b11b7ac94 |
fix(mobile): phone server rows follow the desktop's other-client and sleeping filters (#28819)
* fix(mobile): phone server rows follow the desktop's other-client and sleeping filters A phone created through a desktop now records whom it acted for, so the desktop's Hide other-client workspaces filter keeps the user's own phone workspaces (locally and on servers) and hides only other people's. The desktop relay marks each server row its sidebar would hide, and the rows its own browser or chat tabs keep awake; the phone applies both. * test(e2e): dedupe sidebar row ids in the visibility spec * fix(mobile): address review: owner naming, old-server relayed phones, scope trim * refactor(mobile): record a phone's owner as creator and fold desktop tab activity into the host flag A phone now records its owner as the creator (this host for a directly paired phone, the relaying desktop for a delegated one), so the shared rule is unchanged and onBehalfOf is gone. The relay ORs the desktop's saved browser/chat tabs into hasHostSidebarActivity, so the phone's status dot follows too, and local worktree.ps counts the same signals. The old-server fallback is removed. * test(e2e): poll the phone and the sidebar together in the visibility spec |
||
|
|
222b780276 | Reuse real launcher builds and guard unused runtime RPC imports (#28905) | ||
|
|
0905ad4139 |
feat(native-chat): copy chat selections when Copy on Select is on (#28737)
* feat(native-chat): copy chat selections when Copy on Select is on The terminal's Copy on Select setting now also copies text selected in a native chat transcript, at the point the Add to chat offer already treats a selection as finished. It writes the same text right-click Copy does, skips the composer and other fields, and leaves the setting key as is. * fix(i18n): translate the Copy on Select description and search keywords Settings and Settings search showed English to es, fr, ja, ko and zh users after the description moved to new keys. Extend the old terminal-only translations to cover chat, and translate the new highlight and chat search keywords. |
||
|
|
6d519f7835 |
fix(mobile): keep content clear of the side insets in landscape (#28749)
In landscape the iPhone sensor housing and Android's side nav bar are left/right insets, which no screen or over-the-air page padded for. Fixes #21190 |
||
|
|
39815a086f |
Reduce CI import, frame-capture, teardown, and shard costs (#28879)
* Avoid unused module imports and trim cosmetic test coverage * Capture hidden popup frames and attach CDP before guest creation * Overlap relay test projects within the existing worker budget * Consolidate native preparation and add an opt-in transform cache comparison * Release inherited debugger sockets during final E2E teardown * Refresh unit scheduling weights from all five qualified ARM shards * Avoid retaining unasserted RPC call history in the delivery model * Use the supported unconditional background wait syntax * Overlap unconditional output upload with native preparation * Verify relay integration stays enabled for ordinary unit callers |
||
|
|
232f6048e7 | Remove obsolete Hermes cron documentation and translations (#28877) | ||
|
|
6607c54984 |
perf(ci): cut redundant tests and reuse expensive setup (#28669)
* perf(ci): reduce test overhead and reuse expensive build work * test(ci): keep worker startup outside failure-case deadlines * test: isolate push fixture advisory locks * test(ci): preserve fixed drain retry allowance * Align CI contracts with current triggers and guide content * Stabilize consolidated reveal scenarios and retain behavioral CI contracts * Fix upstream lint blockers while retaining behavior and assertions * Classify global Jira preferences in the WSL migration inventory * Align Jira test fixtures with startup and build safety checks * Include package-test routing dependency in CI checkouts and fixtures * Keep native stream classes intact in transcript dependency tests |
||
|
|
850e71e672 |
Resume in New CLI for OpenCode and OMP chats (#28075)
* feat(chat): Resume in New CLI for OpenCode and OMP chats OpenCode's and OMP's CLIs have no copy flag, but their chat protocol copies a conversation (ACP session/fork). For a fork of one of their chats, the chat's host now has a short-lived agent, launched exactly as the chat's own, copy the conversation; the terminal then opens the copy with a plain resume. The chat keeps its own conversation, so the CLI is never a second writer on it. The copy travels on the existing aiVault.prepareSessionResume fork request as an optional forkedSessionId; a host that predates it answers without one, and the client asks for an Orca update instead of resuming the original. * fix(chat): open an OpenCode 2.x chat's CLI copy with opencode, not opencode2 The copy kept the row's agent, so Resume in New CLI ran `opencode2 --standalone --session <copy>`, which OpenCode 2.x doesn't install. The chat's host now answers the copy with the agent whose CLI opens it: the chat's own. * fix(chat): type the copied conversation's agent as a history agent * fix: preserve independent chat copies through CLI launch * test: keep only the tests that pin user-visible behavior * fix(chat): validate OpenCode copies on their execution host * test(chat): expect copied OMP forks after main integration |
||
|
|
7244bf3e6f |
Reclaim idle worktree space automatically on dev startup (#28864)
* Run safe worktree cleanup on dev and add interactive pnpm reclaim * Rename storage cleanup command to reclaim-space * Keep real stream constructors in transcript dependency tests * Detach cleanup output and align historical cache previews |
||
|
|
ebd4f00453 |
Skip reindexing unchanged transcripts on cold session list reads (#28798)
* Skip reindexing unchanged transcripts on cold session list reads A cold session-list read re-parsed every self-contained transcript and rewrote its search rows, even when nothing had changed. That wasted work and churned the index on every list. - When a transcript's stat (mtime, size, inode, device) matches a complete index entry, the list read now skips the rewrite. Agents with sidecar metadata, unknown sizes, or partial or stale entries still reread. - Test fixtures that replay a "changed" transcript now pass a new mtime so they still exercise the replace path. - New tests cover unchanged skips, changed size/mtime/file identity, sidecar refresh, due/failed repair, and append. - Dependent-transcript and Codex title tests check that cold rereads keep search and title data current. * Make the self-contained transcript check exhaustive over every agent - Replace the hand-maintained `Set` of self-contained agents with a `Record<AiVaultAgent, boolean>`, so adding an agent type fails typecheck until someone decides whether its transcript is self-contained. - Classification is unchanged: the agents that were in the set are `true`, and the rest are `false` with a short note on the sidecar or metadata source they depend on. - Make the replace-generation tests derive the second mtime from the first candidate instead of a hard-coded timestamp, so they stay valid if the default fixture mtime changes. * Guard file-stat reuse with a self-contained transcript test Indexing skips reparsing unchanged transcripts for agents marked self-contained. That is only safe if their parsers read nothing but the transcript itself, and nothing enforced it. A parser that started reading a sidecar or dependency file could serve stale index entries. - Export SELF_CONTAINED_TRANSCRIPT_AGENTS so the test checks the real list - Add a test that parses each self-contained agent's fixture, both direct and cached, and fails if any file other than the transcript is read - Register the test in the SQLite runtime-file include list * Require stored file identity before skipping a transcript rewrite - Rows with a NULL dev or ino were treated as covered, so a file replaced with the same size and mtime could be skipped and never reindexed. - Such rows now count as uncovered, so the rewrite runs and records the identity. - The identity is read through the shared `fileIdentity` helper instead of the raw file fields. * Add regression test for session rows indexed without file identity A session row indexed before file identity (dev/ino) was recorded was never reread once the stat began supplying one, so it could keep stale search content. This test covers that case: the first read with identity replaces the row, and a second read with the same identity leaves it alone. |
||
|
|
cc03142b9c |
feat(wsl-mover): background writers step aside for a project a WSL move is taking (D1b-5c-3) (#28835)
* feat(wsl-mover): background writers back off a project a WSL move is taking (D1b-5c-3) * fix(wsl-mover): re-check before renames, quiet owned automations, stable folder ids (D1b-5c-3) * test(wsl-mover): seed journals one phase at a time and advance existing moves (D1b-5c-3) * fix(wsl-mover): a branch rename that already ran settles even when the move skips the folder (D1b-5c-3) |
||
|
|
8f83f6b076 |
feat(wsl-mover): pause a moving project's terminals through Sleep and prove they exited (D1b-5d) (#28820)
* feat(wsl-mover): pause a moving project's terminals through Sleep and prove they exited (D1b-5d) * fix(wsl-mover): gate again under the fence, swap journal phases atomically, keep sessions frozen through commit (D1b-5d) * test(wsl-mover): the fence e2e walks the journal one phase at a time, as a move writes it (D1b-5d) |
||
|
|
d09605ee8e |
ci: run the Electron browser probes serially, out of the unit shards (#28799)
The three popup compatibility probes ran in the parallel unit shards and timed out at their 14s in-process deadline when other files starved the runner. Move them into the package job's serial Electron lifecycle step, and stop the six probes already in that step from also running in the shards. Claude-Session: p2-headless |
||
|
|
b0308f24ef |
feat(wsl-mover): refuse edits to a project a WSL move is taking (D1b-5c-2) (#28761)
* feat(wsl-mover): refuse edits to a project a WSL move is taking, and drop its background bookkeeping quietly (D1b-5c-2) * fix(wsl-mover): hide only local rows of a moved project; the host's own row keeps its id (D1b-5c-2) * fix(wsl-mover): refuse a user's unread toggle, fence branch deletes, host setups and issue commands; settings unfreeze once the host owns a repo (D1b-5c-2) * test(wsl-mover): the fence e2e refuses edits to moving projects and drops their bookkeeping quietly (D1b-5c-2) * refactor(wsl-mover): name the update type the fence inspects (D1b-5c-2) * test(wsl-mover): run the fence tests that open a real store in the Node runtime (D1b-5c-2) * fix(wsl-mover): a host setup's own repo stays frozen once the host owns it; e2e pins the quiet drop (D1b-5c-2) |
||
|
|
8644ade7fc |
Remove Hermes automation support (#28519)
* Remove external cron history from the automation UI * Simplify the automation editor * Remove Hermes cron discovery and management |
||
|
|
52d9dd2fd7 |
Keep CI failure excerpts bounded and strip malformed ANSI controls linearly (#28671)
* Bound and sanitize CI failure excerpts with linear ANSI stripping * Fix excerpt regression checks and reduce bounded marker overhead * Require signal proof for benchmark timeouts and load consumer fixtures portably * Validate benchmark timeout framing and run required cases with a pinned CI baseline * Strip dense generic ANSI escapes with bounded forward scanning * Use the standard UTF8 encoder for byte-cap test oracles * Gate CI log performance suites behind a dedicated job The wall-clock performance suites run about 1k child workloads up to 63 MiB, so they were sharing runners with unit tests and could fail on noise. They now run only in their own `check_log_performance` job, opted in with `ORCA_CHECK_LOG_PERF=1`. - Remove the pinned-baseline checkout and `ORCA_CHECK_LOG_BASELINE_DIR`. The baseline comparison is now a manual step that reads the pinned ref from local Git history. - Run the current-implementation checks with `compareBaseline: false`, which skips baseline builds and audits. * Inline check-log fixture and drop dedicated perf CI job - Replace the captured failure-log JSON with a synthetic log built by `createCheckLogFixture()`, so no scraped job output or provenance metadata is kept in the repo. - Remove the `check_log_performance` CI job. The log performance suites now run only when `ORCA_CHECK_LOG_PERF=1` is set, as a manual benchmark. - Update the benchmark scripts and tests to use the new fixture helper and drop the `fixturePath` parameter. |
||
|
|
3e25fe70f6 |
fix(unread): a pane is read when it is on screen; closing a waiting tab clears its dot (#28637)
* test(unread): characterize today's workspace and pane unread transitions Pins, against the real store, what every unread trigger does today: the workspace flag per host row, the three tab/pane marker maps, persistence calls and payloads, lastActivityAt and sortEpoch bumps, the Dock count and the floating launcher dot. Events are replayed through one driver file so a later rewiring of who writes the state changes only the driver, never these expectations. * refactor(unread): one workspace-attention module owns every unread transition Every write to the workspace unread flag and the tab/pane/completion markers now goes through src/renderer/src/store/slices/workspace-attention: store operations named by the triggering event (bell, agent attention, pane input, viewed-agent auto-ack, manual mark read/unread) and pure transitions folded into other slices' set() calls (activation, tab activation, close, sleep, hibernate, retire, rekey, reconcile, mirror removal, purge, acknowledge). Behaviour, storage, readers, persistence and wire format are unchanged; the characterization suites pass without edits to their expectations. * refactor(unread): review fixes for the workspace-attention module - Ratchets: moved terminal-tab-attention baseline entries to terminal-tab-appearance; split the characterization setup/observables into an exempt fixture so the driver holds only events. - Removed the test-only writer seam; mocked-store tests assert the event they fire. - The slice owns the experimental pane gate for completions too; groupAttentionEnabled is gone from the attention request and write. - Collapsed marker removals to removeTabsAttention / removePanesAttention / removeCompletionAttention / removeTabMarkers / movePaneAttention; tab activation uses omitRecordKeys. - Mark read/unread is passed in like other store actions; readers of the workspace dot go through isWorkspaceUnread. * feat(unread): on screen means read; the workspace flag follows its markers While Orca's window is visible and focused, the tabs on screen (active tab of the active workspace, plus an open floating panel) are read: every tab, pane and completion marker of each. One scan (the auto-ack hook, now startViewedAttentionScan) replaces the keydown, pointerdown, tab activation, group focus, workspace activation and auto-ack marker clears. After a read, close, sleep, hibernate, retire or purge that removed a marker, a workspace's flag is cleared once no tab or completion marker remains on a live tab; a marker-less flag (Mark as unread, restart, another client) is read when its workspace comes on screen. Mark as read removes every marker in the workspace. Reads and removals dismiss the affected panes' OS notifications in one IPC call. * fix(unread): a bell or completion already on screen leaves no marker or flag write Marking now asks the scan's own on-screen rule (isTabOnScreen: the window is visible and focused and the tab is a scan target), so an on-screen bell no longer persists the flag true then false. A completion in any split pane of the tab on screen is viewed, matching the read. A superseded turn's completion-marker removal settles the flag and dismisses like other removals. * fix(unread): read the screen across hosts, groups and non-agent views; mark only for an absent user - Arriving at a workspace in its own view settles its flag even when it shows an editor, browser or nothing; Settings and other views read nothing. - A bell or completion on screen skips marking only when the user is confirmed present under the scan's own presence gate (input, or main reporting the desktop active within the shared away threshold); otherwise it marks and the gated scan reads it when the user returns. - The active workspace's host is part of the screen, so switching same-id folder rows between hosts is an arrival on the new host. - Removal settles lower every same-id folder row and persist each host. - Opening and closing the floating panel both rescan. - Every visible tab group's active tab is on screen, not only the focused one. - One pane-key parser, one marker-tab enumeration, one removal wrapper (settleAttentionRemovedBy), one remove-and-dismiss; the hook is now useReadOnScreenAttention over on-screen-attention-scan.ts, and terminal keystrokes reach presence through a capture-phase window keydown. * fix(unread): presence only from renderer input; host-exact folder flags; subjectless completions follow the screen - Main's 'not away' answers only the scan that asked; presence is stamped only by trusted input (wheel included), so it can't outlive the threshold. - isWorkspaceViewed uses the on-screen rule (workspace view showing, focused window, user present), so a subjectless completion marks under Settings or while away. - Folder flag writes resolve the row's host like updateFolderWorkspace does (project-group fallback), so the row flipped and the owner persisted match. - A runtime host purge dismisses but doesn't settle flags of surviving rows. - DESKTOP_AWAY_SECONDS is the shared threshold's neutral name. * test(unread): folder flag persists name the row's host * test(unread): drop casts from the folder persistence seeds * fix(unread): point the reliability gate and e2e fixture at the renamed on-screen hook * refactor(unread): drop reshapes the follow-up deletes; keep the flag functions module-internal Reverts the reconcile/mirror/activation/focus/group call sites and the isWorkspaceUnread selector to main, removes removeTabMarkers, removeCompletionAttention and the activation read helpers, and drives tests through public attention actions. * test(unread): read the Dock badge flag down by putting its tab on screen * fix(unread): keep the on-input read action removed after merging main |
||
|
|
81ed8c0feb |
feat(runtime-files): the host serves a chat's scratch folder as a file root (#28397)
* fix(files): never write through a dangling symlink A path that doesn't exist yet was authorized by walking up to the nearest existing folder. A dangling symlink fails realpath with ENOENT, so the walk stepped over it and approved the link's own path; the write then followed the link and created its target outside the allowed folders. The walk now continues from the link's target, and returns that real destination. * refactor(native-chat): rename the chat visuals folder to the chat scratch folder The per-chat folder agents write into is the chat's scratch space, not only a visuals folder. Its root becomes <state>/chat-scratch/<hash> (the hash is unchanged) and the agent variable becomes ORCA_CHAT_SCRATCH_DIR. The old ORCA_CHAT_VISUALS_DIR is never set and is still stripped from what every agent child inherits, Pi included. The visuals skill names the new variable and says the folder is temporary. * test(native-chat): keep the delivery test to the rename The launch parity snapshot already pins that every agent drops the pre-rename variable. * fix(files): refuse any dangling symlink in the missing-path walk Following a dangling link by hand resolved relative targets against the lexical parent, not the link's real folder: through a folder link the walk could loop forever or authorize the wrong file. Refuse instead: realpath can't say where a dangling link leads, but a write would follow it. * feat(runtime-files): the host serves a chat's scratch folder as a file root File commands accept `path:<this host's chat-scratch root>/<32 hex>` and resolve it only when it names this host's own folder, at the file-commands seam so the selector never reaches chat placement. They authorize against the project roots plus the chat-scratch root, refuse files.open/openDiff for the folder, and agentSession.readVisual's ok results carry the file's absolute path so a client can build the selector. * test(runtime-files): name the readVisual helper's parameter type * test(files): drop the loop case that duplicated the dangling-link refusal * test(runtime-files): run the readVisual RPC test, which opens a real SQLite store, under Node * test(runtime-files): put the same-suffix folder inside the authorized root so only the selector refuses it * test(runtime-files): wait for the stores to close before removing the state folder |
||
|
|
cc0217fe96 |
test(ci): bound the Windows OEM process-read test's cold PowerShell start (#28564)
* test(ci): give the Windows OEM process-read test the PowerShell cold-start watchdog The runner's first Windows PowerShell 5.1 start has reached 48 s on packaged CI; the test's 30 s cap failed package (windows) 7 times in ~1,000 runs. Match the 75 s watchdog nsis-process-check already uses for the same cold start. Co-Authored-By: Claude <noreply@anthropic.com> * test(ci): keep the OEM marker alive past a cold read, and bound the PowerShell read A blocking spawnSync can't be interrupted by the test timeout, and the marker child exited at 30 s, so a 48 s cold read could miss it. The reader now has its own 60 s bound. Co-Authored-By: Claude <noreply@anthropic.com> * fix(bench): keep idle-CPU cleanup running when the final process read fails Co-Authored-By: Claude <noreply@anthropic.com> * fix(bench): kill the app tree when the final process read fails Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
dd4d0fa51f |
Repair Jira integration fixtures and verification contracts (#28743)
## ELI5 Keep the test suite working with the Jira features that have just landed. There is no visible app change. ## What Changed The host-picker test now expects the host-qualified selection the parent already accepts. The startup fixture provides Electron's protocol handler and verifies that ready installs the Jira image scheme. Jira edit tests use exported wrappers with module-local setters that are cleared on unmount, preserving the issue/source switch tests without the banned mutable function export. The WSL storage inventory classifies Jira list columns, linked-view choice and prompt templates as device preferences, matching their keys and the existing Linear policy. Skill-guide tests check the actual generated discovery projection rather than assuming it is shorter than every full guide or uses one fixed phrase. The cache test follows the headless workflow's deliberate removal of its push trigger; cache-warmer checks remain intact. ## Why Final CI found six stale integration assumptions after the Jira batch. Updating the fixtures and inventory preserves the landed behavior and the underlying checks. Changing production code, padding the Jira guide, or restoring an intentionally removed workflow trigger would change the product to satisfy incorrect test assumptions. ## Linked Issue Follow-up to #28622, #28631 and #28592. The existing headless trigger policy was introduced in #28664. ## Visual Proof N/A — tests, test fixtures and storage inventory declarations only; no visual or interaction change. ## Testing - [x] Automated tests updated. - [x] 196 focused tests across nine files pass on macOS, covering every failed shard assertion, fixture consumers and the headless workflow policy. - [x] Node and web typechecks pass; all seven changed-code quality stages, formatting and diff checks pass. - Reproduced five failing assertions in startup/export/storage tests and the skill projection failure before the repairs. The host-picker owner also reproduced the stale selection failure before correcting it. - Final full CI will run on this PR. The prior Ubuntu 22.04 persistence job crashed in the native watcher build before persistence tests ran; this change does not claim a Linux release, live Jira or real SSH pass. ## Review Independent source review found no issues; all six reviewed file hashes and the complete diff match committed head `aa703eb50147ba95646cb82f10b6d86b4f890fc4`. Risk: low. No authentication, network, protocol implementation, workflow, renderer behavior, remote messages or migration rewrite logic changes. ## Agent skill upstream boundary - [x] Not applicable; no upstream skill-installer source or artifacts copied. ## Notes The three storage keys contain device-level preferences, without workspace paths or identifiers. Tests preserve host disambiguation and issue/source switching. Platform, SSH and provider behavior remains in the existing implementations. ## Checklist - [x] Small and focused. - [x] Explained the before/after, mechanism and choice of approach. - [x] Visual proof marked N/A with a reason. - [x] Self-reviewed for correctness and cleanup. - [x] Cross-platform and SSH impact considered. - [x] Focused tests, types, formatting and quality checks pass; full CI covers the broader build. |
||
|
|
a0c46b405f |
feat(jira): load attachment images outside issue Markdown (#28631)
Load Jira attachment images separately from issue Markdown so descriptions and comments stay small while screenshots load lazily and open in the existing lightbox. Downloads begin when an image mounts, capture its owning host, require negotiated image support, and use bounded authenticated byte streams and caches. Older clients retain their inline-image handlers. Keep legacy and cancellable downloads in separate in-flight domains so cancelling a new image reader cannot abort a legacy reader; completed bytes share the bounded cache. Preserve the landed assignee, board, transition/comment queue and request lifetimes. Move unchanged project metadata IPC handlers into their domain module and tighten two inherited focus-check ceilings without changing behavior. Validation:410 tests/54 suites; all changed-source quality stages, lint, format, routing and contract checks; exact final node/web types; independent source review;48/48 fresh hidden Electron checks with226 stable source hashes and nine inspected screenshots. Synthetic transports exercise production clients and UI. Current CI static checks, mobile bundle, wire compatibility, Linux/Windows packaging and Windows serve pass; five test shards were pending at review. Extends the existing attachment work credited to Frank (@lifefloating) and Brennan Benson in PR #8938; existing contribution credits and media remain in this PR. |
||
|
|
7ac2dcc7d4 |
feat(cli): let agents read and update Jira tickets safely (#28592)
Expose host-owned verified Jira reads and single-attempt writes through the CLI. Preserve site ambiguity checks, old-host capability refusal, atomic label changes, and uncertainty after failed writes. Refs #11986 and #26055. Co-authored-by: Dan Cieslak <dcieslak19973@users.noreply.github.com> Co-authored-by: kokbee <kok2209@gmail.com> |
||
|
|
f31aada5b4 |
Require a unique full PR gate before merging (#28632)
* Require exact-commit PR checks before agent merges * Give full PR checks a unique required status context * Validate required context ownership on every pull request |
||
|
|
d3b17ca9f4 |
fix(runtime): save a windowless host's chat tabs in its layout (#28354)
* fix(runtime): save a windowless host's chat tabs in its layout Claude-Session: p2-headless * fix(runtime): remove a chat's saved tab on the index's hidden notice, which every close reaches Claude-Session: p2-headless * fix(runtime): a chat whose record is gone loses its saved tab at startup Claude-Session: p2-headless * test: run the windowless chat-layout host test in the Node runtime project Claude-Session: p2-headless * fix(runtime): remove a chat's saved tab from whichever partition still holds it Claude-Session: p2-headless * refactor(runtime): list saved chat tabs across partitions in the layout module Claude-Session: p2-headless * refactor(runtime): read saved chat tabs straight from the store's partitions Claude-Session: p2-headless * fix(chat-tabs): save a windowless chat tab under its own tab id Review pass: the saved tab reuses the id status and attention address, the close checks for a window before scanning partitions, and the host tests seed readable records with unreadable, other-host and no-index cases. Claude-Session: p2-headless * test(chat-tabs): a window opening a windowless-saved chat reuses its tab Claude-Session: p2-headless * test(chat-tabs): check the saved tab with the lookup a window reuses The window's existing-tab lookup moves to the shared projection so the windowless-layout test exercises it without importing the renderer. Claude-Session: p2-headless * test(chat-tabs): assert the reused tab's identity directly Drops the shared lookup added for the test; two functions already carry that name. Claude-Session: p2-headless * fix(chat-tabs): leave an unstamped chat tab in an SSH partition to that host Claude-Session: p2-headless * fix(chat-tabs): count an unstamped chat tab as this host's only in the local partition A desktop mirrors a remote server's chats into that server's runtime partition without a host stamp; the startup sweep must not delete them. Claude-Session: p2-headless * refactor(chat-tabs): one chat tab writer, one startup match, local partition only Chats run only on local hosts, so their saved tabs live in the local partition: the cross-partition listing and its writer go. Open and close share one layout load, the startup fill and sweep become one function beside the index seed, and the tab filter is the existing savedSessionId. Claude-Session: p2-headless * fix(chat-tabs): restore and sweep saved Claude chat tabs like any structured chat The Claude skip predates Claude structured chats (#18519, #18560); an id the host has no record of is already dropped by the readable restorer. Claude-Session: p2-headless |
||
|
|
078f8f314f | refactor: check terminal output methods and organize modules (#27606) | ||
|
|
f894e257e9 | ci: stop daily and post-merge headless-server checks (#28664) | ||
|
|
9c72b3c30f | refactor: check headless session tab methods (#27584) | ||
|
|
b84d371a9f | refactor(runtime): compose typed browser command owners (#27576) | ||
|
|
230b39c279 | refactor(runtime): organize file search and terminal artifacts by domain (#27571) | ||
|
|
ae8842306b |
chore(lint): flag Enter handling on raw text fields that skips the IME guard (#28609)
Adds an oxlint plugin rule that reports Enter-key handlers on raw input, textarea, and contentEditable elements (and keydown listeners on text-field receivers) unless the handler reaches the shared IME guard. Also routes the automation run history window listener through isImeOwnedKeyboardEvent, whose previous nativeEvent check never fired for native events. |
||
|
|
76d1c68ab5 |
Resume in New Native Chat for OpenCode (#28072)
* feat(chat): Resume in New Native Chat for OpenCode terminal conversations An OpenCode conversation started in the terminal now offers "Resume in New Native Chat" in its Session History row menu and its tab's right-click menu, as Claude, Codex, Grok and OMP do: a new OpenCode chat takes it over, showing its history and continuing it. OpenCode 2.x rows (`opencode2`) and 1.x rows both open an OpenCode chat. Two pieces kept OpenCode off the shared adoption path. Its account is a locator, not a folder, so the transcript import now works over the account the chat pins, whatever its kind; the folder agents wrap their lookups in directoryTranscriptImport, unchanged. Its conversations live in a database, so the journal import reads the found database with the reader the OpenCode transcript view already uses. OpenCode refuses to reopen a session from another folder, so the workspace rule covers it too. * test(chat): Pi is now the agent with no transcript import * fix(chat): find an OpenCode 2.x terminal tab's Session History row The pane reports `opencode` while Session History lists the conversation as `opencode2`, so the pane header's and tab menu's Resume in New Native Chat found no row. Match by the chat agent that takes the conversation over. * Fix OpenCode chat adoption environment and pane eligibility * test(chat): adapt parent adoption fixtures to account identities * test: keep only the tests that pin user-visible behavior * refactor(chat): map a Session History row to its chat agent where the chat starts Both Resume in New Native Chat callers repeated the row-to-chat agent lookup. The launch now takes the row agent and maps it once, so the Session History actions file stays unchanged. |
||
|
|
f2df6a706d |
fix(orcad): stamp the build commit so orca skills install works on SSH and headless hosts (#28558)
* fix(orcad): stamp the build commit so orca skills install works on SSH and headless hosts #27603 pins 'orca skills install|update' to the commit the CLI was built from, read from the CLI's out/package.json. The desktop CLI build stamps it, but the server bundle (the 'orca' in SSH-host and 'orca serve' terminals) wrote that file without a commit, so the command always failed there with 'Could not read the commit this Orca CLI was built from'. Stamp it the same way. * test: verify built orcad CLI skill source in artifact preflight |
||
|
|
b83a6f74be |
Finish agent-selection cleanup and restore merge validation (#28607)
* Retire newly translated None option labels * Reserve required verify check for the complete PR gate * Preserve headless default tab styling and stabilize import ordering test * Restore the option mutation clock after crossing fixes * Check both supported workflow extensions |
||
|
|
d8458f14f5 |
Fix browser login popups and menu actions in native windows (#28474)
* fix(browser): preserve unnamed OAuth login popups * fix(browser): preserve form and blank popup sessions * Preserve native request context for Shift popup navigation * Validate route popups with production policy attachment order * Fix browser background modifier request continuation * Route opener-less OAuth popups to Orca tabs and fix menu targets in nati - Plain links and Cmd/Ctrl+Shift-clicks open OAuth URLs as Orca tabs instead of native windows. Those popups have no opener, so they lose nothing by reloading the URL. - Scripted popups that have an opener keep the existing origin-bar window, so their window.opener link still works. - Discard pending popup contents with a navigation veto before closing, so a link's first request cannot reach the server. - A POST with no page to hand over is still blocked, because reloading it by URL would drop its body. - Edit, Help, and paste menu actions now target the main Orca window when a native browser popup has focus. The popup has no Orca renderer to receive those IPC messages. * Cover opener-less OAuth popups and popup request provenance in tests - Add Electron tests showing opener-less OAuth links open as local tabs without loading the discarded page, and that modifier-opened POSTs are never replayed. - Add a test that a remote foreground modifier click keeps the native cross-site request context (cookies, referrer, fetch site). - Add a test that the first adopted link request is blocked when route preparation fails during popup creation. - Launch the CDP fixture through resolveElectronProbeLaunch so Linux runs use the display-aware launcher instead of a hardcoded xvfb-run. - Replace `as` casts in the popup tests with typed declarations. * Route Edit menu actions to native browser popups Copy, Paste, and Select All from the app menu did nothing when a native browser popup was focused. The popup has no Orca renderer to receive the IPC, so the action was dropped. Those actions now go to the focused web contents, falling back to the popup's own contents, so they reach the page the user is in. Also removes the popup request-provenance Electron test, which is no longer part of this change. * Fix static analysis failures in browser popup handling * Handle unavailable permission probe in browser popup fixture * Admit script-opened native popups and enforce minimum size Modifier-clicked `window.open` children from page scripts reach Electron after the opener's first task, so the one-shot admission lapsed and they were left unprepared. Their first request then lacked popup context, and the origin title could be hidden by a hostile page title. - Keep a per-opener pending admission that answers the next native child the opener reports. Unadmitted native children are discarded rather than left as bare windows. - Route popup children through the contents whose window-open handler answered, since Electron reports script-opened children to that contents. - Create native popups content-sized with a 360x200 minimum, matching origin-bar popups, so `resizeTo` cannot shrink the origin title out of view. The limits move to `popup-window-minimum-size.ts` so both paths share them. - Tests: cover scripted admission timing, unadmitted discard, minimum size, and route popup caps. Fixture waits for hidden iframe loads and polls with a longer timeout for CI. * Prime hidden Linux OOPIF hit testing for all opaque popups The popup compatibility test clicked a frame gesture target only for the "route" case when the input was opaque. Hidden Linux OOPIFs need that priming click before hit testing works, and the route's root gesture gate depends on it too. Other opaque cases were skipping it and could fail for the same reason. Apply the priming click to every opaque case and correct the comment to describe the real reason. |
||
|
|
0504670961 |
Fix CLI delivery and fresh launch environments for managed SSH agents (#26464)
* fix: refresh host environments before structured agent command resolution * fix: ship the owning server CLI for managed remote agents * test: include the shipped CLI in remote install fixtures * Bind packaged agent CLI calls to their execution host * Keep owning-server CLI binding scoped to managed agents * Serve the last shell environment to chat starts and refresh it in the background Only the first chat start waits for the login shell; later starts get the last snapshot and refresh it in the background once it is 10 s old. On Windows hosts without the registry addon (managed server slots), the saved PATH is reread with reg.exe. The Windows server CLI launcher stager now checks the target architecture, and the CLI help says ambient selectors are ignored inside a managed server. * Keep the last good shell environment when a refresh fails, and load the Windows profile once A login-shell capture now says whether the shell produced the env or Orca fell back to its own, and only a real capture replaces the snapshot. Windows loads the PowerShell profile once per process and refreshes only the saved PATH. The reg.exe fallback queries the whole key so a missing user Path no longer drops the saved PATH. The first capture starts when the resolvers are created. * Keep the last good login-shell env for other readers while a forced refresh runs * Mark the Claude launch resolver as a reviewed router-aware module It only types the chat command resolver it injects; main's launch-routing guard (#26801) matched the name. * Keep a settled login-shell env for readers during a refresh and share the first run - Any settled cached entry, a fallback included, now serves other readers while a forced capture runs; only a real capture replaces it. - Native chat's first capture shares a run a startup reader already began, so the login shell runs once per process at startup on macOS/Linux. - Type the injected Claude resolver without naming resolveClaudeCommand. - Cover the runtime's Claude command options, the owning-host --host check, and the PTY flag drop. * Drop an inherited owning-host flag from daemon terminal panes The persistent terminal daemon merges its own environment into every pane, so an inherited ORCA_CLI_OWNING_HOST reached desktop panes even though main strips it from the spawn env. Drop it with the other per-launch CLI variables. * Route agents to their own server through ordinary launch defaults; start the first chat after an install The native-chat agent environment now sets the owning server's ORCA_USER_DATA_PATH and empty ORCA_ENVIRONMENT / ORCA_PAIRING_CODE / ORCA_REMOTE_PAIRING, which the CLI already reads as unset; empty rather than deleted, because the spawn merges the launch overlay over the server's own env. The ORCA_CLI_OWNING_HOST flag, the CLI's skip of ambient selectors, the launchers' export of it, the terminal clean-up, the help-text exception and the WSL launcher guard are gone, so a person's terminal follows their own settings as on main. A launch that finds no program in the shell snapshot waits for one shared refresh (at most one shell timeout) and resolves again; create support rereads the shell once before refusing. On Windows a failed PowerShell profile load is retried on the next refresh instead of being kept until restart. * One shared shell reread when an agent's program is missing The pre-create check for OpenCode, OMP and Pi forced its own login-shell capture on every refusal: not shared with launches, one per concurrent check, triggered by an installed-but-too-old version too, and an older capture finishing last could overwrite a newer cached environment. On Windows, every launch miss also reran a failed PowerShell profile load, bypassing the 10-second limit. - One saved shell environment per host: chat launches, their misses, the pre-create check and AI commit messages read it and share one refresh. - The pre-create check resolves the agent's program the way a launch does, so only a missing program waits for the refresh; registrations declare how to resolve their launch and check its version. - A forced login-shell capture joins one already running for the same shell, so captures cannot land out of order. - On Windows, a miss within 10 s of a failed PowerShell load rereads only the saved PATH. * One owner for the cached shell environment Review found two more races in the reread-on-miss machinery, both from the host wrapper keeping its own snapshot beside the login-shell cache: - A launch that found no program could join a reread that started before the agent was installed, and spend its one retry on the old PATH. - On Windows, saved-PATH rereads renewed the snapshot's age, so a failed PowerShell profile was never retried while launches kept missing. The login-shell cache is now the only owner of the captured environment, the run in progress and which result wins (the newest run started; a fallback never replaces a captured env). It takes three options instead of `force`: - startedAfter: a lookup that found nothing waits for a run that started no earlier than the lookup did, joining one if it has, else starting one. - refreshAfterMs: serve the last result and rerun in the background once the last attempt is older. - keepCaptured: a captured env is kept for good; only a fallback is rerun (Windows' PowerShell profile). The host wrapper keeps only policy: the 10 s interval, on Windows the saved PATH laid over the profile env on every read (a miss forces a fresh registry read), and the settings overlays. Its snapshot, pending promise, staleness and Windows "loaded" bookkeeping are gone. A lookup's start time travels with the miss (resolveAgainAfterRefreshOnMissingProgram passes it). * Read an injected shell environment once per runtime, as main does The previous commit read an injected environment on every acquisition, which broke main's 'caches shell exports but re-reads configured overrides for a resume' contract. It is read once when the resolvers are made again. * Model lists reread the environment on a missing program, as launches do Live QA: the first new chat after installing Claude showed "Claude wasn't found on the computer running this chat". The chat launch itself reread the login shell and started the new binary, but Claude's model-list check ran first, with the environment read before the install, and never retried: catalog probes were deliberately built without the miss refresh. The retry now lives in each agent's one shared lookup of its binary and environment, which a launch and a model listing both call: resolveCodexStructuredInvocation, resolveAcpLaunchInvocation, a new shared resolvePiRpcInvocation, and resolveClaudeChildEnvSources (which already had it). Each catalog probe is given the runtime's refreshAfterMissingProgram, and the catalog context no longer strips it, so a listing and a launch reread the same single-owner environment the same way. * Pass optional launch lookups through directly instead of conditional spreads * Read the host shell env through resolveLoginShellEnvironment; drop the extra wait cap CI on the merge with main failed main's new codex-workspace-relative-path test (#26684), which mocks the login-shell module with only resolveLoginShellEnvironment: the wrapper called captureLoginShellEnvironment and imported the spawn-timeout constant. The wrapper only needs the env, so it reads through resolveLoginShellEnvironment again, as on main. The miss wait no longer races its own 5 s timer: the shell run it waits for is already killed at the spawn timeout, so the second cap was a guard over the same bound. The constant goes back to private, as on main. * Test that a hung login shell holds a launch miss for at most one shell timeout The bound lives in the one owner: the shell run is killed at the spawn timeout (5 s) and settles to a fallback, which never replaces a captured snapshot. A mutation raising that timeout fails this test. * A program found through a relative PATH entry is not missing Main's #26684 runs Codex through a workspace-relative PATH entry (`bin`, `node_modules/.bin`). The lookup searches such an entry against Orca's own directory and returns the bare name; the spawn then finds the program in the chat's directory. Counting that bare name as missing made every such launch wait for a login-shell reread. The retry now fires only when the lookup truly found nothing: a bare name back from a PATH whose entries are all absolute. Each agent's one lookup tells it which PATH it searched. * Count a relative PATH entry as found only when it holds the program; Windows miss waits for a saved PATH read started after it looked A bare name with a relative PATH entry is checked from the launch's working directory, so an agent in neither entry still gets its one reread. On Windows the miss no longer joins a registry read that began before the lookup. |
||
|
|
126009d4c5 |
Skip Windows security addon steps when the builder is missing (#28546)
Older requested refs predate the security addon builder, so the desktop template CI job failed trying to run a script that isn't there. Gate the build, keep, and collect steps on that script existing in the checkout. Add a test that pins the gating for both cases. Also drop an unused getAppEnvironment import in orcad-entry.ts. |
||
|
|
e8cd6dcb18 |
chore(i18n): translate 295 new keys to es/fr/ja/ko/zh (#28486)
Scan 2026-10-13: fill target-locale coverage for PRs merged since the 2026-10-07 scan. Adds the missing keys (es 295, fr 294, ja 295, ko 271, zh 283), fixes stale/mistranslated existing values, and leaves the 11 pinned provider-credit-balance keys plus 3 pinned Chat UI keys as English fallback by design. Also commits the apply-translation-delta helper referenced by the scan tracker. |
||
|
|
df88f890c8 |
Avoid repeated mobile recording checks and slow download assertions (#28468)
* Avoid redundant mobile recording comparisons and byte array assertions * Use four Bun workers for the mobile test group * Avoid repeated bridge decoding and costly binary fixture comparisons * Resolve route closure entry paths against their source tree |
||
|
|
04b4e962a7 |
fix: a Claude model that's gone switches to the list's default when you send (#27826)
* fix: start a new Claude chat on the listed default when its remembered model is gone The remembered new-chat model was launched as-is even after Claude stopped offering it, so every new chat failed its first message. The host create and the desktop new-chat picker now derive the same replacement from the host's saved model list: the listed default, effort moved to the nearest level it offers. Explicit models (--model, held picks) run as given; with no saved list the remembered model is kept. * fix: read the host's model list only for a Claude create that needs it The check read host.deps before ruling out other agents and explicit models, so creates whose host carries no deps (and every non-Claude create) paid for a lookup they never use. * fix: picker derives the fallback only where the host would A held model runs as picked on the host, so the picker no longer derives an effort beside it. A cached list whose default marker was dropped for this workspace keeps the placeholder instead of naming its first row. * fix: the new-chat picker picks the replacement for a gone Claude model The host-side check read the saved list separately from the picker, so the two disagreed and a cold start never fired it. The desktop new-chat picker now picks the replacement it shows, once, through the existing pick path, so the first send runs it and the remembered selection heals. The host is back to main. * fix: decide a gone Claude model's replacement at send, from the list in hand The open-time auto-pick raced the chat's create and the list's arrival, so the first send could still launch the gone model. Now, like the common pattern, the picker shows the replacement for a model the host's loaded list lacks, and the send step applies it through the existing pick call, awaited, right before the message goes out. A failed pick never holds the send back. * fix: one verified list decides a gone model's replacement The picker, the send step and the option setter now all read the list the picker shows, less the row a session adds for its own unlisted model, so what is shown is what applies and an explicit pick from that list stays. A listing that is still running, failed, or absent keeps the model. The send step re-derives from the chat's state when it runs, so a second send behind one that already moved the model applies nothing. * fix: only rows a finished host listing names can replace a gone model A single verified flag outlived the catalog it described: live options built from the built-in fallback list, or a malformed or empty host answer, still authorized a replacement the host would refuse. The state now keeps the ids a finished, well-formed host listing names, and a replacement must be both shown in the picker and named there. * fix(native-chat): the send carries the composer's Claude model and effort Every composer send (desktop and phone) carries the model and effort the picker shows, as optional agentSession.send options, only to a host that advertises agent-session.send-options.v1. The host writes a carried value that differs from the chat's own through the existing pick path before accepting the message, so the turn starts on it and the chat records it. A queued card keeps its options and applies them when it goes out; a launch's create call carries the replacement for its first message. Replaces the separate awaited pick before each send. * chore: drop an unrelated lockfile change * refactor(native-chat): one helper names a send's control fields for client and host * ci: rerun against current main * fix(native-chat): a message runs on the model and effort it was sent with The carried selection is now stored with the message (its submission row, and a queued card's row) and applied through the shared option writer as the delivery loop hands the message over, or before the start it needs. A pick made after the message was accepted no longer changes the model it runs on. Direct sends, queue drains and Send now share that one apply point; the early apply before acceptance and the queue-only apply are gone. The picker and the send read one effective selection: the replacement for a model the host's finished listing lacks shows only for a host that takes a send's selection, the phone validates picks against what it shows, a send carries the default the composer shows, a resumed launch's first message gets the same resolution, and only the listing proves a row (no label heuristic). * docs(native-chat): a submission's options are the host's alone * fix(native-chat): an opening message keeps its model; a pending pick resolves like the picker The opening message a create sends now stores the model and effort the chat was created with, so a pick made before its handover no longer changes what it runs on, and a card kept after a restart keeps them. A pick on its way to the host resolves through the same option view the picker shows before a send carries it, so a gone model it names is replaced too; the send selection reads only the shown snapshot. The option writer builds its own record-and-publish context, so the message path no longer duplicates it, and the client and host share one function for a send's fingerprint fields. * ci: rerun against current main |
||
|
|
b9bd8dbe4a |
fix(agent-status): retire any restored agent row whose terminal died while Orca was closed (STA-10194) (#28475)
* wip * refactor(agent-status): tighten the restored-status sweep after review - reword preserveResumeIdentity to the real rule: the pane survives to resume into - one isLocalHookConnectionId predicate for the sweep, removed-worktree cleanup, and runtime PTY sources - the startup wrapper is file-private with a distinct name; probe catch simplified - stale 'liveness reap' wording in the Claude roster comment and test - tests: the Claude roster case is asserted through a later turn settling, and a hydrated resume remnant is pinned as never a candidate |
||
|
|
5f134d8f01 |
feat(mobile): show and use the desktop's server workspaces on the phone (#26501)
* test(e2e): add a paired phone client helper and phone-mirror baseline specs
Later phone-mirror slices need one oracle that drives a real mobile-scoped device with the
phone's own capabilities against a serve host or a paired desktop. Pins #26022 across a serve
restart and today's gap: a desktop's phone does not list the desktop's server workspaces.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* feat(mobile): show SSH host health on the workspace row host badge
Rows on an SSH host the desktop cannot reach looked healthy on the phone. The badge now
adds the desktop sidebar's own word ("devbox · Disconnected"), derived by the same shared
function from the connection status ssh.listTargetSummaries already sends; old desktops keep today's badge.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* test(e2e): compose the phone-mirror topology and check the cold snapshot
Slices need one composer for host, desktop and phone with a single teardown, so specs stop
copying setup. The serve-restart spec now reads the first tab-stream snapshot after the cold
start, which is what the phone builds its tab strip from.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* feat(mobile): name an unhealthy SSH host even in a single-host list
The desktop card shows SSH status on any host count, so the phone now reads target summaries
whenever a repo is on SSH and badges an unhealthy host alone too. Labels and health share one
target-to-host keying, and a hook-level test pins the reply-to-row wiring.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* test(e2e): keep phone-mirror teardown errors from masking test failures
Teardown failures become a test attachment instead of replacing the real failure, and the
cold-snapshot poll reports stream errors together with the PTY list.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(mobile): clear stale SSH host badges and announce them without the dot
A catalog that no longer spans hosts or holds an SSH repo now clears the health and label maps,
so a host that left cannot keep badging rows. The badge gets a screen-reader label ("devbox,
Disconnected") instead of reading the middle dot.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* feat(runtime): let a paired desktop register its phones as delegated mobile devices
A desktop paired to this runtime can now call pairing.delegatedMobileDevice.sync to make each of
its phones an ordinary mobile-scope device it owns, so the phone mirrors that host through the
desktop. Children are never pending, inherit the parent's reach, and die with the parent's grant.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* refactor(runtime): move the mobile device revoke primitive below dispatch
Dispatch binds the caller-bound delegated-device context, so the revoke path it needs now lives in
a layer under it as a pure move; this drops the abstract method seam. No behaviour change.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(runtime): keep a runtime grant until its delegated phones are revoked
A failed child cleanup now leaves the parent in place so a retry can still find the children, and
revokeRuntimeAccess lives beside the revoke it cascades to. Adds restart, rename and bounds coverage,
shares credential minting, and drops the timing-dependent census.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(runtime): fail a delegated sync whose dropped phone cannot be revoked
Matches the parent revoke: a child whose cleanup could not be saved is kept and the sync errors
before minting anything, so the desktop retries instead of believing the server matches its list.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* feat(mobile): relay a paired phone's server-workspace requests through the desktop
A phone mirrors its desktop, including workspaces on the desktop's configured servers. The desktop
now forwards a request that names `executionHost: runtime:<env>` to that server over one socket per
(phone, server), signed in as the phone's delegated device; replies pass through untouched except
stream ids. Hidden until the phone client ships: the relay capability is not advertised yet.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* test(mobile): type the single-host health fixtures inline so the test tsc passes
Fixtures hoisted into consts widened their health maps to string, which the now-required
hostHealthById rejects under the tests-typecheck ratchet; passing them inline keeps the union.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* docs(mobile): state the desktop relay contract in its entry module
main dropped docs/reference, so the relay's contract lives beside the code. Also pins that a
server whose delegated sync fails (e.g. delegated_device_revoke_failed) is unavailable until a
later request syncs it again.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* test(mobile): pin the relay's configured-server adapter
The production hosts adapter was only typechecked; this pins that it resolves configured servers
by exact id only (never a name or a removed server), fences by pairing revision and runtime
identity, and stops reporting retirements once unsubscribed.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* test(mobile): re-record the repo-metadata goldens for SSH host health
The settled metadata state now carries hostHealthById, and an SSH-only catalog now reads the
target summaries so a lone unhealthy host can be badged. Scoped re-record via rpc-recording.mts.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* refactor(mobile): tighten the desktop relay after elegance review
Reuses the shared E2EE socket opener, folds relay state into fewer fields, never sends a request
whose link ended while opening, passes failure codes through, relays only this desktop's own
phones, and drops the unused host-state reader until S2 needs one.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(mobile): bound relay frames and stop sticky relay verdicts
Restores the 8 MiB frame cap on every remote runtime socket, re-checks an update-needed server on
each new link so an in-place upgrade relays, answers a phone the server just declined without
syncing again, and ends a link whose mirrored capability update cannot be queued.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(mobile): run targeted desktop-owned calls on the desktop
The phone tags every call on a workspace screen with its execution host, so a
paired-desktop method carrying `runtime:<env>` now runs locally instead of
failing 'forbidden'. The Mac stays the only reader of the routing table.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* feat(mobile): list the desktop's configured servers and their last workspace rows for the phone
The phone needs the same host list the desktop shows, including servers that are offline or
too old to relay to. mobileRelay.hosts.list/worktrees answer from the desktop's own status and
its own worktree.ps fetches, cached per server and fenced by pairing, never from relayed replies.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* refactor(mobile): list server rows through one desktop fetch, with no side effect on listing
The phone now always lists a server's rows through mobileRelay.hosts.worktrees, so the
desktop's own fetch is both the live source and the cache, and stamping stays in main.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(mobile): fetch a server's full workspace list within a bound, and forget removed servers
The desktop now asks for the phone's full-catalog limit, answers within 5s with the last rows
when a server hangs, drops a server's rows when it is removed or re-paired, and checks the
server's identity after the fetch so a reply from before a re-pair is never served.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* feat(mobile): name a server workspace's host on the phone's calls
A phone reaches a server's workspace through its desktop only if each call names the host; the
direct, cloud-relay and shell-bridge transports now carry `executionHost`, streams keep it for
replay and unsubscribe, and an older shell (no `executionHost` grant) refuses rather than strips it.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(mobile): list managed servers as the desktop sidebar does, and hide rows mid-delete
A server Orca manages on an SSH host now lists once under that host's name, or not at all while
the host is reached over the SSH relay, using the sidebar's own folding. Rows the server is
deleting are left out at reply time, as a phone asking the server itself would see.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(mobile): keep a disconnected server's last rows, and never join a re-paired server's fetch
Retirement also fires on a manual disconnect or managed-server update, which wrongly emptied
the phone's list; rows are now pruned only once a server is no longer configured. Fetches are
shared per server identity, so a poll after a re-pair asks the new server right away.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* feat(mobile): list the paired desktop's server workspaces on the phone
Fixes the phone missing a desktop's `orca serve` workspaces: rows come from the desktop's
mobileRelay.hosts.* with each server's health ("Update needed", "Disconnected"), open through the
relay, follow the desktop's hidden hosts, and the desktop now advertises the relay capability.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* test(mobile): pin that a desktop failing the hosts list leaves only its own rows
The page asks a relaying desktop for its servers; a refusal such as method_not_found must leave
today's list with no server rows and no catalog error.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(mobile): mirror the desktop's shown hosts exactly, one host included
Jinwoo's call: the phone follows visibleWorkspaceHostIds as the desktop sidebar does, so a
one-host list narrows the phone too instead of being read as a single-host focus.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(mobile): scope a server workspace at its route, never the desktop
Every screen under a server workspace's route now reads one context and
gets that server's client and a router that keeps pushes on it, refusing
to fall back to the desktop; pins write to the row's owner like the Mac.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(mobile): never open an unreachable server workspace, and prove the relay in e2e
A server workspace the phone can't reach now gets one explicit screen at its route
and no row tap or action-sheet entry opens it; native routes read the route's host,
a census pins every workspace route, and the e2e reads files and git on the server.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* feat(mobile): gate a server workspace on that server's own features
A targeted status.get now relays to the server, so a server workspace's route
provides that server's status to every screen under it and its action sheet,
while whether the phone can reach the server stays the desktop's answer.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(ci): run the remote-runtime WebSocket opener test on Node
Under the bun vitest project, `ws` resolves to Bun's built-in client, which
ignores maxPayload, so the frame-cap test hung. The opener only ships on
Node/Electron, so run its test in the Node runtime project like its siblings.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(mobile): give the file preview the workspace router, back to 128 page assets
The preview was the one workspace screen on the plain router, so the scoped router
was reached by a route set of its own and esbuild split it into an extra chunk
(129 assets); every workspace screen now uses it and it shares the route's chunk.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* refactor(mobile-relay): derive the phone allowlist from the routing table
Adding a phone method now takes one edit (its route). A server-targeted call on a
legacy non-E2EE socket is refused instead of running locally, and only
execution-host methods enter the relay.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* refactor(mobile-relay): drop unread freshness fields from server workspace lists
The phone reads only the rows, so stale, fetchedAt, totalCount and truncated
were dead wire weight. The desktop still serves a server's last rows while
the server is unreachable.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* refactor(mobile): read only the rows of a server's workspace list
The desktop no longer sends a freshness flag, so the phone's schema drops it.
The relay capability list names the delegated-devices constant instead of
repeating its literal, which also retires a now-circular type check.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(mobile): show a server workspace as disconnected while its server is down
The phone read the desktop's connection state for a server workspace, so a stopped
server kept a green dot and "Loading tabs" spun forever. The workspace route now
narrows that state by the server's health from mobileRelay.hosts.list, reusing the
existing disconnected session UI and refetching tabs when the server returns.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* refactor(mobile): share one desktop server-list feed between the host list and workspaces
A server workspace polled mobileRelay.hosts.list beside the host list's own read, so
the phone could ask the desktop twice per tick and the row badge and the screen could
disagree. One feed per desktop client now owns that read; both readers join it.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* test(e2e): read a server's reachability from the hosts list, not a dropped stale flag
|