Skip to content

feat(desktop): continue a secure session on phone - #67668

Open
joelbrilliant wants to merge 2 commits into
NousResearch:mainfrom
joelbrilliant:feat/desktop-continue-on-phone
Open

joelbrilliant wants to merge 2 commits into
NousResearch:mainfrom
joelbrilliant:feat/desktop-continue-on-phone

Conversation

@joelbrilliant

@joelbrilliant joelbrilliant commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

What changed

  • Adds a Continue on phone action to persisted Desktop sessions.
  • Reads the operator's configured dashboard public URL through a new authenticated endpoint.
  • Probes the public target and only renders a QR code when it is reachable, HTTPS and protected by the dashboard auth gate.
  • Preserves path prefixes and routes the session through its exact Hermes profile.
  • Adds loading, error, retry, copy-link and open-in-browser states in every Desktop locale.

Why

Hermes already supports resuming a session in the dashboard with /chat?resume=<session id>, but Desktop users have to reconstruct that URL themselves. This makes the existing remote path discoverable from the session they are already using.

The security boundary is deliberate. The handoff never embeds a token or credential, refuses HTTP and URL user info, and does not expose a QR code for an unreachable or non-gated dashboard. The new configuration endpoint remains behind the existing dashboard authentication middleware.

Applicability

This is configuration-driven rather than tied to one operator. It works with any HTTPS, OAuth-gated Hermes dashboard, including path-prefixed and multi-profile deployments. No hostname, token, profile name, tunnel provider or install path is built in. Loopback-only and fixed-token deployments intentionally remain unavailable because a scannable URL must not contain credentials.

Verification

  • npx vitest run src/app/chat/continue-on-phone-dialog.test.tsx src/lib/continue-on-phone.test.ts src/app/chat/sidebar: 71 passed.
  • python -m pytest tests/hermes_cli/test_web_server.py -q -k dashboard_remote_access: 2 passed.
  • npm run typecheck: passed.
  • Changed-file ESLint: passed with no warnings.
  • npm run build: passed.
  • Browser verification covered ready, loading and recoverable error states at desktop width and 393 x 852. The narrow layout had no horizontal overflow, and Copy link plus Open in browser both used the exact profile-scoped session URL.

Known upstream test debt

The full Desktop suite still has one ToolsetConfigPanel assertion expecting Installed while the component renders Ready. A clean origin-nous/main worktree reproduced the same failure. The focused test file passes in isolation.

The full test_web_server.py file also reaches the existing shared virtual environment failure where its Python binary references a missing temporary libpython3.11.dylib. The two new endpoint tests pass directly.

@alt-glitch alt-glitch added type/feature New feature or request P3 Low — cosmetic, nice to have comp/desktop Electron desktop app (apps/desktop/*) comp/cli CLI entry point, hermes_cli/, setup wizard area/sessions Session lifecycle, resume, persistence, history needs-decision Awaiting maintainer decision before any implementation sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades labels Jul 19, 2026
@joelbrilliant
joelbrilliant marked this pull request as ready for review July 19, 2026 20:36
@joelbrilliant
joelbrilliant requested a review from a team July 19, 2026 20:36
@joelbrilliant
joelbrilliant force-pushed the feat/desktop-continue-on-phone branch from 51bada5 to 1b79625 Compare July 20, 2026 10:29
@joelbrilliant

Copy link
Copy Markdown
Contributor Author

Rebased onto current main (766c617) and pushed follow-up fixes as 1b7962513. The rebase rewrote history, hence the forced update; the branch is the feature commit plus one fix commit on top.

  • Rebase conflict: apps/desktop/src/hermes.ts, where main's new Custom* endpoint types landed in the same sorted import/export lists as this branch's DashboardRemoteAccessResponse. Both sets kept, sorted.
  • Profile propagation: sessions resolved by id were upserted without a profile tag, so the handoff URL could omit ?profile= and the phone would resume against the gateway's sticky active profile rather than the owning one. resolveStoredSession now stamps the active profile on the untagged row, mirroring the existing upsertOptimisticSession pattern in the same file; all three menu surfaces read from the same store row, so the one chokepoint covers them.
  • Unavailable-state copy: no longer implies a token-authenticated dashboard is unauthenticated. It now states the real requirement, an HTTPS public URL serving a browser sign-in (OAuth-gated) dashboard, and that token-authenticated dashboards cannot be opened from a phone browser. All four locales updated, keys unchanged.
  • Docs: web-dashboard.md gains a "Continue a session on your phone" section covering prerequisites (dashboard.public_url or HERMES_DASHBOARD_PUBLIC_URL, HTTPS, browser sign-in gated dashboard, token-proxy topologies called out as unsupported), that the QR encodes the URL only with no credentials, and the auth-gated GET /api/dashboard/remote-access endpoint.
  • Test fidelity: added a pytest that writes dashboard.public_url into the per-test temp HERMES_HOME config.yaml and asserts the endpoint returns it through real config loading, no resolve_public_url monkeypatch. The existing mocked test stays.

Evidence: pytest -k remote_access 3/3; desktop vitest 40/40 across the dialog, lib, sidebar and utils files; desktop typecheck, eslint on changed files and the full desktop build clean.

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the carefully scoped handoff flow and the auth-gate checks. The feature remains absent from current main, but one profile-routing issue needs correction.

Problems

  • hermes_cli/web_server.py:18249 resolves the URL without accepting or scoping the profile query. Desktop supplies the owning profile in apps/desktop/src/lib/continue-on-phone.ts:64; in global-remote mode Electron appends that query (apps/desktop/electron/connection-config.ts:353-387). The handler therefore ignores it and can return the dashboard process/default profile's dashboard.public_url, while the QR keeps the named session profile.

Suggested changes

  • Accept profile: Optional[str] = None and resolve the URL inside _config_profile_scope(profile) (the existing per-request configuration scope at hermes_cli/web_server.py:15412-15429). Add a named-profile endpoint regression test with distinct public URLs.

Automated hermes-sweeper review.

Comment thread hermes_cli/web_server.py Outdated
@teknium1 teknium1 added the sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform label Jul 25, 2026
joelbrilliant and others added 2 commits July 30, 2026 21:12
- Stamp the active gateway profile on sessions resolved by id in
  resolveStoredSession (matching upsertOptimisticSession), so the
  continue-on-phone URL resumes against the owning profile instead of
  whichever profile the gateway is on later.
- Reword the continue-on-phone unavailable description (en/ja/zh/zh-hant)
  to require OAuth browser sign-in and state that token-authenticated
  dashboards cannot be opened from a phone browser; update the dialog
  test to match.
- Clarify the session-actions-menu OPEN comment: continue on phone is
  orthogonal to tab-vs-window, so every surface always offers it.
- test(web-server): cover /api/dashboard/remote-access through real
  config loading by writing dashboard.public_url into the isolated
  HERMES_HOME config.yaml instead of monkeypatching resolve_public_url.
- docs(web-dashboard): document GET /api/dashboard/remote-access and add
  a "Continue a session on your phone" section covering prerequisites
  (public HTTPS URL, OAuth browser sign-in, no token-proxy topologies)
  and that the QR code encodes no credentials.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/sessions Session lifecycle, resume, persistence, history comp/cli CLI entry point, hermes_cli/, setup wizard comp/desktop Electron desktop app (apps/desktop/*) needs-decision Awaiting maintainer decision before any implementation P3 Low — cosmetic, nice to have sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants