feat(state): persist sender device attribution - #44078
Conversation
7adec7e to
89588c9
Compare
Mirror upstream PR NousResearch#44078 to fork
56f1248 to
9ea7af1
Compare
teknium1
left a comment
There was a problem hiding this comment.
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:376adds the field, but the REST endpoint used bygetSessionMessages()filters it out ingateway/platforms/api_server.py:1697-1703; the desktop cannot hydrate a label.hermes_state.py:3445derives missing attribution on the backend. Desktop sends only{session_id, text}atapps/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.resumealso drops the metadata:tui_gateway/server.py:5669loads conversation rows and_history_to_messages()attui_gateway/server.py:4978-4983emits only role/text for user messages.- The branch is currently conflicting, and current main's
effect_dispositionpersistence froma0a6cd80f5c7850fe1dcff4839c2d4cead44d1c8must 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-121before 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 |
There was a problem hiding this comment.
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.
| senders without requiring every call site to pass a value. Agent | ||
| and tool rows remain NULL. | ||
| """ | ||
| if sender_device is None and role == "user": |
There was a problem hiding this comment.
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.
4cb47d2 to
86d2f40
Compare
…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>
86d2f40 to
ff73c0a
Compare
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
sender_devicecolumn tomessagesand bump the state schema version.config.yaml, MeshBoard registry, Tailscale, then hostname, with caching and non-fatal fallback.NULL, andget_messages_as_conversation()remains unchanged for prompt-cache stability.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 -qpython3 -m pytest tests/test_hermes_constants.py tests/test_hermes_state.py -qpython3 -m pytest tests/gateway/test_session_api.py tests/hermes_cli/test_web_server.py tests/tools/test_session_search.py -qpython3 -m py_compile hermes_constants.py hermes_state.pynpm --prefix apps/desktop run test:ui -- src/lib/chat-messages.test.ts src/lib/chat-runtime.test.tsnpm --prefix apps/desktop run typecheckgit diff --checkAlso 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