Skip to content

feat(bot-mode): bring Group Chats to mobile gateways - #100386

Open
andrexibiza wants to merge 20 commits into
NousResearch:mainfrom
andrexibiza:feat/bot-group-mobile-gateway
Open

andrexibiza wants to merge 20 commits into
NousResearch:mainfrom
andrexibiza:feat/bot-group-mobile-gateway

Conversation

@andrexibiza

@andrexibiza andrexibiza commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Bot Group Chats on mobile gateways

Fixes #89166

Related: #97681
Source composition: #97846, #98073, #96162, #89180

Why this matters to users

A Group Chat should not become unreachable when its owner leaves Desktop. From an authorized Signal, Telegram, WhatsApp, Slack, Matrix, Discord, SMS, email, or other connected chat, the owner can inspect recent activity, send the next instruction, retry failed work, approve or deny an exact hosted command, or Stop the Group Chat without knowing which gateway currently hosts it.

Before this change, mobile clients could not reliably control Desktop-created or gateway-hosted Group Chats, and periodic cross-process lease refreshes could flood adapters that cannot edit a status message in place. After it, Group Chats have one durable /group control surface across Desktop and messaging gateways, while non-editing adapters keep the initial wait notice and terminal warning without receiving a new refresh message every 15 seconds.

What changed

  • /group lists and opens existing Bot Group Chats from mobile messaging clients.
  • /group <number> send <message> appends one durable room turn without acquiring or interrupting the ordinary chat session's turn lease.
  • /group <number> retry and /group <number> stop use exact idempotency, authority, generation, and cancellation fences.
  • /group <number> approvals, approve, and deny expose only exact, owner-authorized hosted command decisions; permanent/session-wide approval is not available from messaging.
  • Telegram, Discord, and Matrix receive native bounded room/participant controls; Signal, WhatsApp, Slack, SMS, email, and other adapters receive the same hierarchy as bounded text.
  • Same-gateway and cross-gateway rooms preserve authoritative replay and can continue without Desktop when their routes are reachable.
  • Classic Desktop-driven rooms use a durable exact-owner mailbox rather than silently executing only part of the group.
  • Periodic session-lease wait refreshes no longer flood adapters that cannot update status in place; the initial wait notice and terminal timeout warning remain.

The mobile path is gateway control, not a model prompt. It dispatches while an ordinary agent turn is active without cancelling, steering, or queueing behind that conversation.

Git authorship and provenance

The prior submitted object 2e2ecc074b4c37f22408cc6f29071e6bb251e314 incorrectly collapsed the composition into one commit authored solely by Axl. Body acknowledgements were not sufficient Git attribution. David Dudok de Wit (@dokterdok) identified the defect in this source-composition review; that assessment is correct. This head corrects it forward-only: the published commit remains the first parent, while the complete source-authored train is the second parent. No force-push, rebase, amend, or source-history rewrite is required.

Preserved source work:

The final bridge 5a467ef9671eed2a2cbd70c011c6c87187daf789 is byte-identical to its source-composed second parent e3ec744b16c591f4e3fda49188e53466bd1fb172; it exists only to advance the already-published PR ref without rewriting history.

The submitted head 63fd45b6245b3430db17fdeb409f38932eb3414d then merges current main@2e25b472108d0f36e02a95bae212255ae76fac0e at 4e1051daa25b8581d914ca893aad38fc66e6e1e2. The three landing-edge conflicts preserve both sides: Slack's resolved Bot predicate plus api_human_users, Discord's guild/profile routing regression, and multi-profile session signatures through the extracted watcher helper.

Reliability and security

  • Stable transport IDs make Send, Retry, Stop, and approval decisions idempotent under redelivery.
  • Owner/admin authorization is re-evaluated at action time.
  • Approval decisions are bound to room authority and epoch, Bot task, execution generation, request, and delivery.
  • Shared or unclassified chats, bot/webhook authors, edited commands, unstable delivery identities, unexpected attachments, and incomplete relay provenance fail closed.
  • Room authority, epoch, member routes, Desktop ownership, command claims, retries, and Stop are durably fenced.
  • Native picker callbacks are bound to the requesting user and exact message, expire, and re-resolve live room state.
  • User/Bot labels and activity previews are bounded and neutralized before client formatting.
  • Classic Desktop execution is bounded; repeated failure becomes explicitly retryable by the owner rather than being driven indefinitely.

Composition order

  1. feat(bot-mode): integrate hosted Group Chats into Desktop #97846 source train through 240f42ff4f, including @jugol's source-authored cross-source creation forward port.
  2. feat(bot-mode): control Group Chats from messaging #98073 messaging/approval train through 6fb588dc7c, including ownership splits and hardening.
  3. fix(gateway): stop lease-wait refreshes flooding non-editing chat adapters #89180 current-architecture source-authored replay 6a746344e42a.
  4. Acceptance regressions and mobile-control documentation.
  5. Lease-status ownership shard e3ec744b16c5: the new owner is 38 lines; gateway/run.py has zero net growth and run_agent.py shrinks by one line relative to the source merge base.
  6. Append-only bridge 5a467ef9671e from the published PR head to the source-composed tree.
  7. Current-main landing-edge merge 4e1051daa25b, with all three conflicts resolved by preserving both contracts.
  8. Windows hygiene 63fd45b6245b: every changed watcher fixture writes text with explicit UTF-8 encoding.

This is the integrated landing object. If it lands, the overlapping source branches must not then be merged independently as duplicate feature payloads; their authorship already survives here in Git.

How to test

scripts/run_tests.sh tests/gateway/test_slack_api_human_senders.py
scripts/run_tests.sh tests/gateway/test_group_chat_slack_adapter.py
scripts/run_tests.sh tests/gateway/test_discord_slash_commands.py
scripts/run_tests.sh tests/tui_gateway/test_change_watcher.py
scripts/run_tests.sh tests/gateway/test_session_lease_wait_refresh.py
scripts/run_tests.sh tests/gateway/test_hosted_room_messaging.py
scripts/run_tests.sh tests/gateway/test_desktop_room_mailbox.py
scripts/run_tests.sh tests/gateway/test_hosted_room_peer.py
scripts/run_tests.sh tests/gateway/test_command_bypass_active_session.py

Exercise from an authenticated owner chat:

/group
/group 2
/group 2 bots
/group 2 send Review the launch checklist
/group 2 approvals
/group 2 approve A1B2C3D4
/group 2 deny A1B2C3D4
/group 2 retry
/group 2 stop

Verification

Local verification on the exact final tree (63fd45b6245b3430db17fdeb409f38932eb3414d, tree 90c847d67a2d204b31c7eaecbca564445f5ed0bf):

  • canonical focused runner: 184 passed, 1 skipped, 0 failed across the nine test files above;
  • python scripts/audit_pr_attribution.py: passed;
  • Ruff on the lease shard and touched Python owners: passed;
  • Windows footgun scan on the complete PR diff: passed, 85 files scanned;
  • import/seam identity: run_agent exports the canonical template object and gateway.run exports the canonical matcher object;
  • git diff --check: passed;
  • current-main merge simulation: passed with no conflicts;
  • no modified legacy file above 2,000 lines grows relative to merge base 2e25b472108d0f36e02a95bae212255ae76fac0e; new session_turn_lease.py is 38 lines.

The incorporated #98073 source head 6fb588dc7c367139dcb25986d611893170066afc is independently green on GitHub for required CI, contributor attribution, Python, JavaScript/TypeScript, Windows, macOS, Docker amd64/arm64, and Nix. Those are source-head receipts, not a substitute for final-head CI. Final hosted CI is not claimed until GitHub completes it on the corrected PR head.

dokterdok and others added 5 commits August 31, 2026 11:38
Surface stale peer authorization and journal exact-grant cleanup across Desktop restarts.
Give Bot Group Chats one durable gateway control surface across Telegram, Discord, Matrix, Signal, WhatsApp, Slack, and other messaging clients. Mobile commands append to the room log or Desktop mailbox without interrupting the ordinary agent session, preserve exact authority/idempotency/stop fences, and suppress standalone lease-wait refresh floods on adapters that cannot update status in place.
@alt-glitch alt-glitch added type/feature New feature or request comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/cli CLI entry point, hermes_cli/, setup wizard comp/desktop Electron desktop app (apps/desktop/*) comp/gateway Gateway runner, session dispatch, delivery comp/plugins Plugin system and bundled plugins comp/tui Terminal UI (ui-tui/ + tui_gateway/) area/sessions Session lifecycle, resume, persistence, history P2 Medium — degraded but workaround exists needs-decision Awaiting maintainer decision before any implementation 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 sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data labels Sep 1, 2026
dokterdok and others added 8 commits September 2, 2026 07:53
Replace the prior flaky-check object while preserving the green source-authored train.
Recompose the reviewed Messaging contract on the current Desktop continuity head.
…pters

Cross-process session turn lease waits emit a periodic 'Still waiting'
refresh roughly every 15s. On adapters without send_or_update_status
the status path falls back to a plain send, so each refresh landed as
another standalone chat message — eight messages for a two-minute wait
on WeCom/Weixin/QQ/Signal-class adapters, drowning the eventual
delivery.

Extract the refresh wording into a module-level template constant and
derive a compile-once matcher from it, then suppress the periodic
refresh in the status callback when the adapter cannot update the
status in place. Adapters that can (Telegram, Slack) keep refreshing
the existing bubble. The initial wait notice and the lease-timeout
warning use different wording and are always delivered.

(cherry picked from commit 6cb8e33)
Rebuild NousResearch#100386 as an append-only composition instead of replacing contributor
history with a single-author squash.

The second-parent train preserves:
- David Dudok de Wit's NousResearch#97846/NousResearch#98073 continuity and messaging commits through
  exact source head 6fb588d.
- jugol's source-authored NousResearch#96162 forward port at
  22b26d0.
- liuhao1024's NousResearch#89180 lease-refresh fix through the adapted source-authored
  replay 6a74634.

The lease-status extraction at e3ec744
keeps gateway/run.py from growing, keeps run_agent.py shrinking, and preserves
the original import seams and behavior.

This merge commit changes no bytes relative to its second parent. Its first
parent is the published NousResearch#100386 head, so the correction is forward-only: no
force-push, rebase, amend, or source-history rewrite.

Related: NousResearch#97681
Fixes: NousResearch#89166
Preserve the resolved Slack Bot-sender predicate with api_human_users, retain Discord guild/profile routing coverage, and compose multi-profile session signatures through the extracted watcher helper.
@andrexibiza

Copy link
Copy Markdown
Contributor Author

I owe David Dudok de Wit (@dokterdok), @jugol, and @liuhao1024 an apology for the malformed Git provenance in the earlier head of this PR.

I collapsed the composed work into 2e2ecc074b4c37f22408cc6f29071e6bb251e314, a single commit authored only by me, and then treated acknowledgements in the PR body as though they were equivalent to preserving authorship in Git. They are not. That erased the contributor-authored topology maintainers need to inspect and land the work correctly.

The repaired head is now 63fd45b6245b3430db17fdeb409f38932eb3414d, advanced forward-only without a force-push or history rewrite. It preserves:

I am sorry. This should have been caught before the original push. Body credit never substitutes for source-authored commits in the graph; overlapping work must be deduplicated and composed with authorship intact before a PR head moves.

andrexibiza commented Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

Landing-edge interlock update — 2026-09-02

Fresh live FILE-LIST reconciliation against main@f6234d00c5d59450adea1d7edd30ad3859375c79 keeps two newer Group Chat carriers in the landing graph:

These are complementary mechanisms, not duplicates of #100386's mobile Group Chat control/continuity composition. Whichever object lands first changes the landing edge for the others: every later carrier must recompose its shared paths on the landed implementation, preserve both contracts, and reacquire exact-head/every-surviving-commit proof. No receipt transfers across that recomposition.

Current #100386 head remains 63fd45b6245b3430db17fdeb409f38932eb3414d. This is a topology receipt, not a self-review or a release-ready claim.

@dokterdok

Copy link
Copy Markdown
Contributor

Thanks, Axl. I appreciate the quick correction and the clear provenance breakdown. All good from my side.

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 comp/cli CLI entry point, hermes_cli/, setup wizard comp/desktop Electron desktop app (apps/desktop/*) comp/gateway Gateway runner, session dispatch, delivery comp/plugins Plugin system and bundled plugins comp/tui Terminal UI (ui-tui/ + tui_gateway/) needs-decision Awaiting maintainer decision before any implementation P2 Medium — degraded but workaround exists sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages 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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Cross-process session lease wait floods chat gateways every 15s and starves final delivery

5 participants