Commit Graph

29 Commits

Author SHA1 Message Date
gavrielc 5a309a0e25 fix: only send onboarding message on first wiring, not re-registration
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 13:49:58 +03:00
gavrielc 4a999ec973 feat: auto-onboarding when a channel is registered
After wiring a channel to an agent group, register.ts writes a task
message to the session that triggers the /welcome container skill.
The agent introduces itself immediately — the user sees typing and
then a greeting without having to send a message first.

Uses kind 'task' (not 'system') so the poll loop picks it up normally.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 13:48:56 +03:00
gavrielc a2badbd525 fix: normalize platform ID at registration, not router lookup
Channel adapters prefix platform IDs with their channel type
(e.g. "telegram:123"). Normalize in register.ts so the DB always
stores the canonical format. Removes fallback lookup from router.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 13:41:07 +03:00
gavrielc 9f5c37fc4c fix: handle platform ID prefix mismatch in router, not register
Move prefix handling from register.ts to router.ts. Users register with
raw platform IDs (what they naturally have), adapters send prefixed IDs
(their internal format). Router now tries stripping the channel type
prefix when the exact lookup fails, matching either format.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 13:39:40 +03:00
gavrielc 6941e37366 fix: auto-prefix platform IDs in register.ts to match Chat SDK format
Chat SDK adapters use prefixed platform IDs (e.g. "telegram:6037840640",
"discord:guildId:channelId") but users provide raw IDs during setup.
Without the prefix, the router can't match the registered messaging group
to incoming messages and silently drops them.

register.ts now auto-prefixes with the channel type if not already present.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 13:38:23 +03:00
gavrielc 57a6491c7e v2: channel isolation model, manage-channels skill, refactored channel skills
- Add three-level isolation model (shared session, same agent, separate agent)
  with agent-shared session mode for cross-channel shared sessions
- Create /manage-channels skill for wiring channels to agent groups
- Refactor all 12 v2 channel skills: lean SKILL.md + VERIFY.md + REMOVE.md
  with structured Channel Info section for platform-specific metadata
- Create /add-discord-v2 skill (was missing)
- Add step 5a to setup SKILL.md invoking /manage-channels after channel install
- Update setup/verify.ts to check all 12 channel token types
- Add docs/v2-isolation-model.md explaining the isolation model
- Update v2-checklist.md and v2-setup-wiring.md to reflect completed work

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 13:19:19 +03:00
gavrielc e7514edd35 fix: wire v2 setup flow — barrel import, registration, verification
- Import channel barrel from src/index.ts so channel skills that
  uncomment lines in src/channels/index.ts actually execute
- Rewrite setup/register.ts to create v2 entities (agent_groups,
  messaging_groups, messaging_group_agents) in data/v2.db instead
  of v1's store/messages.db
- Fix setup/verify.ts to check v2 central DB for registered groups
- Add prominent "MESSAGE DROPPED" warnings in router when no agent
  groups are wired, with actionable guidance

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 12:23:23 +03:00
gavrielc 9486d56b01 v2: make v2 the main entry point, move v1 to src/v1/
- Move all v1 files (index, router, container-runner, db, ipc, types,
  logger, channels/registry, and all utilities) to src/v1/ as a
  fully self-contained archive with no shared dependencies
- Rename v2 files to remove -v2 suffix (index-v2.ts → index.ts, etc.)
- Update all imports across v2 source, tests, and setup files
- Migrate shared utilities (config, env, container-runtime, mount-security,
  timezone, group-folder) from pino logger to v2 log module
- Migrate setup/ files from logger to log with argument order swap
- Container agent-runner: move v1 entry to v1/, rename v2 to index.ts
- Update setup skill to offer all 13 v2 channels
- Install all Chat SDK adapter packages
- dist/index.js now runs v2; dist/v1/index.js runs v1

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 11:40:36 +03:00
gavrielc 5f385974e7 Merge branch 'main' into fix/setup-preserve-mount-allowlist 2026-03-25 22:55:29 +02:00
gavrielc 77b7c658d6 Merge branch 'main' into fix/enable-linger-clean 2026-03-25 22:26:22 +02:00
Daniel M cf5fa1daf0 Merge branch 'main' into upstream/fix-register-claude-md 2026-03-25 15:21:50 +02:00
nanoclaw3 aeabfcc65a fix: enable loginctl linger so user service survives SSH logout
Without linger enabled, systemd terminates all user-level processes
(including the NanoClaw service) when the last SSH session closes.
This adds `loginctl enable-linger` during setup for non-root users.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 03:48:08 +00:00
gavrielc 11847a1af0 fix: validate timezone to prevent crash on POSIX-style TZ values
POSIX-style TZ strings like IST-2 cause a hard RangeError crash in
formatMessages because Intl.DateTimeFormat only accepts IANA identifiers.

- Add isValidTimezone/resolveTimezone helpers to src/timezone.ts
- Make formatLocalTime fall back to UTC on invalid timezone
- Validate TZ candidates in config.ts before accepting
- Add timezone setup step to detect and prompt when autodetection fails
- Use node:22-slim in Dockerfile (node:24-slim Trixie package renames)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 01:04:59 +02:00
glifocat 57085cc02e fix: revert promotion logic — never overwrite existing CLAUDE.md
The promotion logic (overwriting CLAUDE.md when a group becomes main)
is unsafe. Real-world setups use is_main for groups that intentionally
lack admin context — e.g. a family chat (whatsapp_casa) with 144 lines
of custom persona, PARA workspace, task management, and family context.
Overwriting based on missing "## Admin Context" would destroy user work.

register.ts now follows a simple rule: create template for new folders,
never touch existing files. Tests updated to verify preservation across
re-registration and main promotion scenarios.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 13:09:26 +01:00
glifocat 3207c35e50 fix: promote CLAUDE.md to main template when group becomes main
When a non-main group is re-registered with --is-main, the existing
CLAUDE.md (copied from global template) lacked admin context. Now
register.ts detects this promotion case and replaces it with the main
template. Files that already contain "## Admin Context" are preserved.

Adds tests for:
- promoting non-main to main upgrades the template
- cross-channel promotion (e.g. Telegram non-main → main)
- promotion with custom assistant name
- re-registration preserves user-modified main CLAUDE.md
- re-registration preserves user-modified non-main CLAUDE.md

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 12:44:24 +01:00
glifocat 07dc8c977c test: cover multi-channel main and cross-channel name propagation
Replaces single-channel tests with multi-channel scenarios:
- each channel can have its own main with admin context
- non-main groups across channels get global template
- custom name propagates to all channels and groups
- user-modified CLAUDE.md preserved on re-registration
- missing templates handled gracefully

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 12:39:21 +01:00
glifocat b6e18688c2 test: add coverage for CLAUDE.md template copy in register step
Adds 5 tests verifying the template copy and glob-based name update
logic introduced in the parent commit:
- copies global template for non-main groups
- copies main template for main groups
- does not overwrite existing CLAUDE.md
- updates name across all groups/*/CLAUDE.md files
- handles missing template gracefully (no crash)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 12:35:13 +01:00
glifocat 5a12ddd4cb fix(register): create CLAUDE.md in group folder from template
When registering a new group, create CLAUDE.md in the group folder from
the appropriate template (groups/main/ for main groups, groups/global/
for others). Without this, the container agent runs with no CLAUDE.md
since its CWD is /workspace/group (the group folder).

Also update the name-replacement glob to cover all groups/*/CLAUDE.md
files rather than only two hardcoded paths, so newly created files and
any future group folders are updated correctly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 12:19:44 +01:00
Akasha 5f42646598 fix: implement --force flag for mount-allowlist overwrite
The skip message mentioned --force but parseArgs didn't handle it,
making it a false promise. Now --force is parsed and passed through,
allowing users to regenerate the mount allowlist when needed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 16:57:09 -04:00
Akasha ff16e93713 fix: skip mount-allowlist write if file already exists
/setup overwrote ~/.config/nanoclaw/mount-allowlist.json unconditionally,
clobbering any user customizations made after initial setup. Now checks for
the file first and skips with a 'skipped' status if it exists.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 16:55:36 -04:00
NanoClaw Setup b7f8c20a25 fix: setup skill uses 127.0.0.1 for OneCLI and offers dashboard vs CLI choice
- Configure CLI api-host to local instance (defaults to cloud otherwise)
- Use 127.0.0.1 instead of localhost to avoid IPv6 resolution issues
- Present dashboard and CLI as two options with platform guidance
- Accept ONECLI_URL as valid credentials in verify step

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 14:45:58 +02:00
Gabi Simons 260812702c fix: add KillMode=process so remote-control survives restarts
systemd's default KillMode=control-group kills all processes in the
cgroup on service restart, including the detached claude remote-control
process. KillMode=process only kills the main Node.js process, letting
detached children survive. restoreRemoteControl() already handles
reattaching on startup.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 13:12:07 +02:00
gavrielc 04fb44e417 fix: setup registration — use initDatabase/setRegisteredGroup, .ts imports, correct CLI commands
- setup/register.ts: replace inline DB logic with initDatabase() + setRegisteredGroup()
  (fixes missing is_main column on existing DBs, .js MODULE_NOT_FOUND with tsx)
- SKILL.md (telegram, slack, discord): replace broken registerGroup() pseudo-code
  with actual `npx tsx setup/index.ts --step register` commands
- docs/SPEC.md: fix registerGroup → setRegisteredGroup in example

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 22:51:40 +02:00
Gabi Simons 0210aa9ef1 refactor: implement multi-channel architecture (#500)
* refactor: implement channel architecture and dynamic setup

- Introduced ChannelRegistry for dynamic channel loading
- Decoupled WhatsApp from core index.ts and config.ts
- Updated setup wizard to support ENABLED_CHANNELS selection
- Refactored IPC and group registration to be channel-aware
- Verified with 359 passing tests and clean typecheck

* style: fix formatting in config.ts to pass CI

* refactor(setup): full platform-agnostic transformation

- Harmonized all instructional text and help prompts
- Implemented conditional guards for WhatsApp-specific steps
- Normalized CLI terminology across all 4 initial channels
- Unified troubleshooting and verification logic
- Verified 369 tests pass with clean typecheck

* feat(skills): transform WhatsApp into a pluggable skill

- Created .claude/skills/add-whatsapp with full 5-phase interactive setup
- Fixed TS7006 'implicit any' error in IpcDeps
- Added auto-creation of STORE_DIR to prevent crashes on fresh installs
- Verified with 369 passing tests and clean typecheck

* refactor(skills): move WhatsApp from core to pluggable skill

- Move src/channels/whatsapp.ts to add-whatsapp skill add/ folder
- Move src/channels/whatsapp.test.ts to skill add/ folder
- Move src/whatsapp-auth.ts to skill add/ folder
- Create modify/ for barrel file (src/channels/index.ts)
- Create tests/ with skill package validation test
- Update manifest with adds/modifies lists
- Remove WhatsApp deps from core package.json (now skill-managed)
- Remove WhatsApp-specific ghost language from types.ts
- Update SKILL.md to reflect skill-apply workflow

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

* refactor(skills): move setup/whatsapp-auth.ts into WhatsApp skill

The WhatsApp auth setup step is channel-specific — move it from core
to the add-whatsapp skill so core stays minimal.

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

* refactor(skills): convert Telegram skill to pluggable channel pattern

Replace the old direct-integration approach (modifying src/index.ts,
src/config.ts, src/routing.test.ts) with self-registration via the
channel registry, matching the WhatsApp skill pattern.

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

* fix(skills): fix add-whatsapp build failure and improve auth flow

- Add missing @types/qrcode-terminal to manifest npm_dependencies
  (build failed after skill apply without it)
- Make QR-browser the recommended auth method (terminal QR too small,
  pairing codes expire too fast)
- Remove "replace vs alongside" question — channels are additive
- Add pairing code retry guidance and QR-browser fallback

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

* fix: remove hardcoded WhatsApp default and stale Baileys comment

- ENABLED_CHANNELS now defaults to empty (fresh installs must configure
  channels explicitly via /setup; existing installs already have .env)
- Remove Baileys-specific comment from storeMessageDirect() in db.ts

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

* refactor(skills): convert Discord, Slack, Gmail skills to pluggable channel pattern

All channel skills now use the same self-registration pattern:
- registerChannel() factory at module load time
- Barrel file append (src/channels/index.ts) instead of orchestrator modifications
- No more *_ONLY flags (DISCORD_ONLY, SLACK_ONLY) — use ENABLED_CHANNELS instead
- Removed ~2500 lines of old modify/ files (src/index.ts, src/config.ts, src/routing.test.ts)

Gmail retains its container-runner.ts and agent-runner modifications (MCP
mount + server config) since those are independent of channel wiring.

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

* refactor: use getRegisteredChannels instead of ENABLED_CHANNELS

Remove the ENABLED_CHANNELS env var entirely. The orchestrator now
iterates getRegisteredChannelNames() from the channel registry —
channels self-register via barrel imports and their factories return
null when credentials are missing, so unconfigured channels are
skipped automatically.

Deleted setup/channels.ts (and its tests) since its sole purpose was
writing ENABLED_CHANNELS to .env. Refactored verify, groups, and
environment setup steps to detect channels by credential presence
instead of reading ENABLED_CHANNELS.

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

* docs: add breaking change notice and whatsapp migration instructions

CHANGELOG.md documents the pluggable channel architecture shift and
provides migration steps for existing WhatsApp users.

CLAUDE.md updated: Quick Context reflects multi-channel architecture,
Key Files lists registry.ts instead of whatsapp.ts, and a new
Troubleshooting section directs users to /add-whatsapp if WhatsApp
stops connecting after upgrade.

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

* docs: rewrite READMEs for pluggable multi-channel architecture

Reflects the architectural shift from a hardcoded WhatsApp bot to a
pluggable channel platform. Adds upgrading notice, Mermaid architecture
diagram, CI/License/TypeScript/PRs badges, and clarifies that slash
commands run inside the Claude Code CLI.

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

* docs: move pluggable channel architecture details to SPEC.md

Revert READMEs to original tone with only two targeted changes:
- Add upgrading notice for WhatsApp breaking change
- Mention pluggable channels in "What It Supports"

Move Mermaid diagram, channel registry internals, factory pattern
explanation, and self-registration walkthrough into docs/SPEC.md.
Update stale WhatsApp-specific references in SPEC.md to be
channel-agnostic.

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

* docs: move upgrading notice to CHANGELOG, add changelog link

Remove the "Upgrading from Pre-Pluggable Versions" section from
README.md — breaking change details belong in the CHANGELOG. Add a
Changelog section linking to CHANGELOG.md.

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

* docs: expand CHANGELOG with full PR #500 changes

Cover all changes: channel registry, WhatsApp moved to skill, removed
core dependencies, all 5 skills simplified, orchestrator refactored,
setup decoupled. Use Claude Code CLI instructions for migration.

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

* chore: bump version to 1.2.0 for pluggable channel architecture

Minor version bump — new functionality (pluggable channels) with a
managed migration path for existing WhatsApp users. Update version
references in CHANGELOG and update skill.

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

* Fix skill application

* fix: use slotted barrel file to prevent channel merge conflicts

Pre-allocate a named comment slot for each channel in
src/channels/index.ts, separated by blank lines. Each skill's
modify file only touches its own slot, so three-way merges
never conflict when applying multiple channels.

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

* fix: resolve real chat ID during setup for token-based channels

Instead of registering with `pending@telegram` (which never matches
incoming messages), the setup skill now runs an inline bot that waits
for the user to send /chatid, capturing the real chat ID before
registration.

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

* fix: setup delegates to channel skills, fix group sync and Discord metadata

- Restructure setup SKILL.md to delegate channel setup to individual
  channel skills (/add-whatsapp, /add-telegram, etc.) instead of
  reimplementing auth/registration inline with broken placeholder JIDs
- Move channel selection to step 5 where it's immediately acted on
- Fix setup/groups.ts: write sync script to temp file instead of passing
  via node -e which broke on shell escaping of newlines
- Fix Discord onChatMetadata missing channel and isGroup parameters
- Add .tmp-* to .gitignore for temp sync script cleanup

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

* fix: align add-whatsapp skill with main setup patterns

Add headless detection for auth method selection, structured inline
error handling, dedicated number DM flow, and reorder questions to
match main's trigger-first flow.

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

* fix: add missing auth script to package.json

The add-whatsapp skill adds src/whatsapp-auth.ts but doesn't add
the corresponding npm script. Setup and SKILL.md reference `npm run auth`
for WhatsApp QR terminal authentication.

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

* fix: update Discord skill tests to match onChatMetadata signature

The onChatMetadata callback now takes 5 arguments (jid, timestamp,
name, channel, isGroup) but the Discord skill tests only expected 3.
This caused skill application to roll back on test failure.

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

* docs: replace 'pluggable' jargon with clearer language

User-facing text now says "multi-channel" or describes what it does.
Developer-facing text uses "self-registering" or "channel registry".
Also removes extra badge row from README.

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

* docs: align Chinese README with English version

Remove extra badges, replace pluggable jargon, remove upgrade section
(now in CHANGELOG), add missing intro line and changelog section,
fix setup FAQ answer.

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

* fix: warn on installed-but-unconfigured channels instead of silent skip

Channels with missing credentials now emit WARN logs naming the exact
missing variable, so misconfigurations surface instead of being hidden.

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

* docs: simplify changelog to one-liner with compare link

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

* feat: add isMain flag and channel-prefixed group folders

Replace MAIN_GROUP_FOLDER constant with explicit isMain boolean on
RegisteredGroup. Group folders now use channel prefix convention
(e.g., whatsapp_main, telegram_family-chat) to prevent cross-channel
collisions.

- Add isMain to RegisteredGroup type and SQLite schema (with migration)
- Replace all folder-based main group checks with group.isMain
- Add --is-main flag to setup/register.ts
- Strip isMain from IPC payload (defense in depth)
- Update MCP tool description for channel-prefixed naming
- Update all channel SKILL.md files and documentation

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: gavrielc <gabicohen22@yahoo.com>
Co-authored-by: Koshkoshinski <daniel.milliner@gmail.com>
2026-03-03 00:35:45 +02:00
gavrielc 770231687a fix: prevent command injection in setup verify PID check
Validate PID as positive integer and use process.kill() instead of
shell interpolation via execSync, eliminating injection vector.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 12:55:27 +02:00
Gabi Simons 11c201088b refactor: CI optimization, logging improvements, and codebase formatting (#456)
* fix(db): remove unique constraint on folder to support multi-channel agents

* ci: implement automated skill drift detection and self-healing PRs

* fix: align registration logic with Gavriel's feedback and fix build/test issues from Daniel Mi

* style: conform to prettier standards for CI validation

* test: fix branch naming inconsistency in CI (master vs main)

* fix(ci): robust module resolution by removing file extensions in scripts

* refactor(ci): simplify skill validation by removing redundant combination tests

* style: conform skills-engine to prettier, unify logging in index.ts and cleanup unused imports

* refactor: extract multi-channel DB changes to separate branch

Move channel column, folder suffix logic, and related migrations
to feat/multi-channel-db-v2 for independent review. This PR now
contains only CI/CD optimizations, Prettier formatting, and
logging improvements.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 23:13:36 +02:00
Will Monk 18c0432db4 Fix QR data handling in WhatsApp auth (#467)
Running WhatsApp setup fails because the string interpolation of `qrData` does not inject quotes.
2026-02-24 23:32:54 +02:00
Lawyered c6391cceb1 fix: block group folder path escapes 2026-02-22 21:01:53 +02:00
gavrielc 92d14405c5 refactor: move setup scripts out of src/ to reduce build token count
Setup scripts are standalone CLI tools run via tsx with no runtime
imports from the main app. Moving them out of src/ excludes them from
the tsc build output and reduces the compiled bundle size.

- git mv src/setup/ setup/
- Fix imports to use ../src/logger.js and ../src/config.js
- Update package.json, vitest.config.ts, SKILL.md references
- Fix platform tests to be cross-platform (macOS + Linux)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 18:43:22 +02:00