Commit Graph

91 Commits

Author SHA1 Message Date
gavrielc
f26468c9b0 fix: setup skill reliability, requiresTrigger option, agent-browser visibility
Setup skill fixes:
- Run QR auth in foreground with long timeout, not background
- Replace fragile message-based registration with DB group sync lookup
- Personal chats: ask for phone number instead of querying empty DB
- Consolidate trigger word + security model + channel selection into one step
- Remove `timeout` shell command (unavailable on macOS), use Bash tool timeout
- Query 40 groups, display 10 at a time, support name lookup

requiresTrigger support:
- Add requiresTrigger field to RegisteredGroup type and DB schema
- Skip trigger check when requiresTrigger is false (for solo/personal chats)
- Main group still always processes all messages (unchanged)

Agent-browser visibility:
- Append global CLAUDE.md to non-main agent system prompts via SDK
- Add browser tool docs to global and main CLAUDE.md
- Update skill description to be broader (not just "web testing")
- Reference agent-browser.md in root CLAUDE.md key files

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 01:39:31 +02:00
Gavriel Cohen
675ed30ba0 fix: improve container error logging to include full stdout/stderr
Always log detailed input/output/stderr on error (not just in verbose
mode), and stop truncating stderr/stdout in structured log fields.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 00:17:46 +02:00
gavrielc
8dd27bc58d fix: defend against missing structured output and message without content
- Fall back to text result when success subtype has no structured_output
- Treat outputType 'message' without userMessage as 'log' with warning

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 20:29:04 +02:00
gavrielc
2ecda36df2 small tweak to acknowledgement prompt 2026-02-06 20:25:21 +02:00
gavrielc
44f0b3d99c fix: improve agent output schema, tool descriptions, and shutdown robustness
- Rename status→outputType, responded/silent→message/log for clarity
- Remove scheduled task special-casing: userMessage now sent for all contexts
- Update schema, tool, and CLAUDE.md descriptions to be clear and
  non-contradictory about communication mechanisms
- Use full tool name mcp__nanoclaw__send_message in docs
- Change schedule_task target_group to accept JID instead of folder name
- Only show target_group_jid parameter to main group agents
- Add defense-in-depth sanitization and error callback to exec() in shutdown
- Use "user or group" consistently (supports both 1:1 and group chats)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 20:22:45 +02:00
gavrielc
ae177156ec feat: per-group queue, SQLite state, graceful shutdown (#111)
* fix: wire up queue processMessagesFn before recovery to prevent silent message loss

recoverPendingMessages() was called after startMessageLoop(), which meant:
1. Recovery could race with the message loop's first iteration
2. processMessagesFn was set inside startMessageLoop, so recovery
   enqueues would fire runForGroup with processMessagesFn still null,
   silently skipping message processing

Move setProcessMessagesFn and recoverPendingMessages before startMessageLoop
so the queue is fully wired before any messages are enqueued.

https://claude.ai/code/session_01PCY8zNjDa2N29jvBAV5vfL

* feat: structured agent output to fix infinite retry on silent responses (#113)

Use Agent SDK's outputFormat with json_schema to get typed responses
from the agent. The agent now returns { status: 'responded' | 'silent',
userMessage?, internalLog? } instead of a plain string. This fixes a
critical bug where a null/empty agent response caused infinite 5-second
retry loops by conflating "nothing to say" with "error".

- Agent runner: add AGENT_RESPONSE_SCHEMA and parse structured_output
- Host: advance lastAgentTimestamp on both responded AND silent status
- GroupQueue: add exponential backoff (5s-80s) with max 5 retries for
  actual errors, replacing unbounded fixed-interval retries

https://claude.ai/code/session_014SLc8MxP9BYhEhDCLox9U8

Co-authored-by: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-02-06 18:54:26 +02:00
gavrielc
03df69e9b5 fix: address review feedback for per-group queue reliability
- Fix startup recovery running before WhatsApp connects, which could
  permanently lose agent responses by advancing lastAgentTimestamp
  before sock is initialized
- Add 5s retry on container failure so messages aren't silently dropped
  until a new message arrives for the group
- Use `container stop` in shutdown instead of raw SIGTERM to CLI wrapper,
  ensuring proper container cleanup
- Replace unnecessary dynamic imports with static imports in processTaskIpc
- Guard JSON.parse of DB-stored last_agent_timestamp against corruption
- Validate MAX_CONCURRENT_CONTAINERS (default 5, min 1, NaN-safe)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 16:45:00 +02:00
gavrielc
eac9a6acfd feat: per-group queue, SQLite state, graceful shutdown
Add per-group container locking with global concurrency limit to prevent
concurrent containers for the same group (#89) and cap total containers.
Fix message batching bug where lastAgentTimestamp advanced to trigger
message instead of latest in batch, causing redundant re-processing.
Move router state, sessions, and registered groups from JSON files to
SQLite with automatic one-time migration. Add SIGTERM/SIGINT handlers
with graceful shutdown (SIGTERM -> grace period -> SIGKILL). Add startup
recovery for messages missed during crash. Remove dead code: utils.ts,
Session type, isScheduledTask flag, ContainerConfig.env, getTaskRunLogs,
GroupQueue.isActive.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 07:38:07 +02:00
gavrielc
db216a459e fix: proper container lifecycle management to prevent stopped container accumulation
- Name containers (nanoclaw-{group}-{timestamp}) for trackability
- Replace SIGKILL timeout with graceful `container stop` so --rm fires
- Add startup sweep to clean up stopped nanoclaw containers from previous runs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 07:10:26 +02:00
Gavriel Cohen
3a4d340f80 Fix duplicate responses caused by reconnect-stacking loops
WhatsApp reconnections called startMessageLoop/startSchedulerLoop/
startIpcWatcher and setInterval again without stopping the previous
instances, creating parallel loops that processed the same messages.

Add guard flags so each loop starts only once per process lifetime.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 00:18:55 +02:00
Tom Granot
1f8cd26a83 Add voice transcription skill using OpenAI Whisper API (#77)
New skill `/add-voice-transcription` that guides users through adding
automatic voice message transcription to NanoClaw.

Features:
- Uses OpenAI Whisper API for transcription (~$0.006/min)
- Provider-agnostic architecture (supports future Groq/Deepgram/local)
- Secure API key storage in gitignored config file
- Graceful fallback when transcription unavailable
- Voice messages stored as `[Voice: <transcript>]` in database

The skill includes:
- Step-by-step implementation guide
- Database and message handler integration
- Configuration options (enable/disable, fallback message)
- Troubleshooting section
- Security notes and cost management tips
- Removal instructions

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 13:03:50 +02:00
Ejae-dev
117980175e refactor: deduplicate logger into shared module (#39)
three files created identical pino logger instances with the same config.
extract into src/logger.ts and import from each consumer.

net -9 lines, no behavior change.

Co-authored-by: ejae <ejae_dev@ejaes-Mac-mini.home>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 00:40:58 +02:00
yingchao
392ba6262c fix: translate WhatsApp LID JIDs to phone JIDs for self-chat messages (#62)
WhatsApp recently changed to send self-chat messages using LID (Linked
ID) format (e.g., xxxxxx@lid) instead of phone number format (e.g.,
xxxxxx@s.whatsapp.net). This caused messages to yourself to be silently
dropped because they didn't match any registered group.

## How to reproduce
1. Send a message to yourself on WhatsApp with the trigger
2. Message is received by Baileys but remoteJid is in LID format
3. LID JID doesn't match registered group JID (phone format)
4. Message is not stored and no response is sent

## The fix
- Build a LID-to-phone mapping from sock.user on connection open
- Translate incoming LID JIDs to phone JIDs before storing/processing messages
- This allows self-chat messages to correctly match the registered main channel

The mapping is populated from sock.user.id (phone) and sock.user.lid (LID)
which Baileys provides after successful authentication.
2026-02-04 00:33:50 +02:00
BaiJunjie
c9ca34a518 Add X integration skill (#52) 2026-02-04 00:27:04 +02:00
Len Hoare
3084fab45d Remove ToS gray areas section from README (#65)
Removed a section discussing the legitimacy of using the Claude Agent SDK with a subscription.
You shouldn't be saying that. 
You are right, you are not a lawyer. 
Claude's ToS specify fair use about what an agent can be used for. It's nothing to do with whether you've used the SDK. 
Let people make their own minds up. 
To advertise something like this is beyond our pay grade.
2026-02-04 00:11:10 +02:00
Gavriel Cohen
7ff9a65792 Update setup skill to use claude setup-token for auth
Replace manual credential extraction with the proper CLI command.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 21:03:35 +02:00
gavrielc
21c66df2b1 Add prettier
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 17:14:17 +02:00
Tom Granot
1a32bff6ec Improve setup UX with AskUserQuestion tool and security education (#60)
- Add UX note instructing Claude to use AskUserQuestion tool for better
  interactive experience during setup
- Add new Section 7 explaining the main channel's elevated privileges
  (admin control portal) before registration
- Include interactive security acknowledgment with follow-up for users
  choosing shared groups
- Renumber subsequent sections (7→8, 8→9, 9→10)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 17:04:45 +02:00
gavrielc
80e68dc00d Add social preview image
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 13:52:21 +02:00
gavrielc
722351159e Add contribution guidelines and PR checks for skills-only model
- CONTRIBUTING.md: Explain accepted source changes vs skills
- PR template: Checkboxes for contribution type
- CI workflow: Block PRs that add skills while modifying source
- CODEOWNERS: Require maintainer review for source changes

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 13:50:25 +02:00
gavrielc
ff23125800 Add Docker support and integrate /convert-to-docker into setup flow
- Update setup skill to detect platform and offer Docker/Apple Container choice
- On Linux, automatically use Docker via /convert-to-docker skill
- On macOS, ask user preference if Apple Container not installed
- Update README to reflect Docker support and Linux compatibility
- Fix exact line number reference in convert-to-docker skill
- Add thank you to @dotsetgreg for the Docker skill contribution

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 13:35:27 +02:00
gavrielc
849b22400b Fix minor issues in add-parallel skill
- Remove unrelated ASSISTANT_NAME from allowedVars example
- Fix log message reference (remove incorrect [agent-runner] prefix)
- Revert unrelated .gitignore addition (.backups/)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 13:23:28 +02:00
Janni Turunen
1b960c563f Add /add-parallel skill for Parallel AI integration (#28)
Adds a skill to integrate Parallel AI MCP servers for advanced web research.

What it adds:
- Quick Search MCP for fast web lookups (free to use)
- Task MCP for deep research with scheduler-based polling
- Non-blocking design using NanoClaw's scheduler

Usage: Run /add-parallel to add this integration to your fork

Follows NanoClaw philosophy:
- Skills over features - integration is optional via skill
- Keeps base codebase lean
- Users run /add-parallel on their fork for clean code

Co-authored-by: Janni Turunen <janni@Jannis-MacBook-Air.local>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-02 13:16:41 +02:00
Greg King
93e639fdb9 Add /convert-to-docker skill for Docker migration (#23)
* Add /convert-to-docker skill for Docker migration

This skill provides step-by-step instructions for migrating NanoClaw
from Apple Container to Docker, enabling cross-platform support
(macOS and Linux).

The skill covers:
- Updating container-runner.ts mount syntax and spawn command
- Replacing the startup check in index.ts
- Updating build.sh commands
- Updating all documentation references
- Updating setup and debug skills
- Verification and testing steps

Addresses the RFS (Request for Skills) item in README.md.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Remove Container Runtime from RFS section

The /convert-to-docker skill is now being added in this PR,
so it's no longer a "request" - the skill exists.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 09:16:37 +02:00
gavrielc
fb5dbcbc12 Update README.md 2026-02-02 02:42:42 +02:00
gavrielc
40d41542d2 Update README.md 2026-02-02 01:39:37 +02:00
gavrielc
98f82b7645 Update README.md 2026-02-02 01:08:59 +02:00
gavrielc
d20df2e785 Update README.md 2026-02-02 00:56:01 +02:00
Gavriel
4711ec435a Add register_group IPC command for dynamic group registration
Main agent can now register new groups via MCP tool without restart.
Host updates both in-memory state and JSON file, creates group folders.
Authorization enforced at both agent and host level.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 00:08:40 +02:00
gavrielc
05a29d562f Security improvements: per-group session isolation, remove built-in Gmail
- Isolate Claude sessions per-group (data/sessions/{group}/.claude/)
  to prevent cross-group access to conversation history
- Remove Gmail MCP from built-in (now available via /add-gmail skill)
- Add SECURITY.md documenting the security model
- Move docs to docs/ folder (SPEC.md, REQUIREMENTS.md, SECURITY.md)
- Update documentation to reflect changes

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 00:07:59 +02:00
Gavriel
22eb525805 Add Qwibit Ops context and NanoClaw Testing group
- Update main group CLAUDE.md with Qwibit Ops folder access docs
- Add WhatsApp formatting guidelines
- Create NanoClaw Testing group folder

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 23:53:15 +02:00
gavrielc
17f7b84420 Add /add-gmail skill for Gmail integration
Skill guides users through adding Gmail to NanoClaw with two modes:
- Tool Mode: Agent can read/send emails when triggered from WhatsApp
- Channel Mode: Emails can trigger the agent and receive replies

Includes step-by-step GCP OAuth setup, code integration instructions,
and troubleshooting guidance.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 23:52:05 +02:00
gavrielc
d000f33928 Add container output size limiting to prevent memory issues (#18)
* Fix potential memory DoS via unbounded container output

Add CONTAINER_MAX_OUTPUT_SIZE (default 10MB) to limit accumulated
stdout/stderr from container processes. Without this limit, a malicious
or buggy container could emit huge output leading to host memory
exhaustion.

Changes:
- Add configurable CONTAINER_MAX_OUTPUT_SIZE in config.ts
- Implement size-limited output buffering in runContainerAgent
- Log warnings when truncation occurs
- Include truncation status in container logs

https://claude.ai/code/session_01TjVDwwaGwbcFDdmrFF2y8B

* Update package-lock.json

https://claude.ai/code/session_01TjVDwwaGwbcFDdmrFF2y8B

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-02-01 23:09:50 +02:00
gavrielc
33ef0c68d3 Fix message cursor to only advance on successful processing (#17)
Previously, lastTimestamp was unconditionally advanced after each message,
even if processMessage failed. This caused transient errors to permanently
drop messages since they would never be retried.

Now the cursor only advances after successful processing, implementing
at-least-once delivery semantics. On failure, the loop breaks and the
failed message will be retried on the next poll iteration.

https://claude.ai/code/session_01SEQDWxXeZHe7t1bb5cw2CA

Co-authored-by: Claude <noreply@anthropic.com>
2026-02-01 23:05:37 +02:00
gavrielc
c45f0efcdb Escape regex metacharacters in ASSISTANT_NAME for trigger pattern (#16)
ASSISTANT_NAME was interpolated directly into a regex without escaping.
If the name contained regex metacharacters (e.g., @A.*), the trigger
would match unintended patterns. This adds escapeRegex() to properly
escape special characters before building the TRIGGER_PATTERN.

https://claude.ai/code/session_01Lvuxq73qa9S4rtmGpX1WsP

Co-authored-by: Claude <noreply@anthropic.com>
2026-02-01 23:05:13 +02:00
gavrielc
e5b436ab48 Fix group metadata sync setting epoch timestamp for new groups (#15)
updateChatName() was inserting new groups with last_message_time set to
Unix epoch (1970-01-01), causing newly discovered groups to appear at
the bottom of activity-ordered listings. Changed to use current time
for new groups while preserving existing timestamps for known groups.

https://claude.ai/code/session_018rcZvALfF3ND2jfcvBaFo2

Co-authored-by: Claude <noreply@anthropic.com>
2026-02-01 23:04:49 +02:00
gavrielc
df52232763 Pre-launch fixes: error handling, cleanup, consistency
- Remove unused claude-agent-sdk from host deps (only used in container)
- Remove dead scheduler MCP config (built into IPC)
- Remove unused eslint script
- Add clear error message when Apple Container fails to start
- Auto-generate launchd plist with real paths in setup skill
- Standardize Node.js version to 20+ everywhere

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 23:01:04 +02:00
gavrielc
48822ff67d Add mount security allowlist for external directory access (#14)
* Add secure mount allowlist validation

Addresses arbitrary host mount vulnerability by validating additional
mounts against an external allowlist stored at ~/.config/nanoclaw/.
This location is never mounted into containers, making it tamper-proof.

Security measures:
- Allowlist cached in memory (edits require process restart)
- Real path resolution (blocks symlink and .. traversal attacks)
- Blocked patterns for sensitive paths (.ssh, .gnupg, .aws, etc.)
- Non-main groups forced to read-only when nonMainReadOnly is true
- Container path validation prevents /workspace/extra escape

https://claude.ai/code/session_01BPqdNy4EAHHJcdtZ27TXkh

* Add mount allowlist setup to /setup skill

Interactive walkthrough that asks users:
- Whether they want agents to access external directories
- Which directories to allow (with paths)
- Read-write vs read-only for each
- Whether non-main groups should be restricted to read-only

Creates ~/.config/nanoclaw/mount-allowlist.json based on answers.

https://claude.ai/code/session_01BPqdNy4EAHHJcdtZ27TXkh

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-02-01 22:55:08 +02:00
Gavriel
5760b75fa9 Fix timezone handling and message filtering
- Add TIMEZONE config using system timezone for cron expressions
- Filter bot messages by content prefix instead of is_from_me
  (user shares WhatsApp account with bot)
- Format messages as XML for cleaner agent parsing
- Update schedule_task tool to clarify local time usage

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 22:54:44 +02:00
gavrielc
066eeb9646 Make OpenClaw critique specific with actual numbers
52+ modules, 8 config files, 45+ deps, 15 channel providers.
Security is application-level (allowlists) vs actual container isolation.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 22:36:45 +02:00
gavrielc
016a1a0e31 Add group metadata sync for easier group activation
- Sync group names from WhatsApp via groupFetchAllParticipating()
- Store group names in chats table (jid -> name mapping)
- Daily sync with 24h cache, on-demand refresh via IPC
- Write available_groups.json snapshot for agent (main group only)
- Agent can request refresh_groups via IPC if group not found
- Update documentation in main CLAUDE.md and debug skill

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 22:25:29 +02:00
Gavriel
572338b9a6 Add context_mode option for scheduled tasks
Scheduled tasks can now run in either:
- "group" mode: uses the group's conversation session for context
- "isolated" mode: runs with a fresh session (previous behavior)

The tool description guides the agent on when to use each mode and
prompts them to ask the user if unclear. Group mode is now the default.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 22:23:50 +02:00
gavrielc
f6e7f7aca9 Make main group respond to all messages without trigger prefix
Other groups still require @Andy prefix to trigger the agent.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 21:49:39 +02:00
gavrielc
6745a1c54b Apply fixes from closed PRs: sentinel markers, JID lookup, schedule validation
- PR #10: Add sentinel markers for robust JSON parsing between container
  and host. Fallback to last-line parsing for backwards compatibility.

- PR #5: Look up target JID from registeredGroups instead of trusting
  IPC payload, fixing cross-group scheduled tasks getting wrong chat_jid.

- PR #8: Add lightweight schedule validation in container MCP that
  returns errors to agents (cron syntax, positive interval, valid ISO
  timestamp). Also defensive validation on host side.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 20:49:57 +02:00
gavrielc
ade9f2d323 Merge pull request #3 from gavrielc/claude/secure-ipc-access-Ni9l4
Secure IPC with per-group namespaces to prevent privilege escalation
2026-02-01 20:40:27 +02:00
gavrielc
febf90f3c8 Merge pull request #12 from gavrielc/claude/fix-agent-failure-timestamp-yiOZt
Fix: only update lastAgentTimestamp on agent success
2026-02-01 20:40:15 +02:00
gavrielc
8f89f67c4b Merge pull request #11 from gavrielc/claude/fix-message-loss-error-DJwye
Fix message loss when processMessage throws
2026-02-01 20:40:13 +02:00
gavrielc
7310c5ba04 Merge pull request #9 from gavrielc/claude/fix-sensitive-log-data-xb0E8
Remove message content from info-level logs
2026-02-01 20:40:05 +02:00
gavrielc
069bc76016 Merge pull request #7 from gavrielc/claude/fix-home-directory-fallback-FF5Tr
Fix hardcoded home directory fallback in container runner
2026-02-01 20:40:02 +02:00
gavrielc
30bc2262e9 Merge pull request #2 from gavrielc/claude/fix-dotenv-exposure-LEzJ8
Fix security: only expose auth vars to containers, not full .env
2026-02-01 20:39:59 +02:00