Commit Graph

197 Commits

Author SHA1 Message Date
gavrielc
93bb94ff55 fix: only preempt idle containers when scheduled tasks enqueue
Containers that finish work but stay alive in waitForIpcMessage() block
queued scheduled tasks. Previous approaches killed active containers
mid-work. This fix tracks idle state via the session-update marker
(status: success, result: null) and only preempts when the container
is idle-waiting, not actively working.

Closes #293

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 22:19:24 +02:00
Peyton-Spencer
6f177adafe fix: skip empty WhatsApp protocol messages
WhatsApp protocol messages (encryption key distribution, read receipts,
ephemeral settings, senderKeyDistributionMessage) have a message envelope
but no text content. These were being stored in messages.db and processed
by the agent, causing:

1. Agent responds to empty messages, wasting API tokens
2. Container stays alive indefinitely (idle timer resets)
3. Scheduled tasks blocked (queue slot occupied)

This fix skips messages with empty content before calling onMessage,
preventing protocol messages from being stored and processed.

Fixes #250

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-21 17:27:07 +02:00
Stefan Gasser
d336b32460 fix: copy skill subdirectories recursively (#175)
copyFileSync crashes with EISDIR when a skill contains subdirectories
like scripts/. Skills support nested folders (scripts/, examples/,
templates/) per the Claude Code spec. Use fs.cpSync to handle the
complete skill structure.
2026-02-21 17:23:19 +02:00
Gio Lodi
94ba537310 Decouple formatting test from @Andy (#329)
* Fix trigger pattern tests to use config name

Tests hardcoded "Andy" but the pattern is built from
`ASSISTANT_NAME` which comes from `.env`.

---

Generated with the help of Claude Code, https://claude.ai/code

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

* Restore usage comment in trim test

---

Generated with the help of Claude Code, https://claude.ai/code

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

---------

Co-authored-by: Claude Code Opus 4.6 <noreply@anthropic.com>
2026-02-21 17:18:08 +02:00
gavrielc
3c79c61f67 docs: fix README_zh consistency and remove Skills System CLI section
- Fix 你→您 inconsistency in README_zh.md
- Add missing nanoclaw.dev link to README_zh.md header
- Remove Skills System CLI section from both README.md and README_zh.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 17:16:28 +02:00
jiakeboge
3d1859f919 docs(zh): Apply stylistic and consistency improvements to README_zh.md (#328) 2026-02-21 17:15:47 +02:00
gavrielc
646411ff03 docs: add nanoclaw.dev link to README header
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 14:49:20 +02:00
Gavriel Cohen
41e54a9460 fix: pass filePath in setupRerereAdapter stale MERGE_HEAD cleanup
cleanupMergeState() without a filePath runs bare `git reset`, which
resets the entire index and can stage deletions of unrelated files.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 15:14:00 +02:00
gavrielc
7181c49ada feat: add /convert-to-apple-container skill, remove /convert-to-docker (#324)
Docker is now the default runtime. The /convert-to-apple-container skill
uses the new skills engine format (manifest.yaml, modify/, intent files,
tests/) to switch to Apple Container on macOS.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 14:57:05 +02:00
gavrielc
a4072162b7 feat: add voice transcription as nanorepo skill (#326)
Add voice transcription skill package at
.claude/skills/add-voice-transcription/ so it can be applied via the
skills engine. Skill adds src/transcription.ts (OpenAI Whisper), modifies
whatsapp.ts to detect/transcribe voice notes, and includes intent files,
3 test cases, and 8 skill validation tests.

Also fixes skills engine runNpmInstall() to use --legacy-peer-deps,
needed for any skill adding deps with Zod v3 peer requirements.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 14:18:54 +02:00
gavrielc
6b9b3a12c9 docs: update skills to use Docker commands after runtime migration (#325)
All skills now reference Docker CLI instead of Apple Container CLI.
Setup skill defaults to Docker with optional /convert-to-apple-container.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 14:06:31 +02:00
github-actions[bot]
a7faac664a docs: update token count to 36.3k tokens · 18% of context window 2026-02-20 11:20:28 +00:00
gavrielc
607623aa59 feat: convert container runtime from Apple Container to Docker (#323)
Swap container-runtime.ts to the Docker variant:
- CONTAINER_RUNTIME_BIN: 'container' → 'docker'
- readonlyMountArgs: --mount bind,readonly → -v host:container:ro
- ensureContainerRuntimeRunning: container system status → docker info
- cleanupOrphans: Apple Container JSON format → docker ps --filter
- build.sh default: container → docker

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 13:20:13 +02:00
github-actions[bot]
51a50d402b docs: update token count to 36.4k tokens · 18% of context window 2026-02-20 11:14:09 +00:00
gavrielc
c6e1bfecc6 refactor: extract runtime-specific code into src/container-runtime.ts (#321)
Move all container-runtime-specific logic (binary name, mount args,
stop command, startup check, orphan cleanup) into a single file so
swapping runtimes only requires replacing this one file.

Neutralize "Apple Container" references in comments and docs that
would become incorrect after a runtime swap. References that list
both runtimes as options are left unchanged.

No behavior change — Apple Container remains the default runtime.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 13:13:55 +02:00
gavrielc
8fd67916b3 Update README.md (#316) 2026-02-19 20:49:11 +02:00
gavrielc
5a16a9db9f Documentation improvements
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 15:26:57 +02:00
github-actions[bot]
646491ce5c docs: update token count to 36.3k tokens · 18% of context window 2026-02-18 23:55:13 +00:00
gavrielc
51788de3b9 Skills engine v0.1 + multi-channel infrastructure (#307)
* refactor: multi-channel infrastructure with explicit channel/is_group tracking

- Add channels[] array and findChannel() routing in index.ts, replacing
  hardcoded whatsapp.* calls with channel-agnostic callbacks
- Add channel TEXT and is_group INTEGER columns to chats table with
  COALESCE upsert to protect existing values from null overwrites
- is_group defaults to 0 (safe: unknown chats excluded from groups)
- WhatsApp passes explicit channel='whatsapp' and isGroup to onChatMetadata
- getAvailableGroups filters on is_group instead of JID pattern matching
- findChannel logs warnings instead of silently dropping unroutable JIDs
- Migration backfills channel/is_group from JID patterns for existing DBs

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

* feat: skills engine v0.1 — deterministic skill packages with rerere resolution

Three-way merge engine for applying skill packages on top of a core
codebase. Skills declare which files they add/modify, and the engine
uses git merge-file for conflict detection with git rerere for
automatic resolution of previously-seen conflicts.

Key components:
- apply: three-way merge with backup/rollback safety net
- replay: clean-slate replay for uninstall and rebase
- update: core version updates with deletion detection
- rebase: bake applied skills into base (one-way)
- manifest: validation with path traversal protection
- resolution-cache: pre-computed rerere resolutions
- structured: npm deps, env vars, docker-compose merging
- CI: per-skill test matrix with conflict detection

151 unit tests covering merge, rerere, backup, replay, uninstall,
update, rebase, structured ops, and edge cases.

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

* feat: add Discord and Telegram skill packages

Skill packages for adding Discord and Telegram channels to NanoClaw.
Each package includes:
- Channel implementation (add/src/channels/)
- Three-way merge targets for index.ts, config.ts, routing.test.ts
- Intent docs explaining merge invariants
- Standalone integration tests
- manifest.yaml with dependency/conflict declarations

Applied via: npx tsx scripts/apply-skill.ts .claude/skills/add-discord
These are inert until applied — no runtime impact.

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

* remove unused docs (skills-system-status, implementation-guide)

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 01:55:00 +02:00
gavrielc
a689f8b3fa fix: quote ASSISTANT_NAME in .env to handle special characters
Use pipe delimiter in sed and quote the value to prevent breakage
from names containing /, &, \, or spaces.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 10:31:02 +02:00
github-actions[bot]
6f719879aa docs: update token count to 35.6k tokens · 18% of context window 2026-02-18 08:30:11 +00:00
Koshkoshinsk
802805d2ec Fix/WA reconnect, container perms, assist name in env (#297)
* fix: WA 515 stream error reconnect exiting early before key sync

Pass isReconnect flag on 515 reconnect so the registered-creds check
doesn't bail out before the handshake completes (caused "logging in..."
hang after successful pairing).

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

* fix: container permission errors on Docker with non-default uid

Make /home/node world-writable in the Dockerfile so the SDK can write
.claude.json. Add --user flag matching host uid/gid in container-runner
so bind-mounted files are accessible. Skip when running as root (uid 0),
as the container's node user (uid 1000), or on native Windows.

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

* fix: write ASSISTANT_NAME to .env during setup

When a custom assistant name is chosen, persist it to .env so config.ts
picks it up at runtime. Uses temp file for cross-platform sed
compatibility (macOS/Linux/WSL).

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 10:29:55 +02:00
gavrielc
f257b93d34 chore: update Discord invite link
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 07:17:01 +02:00
gavrielc
b7c9d98ec2 fix: ensure setup skill runs Docker conversion before building containers
The setup skill would skip the /convert-to-docker step because:
1. The conversion instructions were buried inline in runtime choice bullets
2. The convert-to-docker skill had disable-model-invocation: true

Restructured step 3 into explicit sub-steps with a hard gate (3b) that
checks source files for Apple Container references before allowing the
build to proceed. Enabled model invocation on the convert-to-docker skill.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 22:17:51 +02:00
gavrielc
ff574a2f8b chore: update social preview with new subtitle
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:10:17 +02:00
gavrielc
b125cb17cc chore: add nanoclaw profile and sales images
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 00:30:26 +02:00
gavrielc
88140ec1bb feat: add setup skill with scripted steps (#258)
Replace inline SKILL.md instructions with executable shell scripts
for each setup phase (environment check, deps, container, auth,
groups, channels, mounts, service, verify). Scripts emit structured
status blocks for reliable parsing.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 00:23:49 +02:00
github-actions[bot]
5694ac9b87 docs: update token count to 35.5k tokens · 18% of context window 2026-02-15 15:34:07 +00:00
gavrielc
5031d0f0d8 ci: add workflow_dispatch trigger to token count workflow (#254)
Allows manual triggering for testing and on-demand badge updates.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 17:33:45 +02:00
gavrielc
c467941804 fix: add git pull --rebase before push in token count workflow (#253)
Prevents push failures when remote has newer commits since checkout.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 17:31:57 +02:00
gavrielc
e4d77cdba0 fix: use GitHub App token for token count workflow
Switches from default GITHUB_TOKEN to a scoped GitHub App token
so the workflow can push to the protected main branch.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 17:11:48 +02:00
gavrielc
9261a25531 feat: add is_bot_message column and support dedicated phone numbers (#235)
* feat: add is_bot_message column and support dedicated phone numbers

Replace fragile content-prefix bot detection with an explicit
is_bot_message database column. The old prefix check (content NOT LIKE
'Andy:%') is kept as a backstop for pre-migration messages.

- Add is_bot_message column with automatic backfill migration
- Add ASSISTANT_HAS_OWN_NUMBER env var to skip name prefix when the
  assistant has its own WhatsApp number
- Move prefix logic into WhatsApp channel (no longer a router concern)
- Remove prefixAssistantName from Channel interface
- Load .env via dotenv so launchd-managed processes pick up config
- WhatsApp bot detection: fromMe for own number, prefix match for shared

Based on #160 and #173.

Co-Authored-By: Stefan Gasser <stefan@stefangasser.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: extract shared .env parser and remove dotenv dependency

Extract .env parsing into src/env.ts, used by both config.ts and
container-runner.ts. Reads only requested keys without loading secrets
into process.env, avoiding leaking API keys to child processes.

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

---------

Co-authored-by: Stefan Gasser <stefan@stefangasser.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 15:31:57 +02:00
gavrielc
c8ab3d95e1 feat: add repo-tokens GitHub Action with token count badge
Reusable composite action that counts codebase tokens using tiktoken
and generates a shields.io-style SVG badge. Color reflects context
window usage: green (<30%), yellow-green (30-50%), yellow (50-70%),
red (70%+). Badge includes hardcoded link back to repo-tokens.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 01:41:14 +02:00
Gavriel Cohen
6f2e10f0c3 fix: typing indicator now shows on every message, not just the first
Two issues fixed:
- Use 'paused' instead of 'available' to stop typing. Baileys'
  sendPresenceUpdate('available') sends a global <presence> stanza and
  ignores the JID, so chatstate never left 'composing' and WhatsApp
  suppressed duplicate composing notifications per XEP-0085.
- Add setTyping call when piping messages to an already-running
  container. Previously only the first message (which spawns a new
  container) triggered the typing indicator.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 14:28:08 +02:00
gavrielc
5c68deef76 fix: repair WhatsApp channel tests (missing Browsers mock and async flush)
Added missing Browsers mock to the Baileys vi.mock and made
triggerMessages async to flush microtasks before assertions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 23:08:34 +02:00
gavrielc
ae474fd344 fix: use available instead of paused when stopping typing indicator
Sending 'paused' after the first response caused WhatsApp to stop
relaying subsequent 'composing' presence updates. Using 'available'
keeps the bot in a state where typing indicators work consistently.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 23:05:36 +02:00
gavrielc
658f6b02d3 fix: send available presence on connect so typing indicators work consistently
Without announcing 'available' after connecting, WhatsApp stops relaying
composing/paused presence updates after the first message.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 22:53:03 +02:00
gavrielc
1549ad503e security: pass secrets via SDK env option and delete temp file (#213)
Pass secrets to the SDK via the `env` query option instead of setting
process.env, so Bash subprocesses never inherit API keys. Delete
/tmp/input.json immediately after reading to remove secrets from disk.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 22:46:42 +02:00
Cole
1a07869329 security: sanitize env vars from agent Bash subprocesses (#171)
Use a PreToolUse SDK hook to prepend `unset ANTHROPIC_API_KEY
CLAUDE_CODE_OAUTH_TOKEN` to every Bash command Kit runs, preventing
secret leakage via env/printenv/echo/$PROC. Secrets are now passed
via stdin JSON instead of mounted env files, closing all known
exfiltration vectors.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 22:33:39 +02:00
gavrielc
c30bd62417 docs: update Chinese README and move language link to badge row
Move the language switcher from above the logo into the badge row
alongside Discord. Update README_zh.md to match current English
README: add Agent Swarms announcement and feature, update
architecture section with all key files, add Community section,
and fix file descriptions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 18:35:24 +02:00
gavrielc
8e125dbc1d Merge pull request #84 from jiakeboge/feature/add-chinese-readme
feat: Add Chinese README
2026-02-13 18:32:37 +02:00
Gavriel Cohen
b5a6757211 fix: pass requiresTrigger through IPC and auto-discover additional directories
- IPC register_group handler now passes requiresTrigger field to registerGroup(),
  fixing groups silently defaulting to trigger-required mode
- Agent runner scans /workspace/extra/* and passes them as additionalDirectories
  to the SDK query, so CLAUDE.md files in mounted dirs are loaded automatically

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 12:18:16 +02:00
Gavriel Cohen
acdc6454db fix: WhatsApp auth improvements and LID translation for DMs
- Add pairing code auth with 515 reconnect handling (Baileys stream
  error after pairing is now retried instead of failing)
- Use Browsers.macOS('Chrome') identifier for WhatsApp compatibility
- Fix LID-to-phone translation for DMs using signalRepository.getPNForLID
- Strip device suffix (:0) from resolved phone JIDs
- Update setup skill with three auth options (browser QR, pairing code,
  terminal QR), DM channel type, and LID troubleshooting

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 22:49:04 +02:00
Tom Granot
6863c0bf6b test: add comprehensive WhatsApp connector tests (#182)
38 tests covering connection lifecycle, authentication, reconnection,
message handling (text, image, video, voice, extended text), LID↔JID
translation, outgoing message queue, group metadata sync, JID ownership,
and typing indicators. Based on deep-dive audit of Baileys v7 internals.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 17:43:28 +02:00
Tom Granot
a35499705c Add Apple Container Networking Setup documentation (#178)
Document the setup process for Apple Container networking on macOS 26, including IP forwarding, NAT configuration, and troubleshooting steps.
2026-02-12 10:55:31 +02:00
gavrielc
464735347c chore: add /groups/ and /launchd/ to CODEOWNERS
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 10:18:15 +02:00
gavrielc
8eb80d4ed0 fix: prevent infinite message replay on container timeout (#164)
Container timeout and idle timeout both fire at 30min, racing the
graceful shutdown. The hard kill returns error status, rolling back
the message cursor even though output was already sent — causing
duplicate messages indefinitely.

- Grace period: hard timeout is now IDLE_TIMEOUT + 30s minimum
- Timeout after output resolves as success (idle cleanup, not failure)
- Don't roll back cursor if output was already sent to user
- Remove src/telegram.ts and config vars (added to PR #156 by mistake)
- Add typecheck step to CI workflow
- Add container-runner timeout behavior tests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 17:25:42 +02:00
gavrielc
2b56fecfdc Refactor index (#156)
* feat: add Telegram channel with agent swarm support

Add Telegram as a messaging channel that can run alongside WhatsApp
or standalone (TELEGRAM_ONLY mode). Includes bot pool support for
agent swarms where each subagent appears as a different bot identity
in the group.

- Add grammy dependency for Telegram Bot API
- Route messages through tg: JID prefix convention
- Add storeMessageDirect for non-Baileys channels
- Add sender field to IPC send_message for swarm identity
- Support TELEGRAM_BOT_TOKEN, TELEGRAM_ONLY, TELEGRAM_BOT_POOL config

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

* docs: add index.ts refactor plan

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

* refactor: extract channel abstraction, IPC, and router from index.ts

Break the 1088-line monolith into focused modules:
- src/channels/whatsapp.ts: WhatsAppChannel class implementing Channel interface
- src/ipc.ts: IPC watcher and task processing with dependency injection
- src/router.ts: message formatting, outbound routing, channel lookup
- src/types.ts: Channel interface, OnInboundMessage, OnChatMetadata types

Also adds regression test suite (98 tests), updates all documentation
and skill files to reflect the new architecture.

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

* ci: add test workflow for PRs

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

* chore: remove accidentally committed pool-bot assets

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

* fix(ci): remove grammy from base dependencies

Grammy is installed by the /add-telegram skill, not a base dependency.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 00:36:37 +02:00
Gavriel Cohen
196abf67cf docs: clarify agent swarms vs teams in Telegram skill
Agent Teams work without swarm support — they just operate behind
the scenes. Swarm support makes subagents visible as separate bots.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 10:15:22 +02:00
Gavriel Cohen
b3f5814f48 feat: move to Claude's native memory management
Enable CLAUDE_CODE_DISABLE_AUTO_MEMORY=0 and
CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1 in container env so
agents use Claude Code's built-in persistent memory instead of
manually editing CLAUDE.md. Remove instructions that told agents to
write context into CLAUDE.md files.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 09:28:25 +02:00