pythonclaw 0.3.3__tar.gz → 0.5.0__tar.gz

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.
Files changed (158) hide show
  1. {pythonclaw-0.3.3/pythonclaw.egg-info → pythonclaw-0.5.0}/PKG-INFO +56 -31
  2. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/README.md +53 -29
  3. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pyproject.toml +16 -2
  4. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/__init__.py +1 -1
  5. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/channels/discord_bot.py +8 -1
  6. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/channels/telegram_bot.py +58 -3
  7. pythonclaw-0.5.0/pythonclaw/channels/whatsapp_bot.py +252 -0
  8. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/config.py +25 -0
  9. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/core/agent.py +144 -16
  10. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/core/memory/manager.py +39 -13
  11. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/core/memory/storage.py +0 -1
  12. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/core/persistent_agent.py +6 -0
  13. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/core/skill_loader.py +90 -7
  14. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/core/skillhub.py +29 -1
  15. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/init.py +2 -1
  16. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/main.py +6 -3
  17. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/onboard.py +48 -1
  18. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/server.py +21 -2
  19. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/session_manager.py +63 -12
  20. pythonclaw-0.5.0/pythonclaw/templates/skills/communication/CATEGORY.md +5 -0
  21. pythonclaw-0.5.0/pythonclaw/templates/skills/communication/email/SKILL.md +65 -0
  22. pythonclaw-0.5.0/pythonclaw/templates/skills/communication/slack/SKILL.md +98 -0
  23. pythonclaw-0.5.0/pythonclaw/templates/skills/communication/slack/slack_api.py +153 -0
  24. pythonclaw-0.5.0/pythonclaw/templates/skills/data/CATEGORY.md +5 -0
  25. pythonclaw-0.5.0/pythonclaw/templates/skills/data/csv_analyzer/SKILL.md +61 -0
  26. pythonclaw-0.5.0/pythonclaw/templates/skills/data/finance/SKILL.md +51 -0
  27. pythonclaw-0.5.0/pythonclaw/templates/skills/data/news/SKILL.md +52 -0
  28. pythonclaw-0.5.0/pythonclaw/templates/skills/data/pdf_reader/SKILL.md +51 -0
  29. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/templates/skills/data/scraper/SKILL.md +1 -0
  30. pythonclaw-0.5.0/pythonclaw/templates/skills/data/weather/SKILL.md +68 -0
  31. pythonclaw-0.5.0/pythonclaw/templates/skills/data/youtube/SKILL.md +54 -0
  32. pythonclaw-0.5.0/pythonclaw/templates/skills/dev/CATEGORY.md +5 -0
  33. pythonclaw-0.5.0/pythonclaw/templates/skills/dev/code_runner/SKILL.md +58 -0
  34. pythonclaw-0.5.0/pythonclaw/templates/skills/dev/github/SKILL.md +78 -0
  35. pythonclaw-0.5.0/pythonclaw/templates/skills/dev/http_request/SKILL.md +50 -0
  36. pythonclaw-0.5.0/pythonclaw/templates/skills/google/CATEGORY.md +5 -0
  37. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/templates/skills/google/workspace/SKILL.md +43 -36
  38. pythonclaw-0.5.0/pythonclaw/templates/skills/google/workspace/check_setup.sh +52 -0
  39. pythonclaw-0.5.0/pythonclaw/templates/skills/media/CATEGORY.md +5 -0
  40. pythonclaw-0.5.0/pythonclaw/templates/skills/media/image_gen/SKILL.md +99 -0
  41. pythonclaw-0.5.0/pythonclaw/templates/skills/media/image_gen/generate.py +103 -0
  42. pythonclaw-0.5.0/pythonclaw/templates/skills/media/spotify/SKILL.md +124 -0
  43. pythonclaw-0.5.0/pythonclaw/templates/skills/media/spotify/spotify_ctl.py +231 -0
  44. pythonclaw-0.5.0/pythonclaw/templates/skills/media/tts/SKILL.md +83 -0
  45. pythonclaw-0.5.0/pythonclaw/templates/skills/media/tts/speak.py +50 -0
  46. pythonclaw-0.5.0/pythonclaw/templates/skills/meta/CATEGORY.md +5 -0
  47. pythonclaw-0.5.0/pythonclaw/templates/skills/meta/skill_creator/SKILL.md +79 -0
  48. pythonclaw-0.5.0/pythonclaw/templates/skills/productivity/CATEGORY.md +5 -0
  49. pythonclaw-0.5.0/pythonclaw/templates/skills/productivity/notion/SKILL.md +99 -0
  50. pythonclaw-0.5.0/pythonclaw/templates/skills/productivity/notion/notion_api.py +185 -0
  51. pythonclaw-0.5.0/pythonclaw/templates/skills/productivity/obsidian/SKILL.md +110 -0
  52. pythonclaw-0.5.0/pythonclaw/templates/skills/productivity/obsidian/obsidian_vault.py +165 -0
  53. pythonclaw-0.5.0/pythonclaw/templates/skills/productivity/trello/SKILL.md +110 -0
  54. pythonclaw-0.5.0/pythonclaw/templates/skills/productivity/trello/trello_api.py +141 -0
  55. pythonclaw-0.5.0/pythonclaw/templates/skills/system/CATEGORY.md +5 -0
  56. pythonclaw-0.5.0/pythonclaw/templates/skills/system/change_persona/SKILL.md +47 -0
  57. pythonclaw-0.5.0/pythonclaw/templates/skills/system/change_setting/SKILL.md +69 -0
  58. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/templates/skills/system/change_setting/update_config.py +1 -0
  59. pythonclaw-0.5.0/pythonclaw/templates/skills/system/change_soul/SKILL.md +46 -0
  60. pythonclaw-0.5.0/pythonclaw/templates/skills/system/model_usage/SKILL.md +73 -0
  61. pythonclaw-0.5.0/pythonclaw/templates/skills/system/model_usage/usage_stats.py +73 -0
  62. pythonclaw-0.5.0/pythonclaw/templates/skills/system/onboarding/SKILL.md +58 -0
  63. pythonclaw-0.5.0/pythonclaw/templates/skills/system/random/SKILL.md +51 -0
  64. pythonclaw-0.5.0/pythonclaw/templates/skills/system/session_logs/SKILL.md +80 -0
  65. pythonclaw-0.5.0/pythonclaw/templates/skills/system/session_logs/search_sessions.py +55 -0
  66. pythonclaw-0.5.0/pythonclaw/templates/skills/system/time/SKILL.md +51 -0
  67. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/templates/skills/system/time/time_util.py +1 -1
  68. pythonclaw-0.5.0/pythonclaw/templates/skills/text/CATEGORY.md +5 -0
  69. pythonclaw-0.5.0/pythonclaw/templates/skills/text/translator/SKILL.md +52 -0
  70. pythonclaw-0.5.0/pythonclaw/templates/skills/web/CATEGORY.md +5 -0
  71. pythonclaw-0.5.0/pythonclaw/templates/skills/web/summarize/SKILL.md +84 -0
  72. pythonclaw-0.5.0/pythonclaw/templates/skills/web/summarize/summarize_url.py +107 -0
  73. pythonclaw-0.5.0/pythonclaw/templates/skills/web/tavily/SKILL.md +62 -0
  74. pythonclaw-0.5.0/pythonclaw/templates/tools/TOOLS.md +43 -0
  75. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/web/app.py +116 -12
  76. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/web/static/index.html +198 -33
  77. {pythonclaw-0.3.3 → pythonclaw-0.5.0/pythonclaw.egg-info}/PKG-INFO +56 -31
  78. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw.egg-info/SOURCES.txt +25 -0
  79. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw.egg-info/requires.txt +1 -0
  80. pythonclaw-0.3.3/pythonclaw/templates/skills/communication/CATEGORY.md +0 -4
  81. pythonclaw-0.3.3/pythonclaw/templates/skills/communication/email/SKILL.md +0 -54
  82. pythonclaw-0.3.3/pythonclaw/templates/skills/data/CATEGORY.md +0 -4
  83. pythonclaw-0.3.3/pythonclaw/templates/skills/data/csv_analyzer/SKILL.md +0 -51
  84. pythonclaw-0.3.3/pythonclaw/templates/skills/data/finance/SKILL.md +0 -41
  85. pythonclaw-0.3.3/pythonclaw/templates/skills/data/news/SKILL.md +0 -39
  86. pythonclaw-0.3.3/pythonclaw/templates/skills/data/pdf_reader/SKILL.md +0 -40
  87. pythonclaw-0.3.3/pythonclaw/templates/skills/data/weather/SKILL.md +0 -42
  88. pythonclaw-0.3.3/pythonclaw/templates/skills/data/youtube/SKILL.md +0 -43
  89. pythonclaw-0.3.3/pythonclaw/templates/skills/dev/CATEGORY.md +0 -4
  90. pythonclaw-0.3.3/pythonclaw/templates/skills/dev/code_runner/SKILL.md +0 -46
  91. pythonclaw-0.3.3/pythonclaw/templates/skills/dev/github/SKILL.md +0 -52
  92. pythonclaw-0.3.3/pythonclaw/templates/skills/dev/http_request/SKILL.md +0 -40
  93. pythonclaw-0.3.3/pythonclaw/templates/skills/google/CATEGORY.md +0 -4
  94. pythonclaw-0.3.3/pythonclaw/templates/skills/meta/CATEGORY.md +0 -4
  95. pythonclaw-0.3.3/pythonclaw/templates/skills/meta/skill_creator/SKILL.md +0 -151
  96. pythonclaw-0.3.3/pythonclaw/templates/skills/system/CATEGORY.md +0 -4
  97. pythonclaw-0.3.3/pythonclaw/templates/skills/system/change_persona/SKILL.md +0 -41
  98. pythonclaw-0.3.3/pythonclaw/templates/skills/system/change_setting/SKILL.md +0 -65
  99. pythonclaw-0.3.3/pythonclaw/templates/skills/system/change_soul/SKILL.md +0 -41
  100. pythonclaw-0.3.3/pythonclaw/templates/skills/system/onboarding/SKILL.md +0 -63
  101. pythonclaw-0.3.3/pythonclaw/templates/skills/system/random/SKILL.md +0 -33
  102. pythonclaw-0.3.3/pythonclaw/templates/skills/system/time/SKILL.md +0 -33
  103. pythonclaw-0.3.3/pythonclaw/templates/skills/text/CATEGORY.md +0 -4
  104. pythonclaw-0.3.3/pythonclaw/templates/skills/text/translator/SKILL.md +0 -47
  105. pythonclaw-0.3.3/pythonclaw/templates/skills/web/CATEGORY.md +0 -4
  106. pythonclaw-0.3.3/pythonclaw/templates/skills/web/tavily/SKILL.md +0 -61
  107. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/LICENSE +0 -0
  108. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/MANIFEST.in +0 -0
  109. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/__main__.py +0 -0
  110. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/core/__init__.py +1 -1
  111. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/core/compaction.py +0 -0
  112. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/core/knowledge/rag.py +0 -0
  113. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/core/llm/anthropic_client.py +0 -0
  114. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/core/llm/base.py +0 -0
  115. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/core/llm/gemini_client.py +0 -0
  116. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/core/llm/openai_compatible.py +0 -0
  117. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/core/llm/response.py +0 -0
  118. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/core/retrieval/__init__.py +1 -1
  119. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/core/retrieval/chunker.py +0 -0
  120. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/core/retrieval/dense.py +2 -2
  121. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/core/retrieval/fusion.py +0 -0
  122. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/core/retrieval/reranker.py +0 -0
  123. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/core/retrieval/retriever.py +1 -1
  124. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/core/retrieval/sparse.py +0 -0
  125. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/core/session_store.py +0 -0
  126. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/core/tools.py +0 -0
  127. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/core/utils.py +0 -0
  128. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/daemon.py +0 -0
  129. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/scheduler/cron.py +0 -0
  130. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/scheduler/heartbeat.py +0 -0
  131. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/templates/persona/demo_persona.md +0 -0
  132. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/templates/skills/communication/email/send_email.py +0 -0
  133. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/templates/skills/data/csv_analyzer/analyze.py +0 -0
  134. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/templates/skills/data/finance/fetch_quote.py +0 -0
  135. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/templates/skills/data/news/search_news.py +0 -0
  136. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/templates/skills/data/pdf_reader/read_pdf.py +0 -0
  137. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/templates/skills/data/scraper/scrape.py +0 -0
  138. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/templates/skills/data/weather/weather.py +1 -1
  139. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/templates/skills/data/youtube/youtube_info.py +0 -0
  140. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/templates/skills/dev/code_runner/run_code.py +1 -1
  141. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/templates/skills/dev/github/gh.py +0 -0
  142. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/templates/skills/dev/http_request/request.py +0 -0
  143. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/templates/skills/system/onboarding/write_identity.py +0 -0
  144. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/templates/skills/system/random/random_util.py +0 -0
  145. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/templates/skills/text/translator/translate.py +0 -0
  146. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/templates/soul/SOUL.md +0 -0
  147. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/web/__init__.py +0 -0
  148. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/web/static/favicon.png +0 -0
  149. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw/web/static/logo.png +0 -0
  150. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw.egg-info/dependency_links.txt +0 -0
  151. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw.egg-info/entry_points.txt +0 -0
  152. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/pythonclaw.egg-info/top_level.txt +0 -0
  153. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/setup.cfg +0 -0
  154. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/tests/test_compaction.py +0 -0
  155. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/tests/test_persistence.py +0 -0
  156. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/tests/test_rag_hybrid.py +0 -0
  157. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/tests/test_skills.py +0 -0
  158. {pythonclaw-0.3.3 → pythonclaw-0.5.0}/tests/test_soul.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pythonclaw
3
- Version: 0.3.3
3
+ Version: 0.5.0
4
4
  Summary: OpenClaw reimagined in pure Python — autonomous AI agent with memory, RAG, skills, web dashboard, and multi-channel support.
5
5
  Author-email: Eric Wang <wangchen2007915@gmail.com>
6
6
  License: MIT
@@ -8,7 +8,7 @@ Project-URL: Homepage, https://github.com/ericwang915/PythonClaw
8
8
  Project-URL: Documentation, https://github.com/ericwang915/PythonClaw#readme
9
9
  Project-URL: Repository, https://github.com/ericwang915/PythonClaw
10
10
  Project-URL: Issues, https://github.com/ericwang915/PythonClaw/issues
11
- Keywords: llm,agent,ai,autonomous,memory,rag,skills,telegram,chatbot,framework
11
+ Keywords: llm,agent,ai,autonomous,memory,rag,skills,telegram,discord,whatsapp,chatbot,framework
12
12
  Classifier: Development Status :: 4 - Beta
13
13
  Classifier: Intended Audience :: Developers
14
14
  Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
@@ -27,6 +27,7 @@ Requires-Dist: anthropic
27
27
  Requires-Dist: google-generativeai
28
28
  Requires-Dist: python-telegram-bot[job-queue]>=20.0
29
29
  Requires-Dist: discord.py>=2.3
30
+ Requires-Dist: pywa[fastapi]>=2.8
30
31
  Requires-Dist: apscheduler>=3.10
31
32
  Requires-Dist: pyyaml>=6.0
32
33
  Requires-Dist: fastapi>=0.100
@@ -83,9 +84,12 @@ Dynamic: license-file
83
84
  | 🌐 | **Web dashboard** | Browser UI for chat, config, skill catalog, identity editing, and marketplace |
84
85
  | 🎙️ | **Voice input** | Deepgram speech-to-text in the web chat |
85
86
  | ⏰ | **Cron jobs** | Schedule tasks via YAML or let the agent create its own |
86
- | 📡 | **Multi-channel** | CLI, Web, Telegram, Discord — same agent, different interfaces |
87
+ | 📡 | **Multi-channel** | CLI, Web, Telegram, Discord, WhatsApp — same agent, different interfaces |
87
88
  | 🔄 | **Daemon mode** | PID-managed background process with `start` / `stop` / `status` |
88
89
  | 🧬 | **Soul + Persona** | Separate core identity from swappable role presentation |
90
+ | 🔧 | **TOOLS.md** | Local environment notes — your cheat sheet for the agent |
91
+ | 🔒 | **Per-group isolation** | Each chat session gets its own memory (optional) |
92
+ | 🔁 | **Concurrency control** | Per-session locks + global semaphore prevent interleaving |
89
93
 
90
94
  ---
91
95
 
@@ -112,7 +116,7 @@ pythonclaw stop
112
116
  ```bash
113
117
  git clone https://github.com/ericwang915/PythonClaw.git
114
118
  cd PythonClaw
115
- pip install -e ".[all]"
119
+ pip install -e .
116
120
  pythonclaw onboard
117
121
  ```
118
122
 
@@ -125,7 +129,7 @@ pythonclaw onboard
125
129
  | `pythonclaw onboard` | Interactive setup wizard — choose LLM provider, enter API key |
126
130
  | `pythonclaw start` | Start the agent as a background daemon |
127
131
  | `pythonclaw start -f` | Start in foreground (no daemonize) |
128
- | `pythonclaw start --channels telegram discord` | Start with messaging channels |
132
+ | `pythonclaw start --channels telegram discord whatsapp` | Start with messaging channels |
129
133
  | `pythonclaw stop` | Stop the running daemon |
130
134
  | `pythonclaw status` | Show daemon status (PID, uptime, port) |
131
135
  | `pythonclaw chat` | Interactive CLI chat (foreground REPL) |
@@ -170,24 +174,25 @@ $ pythonclaw start
170
174
  ## Architecture
171
175
 
172
176
  ```
173
- ┌──────────────────────────────────────────────────────────┐
174
- PythonClaw
175
- ├──────────┬───────────┬───────────┬───────────────────────┤
176
- │ CLI │ Daemon │ Sessions │ Core
177
- │ │ │ │
178
- │ onboard │ start / │ Store(MD) │ Agent
179
- │ chat │ stop / │ Manager │ ├─ Memory (Markdown)
180
- │ skill … │ status │ ├─ RAG (Hybrid)
181
- │ │ │ ├─ Skills (3-tier)
182
- │ Web UI ◄─┤ Channels │ │ ├─ Compaction
183
- │ Voice In │ Telegram │ ├─ Soul + Persona
184
- │ │ Discord └─ Tool Execution
185
- ├──────────┴───────────┴───────────┴───────────────────────┤
186
- │ LLM Provider Abstraction Layer │
187
- DeepSeek Grok Claude │ Gemini │ Kimi │ GLM │
188
- ├──────────────────────────────────────────────────────────┤
189
- │ SkillHub Marketplace (skillhub.club) │
190
- └──────────────────────────────────────────────────────────┘
177
+ ┌──────────────────────────────────────────────────────────────┐
178
+ PythonClaw
179
+ ├──────────┬────────────┬───────────┬──────────────────────────┤
180
+ │ CLI │ Daemon │ Sessions │ Core
181
+ │ │ │ │
182
+ │ onboard │ start / │ Store(MD) │ Agent
183
+ │ chat │ stop / │ Manager │ ├─ Memory (Markdown)
184
+ │ skill … │ status Locks + │ ├─ RAG (Hybrid)
185
+ │ │ Semaphore │ ├─ Skills (3-tier)
186
+ │ Web UI ◄─┤ Channels │ │ ├─ Compaction
187
+ │ Voice In │ Telegram Per-group │ ├─ Soul + Persona
188
+ │ │ Discord Isolation ├─ Group Context
189
+ │ │ WhatsApp │ │ └─ Tool Execution │
190
+ ├──────────┴────────────┴───────────┴──────────────────────────┤
191
+ LLM Provider Abstraction Layer
192
+ │ DeepSeek │ Grok │ Claude │ Gemini │ Kimi │ GLM │
193
+ ├──────────────────────────────────────────────────────────────┤
194
+ │ SkillHub Marketplace (skillhub.club) │
195
+ └──────────────────────────────────────────────────────────────┘
191
196
  ```
192
197
 
193
198
  ---
@@ -220,8 +225,11 @@ See [`pythonclaw.example.json`](pythonclaw.example.json) for the full template.
220
225
  "web": { "host": "0.0.0.0", "port": 7788 },
221
226
  "channels": {
222
227
  "telegram": { "token": "" },
223
- "discord": { "token": "" }
224
- }
228
+ "discord": { "token": "" },
229
+ "whatsapp": { "phoneNumberId": "", "token": "", "verifyToken": "pythonclaw_verify" }
230
+ },
231
+ "isolation": { "perGroup": false },
232
+ "concurrency": { "maxAgents": 4 }
225
233
  }
226
234
  ```
227
235
 
@@ -235,8 +243,8 @@ Environment variables (e.g. `DEEPSEEK_API_KEY`, `TAVILY_API_KEY`) override JSON
235
243
  |----------|---------------|---------------|
236
244
  | **DeepSeek** | `deepseek-chat` | — |
237
245
  | **Grok (xAI)** | `grok-3` | — |
238
- | **Claude (Anthropic)** | `claude-sonnet-4-20250514` | `pip install pythonclaw[anthropic]` |
239
- | **Gemini (Google)** | `gemini-2.0-flash` | `pip install pythonclaw[gemini]` |
246
+ | **Claude (Anthropic)** | `claude-sonnet-4-20250514` | (included) |
247
+ | **Gemini (Google)** | `gemini-2.0-flash` | (included) |
240
248
  | **Kimi (Moonshot)** | `moonshot-v1-128k` | — |
241
249
  | **GLM (Zhipu)** | `glm-4-flash` | — |
242
250
  | Any OpenAI-compatible | Custom | — |
@@ -282,11 +290,28 @@ Also accessible from the web dashboard **Marketplace** tab.
282
290
  ### Markdown Memory
283
291
 
284
292
  ```
285
- context/memory/
293
+ ~/.pythonclaw/context/memory/
286
294
  ├── MEMORY.md # Curated long-term memory
287
295
  └── 2026-02-23.md # Daily append-only log
288
296
  ```
289
297
 
298
+ When **per-group isolation** is enabled (`"isolation": { "perGroup": true }` in config),
299
+ each session (Telegram chat, Discord channel, etc.) gets its own `memory/`, `persona/`,
300
+ and `soul/` under `~/.pythonclaw/context/groups/<session-id>/`, while global memories
301
+ remain accessible via read-through fallback.
302
+
303
+ ### TOOLS.md — Local Notes
304
+
305
+ ```
306
+ ~/.pythonclaw/context/tools/
307
+ └── TOOLS.md # Your environment-specific cheat sheet
308
+ ```
309
+
310
+ Skills define *how* tools work. `TOOLS.md` stores *your* specifics — SSH hosts, device
311
+ nicknames, project paths, preferred defaults, API endpoints. Keeping them apart means
312
+ you can update skills without losing your notes, and share skills without leaking your
313
+ infrastructure. Editable from the web dashboard.
314
+
290
315
  ### Hybrid RAG Pipeline
291
316
 
292
317
  ```
@@ -333,7 +358,7 @@ PythonClaw/
333
358
  │ │ ├── memory/ # Markdown memory
334
359
  │ │ ├── knowledge/ # Knowledge-base RAG
335
360
  │ │ └── retrieval/ # BM25 + dense + fusion + reranker
336
- │ ├── channels/ # Telegram, Discord
361
+ │ ├── channels/ # Telegram, Discord, WhatsApp
337
362
  │ ├── scheduler/ # Cron jobs, heartbeat
338
363
  │ ├── web/ # FastAPI dashboard + static assets
339
364
  │ └── templates/ # Built-in skill templates
@@ -351,7 +376,7 @@ PythonClaw/
351
376
  git clone https://github.com/ericwang915/PythonClaw.git
352
377
  cd PythonClaw
353
378
  python -m venv .venv && source .venv/bin/activate
354
- pip install -e ".[all]"
379
+ pip install -e .
355
380
  pytest tests/ -v
356
381
  ```
357
382
 
@@ -373,7 +398,7 @@ We welcome contributions! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
373
398
  | Dashboard | Web UI | Web UI (localhost:7788) |
374
399
  | Memory | Markdown | Markdown (long-term + daily) |
375
400
  | Skills | Plugin system | Three-tier + SkillHub marketplace |
376
- | Channels | Discord, Telegram, WhatsApp | CLI, Web, Telegram, Discord |
401
+ | Channels | Discord, Telegram, WhatsApp | CLI, Web, Telegram, Discord, WhatsApp |
377
402
  | Voice | — | Deepgram STT |
378
403
  | LLM Providers | OpenAI, Anthropic, Gemini | DeepSeek, Grok, Claude, Gemini, Kimi, GLM |
379
404
  | Daemon | Background process | PID-managed (`start`/`stop`/`status`) |
@@ -42,9 +42,12 @@
42
42
  | 🌐 | **Web dashboard** | Browser UI for chat, config, skill catalog, identity editing, and marketplace |
43
43
  | 🎙️ | **Voice input** | Deepgram speech-to-text in the web chat |
44
44
  | ⏰ | **Cron jobs** | Schedule tasks via YAML or let the agent create its own |
45
- | 📡 | **Multi-channel** | CLI, Web, Telegram, Discord — same agent, different interfaces |
45
+ | 📡 | **Multi-channel** | CLI, Web, Telegram, Discord, WhatsApp — same agent, different interfaces |
46
46
  | 🔄 | **Daemon mode** | PID-managed background process with `start` / `stop` / `status` |
47
47
  | 🧬 | **Soul + Persona** | Separate core identity from swappable role presentation |
48
+ | 🔧 | **TOOLS.md** | Local environment notes — your cheat sheet for the agent |
49
+ | 🔒 | **Per-group isolation** | Each chat session gets its own memory (optional) |
50
+ | 🔁 | **Concurrency control** | Per-session locks + global semaphore prevent interleaving |
48
51
 
49
52
  ---
50
53
 
@@ -71,7 +74,7 @@ pythonclaw stop
71
74
  ```bash
72
75
  git clone https://github.com/ericwang915/PythonClaw.git
73
76
  cd PythonClaw
74
- pip install -e ".[all]"
77
+ pip install -e .
75
78
  pythonclaw onboard
76
79
  ```
77
80
 
@@ -84,7 +87,7 @@ pythonclaw onboard
84
87
  | `pythonclaw onboard` | Interactive setup wizard — choose LLM provider, enter API key |
85
88
  | `pythonclaw start` | Start the agent as a background daemon |
86
89
  | `pythonclaw start -f` | Start in foreground (no daemonize) |
87
- | `pythonclaw start --channels telegram discord` | Start with messaging channels |
90
+ | `pythonclaw start --channels telegram discord whatsapp` | Start with messaging channels |
88
91
  | `pythonclaw stop` | Stop the running daemon |
89
92
  | `pythonclaw status` | Show daemon status (PID, uptime, port) |
90
93
  | `pythonclaw chat` | Interactive CLI chat (foreground REPL) |
@@ -129,24 +132,25 @@ $ pythonclaw start
129
132
  ## Architecture
130
133
 
131
134
  ```
132
- ┌──────────────────────────────────────────────────────────┐
133
- PythonClaw
134
- ├──────────┬───────────┬───────────┬───────────────────────┤
135
- │ CLI │ Daemon │ Sessions │ Core
136
- │ │ │ │
137
- │ onboard │ start / │ Store(MD) │ Agent
138
- │ chat │ stop / │ Manager │ ├─ Memory (Markdown)
139
- │ skill … │ status │ ├─ RAG (Hybrid)
140
- │ │ │ ├─ Skills (3-tier)
141
- │ Web UI ◄─┤ Channels │ │ ├─ Compaction
142
- │ Voice In │ Telegram │ ├─ Soul + Persona
143
- │ │ Discord └─ Tool Execution
144
- ├──────────┴───────────┴───────────┴───────────────────────┤
145
- │ LLM Provider Abstraction Layer │
146
- DeepSeek Grok Claude │ Gemini │ Kimi │ GLM │
147
- ├──────────────────────────────────────────────────────────┤
148
- │ SkillHub Marketplace (skillhub.club) │
149
- └──────────────────────────────────────────────────────────┘
135
+ ┌──────────────────────────────────────────────────────────────┐
136
+ PythonClaw
137
+ ├──────────┬────────────┬───────────┬──────────────────────────┤
138
+ │ CLI │ Daemon │ Sessions │ Core
139
+ │ │ │ │
140
+ │ onboard │ start / │ Store(MD) │ Agent
141
+ │ chat │ stop / │ Manager │ ├─ Memory (Markdown)
142
+ │ skill … │ status Locks + │ ├─ RAG (Hybrid)
143
+ │ │ Semaphore │ ├─ Skills (3-tier)
144
+ │ Web UI ◄─┤ Channels │ │ ├─ Compaction
145
+ │ Voice In │ Telegram Per-group │ ├─ Soul + Persona
146
+ │ │ Discord Isolation ├─ Group Context
147
+ │ │ WhatsApp │ │ └─ Tool Execution │
148
+ ├──────────┴────────────┴───────────┴──────────────────────────┤
149
+ LLM Provider Abstraction Layer
150
+ │ DeepSeek │ Grok │ Claude │ Gemini │ Kimi │ GLM │
151
+ ├──────────────────────────────────────────────────────────────┤
152
+ │ SkillHub Marketplace (skillhub.club) │
153
+ └──────────────────────────────────────────────────────────────┘
150
154
  ```
151
155
 
152
156
  ---
@@ -179,8 +183,11 @@ See [`pythonclaw.example.json`](pythonclaw.example.json) for the full template.
179
183
  "web": { "host": "0.0.0.0", "port": 7788 },
180
184
  "channels": {
181
185
  "telegram": { "token": "" },
182
- "discord": { "token": "" }
183
- }
186
+ "discord": { "token": "" },
187
+ "whatsapp": { "phoneNumberId": "", "token": "", "verifyToken": "pythonclaw_verify" }
188
+ },
189
+ "isolation": { "perGroup": false },
190
+ "concurrency": { "maxAgents": 4 }
184
191
  }
185
192
  ```
186
193
 
@@ -194,8 +201,8 @@ Environment variables (e.g. `DEEPSEEK_API_KEY`, `TAVILY_API_KEY`) override JSON
194
201
  |----------|---------------|---------------|
195
202
  | **DeepSeek** | `deepseek-chat` | — |
196
203
  | **Grok (xAI)** | `grok-3` | — |
197
- | **Claude (Anthropic)** | `claude-sonnet-4-20250514` | `pip install pythonclaw[anthropic]` |
198
- | **Gemini (Google)** | `gemini-2.0-flash` | `pip install pythonclaw[gemini]` |
204
+ | **Claude (Anthropic)** | `claude-sonnet-4-20250514` | (included) |
205
+ | **Gemini (Google)** | `gemini-2.0-flash` | (included) |
199
206
  | **Kimi (Moonshot)** | `moonshot-v1-128k` | — |
200
207
  | **GLM (Zhipu)** | `glm-4-flash` | — |
201
208
  | Any OpenAI-compatible | Custom | — |
@@ -241,11 +248,28 @@ Also accessible from the web dashboard **Marketplace** tab.
241
248
  ### Markdown Memory
242
249
 
243
250
  ```
244
- context/memory/
251
+ ~/.pythonclaw/context/memory/
245
252
  ├── MEMORY.md # Curated long-term memory
246
253
  └── 2026-02-23.md # Daily append-only log
247
254
  ```
248
255
 
256
+ When **per-group isolation** is enabled (`"isolation": { "perGroup": true }` in config),
257
+ each session (Telegram chat, Discord channel, etc.) gets its own `memory/`, `persona/`,
258
+ and `soul/` under `~/.pythonclaw/context/groups/<session-id>/`, while global memories
259
+ remain accessible via read-through fallback.
260
+
261
+ ### TOOLS.md — Local Notes
262
+
263
+ ```
264
+ ~/.pythonclaw/context/tools/
265
+ └── TOOLS.md # Your environment-specific cheat sheet
266
+ ```
267
+
268
+ Skills define *how* tools work. `TOOLS.md` stores *your* specifics — SSH hosts, device
269
+ nicknames, project paths, preferred defaults, API endpoints. Keeping them apart means
270
+ you can update skills without losing your notes, and share skills without leaking your
271
+ infrastructure. Editable from the web dashboard.
272
+
249
273
  ### Hybrid RAG Pipeline
250
274
 
251
275
  ```
@@ -292,7 +316,7 @@ PythonClaw/
292
316
  │ │ ├── memory/ # Markdown memory
293
317
  │ │ ├── knowledge/ # Knowledge-base RAG
294
318
  │ │ └── retrieval/ # BM25 + dense + fusion + reranker
295
- │ ├── channels/ # Telegram, Discord
319
+ │ ├── channels/ # Telegram, Discord, WhatsApp
296
320
  │ ├── scheduler/ # Cron jobs, heartbeat
297
321
  │ ├── web/ # FastAPI dashboard + static assets
298
322
  │ └── templates/ # Built-in skill templates
@@ -310,7 +334,7 @@ PythonClaw/
310
334
  git clone https://github.com/ericwang915/PythonClaw.git
311
335
  cd PythonClaw
312
336
  python -m venv .venv && source .venv/bin/activate
313
- pip install -e ".[all]"
337
+ pip install -e .
314
338
  pytest tests/ -v
315
339
  ```
316
340
 
@@ -332,7 +356,7 @@ We welcome contributions! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
332
356
  | Dashboard | Web UI | Web UI (localhost:7788) |
333
357
  | Memory | Markdown | Markdown (long-term + daily) |
334
358
  | Skills | Plugin system | Three-tier + SkillHub marketplace |
335
- | Channels | Discord, Telegram, WhatsApp | CLI, Web, Telegram, Discord |
359
+ | Channels | Discord, Telegram, WhatsApp | CLI, Web, Telegram, Discord, WhatsApp |
336
360
  | Voice | — | Deepgram STT |
337
361
  | LLM Providers | OpenAI, Anthropic, Gemini | DeepSeek, Grok, Claude, Gemini, Kimi, GLM |
338
362
  | Daemon | Background process | PID-managed (`start`/`stop`/`status`) |
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pythonclaw"
7
- version = "0.3.3"
7
+ version = "0.5.0"
8
8
  description = "OpenClaw reimagined in pure Python — autonomous AI agent with memory, RAG, skills, web dashboard, and multi-channel support."
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
@@ -12,7 +12,7 @@ requires-python = ">=3.10"
12
12
  authors = [
13
13
  {name = "Eric Wang", email = "wangchen2007915@gmail.com"},
14
14
  ]
15
- keywords = ["llm", "agent", "ai", "autonomous", "memory", "rag", "skills", "telegram", "chatbot", "framework"]
15
+ keywords = ["llm", "agent", "ai", "autonomous", "memory", "rag", "skills", "telegram", "discord", "whatsapp", "chatbot", "framework"]
16
16
  classifiers = [
17
17
  "Development Status :: 4 - Beta",
18
18
  "Intended Audience :: Developers",
@@ -31,6 +31,7 @@ dependencies = [
31
31
  "google-generativeai",
32
32
  "python-telegram-bot[job-queue]>=20.0",
33
33
  "discord.py>=2.3",
34
+ "pywa[fastapi]>=2.8",
34
35
  "apscheduler>=3.10",
35
36
  "pyyaml>=6.0",
36
37
  "fastapi>=0.100",
@@ -58,6 +59,7 @@ include = ["pythonclaw*"]
58
59
  [tool.setuptools.package-data]
59
60
  pythonclaw = [
60
61
  "templates/**/*.py",
62
+ "templates/**/*.sh",
61
63
  "templates/**/*.yaml",
62
64
  "templates/**/*.md",
63
65
  "web/static/**/*",
@@ -65,3 +67,15 @@ pythonclaw = [
65
67
 
66
68
  [tool.setuptools.exclude-package-data]
67
69
  "*" = ["__pycache__", "*.pyc", "*.pyo"]
70
+
71
+ [tool.ruff]
72
+ target-version = "py310"
73
+ line-length = 120
74
+
75
+ [tool.ruff.lint]
76
+ select = ["E", "F", "W", "I"]
77
+ ignore = ["E501"]
78
+
79
+ [tool.pytest.ini_options]
80
+ testpaths = ["tests"]
81
+ pythonpath = ["."]
@@ -6,7 +6,7 @@ from .core.llm.base import LLMProvider
6
6
  from .core.llm.openai_compatible import OpenAICompatibleProvider
7
7
  from .init import init
8
8
 
9
- __version__ = "0.3.3"
9
+ __version__ = "0.5.0"
10
10
  __all__ = [
11
11
  "Agent",
12
12
  "LLMProvider",
@@ -183,9 +183,16 @@ class DiscordBot:
183
183
  async def _handle_chat(self, message: discord.Message, content: str, is_dm: bool) -> None:
184
184
  sid = self._session_id(message.author.id if is_dm else message.channel.id, is_dm)
185
185
  agent = self._sm.get_or_create(sid)
186
+
187
+ if self._sm.is_locked(sid):
188
+ await message.reply("Processing previous message…")
189
+
186
190
  async with message.channel.typing():
187
191
  try:
188
- response = agent.chat(content)
192
+ async with self._sm.acquire(sid):
193
+ import asyncio
194
+ loop = asyncio.get_event_loop()
195
+ response = await loop.run_in_executor(None, agent.chat, content)
189
196
  except Exception as exc:
190
197
  logger.exception("[Discord] Agent.chat() raised an exception")
191
198
  response = f"Sorry, something went wrong: {exc}"
@@ -23,10 +23,11 @@ IDs to restrict access. Leave empty (or unset) to allow all users.
23
23
 
24
24
  from __future__ import annotations
25
25
 
26
+ import asyncio
26
27
  import logging
27
28
  from typing import TYPE_CHECKING
28
29
 
29
- from telegram import Update
30
+ from telegram import BotCommand, ReactionTypeEmoji, Update
30
31
  from telegram.ext import (
31
32
  Application,
32
33
  CommandHandler,
@@ -158,17 +159,61 @@ class TelegramBot:
158
159
  return
159
160
  sid = self._session_id(update.effective_chat.id)
160
161
  agent = self._sm.get_or_create(sid)
161
- await update.message.chat.send_action("typing")
162
+
163
+ if self._sm.is_locked(sid):
164
+ await update.message.reply_text("⏳ Processing previous message…")
165
+
166
+ # React to the message so the user knows the bot saw it
167
+ try:
168
+ await update.message.set_reaction([ReactionTypeEmoji("👀")])
169
+ except Exception:
170
+ pass # reaction API may fail on older bot API or in groups
171
+
172
+ # Keep "typing…" visible for the entire duration of processing.
173
+ # Telegram's typing indicator expires after ~5 s, so we resend it
174
+ # on a loop until the agent finishes.
175
+ typing_task = asyncio.create_task(
176
+ self._keep_typing(update.message.chat_id)
177
+ )
162
178
  try:
163
- response = agent.chat(user_text)
179
+ async with self._sm.acquire(sid):
180
+ loop = asyncio.get_event_loop()
181
+ response = await loop.run_in_executor(None, agent.chat, user_text)
164
182
  except Exception as exc:
165
183
  logger.exception("[Telegram] Agent.chat() raised an exception")
166
184
  response = f"Sorry, something went wrong: {exc}"
185
+ finally:
186
+ typing_task.cancel()
187
+
188
+ # Clear the "seen" reaction once we reply
189
+ try:
190
+ await update.message.set_reaction([])
191
+ except Exception:
192
+ pass
193
+
167
194
  for chunk in _split_message(response or "(no response)"):
168
195
  await update.message.reply_text(chunk)
169
196
 
197
+ async def _keep_typing(self, chat_id: int) -> None:
198
+ """Re-send the 'typing' chat action every 4 s until cancelled."""
199
+ try:
200
+ while True:
201
+ await self._app.bot.send_chat_action(chat_id=chat_id, action="typing")
202
+ await asyncio.sleep(4)
203
+ except asyncio.CancelledError:
204
+ pass
205
+ except Exception:
206
+ logger.debug("[Telegram] _keep_typing stopped unexpectedly", exc_info=True)
207
+
170
208
  # ── Lifecycle ─────────────────────────────────────────────────────────────
171
209
 
210
+ _BOT_COMMANDS = [
211
+ BotCommand("start", "Show welcome message"),
212
+ BotCommand("reset", "Start a fresh session"),
213
+ BotCommand("status", "Show session info"),
214
+ BotCommand("compact", "Compact conversation history"),
215
+ ]
216
+
172
217
  def build_application(self) -> Application:
173
218
  app = Application.builder().token(self._token).build()
174
219
  app.add_handler(CommandHandler("start", self._cmd_start))
@@ -179,10 +224,19 @@ class TelegramBot:
179
224
  self._app = app
180
225
  return app
181
226
 
227
+ async def _register_commands(self) -> None:
228
+ """Register slash-commands with Telegram so they appear in the menu."""
229
+ try:
230
+ await self._app.bot.set_my_commands(self._BOT_COMMANDS)
231
+ logger.info("[Telegram] Registered %d bot commands", len(self._BOT_COMMANDS))
232
+ except Exception:
233
+ logger.warning("[Telegram] Failed to register bot commands", exc_info=True)
234
+
182
235
  def run_polling(self) -> None:
183
236
  """Blocking call — starts the bot using long polling (for standalone use)."""
184
237
  app = self.build_application()
185
238
  logger.info("[Telegram] Starting bot (polling mode)...")
239
+ app.post_init = lambda _app: self._register_commands()
186
240
  app.run_polling(drop_pending_updates=True)
187
241
 
188
242
  async def start_async(self) -> None:
@@ -191,6 +245,7 @@ class TelegramBot:
191
245
  logger.info("[Telegram] Initialising bot (async mode)...")
192
246
  await app.initialize()
193
247
  await app.start()
248
+ await self._register_commands()
194
249
  await app.updater.start_polling(drop_pending_updates=True)
195
250
 
196
251
  async def stop_async(self) -> None: