Skip to content

feat(mobile): share with guest and /handoff from Android, iOS and web - #73

Draft
kvnloo wants to merge 5 commits into
upstream-main-550c5fa34bfrom
feat/share-handoff-android
Draft

kvnloo wants to merge 5 commits into
upstream-main-550c5fa34bfrom
feat/share-handoff-android

Conversation

@kvnloo

@kvnloo kvnloo commented Nov 3, 2026

Copy link
Copy Markdown
Owner

Closes #72. Fork prototype; upstream venue upstream discussion 30355.

Problem

On Android there was no way to hand a session off to a new thread or to share it with a friend:
the thread's ⋮ menu had neither, and typing /handoff showed no popup. Mobile could only manage a
guest link someone had already created on web.

What existed (upstream main 550c5fa34b)

  • Guest links (Labs beta, needs T3 Connect): serverEnvironment.createGuestShare
    (packages/client-runtime/src/state/server.ts), created from web's sidebar/thread menu and command
    palette. Mobile only had ThreadGuestShareBar (stop sharing; send/dismiss guest messages).
  • thread.fork with sourcePoint: latest_stable (Orchestrator.ts latestStableRun) already
    forks a thread at its latest completed, checkpointed run into a listed thread; forkSideChat uses
    it with sideChat: true. Cross-provider context moves automatically.
  • No /handoff anywhere. Mobile slash menu: /model /plan /default, then provider commands.

Change

No server command or contract change; client only.

  • Shared rules (packages/shared/src/threadHandoff.ts): /handoff [focus] as the whole
    message; new thread title <title> (handoff); available once a reply has finished and nothing is
    running (mirrors the server's latest_stable); the provider-collision rule.
  • Client op threadEnvironment.forkForHandoff: thread.fork at latest_stable without
    sideChat.
  • /handoff [focus] in web and mobile composers: forks, waits for the new thread, sends the
    focus text there as its first message (with the new thread's own modes), and opens it. The menu
    item shows why it is unavailable when it cannot run; attachments/context are refused with a
    message.
  • A provider's own /handoff stays reachable: T3's built-in is listed first; the provider's is
    listed after it with its own description (or "The provider's own command"). Picking the
    provider's item from the menu sends the text to the provider; typing /handoff or picking T3's
    runs the handoff.
  • Mobile thread menus: Android header overflow (⋮), iOS header Share menu and the command
    palette offer Hand off to new thread, Share with guest (→ New guest link with a confirm
    while one is active) and Stop sharing. Creating a link copies it and opens the native share
    sheet. GuestShareCreateError reasons map to copy that says where to fix it (guest links off →
    Settings → Labs → Guest links on the machine running T3 Code; no T3 Connect → start it there).
  • Docs: docs/user/guest-links.md (mobile creates links), docs/user/composer.md (Hand off to a new
    thread).

Entry points added

Surface Entry points
Web / desktop composer /handoff [focus]
Android thread ⋮ overflow: Hand off to new thread, Share with guest / New guest link, Stop sharing; composer /handoff [focus]; command palette (same three)
iOS / iPadOS thread header Share menu (same three); composer /handoff [focus]; command palette

Verification

Run on the exact head f7ea30a09a in a clean tarball checkout on a fleet runner
(kvnloo/fleet-runner run 41719040164, avrea-ubuntu-latest-8-vcpu, Node 26.12.1, pnpm 12.14.0,
pnpm install --frozen-lockfile). groot could not install: the OOM guard killed pnpm install
three times.

vp test run src/threadHandoff.test.ts                    (packages/shared)   Tests  9 passed (9)
vp test run threadShareMenu.test.ts use-composer-command-menu.test.ts
                                                         (apps/mobile)       Tests 18 passed (18)
vp test run composerSlashCommandSearch.test.ts composerMenuHighlight.test.ts
                                                         (apps/web)          Tests  6 passed (6)
tsc --noEmit --singleThreaded   packages/shared, packages/client-runtime, apps/mobile, apps/web: exit 0
vp fmt --check <26 touched files>                                           exit 0
vp lint --report-unused-disable-directives <24 touched ts/tsx>              0 warnings, 0 errors
pnpm run knip:check                                                         exit 0

Negative controls in the same run (tests must bite):

  • Removing the picked-provider check from resolveHandoffSend → threadHandoff.test.ts fails
    "the provider's picked /handoff goes to the provider" (expected { focus: 'tests' } to be null).
  • Ranking T3 slash commands after provider ones → composerSlashCommandSearch.test.ts fails
    "lists T3's /handoff first and keeps the provider's /handoff reachable" (order flipped).

Not verified

  • No device or browser run yet: no Android/iOS/web dogfood, screenshots or video.
  • A live server round-trip of forkForHandoff (it reuses the thread.fork path the server tests
    cover; no server code changed, so no server test was added).
  • iOS native header menu rendering (type-checked only).
  • Web thread action menu / web command palette "Hand off" entries are not added: web's handoff guard
    needs the thread's runs, which only the chat view holds.

Commit map

  1. d4ff6d5670 feat(client-runtime): fork a thread at its latest reply for /handoff — shared rules +
    tests, forkForHandoff.
  2. 353ee0272f feat(mobile): share with guest and hand off from the thread menus — action builder +
    tests, useThreadShareActions, Android overflow, iOS Share menu, command palette.
  3. 96ef9399fd feat(mobile): /handoff [focus] in the composer — slash item, collision rule, send.
  4. 0cc4192f24 feat(web): /handoff [focus] in the composer — slash item, collision rule, send,
    useThreadHandoff.
  5. f7ea30a09a docs(user): mobile guest links and /handoff.

Model and harness: Claude Opus 5.5 via Hermes ACP in T3 Code.

Adds the shared /handoff rules (parse, title, when it can run, and the
provider-command collision rule) and a client operation that dispatches
the existing thread.fork at latest_stable into a listed thread.
Android's header overflow, the iOS header Share menu and the command
palette offer Hand off to new thread, Share with guest (New guest link
while one is active) and Stop sharing. Creating a link copies it and
opens the share sheet; server refusals name where to turn links on.
T3's /handoff is listed before a provider command of the same name;
picking the provider's from the menu still sends it to the provider.
Same rules as mobile: a whole-message command that forks at the latest
finished reply, opens the new thread and sends the focus there, with a
provider /handoff kept reachable from the menu.
@github-actions github-actions Bot added the vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. label Nov 3, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant