Skip to content

feat(state): persist sender device attribution - #44078

Open
OmarB97 wants to merge 1 commit into
NousResearch:mainfrom
OmarB97:upstream/sender-device-attribution-20260611
Open

feat(state): persist sender device attribution#44078
OmarB97 wants to merge 1 commit into
NousResearch:mainfrom
OmarB97:upstream/sender-device-attribution-20260611

Conversation

@OmarB97

@OmarB97 OmarB97 commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Why

Shared desktop/cloud sessions need to show where a human user message was typed without changing the model replay transcript. Upstream already has desktop cloud-channel UI that can consume sender_device, but the state schema and desktop hydration path do not persist or display it yet.

What

  • Add a sender_device column to messages and bump the state schema version.
  • Resolve a stable local device name from config.yaml, MeshBoard registry, Tailscale, then hostname, with caching and non-fatal fallback.
  • Auto-stamp local user rows only; assistant/tool rows stay NULL, and get_messages_as_conversation() remains unchanged for prompt-cache stability.
  • Preserve sender attribution through replace_messages() and surface it in the desktop user bubble metadata path.

Verification

  • python3 -m pytest tests/test_hermes_constants.py::TestGetDeviceName tests/test_hermes_state.py::TestMessageStorage::test_user_message_sender_device_is_local_metadata tests/test_hermes_state.py::TestMessageStorage::test_append_message_sender_device_override_and_failure tests/test_hermes_state.py::TestMessageStorage::test_replace_messages_preserves_sender_device tests/test_hermes_state.py::TestSchemaInit::test_reconciliation_adds_missing_columns -q
  • python3 -m pytest tests/test_hermes_constants.py tests/test_hermes_state.py -q
  • python3 -m pytest tests/gateway/test_session_api.py tests/hermes_cli/test_web_server.py tests/tools/test_session_search.py -q
  • python3 -m py_compile hermes_constants.py hermes_state.py
  • npm --prefix apps/desktop run test:ui -- src/lib/chat-messages.test.ts src/lib/chat-runtime.test.ts
  • npm --prefix apps/desktop run typecheck
  • git diff --check

Also ran npm --prefix apps/desktop run test:ui -- src; it currently fails in unrelated upstream-main suites (pane-shell, use-gateway-boot, model-settings, streaming, toolset-config-panel). The focused sender-device UI tests and type-check pass.

MeshBoard: hermes-upstream-ports-desktop-lane-20260609

@alt-glitch alt-glitch added type/feature New feature or request P3 Low — cosmetic, nice to have comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint labels Jun 11, 2026
@OmarB97
OmarB97 force-pushed the upstream/sender-device-attribution-20260611 branch from 7adec7e to 89588c9 Compare June 15, 2026 07:02
OmarB97 added a commit to OmarB97/hermes-agent that referenced this pull request Jun 15, 2026
@OmarB97
OmarB97 force-pushed the upstream/sender-device-attribution-20260611 branch from 56f1248 to 9ea7af1 Compare July 5, 2026 19:02

@teknium1 teknium1 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.

Thanks for carrying attribution through storage and the desktop renderer. I found blocking gaps in the end-to-end contract.

Problems

  • apps/desktop/src/types/hermes.ts:376 adds the field, but the REST endpoint used by getSessionMessages() filters it out in gateway/platforms/api_server.py:1697-1703; the desktop cannot hydrate a label.
  • hermes_state.py:3445 derives missing attribution on the backend. Desktop sends only {session_id, text} at apps/desktop/src/app/session/hooks/use-prompt-actions/submit.ts:343-345, so a remote desktop would be labeled with the server's device name.
  • session.resume also drops the metadata: tui_gateway/server.py:5669 loads conversation rows and _history_to_messages() at tui_gateway/server.py:4978-4983 emits only role/text for user messages.
  • The branch is currently conflicting, and current main's effect_disposition persistence from a0a6cd80f5c7850fe1dcff4839c2d4cead44d1c8 must be retained during any salvage.

Suggested changes

  • Define and transport a client-origin attribution value, then cover remote-client, REST hydration, resume fallback, and rewrite/compaction cases.
  • Add the API and resume DTO propagation without putting attribution into get_messages_as_conversation().
  • Resolve the opt-in/configuration design required by AGENTS.md:118-121 before auto-stamping every user row.

Automated hermes-sweeper review.

reasoning_details?: unknown
role: 'assistant' | 'system' | 'tool' | 'user'
/** Device a user message was typed on. */
sender_device?: null | string

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.

getSessionMessages() reads GET /api/sessions/{id}/messages, but GatewayAPI._message_response() in gateway/platforms/api_server.py:1697-1703 has a safe-key whitelist that omits sender_device. This type will therefore never receive the field until the API serializer and its endpoint test are updated.

Comment thread hermes_state.py
senders without requiring every call site to pass a value. Agent
and tool rows remain NULL.
"""
if sender_device is None and role == "user":

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 runs on the backend host, not the desktop client. prompt.submit currently carries only session_id and text (apps/desktop/src/app/session/hooks/use-prompt-actions/submit.ts:343-345), so remote/cloud clients will all be stamped with the server device. Please carry an explicit client-origin value through the protocol or narrow the feature to backend-origin attribution.

@teknium1 teknium1 added 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 sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows sweeper:blast-broad Sweeper blast radius: broad — a core path most sessions hit labels Jul 14, 2026
@OmarB97
OmarB97 force-pushed the upstream/sender-device-attribution-20260611 branch from 4cb47d2 to 86d2f40 Compare July 29, 2026 14:41
…tored desktop thread

thread.tsx was split upstream into thread/*; port the senderDevice label
into thread/user-message.tsx (preserving upstream's clampActive body wrapper).
Python side (hermes_constants get_device_name, hermes_state sender_device
column/INSERTs) hand-resolved onto refactored SCHEMA_SQL + _insert_message_rows.
Relax one conversation-replay assertion for upstream's added replay timestamp
(sender attribution stays local; no behavior change).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@OmarB97
OmarB97 force-pushed the upstream/sender-device-attribution-20260611 branch from 86d2f40 to ff73c0a Compare July 29, 2026 17:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P3 Low — cosmetic, nice to have sweeper:blast-broad Sweeper blast radius: broad — a core path most sessions hit sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows 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