Skip to content

fix(matrix): batch long commands and bypass active session for skill commands - #58565

Open
liuhao1024 wants to merge 1 commit into
NousResearch:mainfrom
liuhao1024:liuhao/cron-bugfix-58559-matrix-command-batch
Open

fix(matrix): batch long commands and bypass active session for skill commands#58565
liuhao1024 wants to merge 1 commit into
NousResearch:mainfrom
liuhao1024:liuhao/cron-bugfix-58559-matrix-command-batch

Conversation

@liuhao1024

Copy link
Copy Markdown
Contributor

What does this PR do?

Fixes two Matrix-specific command dispatch bugs:

  1. Long !command messages bypass text batching: Matrix clients split long messages at ~3900 chars. When a !command normalizes to /command, the adapter sets MessageType.COMMAND and dispatches immediately, so the continuation chunk arrives as a separate plain-text message instead of being aggregated with the command body. Fix: batch COMMAND messages when the body length is at or above _SPLIT_THRESHOLD, while keeping short commands (e.g. /stop, /status) immediate.

  2. Skill commands don't bypass active-session queue: should_bypass_active_session() only checks resolve_command() (built-in commands). Registered skill commands like /arxiv are resolved through a separate registry (get_skill_commands()), so they get queued as user text instead of being dispatched. This matches how _resolve_matrix_bang_command() already checks both registries.

Related Issue

Fixes #58559

Type of Change

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

Changes Made

  • plugins/platforms/matrix/adapter.py: Change _handle_text_message() batching condition to also enqueue MessageType.COMMAND when len(body) >= _SPLIT_THRESHOLD — short commands still dispatch immediately.
  • hermes_cli/commands.py: Extend should_bypass_active_session() to check get_skill_commands() after the built-in command lookup, so skill commands bypass the active-session guard.
  • tests/gateway/test_command_bypass_active_session.py: Add TestSkillCommandBypass class with two tests verifying skill commands bypass and unknown names don't.
  • tests/gateway/test_matrix_command_batching.py: New test file with test_long_matrix_command_is_batched and test_short_matrix_command_dispatches_immediately.

How to Test

  1. Run the regression tests:

    python -m pytest tests/gateway/test_command_bypass_active_session.py tests/gateway/test_matrix_command_batching.py -q

    All tests should pass (42 existing + 2 new bypass tests + 2 new batching tests).

  2. Reproduce the original issue with the script from Matrix bang commands bypass split batching and skill-command active-session dispatch #58559 on the patched tree — both checks should now return True:

    {'long_matrix_command_is_batched': True, 'captured_count': 1}
    {'skill_command_bypasses_active_session': True}
    
  3. Run the full related test suite:

    python -m pytest tests/gateway/test_command_bypass_active_session.py tests/gateway/test_telegram_text_batching.py tests/gateway/test_matrix_command_batching.py -q

    Should pass: 58 tests.

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run pytest tests/ -q and all tests pass
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform: macOS 15.5

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — or N/A
  • I've updated cli-config.yaml.example if I added/changed config keys — or N/A
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — or N/A
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide — N/A (Python-only changes, no platform-specific behavior)
  • I've updated tool descriptions/schemas if I changed tool behavior — or N/A

…commands

Matrix bang commands that normalize to /command are MessageType.COMMAND,
which skipped the text-batching buffer. Long commands near _SPLIT_THRESHOLD
now go through batching so continuation chunks are aggregated.

Skill commands (e.g. /arxiv) are resolved through a separate registry and
were not recognized by should_bypass_active_session(), causing them to be
queued as user text instead of dispatched directly.

Fixes NousResearch#58559
@alt-glitch alt-glitch added type/bug Something isn't working comp/cli CLI entry point, hermes_cli/, setup wizard comp/plugins Plugin system and bundled plugins platform/matrix Matrix adapter (E2EE) P3 Low — cosmetic, nice to have labels Jul 4, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Related: fixes #58559 (Matrix bang-command batching + skill-command active-session bypass). Same Matrix bang-command family as merged salvage #38175 and docs PR #31533, but a distinct mechanism (split-threshold batching + active-session bypass, not !->/ alias normalization) — related, not duplicate.

@bbopen

bbopen commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Hi, thanks for jumping on #58559. I reported that issue and reviewed this PR against the repro plus a few adjacent Matrix/active-session cases. It fixes the long-command happy path, but I found a couple of gaps that seem worth covering before this is considered fully closed:

  • after should_bypass_active_session("arxiv") lets a skill command through the adapter, gateway/run.py's active-running branch still only treats built-ins from resolve_command() as recognized, so /arxiv ... can still fall into pending/interrupt handling instead of the normal slash-command busy response
  • skill/plugin command normalization differs from the existing resolver for underscore/hyphen forms
  • command-like split continuations, e.g. a continuation chunk starting with /, need to stay attached to the pending long command batch
  • a long command arriving while a plain-text batch is pending should not be merged into that text batch, or it loses MessageType.COMMAND

I opened draft follow-up/alternative #58591 with runner-level handling and extra regression tests. Happy for maintainers to use whichever PR shape is cleaner; I mainly wanted the extra edge cases captured so #58559 is fixed end-to-end.

@liuhao1024

Copy link
Copy Markdown
Contributor Author

Hi bbopen, thanks for the thorough review and for testing against the real repro plus adjacent cases. You're right that the4 gaps you identified are valid:

  1. Gateway active-running branch: Agreed — gateway/run.py's running-agent handler only recognizes built-ins from resolve_command(), so skill commands like /arxiv still fall into the pending/interrupt path. Our PR only addressed the should_bypass_active_session gate in hermes_cli/commands.py, not the downstream dispatch.

  2. Underscore/hyphen normalization: Good catch — the existing resolver handles both forms but our skill command lookup doesn't normalize.

  3. Split continuations: The batching logic doesn't account for continuation chunks starting with / needing to stay attached to the pending batch.

  4. Text vs command batch isolation: A long command arriving during a plain-text batch shouldn't merge into it.

Your #58591 looks like a more complete fix that addresses all four gaps at the runner level. Happy for the maintainers to choose whichever PR shape works best — if #58591 is preferred, this one can be closed.

@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 addressing the verified Matrix split-command and active-session issues. The current implementation still has the reported premises (plugins/platforms/matrix/adapter.py:2770-2788; hermes_cli/commands.py:389-409), but this patch does not cover the full dispatch path.

Problems

  • gateway/run.py:9345-9351 resolves only built-ins during an active run, and its busy catch-all is gated on _cmd_def_inner at gateway/run.py:9589-9602. This PR bypasses the adapter guard but does not make a skill command recognized by the runner.
  • The new skill lookup bypasses resolve_skill_command_key() (agent/skill_commands.py:496-512), so underscore/hyphen equivalents such as /claude_code and /claude-code diverge.
  • A slash-prefixed continuation is still classified as MessageType.COMMAND (plugins/platforms/matrix/adapter.py:2772-2774) and dispatched separately by the new condition. Also, _enqueue_text_event() appends into the existing event without changing its type (plugins/platforms/matrix/adapter.py:3463-3473), so a long command can be merged into a pending text batch and lose command semantics.

Suggested changes

  • Handle normalized skill/plugin commands in both active-session layers, then return the normal busy response.
  • Keep command continuations with a pending long command batch and isolate a new long command from a pending text batch; add regressions for both.

Automated hermes-sweeper review.

Comment thread hermes_cli/commands.py
# Skill commands (e.g. /arxiv) are resolved through a separate registry
# and must also bypass the active-session queue, matching how
# _resolve_matrix_bang_command already checks both registries.
try:

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.

Please resolve through resolve_skill_command_key() rather than an exact slash-prefixed lookup. Skill dispatch deliberately normalizes underscores to hyphens (agent/skill_commands.py:496-512), so /claude_code otherwise still fails the active-session bypass for /claude-code.

if msg_type == MessageType.TEXT and self._text_batch_delay_seconds > 0:
should_batch = self._text_batch_delay_seconds > 0 and (
msg_type == MessageType.TEXT
or (msg_type == MessageType.COMMAND and len(body) >= self._SPLIT_THRESHOLD)

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 only batches the initial long command. A short split continuation beginning with / is classified as MessageType.COMMAND and will dispatch immediately instead of joining the pending long command; a pending text batch can also absorb this command and retain MessageType.TEXT in _enqueue_text_event().

@alt-glitch alt-glitch added sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages labels Jul 15, 2026
@teknium1 teknium1 added 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 15, 2026
@alt-glitch alt-glitch added needs-decision Awaiting maintainer decision before any implementation and removed sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades labels Jul 15, 2026
@bbopen

bbopen commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Thank you, @liuhao1024, for the original fix and for the helpful coordination here. I rebuilt draft #58591 on current main as the end-to-end lane for #58559 and preserved your commit as its first commit with the original Author metadata (d769c4fb3; range-diff equivalent to 22d0e1f8). A separate follow-up commit adds only the reviewed runner, continuation-ordering, routed-profile, and Matrix auto-thread regressions/corrections. I will keep #58591 in draft through live CI and review, and will not ask maintainers to merge both overlapping implementations.

@teknium1 teknium1 added the area/sessions Session lifecycle, resume, persistence, history label Jul 19, 2026
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/cli CLI entry point, hermes_cli/, setup wizard comp/plugins Plugin system and bundled plugins needs-decision Awaiting maintainer decision before any implementation P3 Low — cosmetic, nice to have platform/matrix Matrix adapter (E2EE) sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages 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.

Matrix bang commands bypass split batching and skill-command active-session dispatch

4 participants