Commit Graph
1656 Commits
Author SHA1 Message Date
Kelvin Amoaba 9cbef961cc feat(mobile): make Markdown easier to read on the phone (#38870)
* feat(mobile): make Markdown easier to read on the phone

Line height 19 → 21 for body, list and quote text (desktop's 1.6 prose
leading), h1 18/25 and h2 16/23 so heading levels differ, an 8pt gap
between list items, a hairline border on inline code, and denser table
cells (8×4). Existing theme tokens only.

* fix(mobile): announce GitHub task boxes as read-only checkboxes

Screen readers now hear each task marker as a checkbox with its checked
state, disabled because the text can't be edited there.

* fix(mobile): size table columns for the denser cell padding

* fix(mobile): use ARIA props for the task box so pages announce its state

react-native-web ignores accessibilityState, so the box read as unchecked
and became a tab stop. role, aria-checked and aria-disabled reach the DOM
and native alike; focusable={false} keeps it out of the tab order.
2026-11-06 08:06:31 +00:00
OrcaWinandOrcaWin 692c81ca17 test: remove 864 low-value chat and mobile test cases (#38871)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-11-05 22:57:04 -08:00
Brennan Bensonandagustin2606 e92552a413 Keep Claude plugin UI out of chat and recover blocked replies (#37965)
Keep Claude plugin status and toast frames out of native chat, and give terminal-input requests an actionable failure with safe conversation recovery. Preserve genuine outside replies while hiding internal repair content.

Tracked in STA-13468. Built on #37480 by @agustin2606.

Co-authored-by: agustin2606 <agustinvarela2606@gmail.com>
2026-11-05 22:34:16 -08:00
Jinwoo Hong a57c19aa91 Resolve workspace lineage parents in one shared module (#38835)
Inert refactor: move the same-store parent rule and merged resolver into src/shared/worktree/workspace-lineage-graph.ts and add one row identity rule. No behaviour change.
2026-11-06 00:49:05 -05:00
OrcaWinandOrcaWin a4cad3766b test: remove 1296 low-value mock and projection tests (#38837)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-11-05 21:39:35 -08:00
Kelvin Amoaba 4f19002962 feat(mobile): render GitHub alerts, task boxes, inline tags and images in Markdown (#38809)
* feat(mobile): render GitHub alerts, task boxes, inline tags, images and footnotes

GitHub text on the phone now draws what desktop and github.com do:
> [!NOTE]-style alerts (desktop's marker rule, shared), read-only task
boxes, <sub>/<sup>/<kbd>/<ins> and <b>/<i>/<del>/<code> inline tags, https
<img> and Markdown images at their authored width, raised footnote numbers,
and #123 links in the PR view into the PR's own repository.

* fix(mobile): keep linked badges and commented images, read only pixel widths

An <a href> around inline content becomes a link, so a badge keeps its
target; images inside links, comments, icons by height, and percentage
widths stay as before. Footnote numbers follow the text scale.

* fix(mobile): keep images inside headings and styles in place, read link hrefs as HTML does

Only images that a p, div, picture or center places are cut out of HTML,
in one pass; an image in a heading, link or bold stays in its element.
Inline <a> hrefs use the shared attribute reader (quotes and entities),
<code> keeps only the text of tags inside it, and alert titles follow the
text scale.
2026-11-06 05:12:28 +00:00
Kelvin Amoaba a4e24a9e5a feat(mobile): show automatic continue and Switch Account on chat usage-limit notices (#38298)
* feat(mobile): usage-limit card with the host's automatic continue, Continue and Switch Account

* refactor(mobile): give the notice disclosure its own file to fit the usage-limit status

* fix(mobile): drop the cancel failure once the host shows the cut cancelled

* fix(mobile): keep the usage-limit status up while a later re-ask is pending

A re-ask at a new journal cursor no longer blanks the reset line and pills;
the cut keeps its last answer. Don't continue reports its disabled state.
2026-11-06 05:12:00 +00:00
Brennan Bensonandzhangxaochen a38fdb7601 Preserve phone terminal streams and prepare detail cards together (#38798)
* Preserve phone terminal streams and prepare detail cards together

Co-authored-by: zhangxaochen <3416677+zhangxaochen@users.noreply.github.com>

* Animate phone detail cards through the browser compositor

Preserve interruption and completion presentation while keeping native motion unchanged. Honor live reduced motion without restarting opacity.

Co-authored-by: zhangxaochen <3416677+zhangxaochen@users.noreply.github.com>

* Cover phone context card concurrent placement

* Keep phone animation test fixture access typed

* Fix mobile card regression fixture types

* Use named types in mobile card regression fixture

---------

Co-authored-by: zhangxaochen <3416677+zhangxaochen@users.noreply.github.com>
2026-11-05 21:09:47 -08:00
Brennan Benson 6acdb0535c fix(github-project): use content values for built-in grouping and sorting (#38818)
* fix(github-project): derive built-in field display and ordering from content

* fix(i18n): register linked PR summary overflow fallback

* fix: satisfy project projection static checks

* test(github-project): cover normalized phone metadata and update field contracts

* fix(github-project): narrow optional grouping values explicitly

* test(github-project): place fixture safety rationale at assertion start

* test(github-project): isolate Node and native regression boundaries
2026-11-05 21:09:14 -08:00
Kelvin Amoaba 24ed22e16f fix(mobile): close and reopen issues on older servers when their lookup proves the repository (#38829) 2026-11-06 05:02:24 +00:00
Brennan Benson 45a468894d Show proposed files and collapsible details in Codex approvals (#37960)
Show Codex file-change proposals in approval cards and keep verbose operation details collapsed by default on desktop and phone, with reason, access and answer choices visible.

Preserve approval identity and answer behavior, keyboard focus, and scroll position when details expand or collapse.
2026-11-05 20:32:50 -08:00
92e1608971 fix(mobile): unify diagnostics navigation (#37904)
* fix(mobile): unify diagnostics navigation

* fix(mobile): reuse settings bar and bottom insets

Address Orca PR #37904 reporter feedback in comment 6620705890.

* fix(mobile): match diagnostics content spacing to Settings screens

Reuse settingsScreenStyles for the container and content padding, and drop
the leftover bottom spacer now that the bottom inset pads the scroll.

---------

Co-authored-by: terminalchai <213856599+terminalchai@users.noreply.github.com>
Co-authored-by: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com>
2026-11-06 04:17:36 +00:00
Brennan Benson b53db37643 Refuse unsafe branch switches and share one check across phone and desktop (#38744)
* fix(git): refuse unsafe branch switches in an existing workspace

The phone's Switch branch ran `git checkout` with no checks: uncommitted
files moved to the other branch, and a switch could land under a working
agent or mid-rebase. One host check now covers every client: it refuses,
with a plain reason, when files are uncommitted, a merge/rebase/
cherry-pick/revert is in progress, HEAD is detached or unborn, another
git action is running, or an agent in the checkout is working or waiting
for approval. Checks and the checkout run under a per-checkout lock.

After a switch the host clears the old branch's review links and push
target and tells every client, so a phone-only host stays correct, and
records that the user chose the branch so first-work naming never
renames it. The open Commits list follows the new branch.

* fix(git): review round 1 repairs for the guarded branch switch

- check out through the provider so the user's switch keeps the interactive git queue
- a landed switch that Windows left files behind on now tells the user instead of reporting success
- never let a Windows yes/no prompt hold the switch lock
- refuse submodule edits that submodule ignore config hides from status
- record the switch on the target host's metadata row and never fail a landed switch on bookkeeping
- read workspace paths from their ids instead of scanning the catalog on every switch
- admit agent rows like the sidebar roster (listed tab, or a live terminal by pane, tab or handle)
- move the host-side switch hooks into their own module

* test: type the new switch test fakes instead of casting them

* fix(git): second repair round for the guarded branch switch

- report files Windows left behind from git's own unlink warnings, not a status re-read, so
  ordinary branch differences and submodule commit changes are never called failures
- the phone refreshes its branch and files after a switch that landed with a warning
- refuse only edits inside a submodule its ignore config hides, never a commit mismatch alone,
  reading status without optional locks in the interactive queue
- read the agent process probe when a switch runs, not while the runtime is constructed
- check the agent's checkout path before terminal liveness and build the tab list once per switch
- the runtime wiring test no longer reaches real git

* fix(git): scope the switch's agent check to the checkout and keep landed-switch outcomes honest

- find candidate agents by checkout path first; build terminal liveness once, only from this
  checkout's terminals and its workspaces' saved tabs; probe candidates together and re-read
  the snapshot once; no work for unrelated workspaces (contract test at N and 10N)
- a checkout that fails after HEAD moved is recorded as landed; files left behind are named only
  when git reports it could not replace them, otherwise git's own error is shown
- parse git's unlink warnings for paths with apostrophes
- the phone keeps the refusal or warning visible while it reloads the branch
- cover a submodule that is only at another commit

* fix(git): name git's own error when a switch lands after a failing hook; use the shared unlink parser

* fix(git): keep the switch's agent check flat for unrelated folder workspaces

- read folder workspace directories once per switch instead of a linear search per row and terminal
- re-read each candidate's current status by pane key instead of rebuilding the whole snapshot
- skip the hidden-submodule status pass in repos without submodules
- the cost contract covers folder workspaces, many in-checkout agents and PTY passes
- a real-git test covers a switch that lands before a failing post-checkout hook

* fix(git): never quote git's progress line as a landed switch's error; fixed clock in the cost test

* fix(git): decide the hidden-submodule check from the index, not a visible .gitmodules

* perf(git): read only index modes for the submodule shortcut so large repos stay under the output cap
2026-11-05 20:16:50 -08:00
OrcaWinandOrcaWin 8f0ccf0016 perf(mobile): derive state instead of syncing it in effects (#38796)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-11-05 19:29:04 -08:00
Kelvin Amoaba c8c620cd21 feat(mobile): rename a workspace from the workspace menu (#38728)
* feat(mobile): rename a workspace from the workspace menu

The phone's workspace menu gets a Rename row that swaps the drawer to the
text sheet, prefilled and selected. Saving writes worktree.set displayName at
the row's owner, so server rows work too; an empty name returns the label to
its automatic branch or folder name, as desktop's Workspace details does. A
failed save keeps the sheet and text, shows the host's error and offers Retry.

* refactor: share the automatic workspace name rule with the phone's rename sheet
2026-11-06 01:52:38 +00:00
Kelvin Amoaba 6bd548ba5c feat(mobile): render math in chat and PR descriptions, with GitHub's line-count table (#38682)
* feat(mobile): render math in Markdown with desktop chat's delimiter rules

Move the price, citation, closer and display rules plus the KaTeX settings
to src/shared/markdown-math-rules and point desktop chat at them. Mobile's
markdown-it gets the same grammar, and pages typeset formulas with KaTeX,
whose fonts ship as page assets. Mermaid's engine leaves katex external so
both share one katex chunk.

* build(mobile): count KaTeX fonts in the page budget and keep math to one chunk

Only the licensed terminal font keeps its own allowance; KaTeX's fonts
count toward the budget like code. The typesetter imports katex itself,
sharing mermaid's chunk, so math adds one chunk instead of four.

* fix(mobile): draw the PR line-count table's +/− glyphs at GitHub's size

github.com draws \Huge at \Large (1.44em), so the line-count table's
glyphs sit inline at text height. Cap the larger size commands the same
way, and let inline math take its height from KaTeX's struts so rows keep
normal height and numbers stay on the text baseline.

* test(mobile): pin math delimiters to desktop chat's results

* refactor(mobile): drop type assertions from the math tree nodes

* chore(mobile): register the math mdast nodes beside <details>

* fix(mobile): read bracket math only in chat, keep math parsing linear

GitHub text (PRs, Tasks, files) reads $ and $$ only, so \[Bug\] stays a
bracket; chat rows opt into desktop chat's \( and \[ too. Remember failed
block scans and skip dollar-run tails so hostile input stays linear, leave
unfinished math out of remend like desktop, bound the formula cache by bytes,
retry a failed KaTeX load, and count symbol commands in table widths.

* fix(mobile): copy formulas as TeX and keep GitHub's unclosed $$ as text

Share desktop's selection-to-TeX reader so a copied selection carries each
formula once, as its source. An unclosed $$ in GitHub text stays a
paragraph, chat's math delimiters reach HTML-derived Markdown and summaries,
failed block scans only skip openers inside the lines they read, unfinished
math anywhere in the streaming tail skips remend, and table widths no
longer spread every command into Math.max.

* refactor(mobile): share the formula copy handler and cap sizes only in GitHub text

Desktop and mobile copy through one shared handler. GitHub surfaces draw
\Huge as github.com does while chat keeps desktop chat's sizes, PR and file
text skip bracket scanning entirely, and the dollar-run scan and token
alias live in one place.

* chore(mobile): stop tracking the generated KaTeX stylesheet

#38611 committed it by mistake; postinstall writes it and .gitignore now
lists it.

* fix(mobile): follow desktop chat's new dollar rule through the shared module

#38651 changed desktop chat's inline $ rule (shell variables and prices stay
text, a single $ can span lines, $5+2$ is math). Move those checks into
src/shared/markdown-math-rules, point desktop at them, and give mobile's
markdown-it rule the same behaviour.
2026-11-06 00:53:54 +00:00
Jinwoo Hong a9ba59c944 Nest workspaces under parents on other machines (#38740)
* feat(lineage): store cross-store parent links beside same-store lineage (inert)

A workspace's parent can live in another Orca store. Save that link in the
child's store under a new top-level key, crossStoreLineageByChildKey, so older
builds' same-store cleanup never reads it. Nothing writes records yet.

- Types, load normalization (malformed entries dropped one by one), section label.
- Store get/set/remove by child; a child keeps at most one parent record.
- Every maintenance path acts by the child only: deletion and missing-worktree
  pruning, repo removal and deregistered-repo residue, identity migration,
  profile project transfer, WSL move projection and UNC drive conversion, and
  the orcad migration dormant payload (export, validation, import, subtraction).

* fix(lineage): gate cross-store links on host capability; one parent per child

- Migration stage/commit carrying crossStoreLineage requires the new
  orcad.migration-cross-store-lineage.v1 capability; older hosts are refused
  before staging instead of silently dropping the links. Empty arrays are omitted.
- Payload validation and destination prepare reject a child that has both a
  cross-store and a same-store parent, through one shared conflict check.
- Saved entries stay on disk as written; the map is Record<string, unknown> and
  readers go through validCrossStoreLineage. Re-keying moves unreadable entries unchanged.
- Tests: literal expectations; drop touched mock-call and work-count assertions.

* test(lineage): drop clone and save work-count assertions from project group removal test

* feat(lineage): resolve cross-store parents into the sidebar parent map (inert)

- One resolver: a saved record nests its child under the single visible
  row whose store reports the parent's installationId and whose instance
  matches; copied installs, unknown stores and stale instances stay standalone.
- getSidebarLineageParentsByIdentity merges resolved cross-store parents with
  same-store edges before cycle breaking; a valid same-store edge wins.
- Workspace delete collects descendants only from the parent's own store.
- Store identity: paired stores from their verified status hostDescriptor;
  this store from the published descriptor, else host-installation.json.
- Nothing passes a view or writes records yet, so the sidebar is unchanged.

* fix(lineage): import WorkspaceKey from workspace-scope after main move

* fix(lineage): resolve sidebar parents once over complete rows; keep unowned rows' delete rule

- resolveSidebarLineageParentIdentities resolves same-store edges and cross-store
  records once over every visible store's unfiltered rows, then drops cycles.
  Ancestor injection and nesting reuse that map, so a filtered-out twin still
  makes a parent ambiguous.
- Removed, retired or refusing paired servers leave the view: their records stay
  saved and their children standalone. Offline servers keep their last-known id.
- A row with no owner is unknown, not ours: it never resolves, and delete only
  excludes descendants whose store is known and differs from the parent's.

* fix(lineage): refuse UNC rekey collisions and transfers giving a child two parents

The UNC drive conversion rekeys cross-store child keys through the shared
collision merge, so a UNC key and its drive key with different records throw
instead of one silently overwriting the other. Profile project transfer runs the
shared child-key conflict check before writing the target.

* feat(lineage): write, carry and nest cross-machine parents

- Capability worktree.lineage.crossMachine on the child's store; the CLI
  refuses before any request when the server lacks it.
- worktree.set / worktree.create accept crossStoreParent
  { installationId, workspaceKey, instanceId } and write only
  crossStoreLineageByChildKey (refused when it names this store, or the
  child has no identity). Remove Parent clears both.
- worktree.crossStoreParentRef: the parent's own store names it with its
  installationId; reparent refuses a parent whose chain already reaches
  another store. Same-store Set Parent refuses the same.
- worktree.ps rows and lineage lists carry the records; same-store lineage
  fields stay null so older readers show the child standalone.
- The desktop sidebar resolves parents over its own and paired stores'
  records (PR 2's resolver) at the visible-worktree and row-plan call sites.
- CLI: active/current or an inferred terminal parent against a server is
  sent as a cross-store parent.

* fix(lineage): read worktree rows through the record selector; mark the test runtime cast

* fix(lineage): hydrate every store, fence refreshes, honest CLI fallback

- Each host's catalog hydration fetches its lineage snapshot, so this
  desktop's identity and records load whichever store is focused.
- Cross-store records join the per-child request-start fence: a stale
  lineage list can't restore a removed record or override a newer one.
- A failed inferred cross-store parent sends noParent instead of
  forwarding the selector a server could match to a same-store twin.
- Inferred cross-store creates record env-workspace/inferred capture.
- The writer states its trust contract; the chain rule runs at the mint.

* feat(lineage): cross-machine parents on the phone, pickers, chat resume and labels

- Phone: the list nests a child under a parent on another machine, from the
  records rows already carry and the store identities its host verified
  (status.get descriptor; relayed servers' installationId on the host list).
- Set Parent popover, context menu and the composer's parent picker offer
  parents in other visible stores. The parent's own store names it
  (worktree.crossStoreParentRef); the window checks it against that store's
  verified installationId and the child store's capability before any write.
  Remove Parent and Open parent cover cross-machine links.
- Native chat resume ancestors follow resolved cross-machine parents.
- Host sections keep a nested child under its parent and keep its own
  machine label.
- Main sidebar feed resolves cross-store parents (rows carry records; desktop
  facts and paired-host facts carry identities); paired web windows read
  their host's installationId; reveal without an occurrence plan resolves
  them; `worktree create --agent` names a parent on another machine.

* test(lineage): type the mint test's parent row through unknown

* test(lineage): give the cross-machine tests' partial-row casts their SAFETY reasons

* fix(cli): worktree set names a parent on another machine

* feat(lineage): name the machine of parent candidates on another machine

* fix(lineage): Set Parent and Remove Parent act on the clicked row's own host

* fix(lineage): phone resolves parents over every row before the repo filter

* fix(lineage): chat resume nests by host-qualified identity across machines

* fix(lineage): local create refuses a same-install parent before checkout and shares the RPC recorder

* fix(lineage): name parent machines without a focus read; keep create's probes synchronous

* fix(lineage): chat resume keys offers by their own machine; only cross-machine parents leave a section

* fix(lineage): name an older parent server before asking it for a cross-machine ref

* fix(lineage): an app-picked parent on another machine records as manual

* fix(lineage): chat resume keeps base grouping; only resolved cross-store parents move a chat

* revert(lineage): drop chat resume from cross-machine parents; it groups by machine as before

* refactor(lineage): CLI builds cross-machine parents; explicit only

- Drop worktree.crossStoreParentRef and its reparent chain walk: the CLI
  builds the ref from this desktop's installationId (worktree.lineageList
  now reports it) and the parent row it reads. Cross-machine chains are
  allowed; same-store cycle validation and same-install refusal stay.
- No inferred cross-machine parent: remove crossStoreParentInferred and
  the fallback states. Against a server, this machine's terminal, cwd and
  env context is never sent; the CLI prints how to nest explicitly.

* refactor(lineage): build cross-machine parent refs from verified ids; pickers hide only loops; one desktop install id source

* refactor(lineage): refuse moving a linked workspace instead of carrying the link

Orcad migration and profile project transfer now refuse, before any staging or
write, when a workspace in the move has a cross-store parent record (raw entries,
readable or not). Remove the carry machinery: the dormant crossStoreLineage
field, its runtime capability and destination gate, import/subtract/claim code,
transfer carry and the two-parent conflict checker. Also drop the unused
removeCrossStoreWorkspaceLineage.

* docs(lineage): describe cross-store parent refs without the removed mint RPC

* test(lineage): drop the RPC call-list assertion from the cross-store ref test

* fix(lineage): refuse linked moves before any write and for shared-repo children

- Migration refusal passes the child's own host stamp to the scope matcher and
  refuses an unstamped key whose shared repo id the move includes, so such a
  child can't move while its link stays behind.
- Profile transfer checks the read-only source snapshot before pending-move
  recovery or storage migration; the payload check stays as the final guard.

* fix(lineage): refuse a linked profile move before the profile lookup can restore the index

* fix(lineage): startup hydration of other stores loads only cross-machine links

* fix(lineage): list the child's own machine first in the parent picker

* fix(lineage): keep profile argument errors ahead of the linked-move preflight

* fix(worktrees): let the first listing replace a restored server workspace stub

A restored active server workspace starts as a session stub with no head.
If git status fills in its branch before the first listing lands, the
listing merge took that as a concurrent branch switch and kept the stub,
dropping the catalog row (instance id, lineage, provenance). The workspace
then showed standalone until the next listing.

* fix(worktrees): accept a headless row's listing only when it confirms git status

A stale listing could otherwise roll back a branch switch git status saw on
an empty-head row (e.g. an SSH fallback row) during the first connected listing.

* feat(lineage): CLI nests a create on another machine under the caller workspace

- No parent flag and no --no-parent against a server: the caller's workspace
  on this machine (env, else cwd) is sent as an inferred cross-store parent,
  the way --parent-worktree active builds it. The host records it as
  inferred from environment or cwd.
- A server without worktree.lineage.crossMachine, or a caller workspace that
  can't be linked, still creates unnested and prints one note.
- An inferred parent falls away for folder targets, as same-machine folder
  creates record none.

* fix(lineage): CLI cross-machine inference reads the caller terminal and suppresses fallbacks

- The caller terminal's workspace joins env and cwd as an inferred parent,
  with same-machine precedence: env and terminal must agree, cwd only alone.
- A fallback (old server, unlinkable or unresolved caller, ambiguous cwd)
  prints one note and sends noParent, so the server's own create-time
  inference can't nest what the CLI reported as not nested.

* feat(sidebar): drag a workspace onto one on another machine to nest it

Dropping a workspace on a workspace from another machine now sets the
parent through the same cross-machine path as Set Parent: same eligibility
(loops, older servers), same refusal messages. Drop targets carry host
identity so the child's own copy is the one written.

* refactor(lineage): this machine's Orca resolves the caller parent for a create on another machine

- New read-only worktree.resolveCallerParent runs the same-machine create
  resolver over the caller's env, terminal and cwd context, so stale
  contexts are skipped and only validated parents can conflict, and returns
  the cross-store ref or a reason.
- The CLI's own precedence and terminal lookup are removed; it keeps the
  capability gate, one note with noParent on any fallback, and silence with
  no Orca context.

* feat(orchestration): remote worker-start --worktree new-child nests under the coordinator's workspace

worker-start --on <server> --worktree new-child creates the server's worktree
under the coordinator's workspace on this machine, using the same verified
cross-machine parent ref as the CLI's caller auto-nest. A server without
cross-machine parents still starts the worker, top-level, with a warning in
the receipt. Guide, CLI help and docs describe it.

* fix(lineage): resolveCallerParent reads this store's identity without creating it

- Uses the published host descriptor's installationId, else the existing
  host-installation.json; never creates or repairs it. Missing identity
  returns a reason, so the CLI creates unnested with its note.
- Drops the read-only wording: parent resolution does the same catalog
  upkeep a same-machine create does.

* fix(sidebar): validate every cross-machine parent before a multi-select drop writes

A drop of several workspaces builds every cross-machine parent ref first, so an
older server's refusal leaves the whole selection unchanged. Drops the touched
preview-timing test cases.

* refactor(orchestration): build the remote new-child parent ref with the shared caller resolver

Orchestration still picks the caller workspace; resolveCallerParentForAnotherStore
verifies it and builds the ref, and both read the installationId the same way.
Drops the copy and mock-call tests.

* fix(lineage): narrow the inferred source with a guard instead of casts

* fix(lineage): a composer parent picked on another machine nests normally once the destination is that machine

The retained host-qualified pick is classified against the current destination:
same host gives the ordinary parent fields, another store the cross-machine ref.
Also corrects two stale comments.

* perf(lineage): read only the requested child's cross-store entry

* perf(lineage): request each store's lineage list once at startup

* perf(lineage): cap optional caller-parent inference at 3 s and skip a timed-out local Orca

* perf(lineage): return cached sidebar parents when selector inputs are unchanged

* fix(lineage): snapshot selector inputs without a type assertion

* Route startup lineage and the sidebar lineage memo through their owners
2026-11-05 19:42:19 -05:00
OrcaWinandOrcaWin 27b1ed858c test: remove 184 low-value mobile, CLI and ACP tests (#38733)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-11-05 16:02:19 -08:00
OrcaWinandOrcaWin 5bc786c1a7 test: remove 133 low-value workflow, mobile and CLI tests (#38725)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-11-05 15:52:52 -08:00
Kelvin Amoaba 3dd9a49e93 fix(mobile): stop asking to update a current desktop when it can't be reached (#38242) 2026-11-05 23:49:21 +00:00
Kelvin Amoaba 9b2427b958 fix(mobile): say why an agent set to Chat opened as a terminal (#38696)
* fix(mobile): say why a chat launch started a terminal instead

When the host falls back to a terminal for an agent the user set to Chat
(for example Antigravity without its chat runtime installed), show the
host's reason in the launch toast instead of opening a CLI silently.

* fix(mobile): skip the fallback notice for agents that have no chat

* fix(mobile): keep the launch notice toast up long enough to read

* refactor(agent-launch): own the chat-fallback rule in the launch contract
2026-11-05 23:48:01 +00:00
Kelvin Amoaba 69892601e2 feat(mobile): rewind a chat to an earlier message (#38344)
* feat(mobile): rewind a chat to an earlier message

Bring desktop's "Rewind to here" to phone chats. The rewind flow moves
into a shared React-injected hook so desktop and mobile share one set of
rules; desktop behaviour is unchanged. The host now lets phones call
agentSession.rewind behind agent-session.rewind.phone.v1, so a phone on
an older host offers nothing it would be refused.

* feat(mobile): put the message time first, then copy and rewind together
2026-11-05 23:06:47 +00:00
Kelvin Amoaba 3cefb66c96 fix(mobile): send committed IME text to the terminal at commit time (#38699)
* fix(mobile): send committed IME text to the terminal at commit time

WebKit fires no input event after compositionend, so the live input
mirror kept holding committed Japanese text until the next key. Re-run
the mirror with composition ended when the field reports compositionend.

* docs(mobile): say compositionend is the commit signal on every engine
2026-11-05 22:54:05 +00:00
Kelvin Amoaba c884428609 feat(mobile): copy, save and share an image from the full-screen image viewer (#38303)
* feat(mobile): copy, save and share an image from the chat image viewer

* refactor(mobile): pass image viewer actions in so diagrams skip them

* fix(mobile): name the image verb native.image.act and serialize image actions

* fix(mobile): queue image actions per page and skip SVG links

* refactor(mobile): share the cache-folder helper and table the viewer's image actions

* fix(mobile): keep the toast's live region mounted so screen readers hear it

* test(mobile): re-record agent history goldens for the always-mounted toast region

* test(mobile): import the declared routes in the moved routes test
2026-11-05 22:44:36 +00:00
Kelvin Amoaba ce3fe75891 feat(mobile): set, pause, resume and clear a chat's goal from the phone (#38447)
* feat(mobile): set, pause, resume and clear a chat's goal from the phone

The phone sent /goal to the agent as plain text and showed no goal. It now
has desktop's goal mode (Goal chip, goal placeholder) and a goal strip on the
composer's top edge that expands to Pause/Resume and Clear goal.

The goal logic desktop and mobile share moves to src/shared: the goal report
(journal rows over the options answer), status labels, elapsed format, the
goal-mode pick rule and the goal row filter. agentSession.threadGoal joins the
phone allowlist behind agent-session.thread-goal.phone.v1, so older hosts keep
showing the goal without offering changes.

* fix(mobile): open each new goal's strip collapsed

* fix(mobile): keep goal sends' delivery choice and scope goal locks per chat

* fix(mobile): offer goal changes only when the paired desktop relays them

* test(mobile): pin which goal changes the phone offers per host

* refactor(mobile): reuse the queued box style and keep the goal strip stable per chunk

* fix(mobile): set phone goals through the composer's changeThreadGoal
2026-11-05 22:42:44 +00:00
Kelvin Amoaba d68dc03582 feat(mobile): Sign in from a signed-out chat, and name the offline host above the composer (#38295)
* feat(mobile): offer Sign in on a signed-out chat via a host terminal login

* feat(mobile): say which host is out in the chat composer, with Reconnect for the desktop link

* refactor(mobile): fit the host outage line beside main's chat changes
2026-11-05 22:27:44 +00:00
Kelvin Amoaba b9f988be22 feat(mobile): continue a chat that Orca's stop cut off (#38287)
* feat(mobile): continue an interrupted chat from the phone

Desktop offers Continue on a turn Orca's own stop cut off; the phone only
showed the words. The offer and failure rules move to
src/shared/native-chat/native-chat-interrupted-continue.ts, desktop uses
them unchanged, and the phone drives them from its session over
agentSession.continueInterrupted when the host advertises it.

* refactor(native-chat): type the Continue answer instead of casting it

* fix(mobile): let a phone call agentSession.continueInterrupted

The host refused the method from phones, so Continue on the phone always
failed. Allow it on the execution-host route and advertise
agent-session.continue-interrupted.phone.v1, which the phone gates on:
hosts that advertise only continue-interrupted.v1 still refuse phones.

* fix(mobile): hide Continue when an older paired desktop relays the chat

A newer page on a server routed through an older desktop would offer
Continue that the desktop refuses to forward. Gate it on the relaying
desktop too; the workspace server context moves to a react-native-free
module so session hooks can read it. Long failure text now shrinks so
the banner's dismiss stays on screen.

* refactor(mobile): reuse the chat's Try Again look and shared Continue wording

The Continue button takes the Try Again pill styles and common.continue,
the shared helper takes the RPC's answer instead of a call wrapper, and
the phone hook memoizes its result and keys late answers in one place.

* fix(i18n): keep the Continue failure copy inline so catalogs rebuild it

* refactor(mobile): keep the structured session hook under its line cap

* fix(native-chat): check the session's agent audience before continuing a turn
2026-11-05 22:08:45 +00:00
Kelvin Amoaba 78ff857c88 feat(mobile): render PR descriptions and comments with the shared Markdown renderer (#38611)
* feat(mobile): render PR bodies and comments with the shared Markdown renderer

The PR view drew PR bodies, comments and review threads with its own small
parser, so strikethrough, footnotes, task lists, entities, nested lists and
quotes, images and bare links came out raw or mangled, and nothing could be
selected. It now uses MobileMarkdown, like chat, file preview and Tasks.

MobileMarkdown gains collapsible <details> sections (closed unless authored
open), the one thing only the PR parser had. Images without alt text no
longer draw the word "image".

* fix(mobile): keep <details> parsing bounded and browser-like

Cap section nesting, keep each markup scan linear on hostile input, leave
markup inside HTML comments alone, read open as a real attribute, and close
a section whose </details> sits inside a list item with that list.

* fix(mobile): close <details> at an inline </details> and ignore <details-*> tags

A </details> at the end of a paragraph or list item ends the section with
that block, as in a browser. Custom <details-menu>-style tags no longer
open a section, and heading markup in a summary keeps only its text.

* refactor(mobile): simplify <details> parsing and reuse DisclosureChevron

Keep the close-with-container mark on the node instead of a module set,
skip HTML blocks with no details markup up front, scan inline HTML once,
and turn the summary chevron the way other disclosures do.

* chore(mobile): register the <details> mdast node in a declaration file both lint gates accept

* chore(mobile): keep the lint config plain JSON
2026-11-05 21:30:33 +00:00
Kelvin Amoaba 9055eb9344 feat(mobile): desktop chat links and code wrap on the phone (#38294)
* refactor(native-chat): move chat link rules and the file-existence checker to shared

GitHub reference linking, the reference-repo rule, chat file-path linkification,
the per-chat existence checker and the path-existence batcher are platform-free.
Move them to src/shared so the phone's chat can use the same rules; desktop keeps
its host adapters and behaves the same.

* feat(mobile): link #123 refs, check file paths and wrap code in chat

* test(mobile): pin which chat paths and refs become links

* refactor(mobile): parse repo link fields instead of casting

* refactor(mobile): own chat link state in the chat overlay

* fix(mobile): keep unchecked chat paths as precise as before

* fix(mobile): only peek at known paths while reasoning streams

* test(mobile): leave #123 splitting rules to the shared transform's tests

* refactor(native-chat): tidy shared link helpers and skip separator-free text

* test(native-chat): import the existence type from its shared home

* style(mobile): fold the code-span link handler onto one line
2026-11-05 21:24:09 +00:00
Kelvin Amoaba 7860261d18 feat(mobile): paste images and long text into the chat composer (#38591)
* feat(mobile): paste images and long text into the chat composer

* test(mobile): pin which composer pastes attach

* fix(mobile): keep a long paste as text when no upload can start

* fix(mobile): re-encode pasted images agents can't read as PNG

* test(mobile): pin composer paste routing at the composer field

* refactor(mobile): fold composer paste routing into its hook

* fix(mobile): read pasted images in the uploader so the composer needs no shell bridge

* test(mobile): stub the context ring in the composer paste test
2026-11-05 20:59:54 +00:00
Kelvin Amoaba 9b2f3105ee feat(mobile): show the context usage ring and its breakdown in the chat composer (#38371)
* feat(mobile): show the context usage ring in the chat composer

Desktop's ring reads the structured session's context facts; the phone
never read the host's contextUsage answer. The summary rules move to
src/shared so both read the same numbers, and the title card's anchored
card machinery is generalized so the ring's breakdown opens above the
composer without dropping the keyboard.

* refactor(native-chat): share the context ring's critical threshold

* test(mobile): stub the context ring where composer tests stub its siblings

* fix(mobile): keep the context card with the composer and the keyboard up

Touching the card no longer takes focus from the draft, the open card
moves with the composer as the keyboard or draft resizes it, and the
composer's own menu closes it.

* fix(mobile): close the context card when the composer hides, and let long titles wrap

* refactor(mobile): share the anchored card layer and the ring readout

* test(e2e): pass the new options arg in the permission publication test
2026-11-05 20:44:10 +00:00
Kelvin Amoaba a3f08b4075 feat(mobile): pin the agent's plan above the chat composer (#38359)
* feat(mobile): dock the chat's plan above the composer

Desktop docks the latest plan above the composer; the phone only drew it
inline in the transcript. Move desktop's docked-plan rule to src/shared and
draw it on the phone as a collapsible strip in the background-tasks box.

* perf(native-chat): keep the docked plan's identity until the plan changes

* refactor(native-chat): read the docked plan from the newest message back

* refactor(mobile): drop a cast in the docked plan's open memory

* test(mobile): pass the tray's new taskList in the recovery test
2026-11-05 20:31:10 +00:00
Kelvin Amoaba 35720d4e55 feat(mobile): keep unsent chat drafts and skill picks across an app restart (#38268)
* feat(mobile): keep unsent chat drafts across an app reload

Chat drafts and their skill picks lived only in the page document's
memory, so killing the app or an OTA reload lost them. Each
workspace's tab drafts are now saved in the page storage slot shells
since 0.0.59 already allow (left unused by #37900). A clear saves at
once so a sent draft never returns, and tabs the host stops listing
or the phone closes are dropped from storage. A restored draft that
leads with a skill loads the skill catalog so its chip shows again.

* fix(mobile): let a pre-load send win and keep skill scan retries

A send or clear made before a workspace's saved drafts finished
loading no longer gets overwritten by the saved text. A page whose
shell refuses the key stops re-reading it, a dropped write is retried
on the next save, and a failed skill scan is still retried the next
time the menu opens.

* fix(mobile): save pending chat typing when the app goes to the background

A write that fails partway no longer leaves a remembered value that
makes the next clear look already saved, and typing still inside the
250 ms window saves when the page is hidden.

* refactor(mobile): simplify chat draft persistence

Read the saved value back once per write through persistMirroredReadBack,
build the key with the allowlist's own workspaceScopedKey, keep tab-scope
parsing in one place, track the load as one state, stop holding
retirements once the load lands, and skip the per-draft size loop when
the whole value fits. Drops the test-only settle export.
2026-11-05 20:29:11 +00:00
Kelvin Amoaba 585c32da22 feat(mobile): name and link the sending agent on queued chat messages (#38309)
* feat(mobile): name and link agent senders on queued chat messages

Queued cards and the queued-message editor drew the sender from the saved snapshot name as plain text. They now share the transcript card's sender row, so a queued message names its sender's chat by its current title and taps through to it, as desktop does.

* fix(mobile): keep the queued sender line on one row

* refactor(mobile): tidy the queued sender row and editor heading

* fix(mobile): bound the queued sender row to its container width
2026-11-05 20:28:18 +00:00
Brennan Benson 6717c2887d mobile: open file paths in agent questions like the rest of chat (#38620)
* mobile(native-chat): render agent questions through chat markdown so their file paths open

Question cards, multi-step asks, awaiting-input rows and answer receipts drew the
agent's question as plain text, so paths in it did nothing. They now render through
one shared question text on the chat's markdown adapter, like desktop.

* mobile(native-chat): keep question numbers, selection and announcements in the awaiting row

Numbers sit beside each question instead of inside its markdown, transcript questions stay
selectable like transcript prose, and a pending question stays inside the row's live region.

* mobile(native-chat): key awaiting-row questions by their text, like desktop

* test(mobile): mock markdown leaves in the ask card test and match question text by content

* mobile(native-chat): keep repeated questions' keys unique in the awaiting row

* mobile(markdown): keep bold weight inside strong-toned text, so bold in a question title still stands out

* test(mobile): flatten the question title style without a type assertion
2026-11-05 12:23:26 -08:00
Kelvin Amoaba de7fa2addb feat(mobile): show background task rows in the chat transcript (#38264)
* feat(mobile): show background task rows in the chat transcript

* fix(mobile): truncate the task output path like desktop

* fix(mobile): scale and select task rows like the rest of the transcript

* fix(mobile): translate the task output line

* refactor(mobile): read task rows from the shared row content

* test(mobile): restore the text reader for task row tests
2026-11-05 20:16:34 +00:00
Brennan Benson ab8ae366b7 Use one-line cards for all agent messages on desktop and phone (#38615)
* Use one-line agent message cards for every variant

* Remove obsolete agent message preview translations
2026-11-05 11:53:55 -08:00
OrcaWinandOrcaWin 8e46e2066d test: remove 98 low-value mobile tests and 15 entire suites (#38637)
* test: remove 98 low-value mobile tests

* test: drop stale typecheck allowance for removed browser budget test

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-11-05 11:45:58 -08:00
c2494f891b feat(mobile): ask where to open a link tapped in chat (#33525)
* feat(mobile): ask where to open a link tapped in chat

Tapping a web link in a chat message went straight to the system browser.
It now opens a sheet with Open in Orca browser (a browser tab on the host,
like terminal links), Open in Safari, and Copy link. The Orca option is left
out where a browser tab can't open (floating workspace, host without
browser streaming). Other markdown screens keep opening links directly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(mobile): label the external link action "Open in browser"

The action hands the URL to the phone's default browser, which is not
always Safari, so it now says so on every platform, like the browser
setting does. The chat link routing moves into openMarkdownLink so
openMarkdownHref keeps its upstream signature.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(mobile): open mail links in chat directly instead of the link sheet

The Orca browser rejects mailto: URLs, so the sheet's first choice failed with "Enter a valid URL". Also folds openMarkdownLink back into openMarkdownHref and drops the label-list test.

* fix(mobile): send chat web-search result taps to the link sheet too

* chore(mobile): allowlist the chat link sheet strings until session screens are localized

* fix(mobile): offer the Orca browser for a link only on a connected host known to stream it

Before the screencast probe answered, while reconnecting, or with no host client, the link sheet
offered Open in Orca browser and the tap ended in a refusal toast or nothing. It now offers only
the phone's choices until the host is connected and supports it. Tests cover the sheet's three
actions, its two-action form and a failed copy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(mobile): name the link sheet's phone choice "Open in system browser"

It opens whichever browser the phone is set to use, not a particular one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(mobile): keep the chat link sheet with the chat it was opened from

The sheet's URL lived in the session screen, so it survived a notification
switching chats and reopened when returning from a file tab. It now lives in
the chat's row overlays next to the message actions sheet, which already
resets per conversation, and stays mounted until it has slid away. The Orca
browser choice now checks the live connection; showOfflineState is never
true while a chat is on screen.

* refactor(mobile): confirm a copied chat link like other copy actions

Copy link now flips to Copied with a success haptic, as the PR menu does,
instead of a session toast, so the chat overlay no longer threads showToast.
The link handler context defaults to the system browser, which drops the
null fallbacks from its readers.

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com>
2026-11-05 19:45:51 +00:00
Kelvin Amoaba facb32d74f feat(mobile): copy a file's name or relative path from the file explorer (#38622)
* feat(mobile): copy a file's name from the file explorer

Long-pressing a file or folder row opens a sheet with Copy Name and
Copy Relative Path, named like the desktop File Explorer menu.

* fix(mobile): keep a slow copy from closing a later file's sheet

* refactor(mobile): close the file actions sheet at once, like sibling copy actions

* refactor(mobile): own the file row actions state beside its sheet
2026-11-05 19:45:35 +00:00
Brennan Benson 5efad8477c Show model and effort before a folder chat’s first message (#38498)
* fix(native-chat): show defaults for chats that have never started

* fix(native-chat): share default projection across controls and picks

* test(native-chat): prove controls recover from cached discovery

* test(native-chat): type deferred catalog as its wire result

* test(native-chat): preserve the catalog origin literal

* test(native-chat): remove duplicate RPC work-count cases
2026-11-05 11:25:58 -08:00
OrcaWinandOrcaWin 64cd31458d test: remove 31 low-value interaction and state tests (#38629)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-11-05 11:21:57 -08:00
14bc9fef26 fix(mobile): show history scan notices separately from skipped transcripts (#38626)
* Show source scan notices accurately in mobile Agent History

Reuse the existing desktop presentation policy in shared code, keep host failures and source notices separate from skipped transcript files, and retain their scanner-authored messages.

Adapted from the remaining mobile presentation in original PR #15671. Rebased onto main: the classifier now lives in src/shared/ai-vault/scanning/, desktop keeps its translated skip reasons, and mobile reuses its existing skipped-transcript notice.

Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>

* refactor(ai-vault): share the scan issue row key between desktop and mobile

---------

Co-authored-by: Neil <neil@stably.ai>
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
2026-11-05 19:11:35 +00:00
Brennan Benson 799b60b074 Keep orchestration messages tied to their actual agent identity (#37966)
* Show background completion mail as its command source

* Use minted sender handles in identity regression fixtures

* Verify command source labels in translated chat views

* Preserve command-only queued source regression coverage

* Use a minted terminal identity in host sender coverage

* Reuse command sender labels across unchanged message and queue reads

* Keep sender performance contract within the shared typecheck boundary

* Keep command reports in the background surface and preserve observed sender identity

* Use the shipped phone background report translation namespace

* Settle the prior dispatch before testing terminal reuse

* Keep live worker status tied to its recorded agent invocation

* Withdraw worker invocation proof when identity refresh fails

* Drop unproved worker associations at the failed refresh boundary

* Cover ordinary and remote sender rows during failed refresh

* Assert preserved session and peer content on failed sender reads

* Align sender navigation and relay tests with observed provenance

* Keep background report sources neutral and bound sender snapshot caches

* Construct readonly phone report fixture without mutation

* Keep callback-free federated transport assertions exact

* Keep orchestration sender identity separate from endpoint activity

* Validate mailbox receipt fixture shapes without casts

* Keep current Crew endpoint status separate from saved message activity

* Keep sender activity fixtures within lint contracts

* Avoid repeated serialization for fitting relay pages

* Use the valid workspace sidebar value in scale coverage

* Keep operation-count SQL expectation in sync with main
2026-11-05 11:00:15 -08:00
Brennan Benson dd1784f55b Keep partial check details from replacing complete PR status (#38514)
* Keep PR check status tied to complete observations

* Correct checks presentation types and count import

* Preserve localized empty states and declared runtime support

* Correct discovery fixture stack types

* Omit empty failed-check subtitles

* Preserve check identity through refresh and row publication

* Withhold row identity for malformed check errors

* Limit local capability wakes to local task sources

* Correct PR check regression fixtures

* Preserve browser APIs in PR check regression fixture

* Settle PR check adoption after failed detail reads

* Use the parsed PR check detail type during refresh

* Preserve PR metadata across checks discovery and committed query lifetimes

* Keep complete checks proof separate from legacy publication policy

* Keep PR lookup fixtures independent from complete checks collection

* Keep complete-check fixtures within lint contracts

* test: align PR recordings with source check discovery
2026-11-05 10:39:54 -08:00
Brennan Benson 15caa932bc fix(mobile): make file links in phone plan approvals actionable (#38510)
* fix(mobile): make file links in phone plan approvals actionable

The phone chat view now provides its file opener through one per-chat
context. Chat markdown renders through a shared adapter that reads it,
and tool, diff and attachment targets read it directly, replacing the
onOpenFile props threaded by hand through rows and cards. The plan
approval card was the surface that never got it.

Tracked in STA-13722.

* fix(mobile): open attached files while editing a queued message

Composer attachment chips read the chat's file opener from the per-chat
context instead of the structured composer config, which the queued
message editor built without it.

* test(mobile): provide the chat file opener to the attachment preview test
2026-11-05 10:26:43 -08:00
Brennan Benson e076a683a3 fix(acp): permission prompts from an agent's own work wait for you (#38488)
* fix(acp): permission prompts from an agent's own work wait for you

A permission or question that an ACP agent (OMP, Grok, OpenCode, others)
asks during work it started itself, such as picking up a finished
background job, was answered "cancelled" at once and never shown. It now
shows as a card, raises the needs-input alert, and waits for the answer,
unless Stop, a steer or teardown cuts it short.

While OMP refuses a sent message as busy, a request from its own turn now
appears at thread level instead of being cancelled, so it never lands in
the held message's turn.

Tracked in STA-13625.

* fix(acp): keep later requests askable after a Stop, and honor the agent's withdrawal

A Stop or a card's Cancel left the chat refusing every later permission
request until the session ended; the stop now covers only the work it
interrupted. A request the agent withdraws with $/cancel_request now
withdraws its card. Cancel on a card from the agent's own work reaches the
agent even when no message is running.

* test(acp): narrow approval rows before reading their resolution

* fix(acp): a live request's card outlives its turn instead of being cancelled with it

Requests group with the active turn again, as before. A permission or question
the agent is still waiting on now stays pending and answerable when its turn
ends, is replaced, or is settled by the host; only the person's answer, the
agent's withdrawal, Stop, a steer or the session ending settles it. Settled
requests are pruned at turn end.

Stop covers only Orca's running message, so later requests from the agent's
own work can still ask. Cancel on a card with no running turn now reaches the
host from desktop and phone for every agent, and a question the agent
withdraws before its card opens no longer leaves a stuck card.

* fix(acp): never strand a live request without a card, and scope Cancel to it

A request the agent still waits on now always gets an answerable card, even
when its turn settled before the card was written, and answering it releases
the timeline's hold. Cancel on a card from an agent's own turn refuses further
requests from that turn until it ends. Desktop never turns Cancel on a card
into a whole-chat Stop on hosts that can't take a card cancel.

* fix(acp): a new message is never covered by a Stop of the agent's earlier turn

* fix(native-chat): a card's Cancel with no running turn passes the RPC schema

The cancel params required a turn whenever a card was named, so Cancel on a
card from an agent's own work (and Pi's turnless card Cancel) was rejected
before it reached the host. A background-task cancel still names its turn.

* test(rpc): a card with no running turn cancels by itself
2026-11-05 10:25:23 -08:00
OrcaWinandOrcaWin 90113c2265 test: remove low-value mobile browser tests (#38598)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-11-05 09:18:49 -08:00
Neil 18bab90efa refactor(main): group client RPC pairing and host runtime owners (#38584)
* refactor(main): group client RPC pairing and host runtime owners

* test: target concrete mobile notification owner in headless fixture

* test: retarget dynamic quit mocks to runtime metadata owner
2026-11-05 09:16:47 -08:00
Neil ebd724b2f8 refactor(main): group browser runtime code by defining owner (#38576) 2026-11-05 07:44:10 -08:00