Skip to content

[hermes-mesh-session-continuity-substrate][1/n] Add Hermes session presence discovery - #40814

Open
OmarB97 wants to merge 1 commit into
NousResearch:mainfrom
OmarB97:codex/upstream-session-continuity
Open

OmarB97 wants to merge 1 commit into
NousResearch:mainfrom
OmarB97:codex/upstream-session-continuity

Conversation

@OmarB97

@OmarB97 OmarB97 commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

Why

Hermes needs a generic way for separate client surfaces to discover live sessions without hard-coding one private setup, sync tool, or device topology. This PR adds discovery metadata only; it does not make endpoint hints executable and it does not introduce a new transport, so other installations can adopt the mechanism with their own gateway/profile wiring.

The concrete dogfood target is the same Hermes session showing up from hphone and Hermes Desktop on macOS, with the Desktop able to attach through an already configured gateway profile.

What changed

  • hermes_cli.session_presence writes, lists, clears, expires, and deduplicates secret-free JSON presence records so sync-conflict files and same-endpoint successor records collapse to the newest live record.
  • The TUI gateway publishes presence for live sessions, accepts per-session presence hints (presence_client, presence_endpoint, presence_profile), includes a session_id compatibility alias on session.active_list, and exposes session.presence_list over JSON-RPC.
  • The TUI passes optional presence hints from HERMES_CLIENT_NAME, HERMES_SESSION_PRESENCE_ENDPOINT, and HERMES_SESSION_PRESENCE_PROFILE when creating a session.
  • Hermes Desktop polls presence, shows live records in a compact two-line Live section, and can route a presence row through the record/profile metadata to session.activate, falling back to session.resume when only stored history is available.
  • Documentation explains HERMES_SESSION_PRESENCE_DIR, record shape, expiration, and optional private endpoint hints.

How to review

Start with hermes_cli/session_presence.py for the portable record contract, then review tui_gateway/server.py for how live sessions publish those records without secrets. After that, check the Desktop hook/sidebar changes to confirm a presence row only uses configured profiles and existing JSON-RPC attach/resume paths.

For UI review, focus on the Desktop Live row behavior: it should be compact, readable, and two-line rather than clipped into one crowded row.

Evidence

Desktop Live section showing hphone presence

  • Live dogfood on ko-mac/taro: Desktop backend session.presence_list returned one live hphone record for qwen3.6-27b on profile taro; the Desktop sidebar rendered that as a single two-line LIVE row.
  • Manual attach smoke: clicking the Live row targeted 20260606_174039_e478af, and the remote taro gateway accepted the attach WebSocket for the hphone runtime.
  • Local model smoke: the same configured qwen3.6-27b provider path returned ok through a one-shot Hermes CLI prompt after the hphone reset/restart.
  • Regression coverage: the presence tests exercise registry listing, expiration, env override, duplicate conflict records, endpoint successor dedupe, gateway active-list presence publishing, and session.activate compatibility.

Verification

  • PASS: uv run --extra dev python -m pytest tests/hermes_cli/test_session_presence.py tests/test_tui_gateway_server.py -k 'session_active_list_publishes_presence or session_presence_list_reads_registry or session_activate_switches_live_session_without_closing_siblings or session_presence' returned 8 passed on head 690df0d835be6467ac3f18aad2fc5fceacd985e2.
  • PASS: npm --workspace ui-tui run build completed successfully.
  • PASS: npm --prefix apps/desktop run type-check completed successfully.
  • PASS: npm exec eslint src/app/desktop-controller.tsx src/app/chat/sidebar/index.tsx src/app/session/hooks/use-session-actions.ts src/types/hermes.ts from apps/desktop completed successfully.
  • PASS: python3 -m compileall -q tui_gateway/server.py hermes_cli/session_presence.py && git diff --check completed successfully.

Risks / gaps

Collaborators

Participants:

  • @OmarB97 - operator on ko-mac and hphone/taro dogfood target.
  • Codex (GPT-5) - OpenAI coding agent on ko-mac, implementation and verification lane.

Process:

  • Time to finish: multi-turn dogfood and repair pass on June 6, 2026.
  • Iteration: 3 of n across presence, live attach, and mobile statusbar follow-ups.
  • Lead reviewer: @OmarB97 via live screenshots and hphone reports.

