Skip to content

fix(agent): stop synchronized sessions from misidentifying the active host - #102192

Open
fangliquanflq wants to merge 2 commits into
NousResearch:mainfrom
fangliquanflq:fix/gateway-live-runtime-identity
Open

fangliquanflq wants to merge 2 commits into
NousResearch:mainfrom
fangliquanflq:fix/gateway-live-runtime-identity

Conversation

@fangliquanflq

Copy link
Copy Markdown

What does this PR do?

Hermes can restore a persisted system prompt copied from another machine and confidently report the wrong active host. This change places the live machine hostname in the local-runtime prompt and rejects copied or legacy local prompt snapshots when their hostname is missing or no longer matches.

Symptom

A Telegram session served by Atlas can claim it is running on a synchronized personal PC because the prompt exposes only the operating system, home, cwd, and synchronized profile state, not the live machine identity.

Impact

Users in synchronized multi-host setups can receive incorrect host attribution and direct debugging or operational instructions at the wrong machine.

Bug Cause

Trigger: agent/prompt_builder.py:build_environment_hints and agent/conversation_loop.py:_stored_prompt_matches_runtime

Causal chain:

  1. A local-runtime prompt is persisted in a session database and synchronized to another host.
  2. The prompt omits the machine hostname, while restore validation checks model, provider, cwd, and platform only.
  3. When those values match, the new host reuses the stale prompt and the model has no authoritative live host identity, so synchronized state can drive a wrong answer.

Why it is wrong: Profile and filesystem identity are not proof of which process currently serves the session.

Working sibling / contrast: Remote terminal backends intentionally describe the tool execution sandbox instead of the Hermes process host and continue to suppress local host information.

Ruled out: Platform routing is not the missing signal. The prompt already persists Platform: telegram; it does not identify whether that gateway process is on Atlas or the personal PC.

Fix

Add the live socket.gethostname() value to local environment hints. During stored-prompt restoration, compare that anchored hostname with the current process host, reject cross-host snapshots, and rebuild legacy local snapshots once so they gain the new identity field. Keep the existing project-context anchoring so user prose cannot spoof the comparison.

Related Issue

Closes #102170

Type of Change

  • Bug fix (non-breaking change that fixes an issue)

Changes Made

  • agent/prompt_builder.py - expose the live local machine hostname in the stable environment block.
  • agent/conversation_loop.py - invalidate persisted local prompts when the machine identity is absent or stale.
  • tests/agent/test_prompt_builder.py - verify local hostname emission.
  • tests/run_agent/test_compression_persistence.py - cover copied-host, matching-host, and legacy prompt restoration.

How to Test

  1. Build a local environment hint with a known hostname and verify it identifies that host.
  2. Restore a prompt saved with another hostname and verify Hermes rejects it; restore one from the same hostname and verify it remains cache-reusable.
  3. Run:
scripts/run_tests.sh tests/run_agent/test_compression_persistence.py -k 'machine_hostname or legacy_local_prompt'
scripts/run_tests.sh tests/agent/test_prompt_builder.py -k identifies_live_machine

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run the relevant test files and all selected regression tests pass
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform: Windows 11

Documentation & Housekeeping

  • I've updated relevant docstrings - or N/A
  • cli-config.yaml.example is N/A because no config keys changed
  • CONTRIBUTING.md and AGENTS.md are N/A because no architecture or workflow changed
  • I've considered cross-platform impact; the implementation uses Python's cross-platform socket.gethostname()
  • Tool descriptions and schemas are N/A because no tool behavior changed

@alt-glitch alt-glitch added type/bug Something isn't working comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint area/sessions Session lifecycle, resume, persistence, history area/profiles Multi-profile isolation, HERMES_HOME scoping P2 Medium — degraded but workaround exists sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades labels Sep 3, 2026
@lorencato23

Copy link
Copy Markdown

I reviewed this PR against the same reproduction and agree that it addresses the root cause more directly: synchronized session prompt snapshots need a live machine identity, and cross-host/legacy local snapshots must be rebuilt.

I closed my overlapping PR #102201 in favor of this one. It focused on making hermes gateway status use structured service state and show host/profile/PID identity, but it did not prevent the agent from reusing a stale persisted prompt.

The hostname anchoring and restoration invalidation here are the important fix. If maintainers want additional operator-facing diagnostics later, the status changes from #102201 could be considered as a separate follow-up; I would not block this PR on them.

Thanks for the thorough reproduction and regression coverage.

@lorencato23 lorencato23 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

A few follow-up hardening points to consider before merge:

  1. Add a regression test for socket.gethostname() returning an empty string or raising OSError. The current comparison is intentionally conservative, but the behavior should be explicit: a local stored prompt must not silently be treated as verified when the live hostname cannot be read.

  2. Hostname is a useful human-readable signal but not a globally unique gateway identity. Two cloned VMs or machines can share a hostname, and two gateway instances can run on the same host. Please consider documenting that this is host anchoring, not an authentication boundary, and track a follow-up for a stable opaque per-install/runtime identity if stronger guarantees are needed.

  3. It may be worth adding a same-hostname/different-profile or same-hostname/different-gateway test to make the remaining limitation visible and prevent future overclaiming of the guarantee.

These are hardening/follow-up items, not blockers for the PC-versus-Atlas case this PR fixes.

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

area/profiles Multi-profile isolation, HERMES_HOME scoping area/sessions Session lifecycle, resume, persistence, history comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P2 Medium — degraded but workaround exists 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/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Agent confuses active gateway host when profiles are synchronized across PC and Atlas

3 participants