Skip to content

fix(mattermost): seed first thread turn context - #38152

Closed
sweetcornna wants to merge 1 commit into
NousResearch:mainfrom
sweetcornna:codex/fix-37695-mattermost-thread-context
Closed

fix(mattermost): seed first thread turn context#38152
sweetcornna wants to merge 1 commit into
NousResearch:mainfrom
sweetcornna:codex/fix-37695-mattermost-thread-context

Conversation

@sweetcornna

@sweetcornna sweetcornna commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Fetch prior Mattermost thread posts with GET /api/v4/posts/{root_id}/thread on the first Hermes turn in a thread.
  • Prepend sorted prior messages as thread context while excluding the triggering post and prior slash commands.
  • Skip refetching once the thread already has a gateway session, using the session store's key generation when available.

Fixes #37695.

Tests

  • Red: .\.venv\Scripts\python.exe -m pytest tests/gateway/test_mattermost.py::TestMattermostWebSocketParsing::test_first_thread_turn_omits_prior_slash_commands_from_context -q --timeout-method=thread failed because only the triggering message reached the agent.
  • .\.venv\Scripts\python.exe -m pytest tests/gateway/test_mattermost.py::TestMattermostWebSocketParsing::test_first_thread_turn_omits_prior_slash_commands_from_context -q --timeout-method=thread -> 1 passed
  • uv run --extra dev --extra messaging python -m pytest tests/gateway/test_mattermost.py -q --timeout-method=thread -> 46 passed
  • .\.venv\Scripts\python.exe -m ruff check plugins/platforms/mattermost/adapter.py tests/gateway/test_mattermost.py -> All checks passed!
  • .\.venv\Scripts\python.exe -m py_compile plugins/platforms/mattermost/adapter.py tests/gateway/test_mattermost.py -> passed
  • git diff --check -> passed

@alt-glitch alt-glitch added type/feature New feature or request type/test Test coverage or test infrastructure platform/feishu Feishu / Lark adapter comp/gateway Gateway runner, session dispatch, delivery P3 Low — cosmetic, nice to have labels Jun 3, 2026
@sweetcornna
sweetcornna force-pushed the codex/fix-37695-mattermost-thread-context branch from e0842b2 to 86b1049 Compare June 4, 2026 14:44
crisap94 added a commit to crisap94/hermes-agent that referenced this pull request Jun 10, 2026
…ity)

The Mattermost adapter only saw the single message it was @mentioned in and
required a fresh @mention every turn. The Slack adapter already solves both;
this ports that pattern to Mattermost.

- In-thread auto-response: after the first @mention in a thread, subsequent
  messages in that thread auto-trigger the bot. Opt out with
  MATTERMOST_STRICT_MENTION=true (mirrors SLACK_STRICT_MENTION).
- Thread context: on the first turn in a pre-existing thread, prior messages
  are fetched (GET /posts/{root}/thread) and injected via channel_context.
- Security: injected thread history is filtered to allowlisted authors
  (MATTERMOST_ALLOWED_USERS / *_ALLOW_ALL_USERS). The authz layer only checks
  the triggering author; channel_context bypasses it, so unfiltered history
  would leak non-allowlisted users' messages into the model.
- Session-key parity: _has_active_session_for_thread passes the adapter's real
  chat_type so the key matches what handle_message persists.

Adds TestMattermostThreadBehavior (12 cases) and documents
MATTERMOST_STRICT_MENTION + the allowlist-filtered context behavior.

Related: NousResearch#38362, NousResearch#38152, NousResearch#37144 (thread-context PRs without the allowlist filter).
@crisap94

Copy link
Copy Markdown

Thanks for this 🙏 Same note as on #38362: #43805 also seeds first-turn thread context, and adds an allowlist filter on the seeded messages.

Why it matters: authz_mixin only checks the triggering author; injected channel_context bypasses that check, so unfiltered thread history can leak a non-allowlisted user's posts into the model. #43805 filters seeded authors to MATTERMOST_ALLOWED_USERS and adds a MATTERMOST_THREAD_CONTEXT (off/allowlisted/all) control. Glad to consolidate with your approach however the team prefers.

crisap94 added a commit to crisap94/hermes-agent that referenced this pull request Jun 10, 2026
When @mentioned inside an existing thread for the first time, seed the prior
thread history as context (GET /posts/{root}/thread) via MessageEvent.channel_context,
so the agent sees the whole conversation, not just the triggering message.

Security: the authz layer only checks the *triggering* message's author;
injected channel_context bypasses it. So seeded history is filtered to authors
in MATTERMOST_ALLOWED_USERS (or everyone when *_ALLOW_ALL_USERS is set) to avoid
leaking a non-allowlisted user's messages into the model. MATTERMOST_THREAD_CONTEXT
controls the policy: allowlisted (default), off, or all (full thread without
widening who may invoke the bot). Fails open on fetch error.

Stacked on the in-thread auto-response change (uses _has_active_session_for_thread
as the first-turn guard).

Adds TestMattermostThreadContext and documents MATTERMOST_THREAD_CONTEXT.

Supersedes NousResearch#38362, NousResearch#38152 (thread context without the allowlist filter).
@sweetcornna
sweetcornna force-pushed the codex/fix-37695-mattermost-thread-context branch from 86b1049 to 74a8691 Compare June 11, 2026 07:30

@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 implementing the missing Mattermost thread-history path. The premise remains valid on current main: plugins/platforms/mattermost/adapter.py:867-960 keys threads but does not fetch or attach prior posts.

Problems

  • plugins/platforms/mattermost/adapter.py:983 prepends history into text. In shared threads, gateway/run.py:10388-10395 prefixes that whole value with the triggering sender. Use MessageEvent.channel_context so backfill retains its own attribution and the gateway inserts [New message] correctly.
  • plugins/platforms/mattermost/adapter.py:969 checks for / before current main's leading-space command normalization (plugins/platforms/mattermost/adapter.py:881-884, 1197d2bc9). A /new can receive injected context and cease to be a command.
  • The new user-facing .env setting at hermes_cli/config.py:3282 should instead use the existing YAML path added at adapter lines 1314-1316.

Suggested changes

  • Attach seeded history through channel_context, normalize command text before the fetch guard, and add a gateway-level regression covering shared-thread attribution plus /new.

This is an automated hermes-sweeper review.

current_post_id=post_id,
)
if thread_context:
message_text = thread_context + message_text

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.

Blocking: keep this backfill out of message_text. Current GatewayRunner._prepare_inbound_message_text() prefixes shared-session text with the triggering sender before it handles event.channel_context (gateway/run.py:10388-10395), so this assigns every recovered post to the trigger author. Pass the block through MessageEvent.channel_context instead.

thread_id = post.get("root_id") or None
if (
thread_id
and not message_text.startswith("/")

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.

Blocking on current main: leading-space slash commands are normalized later (plugins/platforms/mattermost/adapter.py:881-884, commit 1197d2bc9). For /new, this guard fetches context and line 983 changes the leading character, so command classification no longer occurs. Normalize/check lstrip() before this guard and add a regression test.

Comment thread hermes_cli/config.py
"password": False,
"category": "messaging",
},
"MATTERMOST_THREAD_CONTEXT": {

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 is a new user-facing non-secret behavior setting in the environment configuration. Please make mattermost.thread_context in config.yaml the canonical setup/documented surface instead; the adapter patch already includes a YAML bridge for that key.

@teknium1 teknium1 added sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data 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 labels Jul 14, 2026
@sweetcornna

Copy link
Copy Markdown
Contributor Author

Closing in favor of #43805. That PR covers the same Mattermost first-turn thread-context gap and adds the necessary allowlist filtering so injected thread history cannot bypass the triggering-author authorization boundary.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/gateway Gateway runner, session dispatch, delivery P3 Low — cosmetic, nice to have platform/feishu Feishu / Lark adapter 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-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/feature New feature or request type/test Test coverage or test infrastructure

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(mattermost): add _fetch_thread_context() to seed prior thread messages into LLM context

4 participants