Skip to content

Fix/whatsapp channel bugs#2554

Closed
dvirarad wants to merge 7 commits into
nanocoai:mainfrom
dvirarad:fix/whatsapp-channel-bugs
Closed

Fix/whatsapp channel bugs#2554
dvirarad wants to merge 7 commits into
nanocoai:mainfrom
dvirarad:fix/whatsapp-channel-bugs

Conversation

@dvirarad
Copy link
Copy Markdown
Contributor

Type of Change

  • Feature skill - adds a channel or integration (source code changes + SKILL.md)
  • Utility skill - adds a standalone tool (code files in .claude/skills/<name>/, no source changes)
  • Operational/container skill - adds a workflow or agent skill (SKILL.md only, no source changes)
  • Fix - bug fix or security fix to source code
  • Simplification - reduces or simplifies source code
  • Documentation - docs, README, or CONTRIBUTING changes only

Description

For Skills

  • SKILL.md contains instructions, not inline code (code goes in separate files)
  • SKILL.md is under 500 lines
  • I tested this skill on a fresh clone

dvirarad and others added 7 commits May 12, 2026 07:27
…resh

- Install WhatsApp adapter from upstream/channels per /add-whatsapp skill
- Baileys 7.0.0-rc.9 + qrcode + pino pinned
- Patch 5: strip orphan UTF-16 surrogates from inbound content
  (Patches 2/3/4/6 already in v2 channels branch)
- google-oauth-refresh.ts: refresh ~/.gmail-mcp + ~/.calendar-mcp tokens
  before container spawn if expiring within 10 min

Tests: 323 passed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…P remote

Dockerfile:
- poppler-utils system dep for pdftotext (PDF reader)
- @gongrzhe/server-gmail-autoauth-mcp@1.1.11 + zod-to-json-schema@3.22.5 pinned
- mcp-remote@0.1.29 pinned for external MCP server support
- pdf-reader CLI installed to /usr/local/bin/pdf-reader

container/skills/pdf-reader/: SKILL.md + pdftotext wrapper script (ported from v1)

Voice transcription:
- New src/transcription.ts: OpenAI Whisper API call, reads audio from disk
- Wired into v2 whatsapp.ts after downloadInboundMedia: voice messages
  get transcribed and prepended to content

Gmail-tool: Dockerfile install only (Phase 2). Phase 3 per-agent-group
container.json wiring deferred to deployment time.

MCP remote: Dockerfile install only. Per-agent-group MCP server
registration via container_configs DB at deployment time.

NOT ported (obsolete in v2):
- Image vision (sharp resize): Claude Code's Read tool handles images natively
- Multimodal image content blocks: same — Read tool gives agent vision
- Gmail-as-channel: v2 uses gmail-as-tool model; original inbound polling
  deferred (user can rebuild later if needed)

Build: clean. Tests: 323 passed. Typecheck: clean. Eslint: 19 pre-existing
warnings (no new errors).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The lockfile was previously generated with pnpm 8.9.2 (local Homebrew install)
which produced a v8 format incompatible with the packageManager-declared
pnpm@10.33.0. Server pnpm 11.1.0 (via corepack) refused the v8 lockfile
during migrate-v2.sh bootstrap, blocking the migration.

Build clean, 323 tests pass with the regenerated lockfile.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…at groups)

v2's whatsapp.ts unconditionally dropped fromMe=true messages in non-self-chat
groups, assuming ASSISTANT_HAS_OWN_NUMBER=true. With the v1 shared-number setup
(bot uses the user's own WhatsApp number), every message the user types in any
group is fromMe=true and was being silently filtered before reaching the router.

Symptom: bot ignored user messages in group chats wired to non-self-chat
agent groups (Workshop Andy, Andy LinkedIn, etc.). The Channel metadata
"discovered" event fired but no "Message routed" followed.

Fix: in fromMe filter, only skip non-self-chat fromMe messages when:
- sentMessageCache hits (we just sent them), OR
- ASSISTANT_HAS_OWN_NUMBER=true (dedicated-number mode), OR
- content starts with `${ASSISTANT_NAME}:` (shared-number bot echo prefix)

Otherwise process — it's the user typing on the shared phone.

Deployed live to server first, then back-ported here. Tests still 323/323.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ed groups

Previously every new phone number that DMed the bot triggered a "💬 New
direct message" approval card sent to the owner's main DM. With ~17
inbound contacts post-migration, this became spam.

Change default for `createMessagingGroup` (auto-created on first inbound
from unknown sender) from 'request_approval' → 'strict'. Strangers are
now silently dropped. The owner can manually wire trusted contacts via
ncl messaging-groups update / wirings create when needed.

Server-side: ran SQL UPDATE on existing 17 'request_approval' rows to
'strict' as a separate hot fix.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…data/attachments/

Symptom: agent receives messages like `[image: photo.jpg — saved to
/workspace/attachments/photo.jpg]` but /workspace/attachments/ doesn't
exist inside the container, so `Read` returns "directory not found."
Affects images, voice notes, PDFs.

Root cause: downloadInboundMedia wrote to `${DATA_DIR}/attachments/`
on the host, but data/attachments/ is not mounted into any agent
container. The path rendered in the agent's prompt referenced a
phantom location.

Fix:
- Resolve the wired agent group's folder from chatJid via
  getMessagingGroupByPlatform → getMessagingGroupAgents (priority DESC)
  → getAgentGroup. Skip download for cold-DM / unwired senders.
- Save to ${GROUPS_DIR}/<folder>/attachments/<file> instead. The group
  folder is mounted at /workspace/agent/, so files land at
  /workspace/agent/attachments/<file> in the container.
- Set localPath: "agent/attachments/<file>" so the formatter renders
  /workspace/agent/attachments/<file> (formatter prepends /workspace/).
- Return both localPath (container-relative) and hostPath (absolute,
  for host-side consumers like transcription). Strip hostPath before
  serializing attachments into messages_in.
- transcribeAudioFile now called with audio.hostPath (absolute path)
  instead of audio.localPath, since localPath is no longer resolvable
  against DATA_DIR after this change.

Multi-agent caveat: when one messaging group is wired to several agent
groups (priority list), the file lands only in the highest-priority
group's folder. Documented in a code comment; not relevant to the
current 1:1 wiring setup.

Tests: 323/323 pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The WhatsApp adapter hardcoded `isMention: !isGroup ? true : undefined`,
so a mention of the assistant inside a group was never flagged as a
mention. The router then dropped the message at the unknown-group
gate (router.ts auto-creates a messaging_group only when isMention is
true), so new groups never produced an approval card and the agent
silently ignored them.

Run the configured TRIGGER_PATTERN against the message content for
group messages too. DMs keep their always-true behavior since they
are addressed to the bot by definition.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added the follows-guidelines PR was created using the current contributing template label May 19, 2026
@gavrielc gavrielc closed this May 23, 2026
@gavrielc
Copy link
Copy Markdown
Collaborator

Closing as there is no description and it contains many unrelated code changes

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

Labels

follows-guidelines PR was created using the current contributing template

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants