Skip to content

Android: side chats and subagent viewer (android-next#06, port of upstream #899) - #22

Merged
hoangvu12 merged 15 commits into
mainfrom
android/next-06-side-chats
Oct 15, 2026
Merged

hoangvu12 merged 15 commits into
mainfrom
android/next-06-side-chats

Conversation

@hoangvu12

Copy link
Copy Markdown
Owner

Android: side chats and the subagent viewer. This is android-next#06, which ports upstream zeronsh#899 ("iOS: side chats and subagents", still open) onto Roboco's engine-local core. Nothing goes through zeronsh#899's edge or blob paths.

Behaviour

  • Subagent viewer: tapping an "Agent …" spawn card in the transcript opens that subagent's transcript, read-only. It is live from the engine's WatchDocMessages stream (the same stream desktop's subagent tab uses), freezes when the subagent finishes, and nested cards push another viewer.
  • Background subagents: this covers claude-code run_in_background subagents. The tool result returns immediately and the subagent keeps streaming. The card's dots follow subagent_status.
  • Side Chats & Agents sheet: opened from a header bubbles button with a badge. It has a + menu, a ✓, and foldable Running / Side chats / Finished agents sections.
  • Session ⋯ menu: adds New Side Chat (the New Session canvas in side mode; nothing is written until the first send) and Fork into Side Chat (ForkSideChat).
  • Sessions list: a "● N" pill counts running subagents plus the session's working side chats.
  • Older engines: engines below 0.5.0 get no side-chat entry points, and old docs carry no subagent links.

Core: mobile-client gains fork_side_chat, side-chat create (parentChatId), subagents_of and open_subagent (a read-only SessionCore). The FFI gains SubagentInfo / SubagentState, NewSession.parent_chat_id, SessionRow.running_subagents and WidgetKind::SubagentLink. There are no wire or auth changes.

Tests:

  • Rust mobile-client/tests/side_chats_engine.rs runs against a real engine. It includes the background case: tool result and turn Done first, then nested traffic binds Running and streams into the open viewer, then the nested Done freezes it.
  • Kotlin: SideChatsModelTest, SideChatsSheetTest, SideChatsEntryTest, SubagentLinkTest, RunningAgentsPillTest, SideChatDraftTest.

Known gaps:

  • No live tail line under running agents, because the engine never writes subagent_tail.
  • No swipe-archive or rename in the sheet.
  • Fold state is in-memory only.

Ticket: .scratch/android-next/issues/06-side-chats-subagents.md

hoangvu12 and others added 15 commits October 15, 2026 17:55
…droid-next#06)

The engine-local port of upstream zeron PR zeronsh#899's client half:

- mobile-client: NewSession.parent_chat_id (an empty side chat: the
  parent's host/checkout/sandbox, a sibling of a side chat), the
  pending-chat overlay carries the parent (a side chat never lists on
  the front page), fork_side_chat (the engine's ForkSideChat RPC, a
  side chat's fork lists as its sibling), open_subagent (the spawn
  chip's doc id, read-only: no markChatSeen, no pill count; the
  transcript cache persists it for offline reads), SessionSnapshot::
  subagents() (the spawn chips are the index), and the running-
  subagent count overlay feeding the rows' "● N" pill (cleared when a
  session leaves the registry; the derive folds working side chats in,
  the zeronsh#899 follow-up).
- mobile (FFI): SubagentState/SubagentInfo + SessionHandle.subagents()
  /is_subagent(), CoreClient.fork_side_chat + open_subagent,
  NewSession.parentChatId, SessionRow.runningSubagents, and the
  layout's WidgetKind::SubagentLink — a spawn card whose engine-
  stamped doc exists becomes a whole-card tap target.
- tests: pure subagent derivation/lifecycle tests, the FFI mapping,
  the link widget, and real-engine side_chats_engine.rs (fork, empty
  create, live/finished/resumed subagent docs, running counts).
…t + the Arc import

A failed edit batch (one block not matching) dropped the SessionHandle
half of the subagent surface — only the snapshot method had landed.
Also new_session.rs never imported std::sync::Arc.
…_in_background)

The user's exact shape, driven through a channel-fed harness: the spawn
call's tool result returns at once, the parent turn completes (parks),
and the subagent's nested traffic arrives afterwards — sessions.rs
routes it before the parked gate, so the doc streams and the chip binds
(running dots), then freezes on the agent's Done. Asserts the chip
state, the doc id, the tail, the running count, the live open, and the
frozen read.
…gine

- new_session: import ClientInner (create_side_chat's signature named it
  without a use).
- subagents_of titles a row with subagent_title, a port of the desktop's
  subagent_tab_title (bare task, description/prompt fallback, 40-char cap),
  re-exported for the layout's card link.
- A read-only subagent core skips the WatchQueue task and refuses sends
  (InvalidArgument); it never writes the store (no seen marks, no pill).
- set_running_subagents removes the entry on 0 instead of storing zeros.
- side_chats_engine: Reply::Background was missing from the reply match;
  the run pushed `request` before reading it (use after move); "mock".into()
  has no From<&str> for HarnessId; the background stream held a tokio
  MutexGuard while moving the Arc. The rig now waits for the derived
  parent row (create_side_chat reads it) and the fork test waits for a
  COMPLETE reply. The chip tail asserts None: the engine stamps lifecycle
  only (sessions.rs subagent_chip_update; live tails were rejected).

Co-Authored-By: Claude <noreply@anthropic.com>
…like desktop

SessionRow.running_subagents defaults to 0 and NewSession.parent_chat_id to
None in the generated bindings, so existing Kotlin constructor calls keep
compiling. The subagent card link takes its title from the client's
subagent_title (the desktop tab title) instead of the chip detail line.

Co-Authored-By: Claude <noreply@anthropic.com>
…ipt (android-next#06)

WidgetKind.SubagentLink becomes a full-card tap target (a transcript control) that calls TranscriptActions.openSubagent. SubagentRoute opens the doc read-only through CoreClient.openSubagent and binds the layout engine to it: the engine's WatchDocMessages stream, the same one desktop's subagent tab watches, so a claude-code run_in_background subagent keeps streaming after its spawn call resolved and stops once done. The subtitle follows the spawn chip's state; nested cards push another viewer.

Co-Authored-By: Claude <noreply@anthropic.com>
…t and Fork (android-next#06)

The header's second bar item (badge: '● N' while subagents run, else a quiet count) opens the sheet: Running, Side chats, Finished agents, foldable. The session menu gains New Side Chat (the New Session canvas pushed in side mode: model and effort only, nothing written until the first send, parentChatId on create) and Fork into Side Chat (ForkSideChat, one at a time). Engines older than 0.5.0 get no side-chat entry points.

Co-Authored-By: Claude <noreply@anthropic.com>
SessionRowVM carries runningSubagents; the row draws '● N' before the status glyph (upstream zeronsh#899's SessionCell geometry) and speaks it.

Co-Authored-By: Claude <noreply@anthropic.com>
… draft (android-next#06)

Co-Authored-By: Claude <noreply@anthropic.com>
Conflicts: SessionController keeps engineVersion, sessionRow and harness;
SessionScreenRoute.release stops the side chats and disposes the usage
filler. SideChatsEntryTest passes #21's onShowAccounts and counts its
Account & Usage row (Pin, Copy, side-chat group, Account & Usage, Archive).
serve()'s listener was dropped when rig_with_background returned, so
the engine stopped listening and every first call failed with
"Engine connection lost" (all five side_chats_engine tests on 1ffe097).
@hoangvu12
hoangvu12 merged commit a8b22ea into main Oct 15, 2026
16 of 20 checks passed
hoangvu12 added a commit that referenced this pull request Oct 15, 2026
The first release with the native Android app (signed arm64-v8a and
x86_64 APKs), alongside the desktop and engine:

- Android: the 1:1 iOS port on the shared Rust mobile core (PR #19),
  with context usage always visible, Tailcat pairing, the new logos and
  notification icon, and in-app updates.
- Android: agent accounts - plan usage folded into the context chip,
  switch and remove Claude/Codex/Cursor logins (PR #21).
- Android: side chats and the subagent viewer, including background
  subagents (PR #22).
- Desktop: Tailcat invites as a scannable QR in Settings -> Remote access
  (PR #20); pairing links as QR codes (in PR #19).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant