Skip to content

fix(whatsapp): multi-user session isolation and bridge message handling - #75

Merged
teknium1 merged 2 commits into
NousResearch:mainfrom
satelerd:fix/whatsapp-multi-user-sessions
Feb 27, 2026
Merged

fix(whatsapp): multi-user session isolation and bridge message handling#75
teknium1 merged 2 commits into
NousResearch:mainfrom
satelerd:fix/whatsapp-multi-user-sessions

Conversation

@satelerd

@satelerd satelerd commented Feb 26, 2026

Copy link
Copy Markdown
Contributor

I opened #31 about WhatsApp not working, and after the bridge was added and the gateway auth was fixed I started testing and found three issues. So I patched them locally and it's been working well. Sharing in case it's useful to merge upstream. Awesome project btw.

Summary

  • scripts/whatsapp-bridge/bridge.js: When WHATSAPP_ALLOWED_USERS is empty, fromMe messages in DMs were never filtered. Every agent reply got picked up as a new incoming message, causing interrupts and feedback loops. Fix: skip all fromMe except self-chat, detected by comparing remoteJid to sock.user.id.

  • gateway/session.py: _generate_session_key() returned agent:main:whatsapp:dm for every DM regardless of who's writing. Two people messaging at the same time shared conversation history and interrupted each other. Fix: WhatsApp DM keys now include the chat_id (agent:main:whatsapp:dm:{chat_id}). Other platforms unchanged.

  • gateway/session.py + gateway/run.py: Added user name/ID to the session context prompt so the agent knows who it's talking to. Updated approval session key to match the new format.

Also noticed that provider: auto in config.yaml isn't handled by the gateway credential resolution in run.py — it only checks provider == "nous", so auto falls through to OpenRouter. Didn't include that here since it's a different flow.

Test plan

  • Two different phone numbers messaging simultaneously get independent sessions
  • Agent responses don't trigger new message events in the bridge
  • Self-chat ("Message Yourself") still works
  • Telegram/Discord DM sessions unchanged
  • WhatsApp group chats unchanged

@satelerd
satelerd force-pushed the fix/whatsapp-multi-user-sessions branch from 82299cd to f02f647 Compare February 26, 2026 17:54
@teknium1

Copy link
Copy Markdown
Contributor

LGTM

@teknium1
teknium1 merged commit 80ad657 into NousResearch:main Feb 27, 2026
angelburgosrosado pushed a commit to angelburgosrosado/hermes-agent that referenced this pull request Apr 27, 2026
…ser-sessions

fix(whatsapp): multi-user session isolation and bridge message handling
jarvis-stark-ops pushed a commit to 1Team-Engineering/hermes-agent that referenced this pull request Jun 11, 2026
… document edit-result caveat

Independent review of v6.8 Part 2 flagged 3 polish items. All addressed.

## Regex tightening (NousResearch#75 follow-up)

The bullet-not-applicable regex `\S.{6,}` was accepting reasons of
7 chars (1 + 6) but the docstring and tests asserted ≥8 chars to
match the `imports_match_deliverable_entrypoints` invariant.

Tightened to `\S.{7,}` so 1 + 7 = 8 chars minimum. Same as the
imports_match length check, no off-by-one.

## Integration test gaps (NousResearch#74 follow-up)

Reviewer flagged two missing integration cases:
- Bare `verdict: reject` with no test_quality fields: floor bypasses
  (because verdict IS reject) but reviewer-fields gate catches the
  missing discipline. New test asserts the gate that catches it is
  MissingReviewerFieldViolation, NOT RuntimeFloorViolation. This
  proves the bypass doesn't accidentally green-light no-evidence
  rejects.
- No verdict at all: parsed_verdict is None, is_honest_reject is
  False, floor fires normally. New test asserts a result like "Just
  my thoughts, no verdict line." doesn't silently bypass the floor.

## edit_completed_task_result documentation

The reviewer noted this function exists at kanban_db.py:4180 to back-
fill results on done tasks (via `hermes kanban edit`). It bypasses
EVERY v6.7/v6.8 gate. If a future maintainer wires it into a worker
tool surface, every gate becomes bypassable: complete with low-
discipline, edit to high-discipline, or flip approve↔reject after
the gate already ran.

Added a multi-line .warning:: docstring listing every gate it
bypasses and the specific attacks a worker-accessible wiring would
enable. No code change — this is a comment-level guard for human
ops.

