Skip to content

feat(gateway): config-driven per-chat channel_context injection - #65148

Open
Lazymonter wants to merge 1 commit into
NousResearch:mainfrom
Lazymonter:feat/config-driven-channel-context
Open

feat(gateway): config-driven per-chat channel_context injection#65148
Lazymonter wants to merge 1 commit into
NousResearch:mainfrom
Lazymonter:feat/config-driven-channel-context

Conversation

@Lazymonter

Copy link
Copy Markdown
Contributor

Problem

The inbound pipeline already accepts adapter-provided event.channel_context, but operators cannot attach deterministic context to a specific chat through configuration. Dynamic orchestrators therefore have to patch gateway/run.py and re-apply that patch after every update.

This keeps the inline/file config shape proposed by @liuhao1024 in #43728 and provides a current-main implementation that incorporates the review feedback there around canonical keys, cache identity, runtime config resolution, and integration coverage.

Solution

Add gateway.channel_context_map with inline and JSON-file forms:

gateway:
  channel_context_map:
    "telegram:-1002285219667": "This chat is bound to the release workspace."
gateway:
  channel_context_map: chat-context.json
  • Keys use one canonical <platform>:<chat_id> form, preserving native colons and never falling back to an unqualified ID.
  • Relative paths resolve from the active profile's HERMES_HOME; ~ and ${ENV_VAR} are supported through the existing runtime config path.
  • Adapter/backfill context remains first. Configured context is appended under [Configured chat context] without mutating the inbound event.
  • File-backed maps are checked on each inbound message and parsed only when their path/signature changes. Cache identity includes path, device, inode, size, mtime, and ctime, with bounded LRU state.
  • Missing, malformed, oversized, non-regular, or concurrently changing files fail closed for that message; stale bindings are not reused. The docs specify atomic replacement for external writers.
  • Validation limits files to 1 MiB, 4096 entries, 512-byte UTF-8 keys, and 2 KiB UTF-8 context values. Warning deduplication is also bounded and does not log map keys or values.

Testing

  • Added direct resolver coverage for canonical keys, inline/file config, profile isolation, cache invalidation, atomic replacement, TOCTOU changes, size/type limits, parser/path failures, and bounded caches.
  • Added runtime integration coverage for merge order, event immutability, profile-scoped config loading, and existing sender/context behavior.
  • scripts/run_tests.sh tests/gateway/test_channel_context_map.py tests/gateway/test_session.py tests/gateway/test_shared_group_sender_prefix.py tests/gateway/test_context_ref_expansion_runtime.py tests/gateway/relay/test_channel_context_consume.py -q — 145 passed.
  • Ruff and git diff --check pass.

Closes #43650

@alt-glitch alt-glitch added type/feature New feature or request comp/gateway Gateway runner, session dispatch, delivery comp/cli CLI entry point, hermes_cli/, setup wizard P3 Low — cosmetic, nice to have needs-decision Awaiting maintainer decision before any implementation sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades labels Jul 15, 2026
@teknium1

Copy link
Copy Markdown
Contributor

Thanks for incorporating the #43728 review feedback. Current main still only prepends adapter-provided event.channel_context in gateway/run.py:10566-10570, so the runtime-configured/file-backed behavior remains needed.

The PR constructs a platform-qualified key in gateway/channel_context.py:67-79, validates bounded inline/file maps, and uses a path plus device/inode/size/mtime/ctime signature before cache reuse (gateway/channel_context.py:287-405). The integration resolves under the existing profile-aware runtime-config path and applies in the shared preprocessing method, covering both normal and queued inbound paths (gateway/run.py:2426-2441, gateway/run.py:10517-10896, current main locations).

No blocking correctness issue was identified in the PR diff. The existing channel_overrides.system_prompt mechanism is a static system-prompt alternative (gateway/config.py:409-440), whereas this PR supplies the distinct dynamic inbound-context map described in #43650.

This is an automated hermes-sweeper review.

@teknium1 teknium1 added the sweeper:blast-broad Sweeper blast radius: broad — a core path most sessions hit label Jul 16, 2026
@GottZ

GottZ commented Aug 3, 2026

Copy link
Copy Markdown

This was generated by AI during triage.

Summary

Two open PRs address #43650 through inline and file-backed per-chat context injection. #43728 adds the initial loader and inbound integration, while #65148 retains that configuration shape and adds canonical platform-qualified keys, profile-aware resolution, robust cache invalidation, bounded validation, non-mutating context composition, integration coverage, and documentation.

Related pull requests

Duplicates

#43728 and #65148 implement the same configuration-driven per-chat inbound-context feature for #43650; #65148 is the superseding implementation that incorporates the documented review feedback on #43728.

Suggested consolidation

Keep #65148 open with a salvage path: preserve its canonical-key resolver, profile-aware runtime integration, robust bounded file cache, non-mutating merge behavior, regression coverage, and documentation while the author rebases or splits out a mergeable cut. Close #43728 as a duplicate of #65148 because the latter supersedes its implementation and addresses the COMMENTED keep_open review's specific defects.

Complex graph

flowchart LR
    classDef open fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a
    classDef merged fill:#dcfce7,stroke:#15803d,color:#14532d
    classDef closed fill:#e5e7eb,stroke:#6b7280,color:#1f2937
    classDef unverified fill:#f3f4f6,stroke:#9ca3af,color:#374151
    classDef best stroke-width:3px,stroke:#b45309
    classDef target stroke-width:3px,stroke:#4338ca
    I43650(["issue #43650 (open)"])
    subgraph Dup43728 ["PRs duplicating each other"]
        P43728["PR #43728 (open)"]
        P65148["PR #65148 (open)"]
    end
    P65148 -->|best fix| I43650
    class I43650 open
    class P43728 open
    class P65148 open
    class P65148 best
    class P65148 target
    click I43650 "https://github.com/NousResearch/hermes-agent/issues/43650"
    click P43728 "https://github.com/NousResearch/hermes-agent/pull/43728"
    click P65148 "https://github.com/NousResearch/hermes-agent/pull/65148"
Loading

Graph: solid arrow = fixes / best fix, dashed arrow = partial or unverified (see edge label); boxed group = PRs duplicating each other; amber border = best fix; indigo border = target; gray node = closed (state tag in the node label).

Cross-PR triage: Reviewed 2 pull requests and 1 issue in this complex. Each diff was read against this issue; Assessment working set: 50 kB of PR diffs, 8 kB of issue/PR text, 3 kB of discussion (3 comments), 3 verify verdicts. verdicts reflect diff content, not PR titles. Part of an automated triage batch.

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

Labels

comp/cli CLI entry point, hermes_cli/, setup wizard comp/gateway Gateway runner, session dispatch, delivery needs-decision Awaiting maintainer decision before any implementation 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-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Config-driven per-chat channel_context injection for inbound messages

4 participants