Task context:

  • Task: hermes-mesh-session-continuity-substrate.
  • Feature: Hermes session continuity across configured devices.
  • Size: XL · Risk: medium · Priority: High.

Related work:

@OmarB97
OmarB97 marked this pull request as ready for review June 6, 2026 21:58
@alt-glitch alt-glitch added type/feature New feature or request P2 Medium — degraded but workaround exists comp/tui Terminal UI (ui-tui/ + tui_gateway/) labels Jun 6, 2026
@OmarB97

OmarB97 commented Jun 6, 2026

Copy link
Copy Markdown
Contributor Author

Follow-up pushed in 60c5880e: the session-presence tests now clear any ambient HERMES_SESSION_PRESENCE_DIR by default, while the explicit env-override test still sets it itself.

Why: local dogfood has a real hphone presence directory exported, and that exposed test contamination in the original branch.

Validation after the follow-up:

uv run --extra dev python -m pytest tests/hermes_cli/test_session_presence.py tests/test_tui_gateway_server.py -k 'session_presence or session_active_list'

Result: 6 passed, with the ambient hphone presence env still present.

@OmarB97 OmarB97 changed the title Add Hermes session presence discovery [hermes-mesh-session-continuity-substrate][1/n] Add Hermes session presence discovery Jun 7, 2026
@OmarB97

OmarB97 commented Jun 7, 2026

Copy link
Copy Markdown
Contributor Author

Local MeshBoard settlement update:

  • PR body/title now pass meshctl pr-quality check locally.
  • Focused smoke/verification listed in the PR body passed on the current head.
  • The corresponding fork branch status contexts were posted successfully, but this upstream PR still shows an empty statusCheckRollup in NousResearch/hermes-agent.
  • meshctl pr merge --dry-run is blocked only by missing upstream contexts: Reviewer-first PR quality and smoke.

So this is ready for maintainer-side CI/status/merge handling from the upstream repo namespace.

@OmarB97
OmarB97 force-pushed the codex/upstream-session-continuity branch 2 times, most recently from 9c5a0e7 to b1d7e9d Compare June 10, 2026 00:13
@OmarB97

OmarB97 commented Jun 11, 2026

Copy link
Copy Markdown
Contributor Author

Resolved branch drift against current upstream main.

Updated head: 1a1c3d78f23f020d20182744644aa9f48548f1d4
Base merged: c94e93a6480f3cfdabe0624aac46f06670ffae36

Conflict notes:

  • Kept the current upstream sidebar single-scroll/session-section structure and inserted the Live presence section inside it.
  • Kept the current runtime-info hydration shape in use-session-actions while preserving the presence route/profile helpers.
  • Removed stale composer clearing from the presence-open path so draft ownership stays with the current composer flow.

Verification run locally:

  • python3 -m pytest tests/hermes_cli/test_session_presence.py tests/test_tui_gateway_server.py::test_session_active_list_reports_live_sessions tests/test_tui_gateway_server.py::test_session_active_list_publishes_presence tests/test_tui_gateway_server.py::test_session_presence_list_reads_registry tests/test_tui_gateway_server.py::test_session_activate_returns_inflight_stream_before_completion tests/test_tui_gateway_server.py::test_session_activate_switches_live_session_without_closing_siblings -q (10 passed)
  • npm --prefix apps/desktop run test:ui -- src/app/session/hooks/use-route-resume.test.tsx src/app/session/hooks/use-session-state-cache.test.tsx (7 passed)
  • npm --prefix apps/desktop run typecheck
  • python3 -m py_compile hermes_cli/session_presence.py tui_gateway/server.py
  • git diff --check

GitHub now reports the PR as open/blocked rather than dirty; no checks are currently reported on the branch.

@OmarB97

OmarB97 commented Jun 11, 2026

Copy link
Copy Markdown
Contributor Author

Repaired branch drift against current upstream/main again.

Updated head: 35537585c

Conflict notes:

  • Resolved apps/desktop/src/app/session/hooks/use-session-actions.ts by keeping the session-presence route/profile helpers from this PR and upstream's newer SessionRuntimeInfo/personality runtime metadata patch typing.
  • Upstream's newer desktop/session/file-tree changes are included via the merge.

Verification:

  • PYTHONPATH= /Users/obaradei/.hermes/hermes-agent/venv/bin/python -m pytest -o addopts='' tests/hermes_cli/test_session_presence.py plus the focused session_active/session_presence/session_activate tests (10 passed)
  • npm --prefix apps/desktop run test:ui -- src/app/session/hooks/use-route-resume.test.tsx src/app/session/hooks/use-session-state-cache.test.tsx (2 files, 9 tests)
  • npm --prefix apps/desktop run typecheck
  • py_compile hermes_cli/session_presence.py tui_gateway/server.py
  • git diff --check upstream/main...HEAD

Note: this fresh repair worktree needed npm ci --workspace apps/desktop first; npm reported the existing @icons-pack/react-simple-icons Node >=24 warning on Node 22.22.3.

@OmarB97

OmarB97 commented Jun 11, 2026

Copy link
Copy Markdown
Contributor Author

Pushed a small CI follow-up to repair the attribution check on this refreshed branch.

What changed:

  • Added omar@kostudios.io to scripts/release.py AUTHOR_MAP, matching the current contributor attribution workflow requirement.

Verification:

  • Local attribution-check equivalent over merge-base..HEAD passes for omar@kostudios.io.
  • git diff --check upstream/main...HEAD passes.

@OmarB97

OmarB97 commented Jun 11, 2026

Copy link
Copy Markdown
Contributor Author

The Docker build failure on the refreshed head was a GitHub Actions cache blob miss during the build context copy (HTTP 404 BlobNotFound), not a source failure. The rerun endpoint rejected the completed workflow, so I pushed an empty CI retrigger commit to the PR branch. Product diff is unchanged from the attribution-map repair; waiting on the fresh check run now.

@OmarB97

OmarB97 commented Jun 11, 2026

Copy link
Copy Markdown
Contributor Author

Correction to my previous note: the empty CI retrigger commit is ed7112b335 (ed7112b335fce4a290086c42174412e4522dda52).

@OmarB97
OmarB97 force-pushed the codex/upstream-session-continuity branch from ed7112b to 3d9e268 Compare June 11, 2026 19:47
@OmarB97

OmarB97 commented Jun 11, 2026

Copy link
Copy Markdown
Contributor Author

Ready for maintainer merge. I refreshed/rebased this branch onto the current upstream line and re-ran local verification: py_compile for the session presence/gateway files, focused session_presence tests, targeted TUI gateway presence tests, desktop typecheck, ui-tui typecheck, and git diff --check. GitHub required checks are green. MeshBoard dry-run accepted it as ready; the actual merge attempt is blocked only by my token lacking upstream MergePullRequest permission.

@OmarB97
OmarB97 force-pushed the codex/upstream-session-continuity branch from 3d9e268 to e73ac9a Compare June 12, 2026 14:13
@OmarB97
OmarB97 force-pushed the codex/upstream-session-continuity branch from e73ac9a to a36d542 Compare June 19, 2026 18:11
@alt-glitch alt-glitch added comp/desktop Electron desktop app (apps/desktop/*) comp/cli CLI entry point, hermes_cli/, setup wizard sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state labels Jun 26, 2026
@OmarB97
OmarB97 force-pushed the codex/upstream-session-continuity branch 2 times, most recently from 8ded2d4 to 1be4b88 Compare July 6, 2026 01:14
@OmarB97

OmarB97 commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

Re-authored onto current upstream/main (branch was CONFLICTING against main).

Desktop UI: rebuilt, not restored

The original diff added a bespoke SidebarPresenceSection that called SidebarSectionHeader, which upstream has since moved out of sidebar/index.tsx into sessions-section.tsx — so that section no longer compiles. It has been re-implemented onto the current virtualized sidebar: session presence now feeds the existing workingSessionIdSet, so live sessions light the accent "working" dot on their virtualized session-row.tsx rows (mirroring how origin/main decorates rows) instead of a separate section.

openPresenceSession is ported and wired end-to-end (use-session-actionsdesktop-controller → an optional onOpenPresenceSession prop on ChatSidebar) but is a dormant extension point for now — the same state as origin/main's presence scaffold — because the bespoke section that used to trigger it was intentionally dropped.

Backend/substrate ported as-authored: hermes_cli/session_presence.py registry, session.presence_list RPC, presence publish/clear on the gateway, TUI presence params, store atom/types, and the useSessionPresence polling hook.

Verified headless

  • npm run --prefix apps/desktop typecheck — pass
  • npm run --prefix apps/desktop build — pass (dist emitted)
  • ui-tui typecheck — pass
  • pytest tests/hermes_cli/test_session_presence.py tests/test_tui_gateway_server.py — 310 passed, all 7 presence tests green. The single failure (test_browser_manage_connect_default_local_reports_launch_hint) is pre-existing/environmental (no Chromium binary on the macOS runner); browser_connect.py is untouched here.

Needs manual QA (not verifiable headless)

  • Confirm at runtime that a live session surfaces the accent live-dot on its sidebar row while session.presence_list reports it.
  • Optional follow-up: add a UI affordance (or a cross-instance "Live" surface) that triggers openPresenceSession, if opening presence records not present in the local session list is desired.

@OmarB97
OmarB97 force-pushed the codex/upstream-session-continuity branch from 1be4b88 to c7004b0 Compare July 10, 2026 16:00
@OmarB97

OmarB97 commented Jul 10, 2026

Copy link
Copy Markdown
Contributor Author

Refreshed onto current upstream/main (rebase, real merge-base — this branch's fork point is directly in upstream/main's history). One conflict, in tui_gateway/server.py: upstream changed _notify_session_boundary's signature to take a third _session_source(session) argument (already used consistently everywhere else in the file) since this branch was opened. Kept the PR's new clear_session_presence(...) cleanup block on session finalize, and switched the call to upstream's current 3-arg form instead of the PR's original 2-arg call. The other 13 files (5 auto-merges + 8 pure additions) applied with zero conflict — diff-stat is byte-identical to the original (14 files, 769/-5). Ran the touched-module suites locally: test_session_presence.py, test_tui_gateway_server.py — 323 passed; tsc --noEmit clean in both apps/desktop and ui-tui. Original head: 1be4b88708; new head: c7004b0b8b. Force-pushed with lease; checks re-running.

@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 substantive presence substrate and the focused registry tests. I found several blockers before this can provide the documented cross-device Desktop flow.

Problems

  • apps/desktop/src/app/chat/sidebar/index.tsx:209 declares onOpenPresenceSession, but ChatSidebar never consumes it. Lines 346-362 only add presence IDs to existing rows' working-dot set; remote-only records cannot be rendered or opened. This conflicts with docs/session-presence.md:77-80.
  • Presence expires after 90 seconds (hermes_cli/session_presence.py:21,95). The PR publishes only from _init_session, live-payload attachment, and session.active_list (tui_gateway/server.py:4745,6132,6173), while Desktop polls only session.presence_list.
  • session.presence_list reads launch _hermes_home (tui_gateway/server.py:6185-6188) although writes may use profile_home (6033-6072); resumed sessions publish before profile_home is restored (5885-5908).

Suggested changes

  • Use a profile-aware config.yaml presence configuration rather than the documented non-secret HERMES_SESSION_PRESENCE_* controls.
  • Add lifecycle-owned refresh and multi-profile write/list/clear coverage.
  • Render actionable presence rows, or narrow the Desktop/documentation claim.

This is an automated hermes-sweeper review.

@@ -204,6 +206,7 @@ interface ChatSidebarProps extends React.ComponentProps<typeof Sidebar> {
onLoadMoreSessions: () => Promise<void> | void
onLoadMoreProfileSessions?: (profile: string) => Promise<void> | void
onLoadMoreMessaging?: (platform: string) => Promise<void> | void
onOpenPresenceSession?: (record: SessionPresenceRecord) => void

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.

onOpenPresenceSession is never destructured or invoked by ChatSidebar; the presence data only contributes IDs to workingSessionIdSet. A remote-only presence record therefore has no row to click and cannot follow this route. Please render and wire an actionable presence row, or remove the dormant path and narrow the documentation.

Comment thread tui_gateway/server.py
@@ -4727,6 +4742,7 @@ def _init_session(
# session startup resilient).
pass
_wire_callbacks(sid)
_publish_session_presence(sid, _sessions.get(sid) or {})

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.

This is one of the only publication paths. Desktop adds a session.presence_list poll, not an active_list poll, and session.create does not reach this _init_session path. With the 90-second TTL, a Desktop-created live session can be absent or expire without an independent lifecycle refresh.

Comment thread tui_gateway/server.py
rid,
{
"sessions": list_session_presence(
hermes_home=_hermes_home,

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.

Records may be written under session.profile_home by _session_presence_home, but this reader is pinned to launch _hermes_home. In addition, resume publishes before profile_home is restored. Define and test a single profile-aware read/write/clear scope so records do not become invisible or stale across profiles.

Comment thread docs/session-presence.md
$HERMES_HOME/session-presence/active/
```

Set `HERMES_SESSION_PRESENCE_DIR` to place records in any other private shared

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.

This introduces a documented non-secret HERMES_* behavioral configuration surface. Repository policy requires behavioral settings to be configured through config.yaml; please move the storage/endpoint/profile settings there (an internal compatibility bridge is fine if required).

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform area/sessions Session lifecycle, resume, persistence, history labels Jul 14, 2026
Reapply Hermes session presence discovery (hermes_cli/session_presence.py,
the tui_gateway/server.py wiring, desktop sidebar/session-actions presence
UI, and docs/evidence) onto the post-history-replacement mainline.

Conflict resolution (tui_gateway/server.py, one spot): upstream changed
_notify_session_boundary's signature to take a third `_session_source(session)`
argument (used consistently everywhere else in the file) since this branch
was opened. Kept the PR's new clear_session_presence(...) cleanup block on
session finalize, and called _notify_session_boundary with upstream's
current 3-arg form instead of the PR's original 2-arg call. The other 13
files (5 more auto-merge candidates plus 8 pure additions) applied with
zero conflict.

Ran the touched-module suites locally: tests/hermes_cli/test_session_presence.py,
tests/test_tui_gateway_server.py — 323 passed. `tsc --noEmit` clean in both
apps/desktop and ui-tui.

Refresh of PR NousResearch#40814; original head 1be4b8870875a492959163ff1891e2b43d635040.
@OmarB97
OmarB97 force-pushed the codex/upstream-session-continuity branch from c7004b0 to b7337d0 Compare July 29, 2026 14:42

@GottZ GottZ left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This was generated by AI during triage.

Summary

Two PRs address the live-session continuity complex: #40814 adds presence discovery, publication, polling, and activation scaffolding, while #40822 contains that stacked presence work plus multi-client event fanout. The diffs target distinct layers, but both retain correctness gaps that prevent the documented end-to-end flow.

Related pull requests

  • #40814 related — (+775/-4) — keep open with a salvage path: the registry, presence RPC, lifecycle hooks, and focused tests are reusable, but the diff only marks existing sidebar rows as working and never consumes onOpenPresenceSession, so remote-only records cannot be rendered or opened as documented; publication also lacks a periodic refresh for the 90-second TTL, and reads are not consistently profile-aware. This follows the contributor keep_open review on #40814 and requires its UI, refresh, profile-home, and config.yaml blockers to be addressed.
  • #40822 related — (+1114/-36) — author action: rebase onto main, or split out the part that can merge: preserve the fanout-specific FanoutTransport, attach/detach lifecycle, stale-client handling, and regression tests, but separate them from the full #40814 presence stack. This follows the contributor keep_open review on #40822: the diff still assigns queued["transport"] in the queued-prompt drain path rather than preserving fanout, and the inherited presence settings must be moved from user-facing environment variables to config.yaml.

Duplicates

#40822 is stacked on #40814 and therefore duplicates #40814's presence, Desktop, documentation, and registry changes; only #40822's fanout-specific gateway transport changes and tests are distinct. It should not be treated as a wholesale duplicate while that delta remains unsplit.

Suggested consolidation

Keep #40814 open with a salvage path limited to the presence registry/RPC and tests, with author action required to implement a usable remote-only Desktop row, periodic TTL refresh, profile-consistent storage, and config.yaml configuration. For #40822, author action: rebase onto main, or split out the fanout-only delta and add a two-client queued-prompt regression fix using the shared attach path; once split, the duplicated #40814 portion can be dropped rather than maintained in both PRs. These actions explicitly preserve both contributor keep_open reviews and do not recommend merging either current diff.

Cross-PR triage: Reviewed 2 pull requests and 0 issues in this complex. Each diff was read against this issue; Assessment working set: 101 kB of PR diffs, 9 kB of issue/PR text, 14 kB of discussion (20 comments), 0 verify verdicts. verdicts reflect diff content, not PR titles. Part of an automated triage batch.

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/*) comp/tui Terminal UI (ui-tui/ + tui_gateway/) P2 Medium — degraded but workaround exists 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-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.

4 participants