107/107 in test_kanban_completion_gates.py pass (105 + 2 new).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
jarvis-stark-ops added a commit to 1Team-Engineering/hermes-agent that referenced this pull request Jun 11, 2026
…_applicable evidence (NousResearch#74, NousResearch#75)

Two precision fixes for the v6.7 completion gates surfaced by the
2026-06-10 validation chain on hermes-dashboard. Both touch
kanban_completion_gates.py and ship together.

Closes hermes-jarvis#74 (honest-reject floor bypass)
Closes hermes-jarvis#75 (bullet not_applicable for evidence)

NousResearch#74: verify_runtime_floor gains is_honest_reject kwarg. When True
AND worker is in REVIEW_ROLES, floor bypassed. Build/orchestration
roles unaffected — a friday 60-second 'implementation' that ends
in reject still warrants the 5-min floor. Verdict parsed once at
top of _v6_7_run_completion_gates via existing _v6_7_parse_verdict.

NousResearch#75: _field_present for test_quality.evidence accepts
'- not_applicable: <reason>' bullet with reason ≥8 chars (regex
tightened from \\S.{6,} to \\S.{7,} per self-review). Same
code_change_context rule — code-touching reviews require real
citations.

Polish from self-reviews:
- Tightened bullet regex to enforce ≥8 chars
- Added 2 integration tests (bare reject hits reviewer-fields,
  null verdict still subject to floor)
- Documented edit_completed_task_result as bypassing every gate
- Removed stale dangling reference in kanban_tools.py
  (_enforce_kanban_complete_hygiene call) that would NameError

107 tests in test_kanban_completion_gates.py. 191/191 across full
v6.7+v6.8 + adjacent regression set, zero failures.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
waefrebeorn pushed a commit to waefrebeorn/slermes that referenced this pull request Jul 2, 2026
…ser-sessions

fix(whatsapp): multi-user session isolation and bridge message handling
ohungerCH added a commit to ohungerCH/hermes-agent that referenced this pull request Jul 11, 2026
VaultStore.recall() + vault_shadow_recall() + memory(action='recall'),
flag-gated (recall_enabled, default aus), dark. tsvector-first -- exakt-KNN
ist inert bis DLP+Embedding+Zone 52 (jede confirmed Zeile hat embedding=NULL).

- Vollständiger WHERE-Kontrakt (verlustfreie Inverse des Write-Provenienz-
  Pfads): lifecycle_status='confirmed' AND deleted_at/quarantined_at/
  superseded_at IS NULL AND tsvector-Match; tenant/owner via RLS-GUCs,
  NICHT im Query-Text.
- Untrusted-Wrap am Modell-Rand (entity-encode &,<,>): recalled Text ist
  DATEN, keine Anweisung -- Security-Vokabular bleibt abrufbar (NousResearch#75 warn-vs-
  block), Wrap-Escape unmöglich (Task NousResearch#34). Kein sanitization_state-Filter.
- Fail-soft + Ehrlichkeits-Klausel: leer != Abwesenheit; Fehler/tote DB ->
  available=False, NIE als "kein Gedächtnis".
- Read-only unter vault_transaction (RLS), fetchall innerhalb der Txn.

Beweis: 197 Unit-Tests grün (WHERE-Korrektheit, RLS-GUC-Reihenfolge, Wrap-
Escape, fail-soft, Ehrlichkeit). recall_enabled bleibt aus (kein Deploy).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ohungerCH added a commit to ohungerCH/hermes-agent that referenced this pull request Jul 11, 2026
Macht die Recall-Bedingung self-contained: der ungefilterte FTS-Index heisst,
die Query trägt die ganze Korrektheit -- sie darf sich nicht still auf die
Schreibseiten-Invariante confirmed⟹sanitisiert verlassen (wie deleted/
quarantined/superseded auch explizit stehen). Heute redundant (scanner-tot ->
stage -> candidate), aber fail-closed gegen einen künftigen Confirm-Promotion-
Flow, der Scanner-Liveness umginge. KEINE Kastration: Security-Vokabular ist
warn-only (NousResearch#75) -> scanner-gesund -> 'applied'. Test-Assertion + self-test-
Zeile (confirmed+pending -> ausgeschlossen) beweisen das Prädikat.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Meraniya pushed a commit to Meraniya/hermes-agent that referenced this pull request Aug 6, 2026
…nd dispatch (NousResearch#75)

service_manager.py (added in NousResearch#73, 1123 lines) shipped with zero test
coverage despite being on the gateway lifecycle critical path. Adds unit
tests for detect/get_service_manager backend selection, the S6ServiceManager
start/stop/restart/is_running/register paths, systemd+launchd restart,
the _dispatch_via_service_manager_if_s6 CLI wiring, and write_platform_config_field.
All system calls (s6-svc/systemctl/launchctl) are mocked — no real services touched.


Claude-Session: https://claude.ai/code/session_01PQKCc5mDedYAiCNyXnTezh

Co-authored-by: Claude <noreply@anthropic.com>
Meraniya pushed a commit to Meraniya/hermes-agent that referenced this pull request Aug 6, 2026
…ousResearch#77)

service_manager.py's S6ServiceManager._render_finish_script() imports
GATEWAY_FATAL_CONFIG_EXIT_CODE from gateway.restart, but the constant was
never defined there — only GATEWAY_SERVICE_RESTART_EXIT_CODE existed. This
raises ImportError on the real register_profile_gateway path, breaking s6
profile-gateway registration in production. Surfaced by test coverage
added in NousResearch#75 but left unfixed there.

Value chosen: 78 (EX_CONFIG in sysexits.h), distinct from
GATEWAY_SERVICE_RESTART_EXIT_CODE=75 so the s6 finish script can tell a
fatal config error (do not respawn) apart from a normal restart request.

Co-authored-by: Claude <noreply@anthropic.com>
sijav added a commit to sijav/sijav-agent that referenced this pull request Aug 7, 2026
… 5/Fable 5 no longer price as 'unknown' (NousResearch#75) [roast 9.7/9.6/9.6]

_OFFICIAL_DOCS_PRICING was missing the current Claude 5 models, so Opus 5 (the
default) and Fable 5 sessions resolved to unknown cost. Adds first-party
claude-opus-5 ($5/$25), claude-opus-5-fast ($10/$50), claude-fable-5 ($10/$50),
claude-mythos-5 ($10/$50), dated claude-haiku-4-5-20251001 ($1/$5), and Bedrock
anthropic.claude-opus-5 / anthropic.claude-fable-5. Cache rates follow the file
convention (read 0.1x, write 1.25x input). Prices cross-checked against the
claude-api skill; all 7 models verified real (incl. Mythos 5 = Project Glasswing).
Pure additions (0 deletions). 4 new resolver tests; fail-before/pass-after proven.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants