Skip to content

fix(agent): persist display_metadata for every user message, derived from source - #96081

Open
TwoRobotsinaTrenchcoat wants to merge 1 commit into
NousResearch:mainfrom
TwoRobotsinaTrenchcoat:fix/display-metadata-persist
Open

TwoRobotsinaTrenchcoat wants to merge 1 commit into
NousResearch:mainfrom
TwoRobotsinaTrenchcoat:fix/display-metadata-persist

Conversation

@TwoRobotsinaTrenchcoat

@TwoRobotsinaTrenchcoat TwoRobotsinaTrenchcoat commented Aug 27, 2026 •

Copy link
Copy Markdown

What does this PR do?

Fixes a bug where display_metadata was silently dropped for every normal user message.

In agent/turn_context.py, display_metadata was only persisted inside the if display_kind: block. Normal user messages (role user, no display_kind) never entered that branch, so their display_metadata — attribution like origin, platform, chat_id — was never written to the session store. This breaks downstream consumers that rely on per-message attribution (e.g. session search, annotation rendering, the HUD footer).

The fix un-nests the persistence so it runs for every message, and derives origin/platform attribution from the message source when not explicitly provided (mirroring how the gateway already computes these).

Related Issue

No open issue exists for this bug. Symptom verified locally: a [Replying to: ...] reply-composite row in state.db carried no display_metadata while a sibling message did.

Type of Change

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

Changes Made

  • agent/turn_context.py: moved display_metadata persistence out of the display_kind guard; added _derive_source_annotation(agent, content) helper that reads platform / chat_id / etc. off the source and returns a minimal attribution dict. Every user message now persists attribution.
  • tests/agent/test_turn_context.py: added tests covering (a) message with explicit display_metadata, (b) message with source-derived attribution, (c) CLI message (platform=cli) gets at least platform + origin.

How to Test

  1. Start a session, send a normal message (no display_kind).
  2. Inspect the resulting row in state.db messages — display_metadata should be present with origin/platform.
  3. Before the fix: display_metadata was absent for that row.

Checklist

  • I've read the Contributing Guide
  • Commit messages follow Conventional Commits
  • I searched existing PRs (no duplicate of this fix)
  • PR contains only changes related to this fix
  • pytest tests/agent/test_turn_context.py passes (22 tests)
  • Tests added (required for bug fixes)
  • Tested on: Linux (aarch64, Ubuntu 26.04)
  • No new config keys / docs needed (N/A)
  • Cross-platform: pure-Python, no OS assumptions (N/A)

Companion PR (read path)

This PR is the write path — it persists display_metadata onto every user message. The read path (making session_search actually surface that column) is #97523. Merged together, per-message attribution survives compaction end-to-end; either alone is incomplete.

…from source

The display_metadata field existed but was nested inside the display_kind
check, so it was dropped for every normal (non-summary) user message.
Un-nest the persistence and derive origin attribution from the message
source when not explicitly provided.

Tests use synthetic fixture values (no real user/chat IDs).

Co-authored-by: Hermes Contributor <contrib@users.noreply.github.com>
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint area/sessions Session lifecycle, resume, persistence, history sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state labels Aug 27, 2026
@Enough1122

Copy link
Copy Markdown

AI code review — automated review for reference; please use your judgment.

fix(agent): persist display_metadata for every user message, derived from source — closes silent attribution drop.

  • agent/turn_context.py previously nested display_metadata inside if persist_user_display_kind: — plain user messages (no display_kind) never persisted attribution, so compaction/cross-session continuity lost source context. Now if persist_user_display_metadata is None: derives from agent source attributes (platform, chat_id, chat_type, user_id, user_name, chat_name resolved at init in agent_init.py:681-684), with user_name/chat_name neutralized via gateway/session.neutralize_untrusted_inline_text (falls back to str()[:200] if sanitizer unavailable). _classify_origin maps voice wrappers ("…", [voice message could not…, [The user sent a voice message…) → voice else text.
  • Derived annotation only when caller didn't supply one — caller payload wins verbatim (persist_user_display_metadata truthy → no derivation). Stored on user_msg["display_metadata"] in the transcript; build already strips it from outgoing copies, so prompt surface is unchanged unless a consumer reads the row metadata.
  • Tests assert plain Telegram group row now carries chat_id/chat_name/user_name/platform/origin, CLI row gets minimal platform+origin without chat fields, and custom payload is preserved.

@TwoRobotsinaTrenchcoat

Copy link
Copy Markdown
Author

Coordination check: v0.21.0 added native display_metadata persistence — persist_user_display_metadata in conversation_loop.py, storage in session_transcript.py — but from our trace, the native path serves TUI/desktop delegation events (async-delegation-complete rendering). This PR covers a different writer: per-user-message display metadata on the gateway chat-platform path, derived from source.

If the native path is intended to generalize to gateway messages, happy to re-scope or withdraw; if not, this PR remains complementary. Which is it? @teknium1

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/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-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.

3 participants