Skip to content

feat: make agent-frontend reconnect-safe on mobile #11

Description

@gosunuts

Summary

Mobile browsers are not reliable long-lived clients. Backgrounding Chrome/Samsung Internet can freeze or discard the page, suspend timers, and drop WebSocket connections. agent-frontend should therefore be designed around recoverable sessions, not around keeping one connection alive forever.

This is important because the same class of problem already appears when using OMP directly from Termux: after background/resume, resize, or reconnect, the UI may redraw from the wrong place, scroll back to the top, or otherwise lose the user's viewing state. A browser frontend gives us better control over UI state than a terminal TUI, but only if reconnect is treated as a normal lifecycle event.

Design principle

Do not model the browser/WebSocket as the agent session.

Wrong:
WebSocket = OMP session
WebSocket closed = session lost

Preferred:
OMP session = durable host-side state
Browser = attachable/disposable client
WebSocket = temporary transport

The goal is not "never disconnect". On mobile that is unrealistic.

The goal is:

disconnects may happen at any time, but the user should lose neither work nor UI context.

Expected behavior

When the mobile browser is backgrounded, frozen, reloaded, or its WebSocket is dropped:

  1. The underlying OMP/agent session keeps running on the host.
  2. The browser reconnects to the same session.
  3. Missed output/tool events are replayed or the current transcript is resynchronized.
  4. The previous viewing position is restored.
  5. Draft input is preserved where possible.
  6. If the user was following the latest output, the UI resumes at the tail.
  7. If the user was reading older output, new output must not force-scroll them away from that position.

Suggested state model

Host/bridge should own durable session state such as:

session_id
event_seq
transcript
running jobs / tool state

The client should keep lightweight view state such as:

session_id
last_received_seq
scroll_anchor
draft
follow_tail

Reconnect can then look roughly like:

connect(session_id, after=last_received_seq)
        ↓
replay missed events / resync transcript
        ↓
restore scroll anchor

For scroll restoration, prefer a message-based anchor over a raw pixel offset, e.g. message_id + local offset. Pixel-only restoration becomes fragile when streamed content changes height.

Mobile-specific requirements

  • Treat visibilitychange, page freeze/discard, reload, network transition, and WebSocket loss as normal conditions.
  • Persist enough client state before the page becomes hidden to restore the session after process death/reload.
  • Do not assume beforeunload will run.
  • Reconnect should be idempotent and safe after partial event delivery.
  • Duplicate events after reconnect must not duplicate messages/tool results in the UI.
  • Soft-keyboard resize should not destroy scroll position.

Why this belongs in agent-frontend

agent-terminal / ttyd remains useful as a raw terminal escape hatch, but it inherits terminal scrollback, PTY resize, and TUI redraw limitations.

agent-frontend can do better because it owns the DOM, transcript model, composer, scroll state, and reconnect behavior directly. This should be one of the main reasons to prefer the frontend on phones/tablets.

A useful product hierarchy would be:

agent-frontend  → primary mobile agent UX
agent-terminal  → raw shell / recovery / debugging
collab/session sharing → sharing layer

Acceptance criteria

  • Agent continues running when the browser disconnects.
  • Reconnecting/reloading reattaches to the same session.
  • Missed events are replayed or transcript state is resynchronized without duplication.
  • Scroll position is restored after reconnect/reload.
  • Tail-following resumes only when the user was already at the bottom.
  • Reading older output is not interrupted by incoming events.
  • Draft input survives temporary disconnect/reload where feasible.
  • Mobile keyboard resize/background-resume does not reset the transcript to the top.

Non-goal

Keeping a mobile browser WebSocket alive indefinitely. The frontend should remain correct even when the connection is routinely destroyed and recreated.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions