@rozek/nanoclaw 1.2.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.json +1 -0
- package/.claude/skills/add-compact/SKILL.md +135 -0
- package/.claude/skills/add-discord/SKILL.md +203 -0
- package/.claude/skills/add-gmail/SKILL.md +220 -0
- package/.claude/skills/add-image-vision/SKILL.md +94 -0
- package/.claude/skills/add-ollama-tool/SKILL.md +153 -0
- package/.claude/skills/add-parallel/SKILL.md +290 -0
- package/.claude/skills/add-pdf-reader/SKILL.md +104 -0
- package/.claude/skills/add-reactions/SKILL.md +117 -0
- package/.claude/skills/add-slack/SKILL.md +207 -0
- package/.claude/skills/add-telegram/SKILL.md +222 -0
- package/.claude/skills/add-telegram-swarm/SKILL.md +384 -0
- package/.claude/skills/add-voice-transcription/SKILL.md +148 -0
- package/.claude/skills/add-whatsapp/SKILL.md +372 -0
- package/.claude/skills/convert-to-apple-container/SKILL.md +175 -0
- package/.claude/skills/customize/SKILL.md +110 -0
- package/.claude/skills/debug/SKILL.md +349 -0
- package/.claude/skills/get-qodo-rules/SKILL.md +122 -0
- package/.claude/skills/get-qodo-rules/references/output-format.md +41 -0
- package/.claude/skills/get-qodo-rules/references/pagination.md +33 -0
- package/.claude/skills/get-qodo-rules/references/repository-scope.md +26 -0
- package/.claude/skills/qodo-pr-resolver/SKILL.md +326 -0
- package/.claude/skills/qodo-pr-resolver/resources/providers.md +329 -0
- package/.claude/skills/setup/SKILL.md +218 -0
- package/.claude/skills/update-nanoclaw/SKILL.md +235 -0
- package/.claude/skills/update-skills/SKILL.md +130 -0
- package/.claude/skills/use-local-whisper/SKILL.md +152 -0
- package/.claude/skills/x-integration/SKILL.md +417 -0
- package/.claude/skills/x-integration/agent.ts +243 -0
- package/.claude/skills/x-integration/host.ts +159 -0
- package/.claude/skills/x-integration/lib/browser.ts +148 -0
- package/.claude/skills/x-integration/lib/config.ts +62 -0
- package/.claude/skills/x-integration/scripts/like.ts +56 -0
- package/.claude/skills/x-integration/scripts/post.ts +66 -0
- package/.claude/skills/x-integration/scripts/quote.ts +80 -0
- package/.claude/skills/x-integration/scripts/reply.ts +74 -0
- package/.claude/skills/x-integration/scripts/retweet.ts +62 -0
- package/.claude/skills/x-integration/scripts/setup.ts +87 -0
- package/.env.example +1 -0
- package/.github/CODEOWNERS +10 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +14 -0
- package/.github/workflows/bump-version.yml +32 -0
- package/.github/workflows/ci.yml +25 -0
- package/.github/workflows/merge-forward-skills.yml +160 -0
- package/.github/workflows/update-tokens.yml +42 -0
- package/.husky/pre-commit +1 -0
- package/.mcp.json +3 -0
- package/.nvmrc +1 -0
- package/.prettierrc +3 -0
- package/CHANGELOG.md +8 -0
- package/CLAUDE.md +64 -0
- package/CONTRIBUTING.md +23 -0
- package/CONTRIBUTORS.md +15 -0
- package/LICENSE +21 -0
- package/NanoClaw_with_Web-Support.md +290 -0
- package/README.md +261 -0
- package/README_zh.md +200 -0
- package/assets/nanoclaw-favicon.png +0 -0
- package/assets/nanoclaw-icon.png +0 -0
- package/assets/nanoclaw-logo-dark.png +0 -0
- package/assets/nanoclaw-logo.png +0 -0
- package/assets/nanoclaw-profile.jpeg +0 -0
- package/assets/nanoclaw-sales.png +0 -0
- package/assets/social-preview.jpg +0 -0
- package/config-examples/mount-allowlist.json +25 -0
- package/container/Dockerfile +70 -0
- package/container/agent-runner/package-lock.json +1524 -0
- package/container/agent-runner/package.json +21 -0
- package/container/agent-runner/src/index.ts +558 -0
- package/container/agent-runner/src/ipc-mcp-stdio.ts +338 -0
- package/container/agent-runner/tsconfig.json +15 -0
- package/container/build.sh +23 -0
- package/container/skills/agent-browser/SKILL.md +159 -0
- package/container/skills/capabilities/SKILL.md +100 -0
- package/container/skills/status/SKILL.md +104 -0
- package/dist/channels/index.d.ts +2 -0
- package/dist/channels/index.d.ts.map +1 -0
- package/dist/channels/index.js +9 -0
- package/dist/channels/index.js.map +1 -0
- package/dist/channels/registry.d.ts +13 -0
- package/dist/channels/registry.d.ts.map +1 -0
- package/dist/channels/registry.js +11 -0
- package/dist/channels/registry.js.map +1 -0
- package/dist/channels/registry.test.d.ts +2 -0
- package/dist/channels/registry.test.d.ts.map +1 -0
- package/dist/channels/registry.test.js +32 -0
- package/dist/channels/registry.test.js.map +1 -0
- package/dist/channels/web.d.ts +2 -0
- package/dist/channels/web.d.ts.map +1 -0
- package/dist/channels/web.js +1738 -0
- package/dist/channels/web.js.map +1 -0
- package/dist/cli.d.ts +11 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +182 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +19 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +36 -0
- package/dist/config.js.map +1 -0
- package/dist/container-runner.d.ts +44 -0
- package/dist/container-runner.d.ts.map +1 -0
- package/dist/container-runner.js +467 -0
- package/dist/container-runner.js.map +1 -0
- package/dist/container-runner.test.d.ts +2 -0
- package/dist/container-runner.test.d.ts.map +1 -0
- package/dist/container-runner.test.js +150 -0
- package/dist/container-runner.test.js.map +1 -0
- package/dist/container-runtime.d.ts +22 -0
- package/dist/container-runtime.d.ts.map +1 -0
- package/dist/container-runtime.js +96 -0
- package/dist/container-runtime.js.map +1 -0
- package/dist/container-runtime.test.d.ts +2 -0
- package/dist/container-runtime.test.d.ts.map +1 -0
- package/dist/container-runtime.test.js +93 -0
- package/dist/container-runtime.test.js.map +1 -0
- package/dist/credential-proxy.d.ts +21 -0
- package/dist/credential-proxy.d.ts.map +1 -0
- package/dist/credential-proxy.js +95 -0
- package/dist/credential-proxy.js.map +1 -0
- package/dist/credential-proxy.test.d.ts +2 -0
- package/dist/credential-proxy.test.d.ts.map +1 -0
- package/dist/credential-proxy.test.js +134 -0
- package/dist/credential-proxy.test.js.map +1 -0
- package/dist/db.d.ts +115 -0
- package/dist/db.d.ts.map +1 -0
- package/dist/db.js +549 -0
- package/dist/db.js.map +1 -0
- package/dist/db.test.d.ts +2 -0
- package/dist/db.test.d.ts.map +1 -0
- package/dist/db.test.js +360 -0
- package/dist/db.test.js.map +1 -0
- package/dist/env.d.ts +8 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +42 -0
- package/dist/env.js.map +1 -0
- package/dist/formatting.test.d.ts +2 -0
- package/dist/formatting.test.d.ts.map +1 -0
- package/dist/formatting.test.js +183 -0
- package/dist/formatting.test.js.map +1 -0
- package/dist/group-folder.d.ts +5 -0
- package/dist/group-folder.d.ts.map +1 -0
- package/dist/group-folder.js +44 -0
- package/dist/group-folder.js.map +1 -0
- package/dist/group-folder.test.d.ts +2 -0
- package/dist/group-folder.test.d.ts.map +1 -0
- package/dist/group-folder.test.js +29 -0
- package/dist/group-folder.test.js.map +1 -0
- package/dist/group-queue.d.ts +34 -0
- package/dist/group-queue.d.ts.map +1 -0
- package/dist/group-queue.js +263 -0
- package/dist/group-queue.js.map +1 -0
- package/dist/group-queue.test.d.ts +2 -0
- package/dist/group-queue.test.d.ts.map +1 -0
- package/dist/group-queue.test.js +341 -0
- package/dist/group-queue.test.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +518 -0
- package/dist/index.js.map +1 -0
- package/dist/ipc-auth.test.d.ts +2 -0
- package/dist/ipc-auth.test.d.ts.map +1 -0
- package/dist/ipc-auth.test.js +434 -0
- package/dist/ipc-auth.test.js.map +1 -0
- package/dist/ipc.d.ts +32 -0
- package/dist/ipc.d.ts.map +1 -0
- package/dist/ipc.js +311 -0
- package/dist/ipc.js.map +1 -0
- package/dist/logger.d.ts +3 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +14 -0
- package/dist/logger.js.map +1 -0
- package/dist/mount-security.d.ts +34 -0
- package/dist/mount-security.d.ts.map +1 -0
- package/dist/mount-security.js +325 -0
- package/dist/mount-security.js.map +1 -0
- package/dist/remote-control.d.ts +32 -0
- package/dist/remote-control.d.ts.map +1 -0
- package/dist/remote-control.js +185 -0
- package/dist/remote-control.js.map +1 -0
- package/dist/remote-control.test.d.ts +2 -0
- package/dist/remote-control.test.d.ts.map +1 -0
- package/dist/remote-control.test.js +321 -0
- package/dist/remote-control.test.js.map +1 -0
- package/dist/router.d.ts +8 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +37 -0
- package/dist/router.js.map +1 -0
- package/dist/routing.test.d.ts +2 -0
- package/dist/routing.test.d.ts.map +1 -0
- package/dist/routing.test.js +81 -0
- package/dist/routing.test.js.map +1 -0
- package/dist/sender-allowlist.d.ts +14 -0
- package/dist/sender-allowlist.d.ts.map +1 -0
- package/dist/sender-allowlist.js +79 -0
- package/dist/sender-allowlist.js.map +1 -0
- package/dist/sender-allowlist.test.d.ts +2 -0
- package/dist/sender-allowlist.test.d.ts.map +1 -0
- package/dist/sender-allowlist.test.js +186 -0
- package/dist/sender-allowlist.test.js.map +1 -0
- package/dist/session-commands.d.ts +47 -0
- package/dist/session-commands.d.ts.map +1 -0
- package/dist/session-commands.js +102 -0
- package/dist/session-commands.js.map +1 -0
- package/dist/session-commands.test.d.ts +2 -0
- package/dist/session-commands.test.d.ts.map +1 -0
- package/dist/session-commands.test.js +190 -0
- package/dist/session-commands.test.js.map +1 -0
- package/dist/task-scheduler.d.ts +22 -0
- package/dist/task-scheduler.d.ts.map +1 -0
- package/dist/task-scheduler.js +210 -0
- package/dist/task-scheduler.js.map +1 -0
- package/dist/task-scheduler.test.d.ts +2 -0
- package/dist/task-scheduler.test.d.ts.map +1 -0
- package/dist/task-scheduler.test.js +107 -0
- package/dist/task-scheduler.test.js.map +1 -0
- package/dist/timezone.d.ts +6 -0
- package/dist/timezone.d.ts.map +1 -0
- package/dist/timezone.js +17 -0
- package/dist/timezone.js.map +1 -0
- package/dist/timezone.test.d.ts +2 -0
- package/dist/timezone.test.d.ts.map +1 -0
- package/dist/timezone.test.js +23 -0
- package/dist/timezone.test.js.map +1 -0
- package/dist/types.d.ts +78 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/docs/APPLE-CONTAINER-NETWORKING.md +90 -0
- package/docs/DEBUG_CHECKLIST.md +143 -0
- package/docs/REQUIREMENTS.md +196 -0
- package/docs/SDK_DEEP_DIVE.md +643 -0
- package/docs/SECURITY.md +122 -0
- package/docs/SPEC.md +785 -0
- package/docs/docker-sandboxes.md +359 -0
- package/docs/nanoclaw-architecture-final.md +1063 -0
- package/docs/nanorepo-architecture.md +168 -0
- package/docs/skills-as-branches.md +662 -0
- package/groups/global/CLAUDE.md +58 -0
- package/groups/main/CLAUDE.md +246 -0
- package/launchd/com.nanoclaw.plist +32 -0
- package/package.json +45 -0
- package/repo-tokens/README.md +113 -0
- package/repo-tokens/action.yml +186 -0
- package/repo-tokens/badge.svg +23 -0
- package/repo-tokens/examples/green.svg +14 -0
- package/repo-tokens/examples/red.svg +14 -0
- package/repo-tokens/examples/yellow-green.svg +14 -0
- package/repo-tokens/examples/yellow.svg +14 -0
- package/scripts/run-migrations.ts +105 -0
- package/setup/container.ts +144 -0
- package/setup/environment.test.ts +121 -0
- package/setup/environment.ts +94 -0
- package/setup/groups.ts +229 -0
- package/setup/index.ts +58 -0
- package/setup/mounts.ts +115 -0
- package/setup/platform.test.ts +120 -0
- package/setup/platform.ts +132 -0
- package/setup/register.test.ts +257 -0
- package/setup/register.ts +177 -0
- package/setup/service.test.ts +187 -0
- package/setup/service.ts +362 -0
- package/setup/status.ts +16 -0
- package/setup/verify.ts +192 -0
- package/setup.sh +161 -0
- package/src/channels/index.ts +12 -0
- package/src/channels/registry.test.ts +42 -0
- package/src/channels/registry.ts +32 -0
- package/src/channels/web.ts +1856 -0
- package/src/cli.ts +209 -0
- package/src/config.ts +73 -0
- package/src/container-runner.test.ts +210 -0
- package/src/container-runner.ts +707 -0
- package/src/container-runtime.test.ts +149 -0
- package/src/container-runtime.ts +127 -0
- package/src/credential-proxy.test.ts +192 -0
- package/src/credential-proxy.ts +125 -0
- package/src/db.test.ts +484 -0
- package/src/db.ts +803 -0
- package/src/env.ts +42 -0
- package/src/formatting.test.ts +256 -0
- package/src/group-folder.test.ts +43 -0
- package/src/group-folder.ts +44 -0
- package/src/group-queue.test.ts +484 -0
- package/src/group-queue.ts +365 -0
- package/src/index.ts +731 -0
- package/src/ipc-auth.test.ts +679 -0
- package/src/ipc.ts +461 -0
- package/src/logger.ts +16 -0
- package/src/mount-security.ts +419 -0
- package/src/remote-control.test.ts +397 -0
- package/src/remote-control.ts +224 -0
- package/src/router.ts +52 -0
- package/src/routing.test.ts +170 -0
- package/src/sender-allowlist.test.ts +216 -0
- package/src/sender-allowlist.ts +128 -0
- package/src/session-commands.test.ts +247 -0
- package/src/session-commands.ts +163 -0
- package/src/task-scheduler.test.ts +129 -0
- package/src/task-scheduler.ts +295 -0
- package/src/timezone.test.ts +29 -0
- package/src/timezone.ts +16 -0
- package/src/types.ts +107 -0
- package/tsconfig.json +20 -0
- package/vitest.config.ts +7 -0
- package/vitest.skills.config.ts +7 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: add-compact
|
|
3
|
+
description: Add /compact command for manual context compaction. Solves context rot in long sessions by forwarding the SDK's built-in /compact slash command. Main-group or trusted sender only.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Add /compact Command
|
|
7
|
+
|
|
8
|
+
Adds a `/compact` session command that compacts conversation history to fight context rot in long-running sessions. Uses the Claude Agent SDK's built-in `/compact` slash command — no synthetic system prompts.
|
|
9
|
+
|
|
10
|
+
**Session contract:** `/compact` keeps the same logical session alive. The SDK returns a new session ID after compaction (via the `init` system message), which the agent-runner forwards to the orchestrator as `newSessionId`. No destructive reset occurs — the agent retains summarized context.
|
|
11
|
+
|
|
12
|
+
## Phase 1: Pre-flight
|
|
13
|
+
|
|
14
|
+
Check if `src/session-commands.ts` exists:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
test -f src/session-commands.ts && echo "Already applied" || echo "Not applied"
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
If already applied, skip to Phase 3 (Verify).
|
|
21
|
+
|
|
22
|
+
## Phase 2: Apply Code Changes
|
|
23
|
+
|
|
24
|
+
Merge the skill branch:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
git fetch upstream skill/compact
|
|
28
|
+
git merge upstream/skill/compact
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
> **Note:** `upstream` is the remote pointing to `qwibitai/nanoclaw`. If using a different remote name, substitute accordingly.
|
|
32
|
+
|
|
33
|
+
This adds:
|
|
34
|
+
- `src/session-commands.ts` (extract and authorize session commands)
|
|
35
|
+
- `src/session-commands.test.ts` (unit tests for command parsing and auth)
|
|
36
|
+
- Session command interception in `src/index.ts` (both `processGroupMessages` and `startMessageLoop`)
|
|
37
|
+
- Slash command handling in `container/agent-runner/src/index.ts`
|
|
38
|
+
|
|
39
|
+
### Validate
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npm test
|
|
43
|
+
npm run build
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Rebuild container
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
./container/build.sh
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Restart service
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
launchctl kickstart -k gui/$(id -u)/com.nanoclaw # macOS
|
|
56
|
+
# Linux: systemctl --user restart nanoclaw
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Phase 3: Verify
|
|
60
|
+
|
|
61
|
+
### Integration Test
|
|
62
|
+
|
|
63
|
+
1. Start NanoClaw in dev mode: `npm run dev`
|
|
64
|
+
2. From the **main group** (self-chat), send exactly: `/compact`
|
|
65
|
+
3. Verify:
|
|
66
|
+
- The agent acknowledges compaction (e.g., "Conversation compacted.")
|
|
67
|
+
- The session continues — send a follow-up message and verify the agent responds coherently
|
|
68
|
+
- A conversation archive is written to `groups/{folder}/conversations/` (by the PreCompact hook)
|
|
69
|
+
- Container logs show `Compact boundary observed` (confirms SDK actually compacted)
|
|
70
|
+
- If `compact_boundary` was NOT observed, the response says "compact_boundary was not observed"
|
|
71
|
+
4. From a **non-main group** as a non-admin user, send: `@<assistant> /compact`
|
|
72
|
+
5. Verify:
|
|
73
|
+
- The bot responds with "Session commands require admin access."
|
|
74
|
+
- No compaction occurs, no container is spawned for the command
|
|
75
|
+
6. From a **non-main group** as the admin (device owner / `is_from_me`), send: `@<assistant> /compact`
|
|
76
|
+
7. Verify:
|
|
77
|
+
- Compaction proceeds normally (same behavior as main group)
|
|
78
|
+
8. While an **active container** is running for the main group, send `/compact`
|
|
79
|
+
9. Verify:
|
|
80
|
+
- The active container is signaled to close (authorized senders only — untrusted senders cannot kill in-flight work)
|
|
81
|
+
- Compaction proceeds via a new container once the active one exits
|
|
82
|
+
- The command is not dropped (no cursor race)
|
|
83
|
+
10. Send a normal message, then `/compact`, then another normal message in quick succession (same polling batch):
|
|
84
|
+
11. Verify:
|
|
85
|
+
- Pre-compact messages are sent to the agent first (check container logs for two `runAgent` calls)
|
|
86
|
+
- Compaction proceeds after pre-compact messages are processed
|
|
87
|
+
- Messages **after** `/compact` in the batch are preserved (cursor advances to `/compact`'s timestamp only) and processed on the next poll cycle
|
|
88
|
+
12. From a **non-main group** as a non-admin user, send `@<assistant> /compact`:
|
|
89
|
+
13. Verify:
|
|
90
|
+
- Denial message is sent ("Session commands require admin access.")
|
|
91
|
+
- The `/compact` is consumed (cursor advanced) — it does NOT replay on future polls
|
|
92
|
+
- Other messages in the same batch are also consumed (cursor is a high-water mark — this is an accepted tradeoff for the narrow edge case of denied `/compact` + other messages in the same polling interval)
|
|
93
|
+
- No container is killed or interrupted
|
|
94
|
+
14. From a **non-main group** (with `requiresTrigger` enabled) as a non-admin user, send bare `/compact` (no trigger prefix):
|
|
95
|
+
15. Verify:
|
|
96
|
+
- No denial message is sent (trigger policy prevents untrusted bot responses)
|
|
97
|
+
- The `/compact` is consumed silently
|
|
98
|
+
- Note: in groups where `requiresTrigger` is `false`, a denial message IS sent because the sender is considered reachable
|
|
99
|
+
16. After compaction, verify **no auto-compaction** behavior — only manual `/compact` triggers it
|
|
100
|
+
|
|
101
|
+
### Validation on Fresh Clone
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
git clone <your-fork> /tmp/nanoclaw-test
|
|
105
|
+
cd /tmp/nanoclaw-test
|
|
106
|
+
claude # then run /add-compact
|
|
107
|
+
npm run build
|
|
108
|
+
npm test
|
|
109
|
+
./container/build.sh
|
|
110
|
+
# Manual: send /compact from main group, verify compaction + continuation
|
|
111
|
+
# Manual: send @<assistant> /compact from non-main as non-admin, verify denial
|
|
112
|
+
# Manual: send @<assistant> /compact from non-main as admin, verify allowed
|
|
113
|
+
# Manual: verify no auto-compaction behavior
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Security Constraints
|
|
117
|
+
|
|
118
|
+
- **Main-group or trusted/admin sender only.** The main group is the user's private self-chat and is trusted (see `docs/SECURITY.md`). Non-main groups are untrusted — a careless or malicious user could wipe the agent's short-term memory. However, the device owner (`is_from_me`) is always trusted and can compact from any group.
|
|
119
|
+
- **No auto-compaction.** This skill implements manual compaction only. Automatic threshold-based compaction is a separate concern and should be a separate skill.
|
|
120
|
+
- **No config file.** NanoClaw's philosophy is customization through code changes, not configuration sprawl.
|
|
121
|
+
- **Transcript archived before compaction.** The existing `PreCompact` hook in the agent-runner archives the full transcript to `conversations/` before the SDK compacts it.
|
|
122
|
+
- **Session continues after compaction.** This is not a destructive reset. The conversation continues with summarized context.
|
|
123
|
+
|
|
124
|
+
## What This Does NOT Do
|
|
125
|
+
|
|
126
|
+
- No automatic compaction threshold (add separately if desired)
|
|
127
|
+
- No `/clear` command (separate skill, separate semantics — `/clear` is a destructive reset)
|
|
128
|
+
- No cross-group compaction (each group's session is isolated)
|
|
129
|
+
- No changes to the container image, Dockerfile, or build script
|
|
130
|
+
|
|
131
|
+
## Troubleshooting
|
|
132
|
+
|
|
133
|
+
- **"Session commands require admin access"**: Only the device owner (`is_from_me`) or main-group senders can use `/compact`. Other users are denied.
|
|
134
|
+
- **No compact_boundary in logs**: The SDK may not emit this event in all versions. Check the agent-runner logs for the warning message. Compaction may still have succeeded.
|
|
135
|
+
- **Pre-compact failure**: If messages before `/compact` fail to process, the error message says "Failed to process messages before /compact." The cursor advances past sent output to prevent duplicates; `/compact` remains pending for the next attempt.
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: add-discord
|
|
3
|
+
description: Add Discord bot channel integration to NanoClaw.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Add Discord Channel
|
|
7
|
+
|
|
8
|
+
This skill adds Discord support to NanoClaw, then walks through interactive setup.
|
|
9
|
+
|
|
10
|
+
## Phase 1: Pre-flight
|
|
11
|
+
|
|
12
|
+
### Check if already applied
|
|
13
|
+
|
|
14
|
+
Check if `src/channels/discord.ts` exists. If it does, skip to Phase 3 (Setup). The code changes are already in place.
|
|
15
|
+
|
|
16
|
+
### Ask the user
|
|
17
|
+
|
|
18
|
+
Use `AskUserQuestion` to collect configuration:
|
|
19
|
+
|
|
20
|
+
AskUserQuestion: Do you have a Discord bot token, or do you need to create one?
|
|
21
|
+
|
|
22
|
+
If they have one, collect it now. If not, we'll create one in Phase 3.
|
|
23
|
+
|
|
24
|
+
## Phase 2: Apply Code Changes
|
|
25
|
+
|
|
26
|
+
### Ensure channel remote
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
git remote -v
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
If `discord` is missing, add it:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
git remote add discord https://github.com/qwibitai/nanoclaw-discord.git
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Merge the skill branch
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
git fetch discord main
|
|
42
|
+
git merge discord/main || {
|
|
43
|
+
git checkout --theirs package-lock.json
|
|
44
|
+
git add package-lock.json
|
|
45
|
+
git merge --continue
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
This merges in:
|
|
50
|
+
- `src/channels/discord.ts` (DiscordChannel class with self-registration via `registerChannel`)
|
|
51
|
+
- `src/channels/discord.test.ts` (unit tests with discord.js mock)
|
|
52
|
+
- `import './discord.js'` appended to the channel barrel file `src/channels/index.ts`
|
|
53
|
+
- `discord.js` npm dependency in `package.json`
|
|
54
|
+
- `DISCORD_BOT_TOKEN` in `.env.example`
|
|
55
|
+
|
|
56
|
+
If the merge reports conflicts, resolve them by reading the conflicted files and understanding the intent of both sides.
|
|
57
|
+
|
|
58
|
+
### Validate code changes
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
npm install
|
|
62
|
+
npm run build
|
|
63
|
+
npx vitest run src/channels/discord.test.ts
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
All tests must pass (including the new Discord tests) and build must be clean before proceeding.
|
|
67
|
+
|
|
68
|
+
## Phase 3: Setup
|
|
69
|
+
|
|
70
|
+
### Create Discord Bot (if needed)
|
|
71
|
+
|
|
72
|
+
If the user doesn't have a bot token, tell them:
|
|
73
|
+
|
|
74
|
+
> I need you to create a Discord bot:
|
|
75
|
+
>
|
|
76
|
+
> 1. Go to the [Discord Developer Portal](https://discord.com/developers/applications)
|
|
77
|
+
> 2. Click **New Application** and give it a name (e.g., "Andy Assistant")
|
|
78
|
+
> 3. Go to the **Bot** tab on the left sidebar
|
|
79
|
+
> 4. Click **Reset Token** to generate a new bot token — copy it immediately (you can only see it once)
|
|
80
|
+
> 5. Under **Privileged Gateway Intents**, enable:
|
|
81
|
+
> - **Message Content Intent** (required to read message text)
|
|
82
|
+
> - **Server Members Intent** (optional, for member display names)
|
|
83
|
+
> 6. Go to **OAuth2** > **URL Generator**:
|
|
84
|
+
> - Scopes: select `bot`
|
|
85
|
+
> - Bot Permissions: select `Send Messages`, `Read Message History`, `View Channels`
|
|
86
|
+
> - Copy the generated URL and open it in your browser to invite the bot to your server
|
|
87
|
+
|
|
88
|
+
Wait for the user to provide the token.
|
|
89
|
+
|
|
90
|
+
### Configure environment
|
|
91
|
+
|
|
92
|
+
Add to `.env`:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
DISCORD_BOT_TOKEN=<their-token>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Channels auto-enable when their credentials are present — no extra configuration needed.
|
|
99
|
+
|
|
100
|
+
Sync to container environment:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
mkdir -p data/env && cp .env data/env/env
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
The container reads environment from `data/env/env`, not `.env` directly.
|
|
107
|
+
|
|
108
|
+
### Build and restart
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
npm run build
|
|
112
|
+
launchctl kickstart -k gui/$(id -u)/com.nanoclaw
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Phase 4: Registration
|
|
116
|
+
|
|
117
|
+
### Get Channel ID
|
|
118
|
+
|
|
119
|
+
Tell the user:
|
|
120
|
+
|
|
121
|
+
> To get the channel ID for registration:
|
|
122
|
+
>
|
|
123
|
+
> 1. In Discord, go to **User Settings** > **Advanced** > Enable **Developer Mode**
|
|
124
|
+
> 2. Right-click the text channel you want the bot to respond in
|
|
125
|
+
> 3. Click **Copy Channel ID**
|
|
126
|
+
>
|
|
127
|
+
> The channel ID will be a long number like `1234567890123456`.
|
|
128
|
+
|
|
129
|
+
Wait for the user to provide the channel ID (format: `dc:1234567890123456`).
|
|
130
|
+
|
|
131
|
+
### Register the channel
|
|
132
|
+
|
|
133
|
+
The channel ID, name, and folder name are needed. Use `npx tsx setup/index.ts --step register` with the appropriate flags.
|
|
134
|
+
|
|
135
|
+
For a main channel (responds to all messages):
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
npx tsx setup/index.ts --step register -- --jid "dc:<channel-id>" --name "<server-name> #<channel-name>" --folder "discord_main" --trigger "@${ASSISTANT_NAME}" --channel discord --no-trigger-required --is-main
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
For additional channels (trigger-only):
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
npx tsx setup/index.ts --step register -- --jid "dc:<channel-id>" --name "<server-name> #<channel-name>" --folder "discord_<channel-name>" --trigger "@${ASSISTANT_NAME}" --channel discord
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Phase 5: Verify
|
|
148
|
+
|
|
149
|
+
### Test the connection
|
|
150
|
+
|
|
151
|
+
Tell the user:
|
|
152
|
+
|
|
153
|
+
> Send a message in your registered Discord channel:
|
|
154
|
+
> - For main channel: Any message works
|
|
155
|
+
> - For non-main: @mention the bot in Discord
|
|
156
|
+
>
|
|
157
|
+
> The bot should respond within a few seconds.
|
|
158
|
+
|
|
159
|
+
### Check logs if needed
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
tail -f logs/nanoclaw.log
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Troubleshooting
|
|
166
|
+
|
|
167
|
+
### Bot not responding
|
|
168
|
+
|
|
169
|
+
1. Check `DISCORD_BOT_TOKEN` is set in `.env` AND synced to `data/env/env`
|
|
170
|
+
2. Check channel is registered: `sqlite3 store/messages.db "SELECT * FROM registered_groups WHERE jid LIKE 'dc:%'"`
|
|
171
|
+
3. For non-main channels: message must include trigger pattern (@mention the bot)
|
|
172
|
+
4. Service is running: `launchctl list | grep nanoclaw`
|
|
173
|
+
5. Verify the bot has been invited to the server (check OAuth2 URL was used)
|
|
174
|
+
|
|
175
|
+
### Bot only responds to @mentions
|
|
176
|
+
|
|
177
|
+
This is the default behavior for non-main channels (`requiresTrigger: true`). To change:
|
|
178
|
+
- Update the registered group's `requiresTrigger` to `false`
|
|
179
|
+
- Or register the channel as the main channel
|
|
180
|
+
|
|
181
|
+
### Message Content Intent not enabled
|
|
182
|
+
|
|
183
|
+
If the bot connects but can't read messages, ensure:
|
|
184
|
+
1. Go to [Discord Developer Portal](https://discord.com/developers/applications)
|
|
185
|
+
2. Select your application > **Bot** tab
|
|
186
|
+
3. Under **Privileged Gateway Intents**, enable **Message Content Intent**
|
|
187
|
+
4. Restart NanoClaw
|
|
188
|
+
|
|
189
|
+
### Getting Channel ID
|
|
190
|
+
|
|
191
|
+
If you can't copy the channel ID:
|
|
192
|
+
- Ensure **Developer Mode** is enabled: User Settings > Advanced > Developer Mode
|
|
193
|
+
- Right-click the channel name in the server sidebar > Copy Channel ID
|
|
194
|
+
|
|
195
|
+
## After Setup
|
|
196
|
+
|
|
197
|
+
The Discord bot supports:
|
|
198
|
+
- Text messages in registered channels
|
|
199
|
+
- Attachment descriptions (images, videos, files shown as placeholders)
|
|
200
|
+
- Reply context (shows who the user is replying to)
|
|
201
|
+
- @mention translation (Discord `<@botId>` → NanoClaw trigger format)
|
|
202
|
+
- Message splitting for responses over 2000 characters
|
|
203
|
+
- Typing indicators while the agent processes
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: add-gmail
|
|
3
|
+
description: Add Gmail integration to NanoClaw. Can be configured as a tool (agent reads/sends emails when triggered from WhatsApp) or as a full channel (emails can trigger the agent, schedule tasks, and receive replies). Guides through GCP OAuth setup and implements the integration.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Add Gmail Integration
|
|
7
|
+
|
|
8
|
+
This skill adds Gmail support to NanoClaw — either as a tool (read, send, search, draft) or as a full channel that polls the inbox.
|
|
9
|
+
|
|
10
|
+
## Phase 1: Pre-flight
|
|
11
|
+
|
|
12
|
+
### Check if already applied
|
|
13
|
+
|
|
14
|
+
Check if `src/channels/gmail.ts` exists. If it does, skip to Phase 3 (Setup). The code changes are already in place.
|
|
15
|
+
|
|
16
|
+
### Ask the user
|
|
17
|
+
|
|
18
|
+
Use `AskUserQuestion`:
|
|
19
|
+
|
|
20
|
+
AskUserQuestion: Should incoming emails be able to trigger the agent?
|
|
21
|
+
|
|
22
|
+
- **Yes** — Full channel mode: the agent listens on Gmail and responds to incoming emails automatically
|
|
23
|
+
- **No** — Tool-only: the agent gets full Gmail tools (read, send, search, draft) but won't monitor the inbox. No channel code is added.
|
|
24
|
+
|
|
25
|
+
## Phase 2: Apply Code Changes
|
|
26
|
+
|
|
27
|
+
### Ensure channel remote
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
git remote -v
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
If `gmail` is missing, add it:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
git remote add gmail https://github.com/qwibitai/nanoclaw-gmail.git
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Merge the skill branch
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
git fetch gmail main
|
|
43
|
+
git merge gmail/main || {
|
|
44
|
+
git checkout --theirs package-lock.json
|
|
45
|
+
git add package-lock.json
|
|
46
|
+
git merge --continue
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
This merges in:
|
|
51
|
+
- `src/channels/gmail.ts` (GmailChannel class with self-registration via `registerChannel`)
|
|
52
|
+
- `src/channels/gmail.test.ts` (unit tests)
|
|
53
|
+
- `import './gmail.js'` appended to the channel barrel file `src/channels/index.ts`
|
|
54
|
+
- Gmail credentials mount (`~/.gmail-mcp`) in `src/container-runner.ts`
|
|
55
|
+
- Gmail MCP server (`@gongrzhe/server-gmail-autoauth-mcp`) and `mcp__gmail__*` allowed tool in `container/agent-runner/src/index.ts`
|
|
56
|
+
- `googleapis` npm dependency in `package.json`
|
|
57
|
+
|
|
58
|
+
If the merge reports conflicts, resolve them by reading the conflicted files and understanding the intent of both sides.
|
|
59
|
+
|
|
60
|
+
### Add email handling instructions (Channel mode only)
|
|
61
|
+
|
|
62
|
+
If the user chose channel mode, append the following to `groups/main/CLAUDE.md` (before the formatting section):
|
|
63
|
+
|
|
64
|
+
```markdown
|
|
65
|
+
## Email Notifications
|
|
66
|
+
|
|
67
|
+
When you receive an email notification (messages starting with `[Email from ...`), inform the user about it but do NOT reply to the email unless specifically asked. You have Gmail tools available — use them only when the user explicitly asks you to reply, forward, or take action on an email.
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Validate code changes
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
npm install
|
|
74
|
+
npm run build
|
|
75
|
+
npx vitest run src/channels/gmail.test.ts
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
All tests must pass (including the new Gmail tests) and build must be clean before proceeding.
|
|
79
|
+
|
|
80
|
+
## Phase 3: Setup
|
|
81
|
+
|
|
82
|
+
### Check existing Gmail credentials
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
ls -la ~/.gmail-mcp/ 2>/dev/null || echo "No Gmail config found"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
If `credentials.json` already exists, skip to "Build and restart" below.
|
|
89
|
+
|
|
90
|
+
### GCP Project Setup
|
|
91
|
+
|
|
92
|
+
Tell the user:
|
|
93
|
+
|
|
94
|
+
> I need you to set up Google Cloud OAuth credentials:
|
|
95
|
+
>
|
|
96
|
+
> 1. Open https://console.cloud.google.com — create a new project or select existing
|
|
97
|
+
> 2. Go to **APIs & Services > Library**, search "Gmail API", click **Enable**
|
|
98
|
+
> 3. Go to **APIs & Services > Credentials**, click **+ CREATE CREDENTIALS > OAuth client ID**
|
|
99
|
+
> - If prompted for consent screen: choose "External", fill in app name and email, save
|
|
100
|
+
> - Application type: **Desktop app**, name: anything (e.g., "NanoClaw Gmail")
|
|
101
|
+
> 4. Click **DOWNLOAD JSON** and save as `gcp-oauth.keys.json`
|
|
102
|
+
>
|
|
103
|
+
> Where did you save the file? (Give me the full path, or paste the file contents here)
|
|
104
|
+
|
|
105
|
+
If user provides a path, copy it:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
mkdir -p ~/.gmail-mcp
|
|
109
|
+
cp "/path/user/provided/gcp-oauth.keys.json" ~/.gmail-mcp/gcp-oauth.keys.json
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
If user pastes JSON content, write it to `~/.gmail-mcp/gcp-oauth.keys.json`.
|
|
113
|
+
|
|
114
|
+
### OAuth Authorization
|
|
115
|
+
|
|
116
|
+
Tell the user:
|
|
117
|
+
|
|
118
|
+
> I'm going to run Gmail authorization. A browser window will open — sign in and grant access. If you see an "app isn't verified" warning, click "Advanced" then "Go to [app name] (unsafe)" — this is normal for personal OAuth apps.
|
|
119
|
+
|
|
120
|
+
Run the authorization:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
npx -y @gongrzhe/server-gmail-autoauth-mcp auth
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
If that fails (some versions don't have an auth subcommand), try `timeout 60 npx -y @gongrzhe/server-gmail-autoauth-mcp || true`. Verify with `ls ~/.gmail-mcp/credentials.json`.
|
|
127
|
+
|
|
128
|
+
### Build and restart
|
|
129
|
+
|
|
130
|
+
Clear stale per-group agent-runner copies (they only get re-created if missing, so existing copies won't pick up the new Gmail server):
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
rm -r data/sessions/*/agent-runner-src 2>/dev/null || true
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Rebuild the container (agent-runner changed):
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
cd container && ./build.sh
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Then compile and restart:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
npm run build
|
|
146
|
+
launchctl kickstart -k gui/$(id -u)/com.nanoclaw # macOS
|
|
147
|
+
# Linux: systemctl --user restart nanoclaw
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Phase 4: Verify
|
|
151
|
+
|
|
152
|
+
### Test tool access (both modes)
|
|
153
|
+
|
|
154
|
+
Tell the user:
|
|
155
|
+
|
|
156
|
+
> Gmail is connected! Send this in your main channel:
|
|
157
|
+
>
|
|
158
|
+
> `@Andy check my recent emails` or `@Andy list my Gmail labels`
|
|
159
|
+
|
|
160
|
+
### Test channel mode (Channel mode only)
|
|
161
|
+
|
|
162
|
+
Tell the user to send themselves a test email. The agent should pick it up within a minute. Monitor: `tail -f logs/nanoclaw.log | grep -iE "(gmail|email)"`.
|
|
163
|
+
|
|
164
|
+
Once verified, offer filter customization via `AskUserQuestion` — by default, only emails in the Primary inbox trigger the agent (Promotions, Social, Updates, and Forums are excluded). The user can keep this default or narrow further by sender, label, or keywords. No code changes needed for filters.
|
|
165
|
+
|
|
166
|
+
### Check logs if needed
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
tail -f logs/nanoclaw.log
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Troubleshooting
|
|
173
|
+
|
|
174
|
+
### Gmail connection not responding
|
|
175
|
+
|
|
176
|
+
Test directly:
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
npx -y @gongrzhe/server-gmail-autoauth-mcp
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### OAuth token expired
|
|
183
|
+
|
|
184
|
+
Re-authorize:
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
rm ~/.gmail-mcp/credentials.json
|
|
188
|
+
npx -y @gongrzhe/server-gmail-autoauth-mcp
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Container can't access Gmail
|
|
192
|
+
|
|
193
|
+
- Verify `~/.gmail-mcp` is mounted: check `src/container-runner.ts` for the `.gmail-mcp` mount
|
|
194
|
+
- Check container logs: `cat groups/main/logs/container-*.log | tail -50`
|
|
195
|
+
|
|
196
|
+
### Emails not being detected (Channel mode only)
|
|
197
|
+
|
|
198
|
+
- By default, the channel polls unread Primary inbox emails (`is:unread category:primary`)
|
|
199
|
+
- Check logs for Gmail polling errors
|
|
200
|
+
|
|
201
|
+
## Removal
|
|
202
|
+
|
|
203
|
+
### Tool-only mode
|
|
204
|
+
|
|
205
|
+
1. Remove `~/.gmail-mcp` mount from `src/container-runner.ts`
|
|
206
|
+
2. Remove `gmail` MCP server and `mcp__gmail__*` from `container/agent-runner/src/index.ts`
|
|
207
|
+
3. Rebuild and restart
|
|
208
|
+
4. Clear stale agent-runner copies: `rm -r data/sessions/*/agent-runner-src 2>/dev/null || true`
|
|
209
|
+
5. Rebuild: `cd container && ./build.sh && cd .. && npm run build && launchctl kickstart -k gui/$(id -u)/com.nanoclaw` (macOS) or `systemctl --user restart nanoclaw` (Linux)
|
|
210
|
+
|
|
211
|
+
### Channel mode
|
|
212
|
+
|
|
213
|
+
1. Delete `src/channels/gmail.ts` and `src/channels/gmail.test.ts`
|
|
214
|
+
2. Remove `import './gmail.js'` from `src/channels/index.ts`
|
|
215
|
+
3. Remove `~/.gmail-mcp` mount from `src/container-runner.ts`
|
|
216
|
+
4. Remove `gmail` MCP server and `mcp__gmail__*` from `container/agent-runner/src/index.ts`
|
|
217
|
+
5. Uninstall: `npm uninstall googleapis`
|
|
218
|
+
6. Rebuild and restart
|
|
219
|
+
7. Clear stale agent-runner copies: `rm -r data/sessions/*/agent-runner-src 2>/dev/null || true`
|
|
220
|
+
8. Rebuild: `cd container && ./build.sh && cd .. && npm run build && launchctl kickstart -k gui/$(id -u)/com.nanoclaw` (macOS) or `systemctl --user restart nanoclaw` (Linux)
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: add-image-vision
|
|
3
|
+
description: Add image vision to NanoClaw agents. Resizes and processes WhatsApp image attachments, then sends them to Claude as multimodal content blocks.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Image Vision Skill
|
|
7
|
+
|
|
8
|
+
Adds the ability for NanoClaw agents to see and understand images sent via WhatsApp. Images are downloaded, resized with sharp, saved to the group workspace, and passed to the agent as base64-encoded multimodal content blocks.
|
|
9
|
+
|
|
10
|
+
## Phase 1: Pre-flight
|
|
11
|
+
|
|
12
|
+
1. Check if `src/image.ts` exists — skip to Phase 3 if already applied
|
|
13
|
+
2. Confirm `sharp` is installable (native bindings require build tools)
|
|
14
|
+
|
|
15
|
+
**Prerequisite:** WhatsApp must be installed first (`skill/whatsapp` merged). This skill modifies WhatsApp channel files.
|
|
16
|
+
|
|
17
|
+
## Phase 2: Apply Code Changes
|
|
18
|
+
|
|
19
|
+
### Ensure WhatsApp fork remote
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
git remote -v
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
If `whatsapp` is missing, add it:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
git remote add whatsapp https://github.com/qwibitai/nanoclaw-whatsapp.git
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Merge the skill branch
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
git fetch whatsapp skill/image-vision
|
|
35
|
+
git merge whatsapp/skill/image-vision || {
|
|
36
|
+
git checkout --theirs package-lock.json
|
|
37
|
+
git add package-lock.json
|
|
38
|
+
git merge --continue
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
This merges in:
|
|
43
|
+
- `src/image.ts` (image download, resize via sharp, base64 encoding)
|
|
44
|
+
- `src/image.test.ts` (8 unit tests)
|
|
45
|
+
- Image attachment handling in `src/channels/whatsapp.ts`
|
|
46
|
+
- Image passing to agent in `src/index.ts` and `src/container-runner.ts`
|
|
47
|
+
- Image content block support in `container/agent-runner/src/index.ts`
|
|
48
|
+
- `sharp` npm dependency in `package.json`
|
|
49
|
+
|
|
50
|
+
If the merge reports conflicts, resolve them by reading the conflicted files and understanding the intent of both sides.
|
|
51
|
+
|
|
52
|
+
### Validate code changes
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
npm install
|
|
56
|
+
npm run build
|
|
57
|
+
npx vitest run src/image.test.ts
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
All tests must pass and build must be clean before proceeding.
|
|
61
|
+
|
|
62
|
+
## Phase 3: Configure
|
|
63
|
+
|
|
64
|
+
1. Rebuild the container (agent-runner changes need a rebuild):
|
|
65
|
+
```bash
|
|
66
|
+
./container/build.sh
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
2. Sync agent-runner source to group caches:
|
|
70
|
+
```bash
|
|
71
|
+
for dir in data/sessions/*/agent-runner-src/; do
|
|
72
|
+
cp container/agent-runner/src/*.ts "$dir"
|
|
73
|
+
done
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
3. Restart the service:
|
|
77
|
+
```bash
|
|
78
|
+
launchctl kickstart -k gui/$(id -u)/com.nanoclaw
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Phase 4: Verify
|
|
82
|
+
|
|
83
|
+
1. Send an image in a registered WhatsApp group
|
|
84
|
+
2. Check the agent responds with understanding of the image content
|
|
85
|
+
3. Check logs for "Processed image attachment":
|
|
86
|
+
```bash
|
|
87
|
+
tail -50 groups/*/logs/container-*.log
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Troubleshooting
|
|
91
|
+
|
|
92
|
+
- **"Image - download failed"**: Check WhatsApp connection stability. The download may timeout on slow connections.
|
|
93
|
+
- **"Image - processing failed"**: Sharp may not be installed correctly. Run `npm ls sharp` to verify.
|
|
94
|
+
- **Agent doesn't mention image content**: Check container logs for "Loaded image" messages. If missing, ensure agent-runner source was synced to group caches.
|