Skip to content

feat(bot-mode): support declarative CLI roster - #102195

Open
Ahmett101 wants to merge 1 commit into
NousResearch:mainfrom
Ahmett101:feat/102155-cli-bot-mode-roster
Open

Ahmett101 wants to merge 1 commit into
NousResearch:mainfrom
Ahmett101:feat/102155-cli-bot-mode-roster

Conversation

@Ahmett101

Copy link
Copy Markdown

Summary

Adds a CLI-native Bot Mode declaration path so agent.bot_mode.enabled: true in config.yaml enables the Bot Chat protocol and message_agent tool without requiring Desktop-written ui_meta["hermes-bots"]. Optional agent.bot_mode.roster entries now constrain local profile DMs, while existing Desktop ui_meta behavior remains supported.


Changes

  • tools/bot_mode_probe.py: reads agent.bot_mode from root config.yaml, treats enabled: true as a managed Bot Mode install, filters local roster entries by optional roster, and includes config state in the Bot Chat capability fingerprint.
  • tools/bot_mode_dm.py: applies the configured local roster to message_agent target validation and teammate error payloads.
  • tests/tools/test_bot_mode_probe.py: adds regression coverage for config-only Bot Mode enablement and roster-limited prompt injection.
  • tests/tools/test_bot_mode_dm.py: adds regression coverage for config-only tool injection and rejection of unlisted local teammates.

How to Test

/data/data/com.termux/files/home/.hermes/hermes-agent/venv/bin/python -m pytest tests/tools/test_bot_mode_probe.py tests/tools/test_bot_mode_dm.py -q
# ✅ 60 passed, 1 skipped

ruff check tools/bot_mode_probe.py tools/bot_mode_dm.py tests/tools/test_bot_mode_probe.py tests/tools/test_bot_mode_dm.py
# ✅ All checks passed

/data/data/com.termux/files/home/.hermes/hermes-agent/venv/bin/python scripts/check-windows-footguns.py tools/bot_mode_probe.py tools/bot_mode_dm.py tests/tools/test_bot_mode_probe.py tests/tools/test_bot_mode_dm.py
# ✅ No Windows footguns found

Full scripts/run_tests.sh was attempted on Termux and failed before exercising the suite because the parallel runner crashed with repeated PermissionError(13, 'Permission denied'); scoped regression tests passed.

Checklist

  • Tests pass — 60/60 targeted, 1 skipped
  • Follows Conventional Commits
  • Changes scoped to this fix only
  • Cross-platform impact assessed (Linux / macOS / WSL2 / Windows / Termux)
  • profile-safe paths used — no hardcoded ~/.hermes added for new fallback paths
  • .env not used for non-credential settings (behavioral settings → config.yaml)

Risk & Impact

Low. The existing Desktop-managed ui_meta path is preserved; config-based Bot Mode only activates when users explicitly set agent.bot_mode.enabled: true.

Type: New feature
Closes: #102155

@alt-glitch alt-glitch added type/feature New feature or request comp/tools Tool registry, model_tools, toolsets area/config Config system, migrations, profiles P3 Low — cosmetic, nice to have needs-decision Awaiting maintainer decision before any implementation labels Sep 3, 2026

Copy link
Copy Markdown

Topology / landing interlock, verified against live main@63279301bcbdc185c1b07b98a9312eb0c862f26d.

This is a real complementary Bot Mode authority slice, not a duplicate, but it now has two active semantic/file collisions that cannot be left to merge timing:

The composed authority law must be one decision across all three axes: the current session/source is allowed; the declarative install/config roster allows the relationship; and the target profile is currently enabled/readable. Prompt projection and tool/schema exposure must derive from that same law, while dispatch rechecks the live revocable pieces before spawning delivery.

Landing order for the current graph: compose #101057's source/session boundary first; reconcile this PR's agent.bot_mode.enabled + optional roster semantics into that object; then rebase/compose #100758 on top rather than allowing whichever PR lands last to redefine the roster or callable-target law. Any equivalent order is valid only if the final composed object proves the same single authority contract.

Required composition matrix: config-only enable; roster-denied target; bot.enabled: false; corrupt/unreadable target metadata; canonical Bot Chat; CLI; Telegram; allowed human gateway source; denied API/A2A source; restored session; and same-session source change.

Exact-head proof for this PR is otherwise clean right now: 2215b6e88d55cbbd7ae7de9431a2b2a86d48cc81 has CI 33760623189, Docker 33760622131, and Nix 33760622105 all successful. This interlock is about the landing semantics/composition object, not a failure of the current isolated head.

@andrexibiza andrexibiza left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exact-head review on 2215b6e88d55cbbd7ae7de9431a2b2a86d48cc81 (one surviving commit). CI 33760623189, Docker 33760622131, and Nix 33760622105 are green.

One code-level blocker remains in the declarative authority path: _configured_targets() treats any non-list agent.bot_mode.roster as None, and every caller interprets None as “no restriction; allow every local profile.” Because this PR reads config.yaml directly rather than through a typed/validated bot-mode schema, a malformed restriction such as roster: {from: default, to: [coder]} silently fails open and exposes every local teammate even though the operator attempted to constrain the relationship. Invalid/ambiguous authority configuration needs to deny local targets (or be rejected by canonical config validation) rather than becoming the unrestricted case. Please add the inverse regression for malformed roster shape and keep the explicit missing-roster case as the only unrestricted path.

The previously posted #101057/#100758 composition interlock still applies after that fix; I found no additional isolated-head defect in this pass.

howdeploy added a commit to howdeploy/hermes-agent that referenced this pull request Sep 5, 2026
Rematerialize NousResearch#100758 on main f58fcc8, retaining the tested source-first composition of NousResearch#101057 and the declarative roster from NousResearch#102195, together with durable leases, publication fencing, chain-qualified compression recovery, repeated-step identities and strict configuration policy.

Original development history: 924fce6, retained in backup/pr100758-before-landing-20260905. Source-authority contribution: NousResearch#101057 at 8355ab1. Declarative roster contribution: NousResearch#102195 at 2215b6e. The NousResearch#99333 client salvage and StodsEcho5 credit remain inherited from main via NousResearch#101214.

Validated with scripts/run_tests.sh: 906 passed, 0 failed, 9 skipped across 43 files; retries disabled. Ruff 0.16.4 passed for all changed Python files. Hosted verification must reference this new commit, not earlier tips.

Co-authored-by: Brooklyn Nicholson <brooklyn.bb.nicholson@gmail.com>
Co-authored-by: Ahmett101 <ahmet.tunc@gmail.com>

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/config Config system, migrations, profiles comp/tools Tool registry, model_tools, toolsets needs-decision Awaiting maintainer decision before any implementation P3 Low — cosmetic, nice to have type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Declarative inter-agent communication roster for multiplexed gateways (CLI-only)

3 participants