@swarmclawai/swarmclaw 0.6.7 → 0.7.0

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 (203) hide show
  1. package/README.md +82 -39
  2. package/next.config.ts +31 -6
  3. package/package.json +3 -2
  4. package/src/app/api/agents/[id]/thread/route.ts +1 -0
  5. package/src/app/api/agents/route.ts +19 -5
  6. package/src/app/api/approvals/route.ts +22 -0
  7. package/src/app/api/chatrooms/[id]/chat/route.ts +4 -0
  8. package/src/app/api/clawhub/install/route.ts +2 -2
  9. package/src/app/api/eval/run/route.ts +37 -0
  10. package/src/app/api/eval/scenarios/route.ts +24 -0
  11. package/src/app/api/eval/suite/route.ts +29 -0
  12. package/src/app/api/mcp-servers/[id]/conformance/route.ts +26 -0
  13. package/src/app/api/mcp-servers/[id]/invoke/route.ts +81 -0
  14. package/src/app/api/memory/graph/route.ts +46 -0
  15. package/src/app/api/memory/route.ts +36 -5
  16. package/src/app/api/notifications/route.ts +3 -0
  17. package/src/app/api/plugins/install/route.ts +57 -5
  18. package/src/app/api/plugins/marketplace/route.ts +73 -22
  19. package/src/app/api/plugins/route.ts +61 -1
  20. package/src/app/api/plugins/ui/route.ts +34 -0
  21. package/src/app/api/sessions/[id]/checkpoints/route.ts +31 -0
  22. package/src/app/api/sessions/[id]/restore/route.ts +36 -0
  23. package/src/app/api/settings/route.ts +62 -0
  24. package/src/app/api/setup/doctor/route.ts +22 -5
  25. package/src/app/api/souls/[id]/route.ts +65 -0
  26. package/src/app/api/souls/route.ts +70 -0
  27. package/src/app/api/tasks/[id]/approve/route.ts +4 -3
  28. package/src/app/api/tasks/[id]/route.ts +16 -3
  29. package/src/app/api/tasks/route.ts +10 -2
  30. package/src/app/api/usage/route.ts +9 -2
  31. package/src/app/globals.css +27 -0
  32. package/src/app/page.tsx +10 -5
  33. package/src/cli/index.js +37 -0
  34. package/src/components/activity/activity-feed.tsx +9 -2
  35. package/src/components/agents/agent-avatar.tsx +5 -1
  36. package/src/components/agents/agent-card.tsx +55 -9
  37. package/src/components/agents/agent-sheet.tsx +112 -34
  38. package/src/components/agents/inspector-panel.tsx +1 -1
  39. package/src/components/agents/soul-library-picker.tsx +84 -13
  40. package/src/components/auth/access-key-gate.tsx +63 -54
  41. package/src/components/auth/user-picker.tsx +37 -32
  42. package/src/components/chat/activity-moment.tsx +2 -0
  43. package/src/components/chat/chat-area.tsx +11 -0
  44. package/src/components/chat/chat-header.tsx +69 -25
  45. package/src/components/chat/chat-tool-toggles.tsx +2 -2
  46. package/src/components/chat/checkpoint-timeline.tsx +112 -0
  47. package/src/components/chat/code-block.tsx +3 -1
  48. package/src/components/chat/exec-approval-card.tsx +8 -1
  49. package/src/components/chat/message-bubble.tsx +164 -4
  50. package/src/components/chat/message-list.tsx +46 -4
  51. package/src/components/chat/session-approval-card.tsx +80 -0
  52. package/src/components/chat/session-debug-panel.tsx +106 -84
  53. package/src/components/chat/streaming-bubble.tsx +6 -5
  54. package/src/components/chat/task-approval-card.tsx +78 -0
  55. package/src/components/chat/thinking-indicator.tsx +48 -12
  56. package/src/components/chat/tool-call-bubble.tsx +3 -0
  57. package/src/components/chat/tool-request-banner.tsx +39 -20
  58. package/src/components/chatrooms/chatroom-list.tsx +11 -4
  59. package/src/components/chatrooms/chatroom-sheet.tsx +7 -2
  60. package/src/components/connectors/connector-list.tsx +33 -11
  61. package/src/components/connectors/connector-sheet.tsx +37 -7
  62. package/src/components/home/home-view.tsx +54 -24
  63. package/src/components/input/chat-input.tsx +22 -1
  64. package/src/components/knowledge/knowledge-list.tsx +17 -18
  65. package/src/components/knowledge/knowledge-sheet.tsx +9 -5
  66. package/src/components/layout/app-layout.tsx +87 -19
  67. package/src/components/mcp-servers/mcp-server-list.tsx +352 -50
  68. package/src/components/mcp-servers/mcp-server-sheet.tsx +25 -9
  69. package/src/components/memory/memory-browser.tsx +73 -45
  70. package/src/components/memory/memory-graph-view.tsx +203 -0
  71. package/src/components/memory/memory-list.tsx +20 -13
  72. package/src/components/plugins/plugin-list.tsx +214 -60
  73. package/src/components/plugins/plugin-sheet.tsx +119 -24
  74. package/src/components/projects/project-list.tsx +17 -9
  75. package/src/components/providers/provider-list.tsx +21 -6
  76. package/src/components/providers/provider-sheet.tsx +42 -25
  77. package/src/components/runs/run-list.tsx +17 -13
  78. package/src/components/schedules/schedule-card.tsx +10 -3
  79. package/src/components/schedules/schedule-list.tsx +2 -2
  80. package/src/components/schedules/schedule-sheet.tsx +28 -9
  81. package/src/components/secrets/secret-sheet.tsx +7 -2
  82. package/src/components/secrets/secrets-list.tsx +18 -5
  83. package/src/components/sessions/new-session-sheet.tsx +183 -376
  84. package/src/components/sessions/session-card.tsx +10 -2
  85. package/src/components/settings/gateway-connection-panel.tsx +9 -8
  86. package/src/components/shared/command-palette.tsx +13 -5
  87. package/src/components/shared/empty-state.tsx +20 -8
  88. package/src/components/shared/hint-tip.tsx +31 -0
  89. package/src/components/shared/notification-center.tsx +134 -86
  90. package/src/components/shared/profile-sheet.tsx +4 -0
  91. package/src/components/shared/settings/plugin-manager.tsx +360 -135
  92. package/src/components/shared/settings/section-capability-policy.tsx +3 -3
  93. package/src/components/shared/settings/section-runtime-loop.tsx +149 -4
  94. package/src/components/skills/clawhub-browser.tsx +1 -0
  95. package/src/components/skills/skill-list.tsx +31 -12
  96. package/src/components/skills/skill-sheet.tsx +20 -7
  97. package/src/components/tasks/approvals-panel.tsx +224 -0
  98. package/src/components/tasks/task-board.tsx +20 -12
  99. package/src/components/tasks/task-card.tsx +21 -7
  100. package/src/components/tasks/task-column.tsx +4 -3
  101. package/src/components/tasks/task-list.tsx +1 -1
  102. package/src/components/tasks/task-sheet.tsx +130 -1
  103. package/src/components/ui/dialog.tsx +1 -0
  104. package/src/components/ui/sheet.tsx +1 -0
  105. package/src/components/usage/metrics-dashboard.tsx +72 -48
  106. package/src/components/wallets/wallet-panel.tsx +65 -41
  107. package/src/components/wallets/wallet-section.tsx +9 -3
  108. package/src/components/webhooks/webhook-list.tsx +21 -12
  109. package/src/components/webhooks/webhook-sheet.tsx +13 -3
  110. package/src/lib/approval-display.test.ts +45 -0
  111. package/src/lib/approval-display.ts +62 -0
  112. package/src/lib/clipboard.ts +38 -0
  113. package/src/lib/memory.ts +8 -0
  114. package/src/lib/providers/claude-cli.ts +5 -3
  115. package/src/lib/providers/index.ts +67 -21
  116. package/src/lib/runtime-loop.ts +3 -2
  117. package/src/lib/server/approvals.ts +150 -0
  118. package/src/lib/server/chat-execution.ts +319 -74
  119. package/src/lib/server/chatroom-helpers.ts +63 -5
  120. package/src/lib/server/chatroom-orchestration.ts +74 -0
  121. package/src/lib/server/clawhub-client.ts +82 -6
  122. package/src/lib/server/connectors/manager.ts +27 -1
  123. package/src/lib/server/context-manager.ts +132 -50
  124. package/src/lib/server/cost.test.ts +73 -0
  125. package/src/lib/server/cost.ts +165 -34
  126. package/src/lib/server/daemon-state.ts +112 -1
  127. package/src/lib/server/data-dir.ts +18 -1
  128. package/src/lib/server/eval/runner.ts +126 -0
  129. package/src/lib/server/eval/scenarios.ts +218 -0
  130. package/src/lib/server/eval/scorer.ts +96 -0
  131. package/src/lib/server/eval/store.ts +37 -0
  132. package/src/lib/server/eval/types.ts +48 -0
  133. package/src/lib/server/execution-log.ts +12 -8
  134. package/src/lib/server/guardian.ts +34 -0
  135. package/src/lib/server/heartbeat-service.ts +53 -1
  136. package/src/lib/server/integrity-monitor.ts +208 -0
  137. package/src/lib/server/langgraph-checkpoint.ts +10 -0
  138. package/src/lib/server/link-understanding.ts +55 -0
  139. package/src/lib/server/llm-response-cache.test.ts +102 -0
  140. package/src/lib/server/llm-response-cache.ts +227 -0
  141. package/src/lib/server/main-agent-loop.ts +115 -16
  142. package/src/lib/server/main-session.ts +6 -3
  143. package/src/lib/server/mcp-conformance.test.ts +18 -0
  144. package/src/lib/server/mcp-conformance.ts +233 -0
  145. package/src/lib/server/memory-db.ts +193 -19
  146. package/src/lib/server/memory-retrieval.test.ts +56 -0
  147. package/src/lib/server/mmr.ts +73 -0
  148. package/src/lib/server/orchestrator-lg.ts +7 -1
  149. package/src/lib/server/orchestrator.ts +4 -3
  150. package/src/lib/server/plugins.ts +662 -132
  151. package/src/lib/server/process-manager.ts +18 -0
  152. package/src/lib/server/query-expansion.ts +57 -0
  153. package/src/lib/server/queue.ts +280 -11
  154. package/src/lib/server/runtime-settings.ts +9 -0
  155. package/src/lib/server/session-run-manager.test.ts +23 -0
  156. package/src/lib/server/session-run-manager.ts +32 -2
  157. package/src/lib/server/session-tools/canvas.ts +85 -50
  158. package/src/lib/server/session-tools/chatroom.ts +130 -127
  159. package/src/lib/server/session-tools/connector.ts +233 -454
  160. package/src/lib/server/session-tools/context-mgmt.ts +87 -105
  161. package/src/lib/server/session-tools/crud.ts +84 -7
  162. package/src/lib/server/session-tools/delegate.ts +351 -752
  163. package/src/lib/server/session-tools/discovery.ts +198 -0
  164. package/src/lib/server/session-tools/edit_file.ts +82 -0
  165. package/src/lib/server/session-tools/file-send.test.ts +39 -0
  166. package/src/lib/server/session-tools/file.ts +257 -425
  167. package/src/lib/server/session-tools/git.ts +87 -47
  168. package/src/lib/server/session-tools/http.ts +95 -33
  169. package/src/lib/server/session-tools/index.ts +217 -138
  170. package/src/lib/server/session-tools/memory.ts +154 -239
  171. package/src/lib/server/session-tools/monitor.ts +126 -0
  172. package/src/lib/server/session-tools/normalize-tool-args.test.ts +61 -0
  173. package/src/lib/server/session-tools/normalize-tool-args.ts +48 -0
  174. package/src/lib/server/session-tools/openclaw-nodes.ts +82 -99
  175. package/src/lib/server/session-tools/openclaw-workspace.ts +103 -93
  176. package/src/lib/server/session-tools/platform.ts +86 -0
  177. package/src/lib/server/session-tools/plugin-creator.ts +239 -0
  178. package/src/lib/server/session-tools/sample-ui.ts +97 -0
  179. package/src/lib/server/session-tools/sandbox.ts +175 -148
  180. package/src/lib/server/session-tools/schedule.ts +78 -0
  181. package/src/lib/server/session-tools/session-info.ts +104 -410
  182. package/src/lib/server/session-tools/shell-normalize.test.ts +43 -0
  183. package/src/lib/server/session-tools/shell.ts +171 -143
  184. package/src/lib/server/session-tools/subagent.ts +77 -77
  185. package/src/lib/server/session-tools/wallet.ts +182 -106
  186. package/src/lib/server/session-tools/web.ts +181 -327
  187. package/src/lib/server/storage.ts +36 -0
  188. package/src/lib/server/stream-agent-chat.ts +348 -242
  189. package/src/lib/server/task-quality-gate.test.ts +44 -0
  190. package/src/lib/server/task-quality-gate.ts +67 -0
  191. package/src/lib/server/task-validation.test.ts +78 -0
  192. package/src/lib/server/task-validation.ts +67 -2
  193. package/src/lib/server/tool-aliases.ts +68 -0
  194. package/src/lib/server/tool-capability-policy.ts +24 -5
  195. package/src/lib/server/tool-retry.ts +62 -0
  196. package/src/lib/server/transcript-repair.ts +72 -0
  197. package/src/lib/setup-defaults.ts +1 -0
  198. package/src/lib/tasks.ts +7 -1
  199. package/src/lib/tool-definitions.ts +24 -23
  200. package/src/lib/validation/schemas.ts +13 -0
  201. package/src/lib/view-routes.ts +2 -23
  202. package/src/stores/use-app-store.ts +23 -1
  203. package/src/types/index.ts +155 -10
package/README.md CHANGED
@@ -12,7 +12,7 @@ The orchestration dashboard for OpenClaw. Manage a swarm of OpenClaws + 14 other
12
12
 
13
13
  Inspired by [OpenClaw](https://github.com/openclaw).
14
14
 
15
- **[Documentation](https://swarmclaw.ai/docs)** | **[Website](https://swarmclaw.ai)**
15
+ **[Documentation](https://swarmclaw.ai/docs)** | **[Plugin Tutorial](https://swarmclaw.ai/docs/plugin-tutorial)** | **[Website](https://swarmclaw.ai)**
16
16
 
17
17
  ![Dashboard](public/screenshots/dashboard.png)
18
18
  ![Agent Builder](public/screenshots/agents.png)
@@ -85,7 +85,7 @@ curl -fsSL https://raw.githubusercontent.com/swarmclawai/swarmclaw/main/install.
85
85
  ```
86
86
 
87
87
  The installer resolves the latest stable release tag and installs that version by default.
88
- To pin a version: `SWARMCLAW_VERSION=v0.6.6 curl ... | bash`
88
+ To pin a version: `SWARMCLAW_VERSION=v0.7.0 curl ... | bash`
89
89
 
90
90
  Or run locally from the repo (friendly for non-technical users):
91
91
 
@@ -158,7 +158,7 @@ Notes:
158
158
  - **Agent Fleet Management** — Avatar seeds with generated avatars, running/approval fleet filters, soft-delete agent trash with restore/permanent delete, and approval counters in agent cards
159
159
  - **Agent Tools** — Shell, process control for long-running commands, files, edit file, send file, web search, web fetch, CLI delegation (Claude/Codex/OpenCode), Playwright browser automation, sub-agent spawning, canvas presentation, direct HTTP requests, git operations, persistent memory, and sandboxed code execution (JS/TS via Deno, Python)
160
160
  - **Platform Tools** — Agents can manage other agents, tasks, schedules, skills, connectors, sessions, and encrypted secrets via built-in platform tools
161
- - **Orchestration** — Multi-agent workflows powered by LangGraph with automatic sub-agent routing, checkpointed execution, and rich delegation cards that link to sub-agent chat threads
161
+ - **Orchestration** — Multi-agent workflows powered by LangGraph with automatic sub-agent routing, checkpointed execution, checkpoint timeline with time-travel restore, and rich delegation cards that link to sub-agent chat threads
162
162
  - **Agentic Execution Policy** — Tool-first autonomous action loop with progress updates, evidence-driven answers, and better use of platform tools for long-lived work
163
163
  - **Runtime Date/Time Grounding** — Session, orchestrator, chatroom, and connector prompts include authoritative current timestamp context to reduce stale-date behavior
164
164
  - **Task Board** — Queue and track agent tasks with status, comments, structured result artifacts (`outputFiles`, uploads), completion reports, and archiving. Strict capability policy pauses tasks for human approval before tool execution
@@ -178,15 +178,21 @@ Notes:
178
178
  - **Skills System** — Discover local skills, import skills from URL, and load OpenClaw `SKILL.md` files (frontmatter-compatible)
179
179
  - **Execution Logging** — Structured audit trail for triggers, tool calls, file ops, commits, and errors in a dedicated `logs.db`
180
180
  - **Context Management** — Auto-compaction of conversation history when approaching context limits, with manual `context_status` and `context_summarize` tools for agents
181
- - **Memory** — Per-agent and per-session memory with hybrid FTS5 + vector embeddings search, relevance-based memory recall injected into runs, and periodic auto-journaling for durable execution context
182
- - **Cost Tracking** — Per-message token counting and cost estimation displayed in the chat header
183
- - **Provider Health Metrics** — Usage dashboard surfaces provider request volume, success rates, models used, and last-used timestamps
181
+ - **Memory** — Per-agent and per-session memory with hybrid FTS5 + vector embeddings search, query expansion (LLM-generated semantic variants), MMR diversity ranking, cross-agent search (`scope: all`), pinned memories (always preloaded), memory sharing between agents, linked memory graph with interactive visualization, image attachments, and periodic auto-journaling for durable execution context
182
+ - **Knowledge Base** — Shared knowledge store (`knowledge_store` / `knowledge_search` actions) with tags, source tracking, and provenance URLs separate from per-agent memories
183
+ - **Memory Graph Visualization** — Interactive force-directed graph view of linked memories with node details and relationship exploration
184
+ - **Cost Tracking** — Per-message token counting and cost estimation displayed in the chat header, with per-agent monthly budget caps (`warn` or `block` enforcement)
185
+ - **Provider Health Metrics** — Usage dashboard surfaces provider request volume, success rates, average latency, models used, and last-used timestamps
184
186
  - **Model Failover** — Automatic key rotation on rate limits and auth errors with configurable fallback credentials
185
- - **Plugin System** — Extend agent behavior with JS plugins (hooks: beforeAgentStart, afterAgentComplete, beforeToolExec, afterToolExec, onMessage)
187
+ - **Plugin System** — Extend agent behavior with JS plugins (hooks: beforeAgentStart, afterAgentComplete, beforeToolExec, afterToolExec, onMessage, onTaskComplete, onAgentDelegation). Plugins can also define custom tools that agents can use
186
188
  - **Secrets Vault** — Encrypted storage for API keys and service tokens
187
189
  - **Custom Providers** — Add any OpenAI-compatible API as a provider
188
190
  - **MCP Servers** — Connect agents to any Model Context Protocol server. Per-agent server selection with tool discovery and per-tool disable toggles
189
191
  - **Sandboxed Code Execution** — Agents can write and run JS/TS (Deno) or Python scripts in an isolated sandbox with network access, scoped filesystem, and artifact output
192
+ - **Eval Framework** — Built-in agent evaluation with scenario-based testing across categories (coding, research, companionship, multi-step, memory, planning, tool-usage), weighted scoring criteria, and LLM judge support
193
+ - **Guardian Auto-Recovery** — Automatic workspace recovery when agents fail critically, rolling back to the last known good state via git reset
194
+ - **Soul Library** — Browse and apply pre-built personality templates (archetypes) to agents, or create custom souls for reuse across your swarm
195
+ - **Context Degradation Warnings** — Proactive alerts when context usage exceeds 85%, with strategy recommendations (save to memory, summarize, checkpoint)
190
196
  - **Real-Time Sync** — WebSocket push notifications for instant UI updates across tabs and devices (fallback to polling when WS is unavailable)
191
197
  - **Mobile-First UI** — Responsive glass-themed dark interface, works on phone and desktop
192
198
 
@@ -195,8 +201,9 @@ Notes:
195
201
  All config lives in `.env.local` (auto-generated):
196
202
 
197
203
  ```
198
- ACCESS_KEY=<your-access-key> # Auth key for the dashboard
199
- CREDENTIAL_SECRET=<auto-generated> # AES-256 encryption key for stored credentials
204
+ ACCESS_KEY=<your-access-key> # Auth key for the dashboard
205
+ CREDENTIAL_SECRET=<auto-generated> # AES-256 encryption key for stored credentials
206
+ SWARMCLAW_PLUGIN_FAILURE_THRESHOLD=3 # Consecutive failures before auto-disabling a plugin
200
207
  ```
201
208
 
202
209
  Data is stored in `data/swarmclaw.db` (SQLite with WAL mode), `data/memory.db` (agent memory with FTS5 + vector embeddings), `data/logs.db` (execution audit trail), and `data/langgraph-checkpoints.db` (orchestrator checkpoints). Back the `data/` directory up if you care about your sessions, agents, and credentials. Existing JSON file data is auto-migrated to SQLite on first run.
@@ -329,6 +336,7 @@ Agents with platform tools enabled can manage the SwarmClaw instance:
329
336
  | Manage Agents | List, create, update, delete agents |
330
337
  | Manage Tasks | Create and manage task board items with agent assignment |
331
338
  | Manage Schedules | Create cron, interval, or one-time scheduled jobs |
339
+ | Reminders | Schedule a conversational wake event in the current chat (`schedule_wake`) |
332
340
  | Manage Skills | List, create, update reusable skill definitions |
333
341
  | Manage Documents | Upload/search/get/delete indexed docs for lightweight RAG workflows |
334
342
  | Manage Webhooks | Register external webhook endpoints that trigger agent sessions |
@@ -442,43 +450,68 @@ When enabled, new memories get vector embeddings. Search uses both FTS5 keyword
442
450
 
443
451
  Agents and sessions can have **fallback credentials**. If the primary API key gets a 401, 429, or 500 error, SwarmClaw automatically retries with the next credential. Configure fallback keys in the agent builder UI.
444
452
 
445
- ## Plugins
453
+ ## Plugin System
446
454
 
447
- Extend agent behavior with JS plugins. Three ways to install:
455
+ SwarmClaw features a powerful, modular plugin system designed for both agent enhancement and application extensibility. It is fully compatible with the **OpenClaw** plugin format.
448
456
 
449
- 1. **Marketplace** Browse and install approved plugins from Settings Plugins Marketplace
450
- 2. **URL** — Install from any HTTPS URL via Settings → Plugins → Install from URL
451
- 3. **Manual** — Drop `.js` files into `data/plugins/`
457
+ Plugins can be managed in **Settings Plugins** and installed via the Marketplace, URL, or by dropping `.js` files into `data/plugins/`.
452
458
 
453
- ### Plugin Format (SwarmClaw)
459
+ Docs:
460
+ - Full docs: https://swarmclaw.ai/docs
461
+ - Plugin tutorial: https://swarmclaw.ai/docs/plugin-tutorial
462
+
463
+ ### Extension Points
464
+
465
+ Unlike standard tool systems, SwarmClaw plugins can modify the application itself:
466
+
467
+ - **Agent Tools**: Define custom tools that agents can autonomously discover and use.
468
+ - **Lifecycle Hooks**: Intercept events like `beforeAgentStart`, `afterToolExec`, and `onMessage`.
469
+ - **UI Extensions**:
470
+ - `sidebarItems`: Inject new navigation links into the main sidebar.
471
+ - `headerWidgets`: Add status badges or indicators to the chat header (e.g., Wallet Balance).
472
+ - `chatInputActions`: Add custom action buttons next to the chat input (e.g., "Quick Scan").
473
+ - `plugin-ui` Messages: Render rich, interactive React cards in the chat stream.
474
+ - **Deep Chat Hooks**:
475
+ - `transformInboundMessage`: Modify user messages before they reach the agent.
476
+ - `transformOutboundMessage`: Modify agent responses before they are saved or displayed.
477
+ - **Custom Providers**: Add new LLM backends (e.g., a specialized local model or a new API).
478
+ - **Custom Connectors**: Build new chat platform bridges (e.g., a proprietary internal messenger).
479
+
480
+ ### Autonomous Capability Discovery
481
+
482
+ Agents in SwarmClaw are "aware" of the plugin system. If an agent lacks a tool needed for a task, it can:
483
+ 1. **Discover**: Scan the system for all installed plugins.
484
+ 2. **Search Marketplace**: Autonomously search **ClawHub** and the **SwarmClaw Registry** for new capabilities.
485
+ 3. **Request Access**: Prompt the user in-chat to enable a specific installed plugin.
486
+ 4. **Install Request**: Suggest installing a new plugin from a marketplace URL to fill a capability gap (requires user approval).
487
+
488
+ ### Example Plugin (SwarmClaw Format)
454
489
 
455
490
  ```js
456
491
  module.exports = {
457
- name: 'my-plugin',
458
- description: 'What it does',
459
- hooks: {
460
- beforeAgentStart: async ({ session, message }) => { /* ... */ },
461
- afterAgentComplete: async ({ session, response }) => { /* ... */ },
462
- beforeToolExec: async ({ toolName, input }) => { /* ... */ },
463
- afterToolExec: async ({ toolName, input, output }) => { /* ... */ },
464
- onMessage: async ({ session, message }) => { /* ... */ },
492
+ name: 'my-custom-extension',
493
+ ui: {
494
+ sidebarItems: [{ id: 'dashboard', label: 'My View', href: '/custom-view' }],
495
+ headerWidgets: [{ id: 'status', label: '🟢 Active' }]
465
496
  },
466
- }
497
+ tools: [{
498
+ name: 'custom_action',
499
+ description: 'Perform a specialized task',
500
+ parameters: { type: 'object', properties: { input: { type: 'string' } } },
501
+ execute: async (args) => {
502
+ // Logic here
503
+ return { kind: 'plugin-ui', text: 'Rich result card content' };
504
+ }
505
+ }]
506
+ };
467
507
  ```
468
508
 
469
- ### OpenClaw Plugin Compatibility
509
+ ### Lifecycle Management
470
510
 
471
- SwarmClaw natively supports the OpenClaw plugin format. Drop an OpenClaw plugin into `data/plugins/` and it works automatically — lifecycle hooks are mapped:
472
-
473
- | OpenClaw Hook | SwarmClaw Hook |
474
- |-|-|
475
- | `onAgentStart` | `beforeAgentStart` |
476
- | `onAgentComplete` | `afterAgentComplete` |
477
- | `onToolCall` | `beforeToolExec` |
478
- | `onToolResult` | `afterToolExec` |
479
- | `onMessage` | `onMessage` |
480
-
481
- Plugin API: `GET /api/plugins`, `POST /api/plugins`, `GET /api/plugins/marketplace`, `POST /api/plugins/install`.
511
+ - **Versioning**: All plugins support semantic versioning (e.g., `v1.2.3`).
512
+ - **Updates**: Plugins can be updated individually or in bulk via the Plugins manager.
513
+ - **Hot-Reload**: The system automatically reloads plugin logic when a file is updated or a new plugin is installed.
514
+ - **Stability Guardrails**: Consecutive plugin failures are tracked in `data/plugin-failures.json`; failing plugins are auto-disabled, a warning notification is emitted in-app, and users can re-enable manually from the Plugins manager.
482
515
 
483
516
  ## Deploy to a VPS
484
517
 
@@ -568,7 +601,7 @@ npm run dev:webpack
568
601
  ### First-Run Helpers
569
602
 
570
603
  ```bash
571
- npm run setup:easy # setup only (does not start server)
604
+ npm run setup:easy # setup only (installs Deno if missing; does not start server)
572
605
  npm run quickstart # setup + start dev server
573
606
  npm run quickstart:prod # setup + build + start production server
574
607
  npm run update:easy # safe update helper for local installs
@@ -579,8 +612,8 @@ npm run update:easy # safe update helper for local installs
579
612
  SwarmClaw uses tag-based releases (`vX.Y.Z`) as the stable channel.
580
613
 
581
614
  ```bash
582
- # example patch release
583
- npm version patch
615
+ # example minor release (v0.7.0 style)
616
+ npm version minor
584
617
  git push origin main --follow-tags
585
618
  ```
586
619
 
@@ -589,6 +622,16 @@ On `v*` tags, GitHub Actions will:
589
622
  2. Create a GitHub Release
590
623
  3. Build and publish Docker images to `ghcr.io/swarmclawai/swarmclaw` (`:vX.Y.Z`, `:latest`, `:sha-*`)
591
624
 
625
+ #### v0.7.0 Release Readiness Notes
626
+
627
+ Before shipping `v0.7.0`, confirm the following user-facing changes are reflected in docs:
628
+
629
+ 1. Plugins UI now shows richer installed plugin details (description fallback, source/status, capability badges, and clearer detail sheet metadata).
630
+ 2. Marketplace plugin installs normalize legacy URLs from `swarmclawai/plugins` to `swarmclawai/swarmforge` to avoid 404 installs.
631
+ 3. Hydration fixes removed nested `<button>` structures in list-card UIs (Plugins, Providers, Secrets, MCP Servers).
632
+ 4. Clipboard actions now use a browser-safe fallback when `navigator.clipboard` is unavailable.
633
+ 5. Site docs/release notes are updated in `swarmclaw-site` (especially `content/docs/plugins.md`, `content/docs/release-notes.md`, and `public/registry/plugins.json`).
634
+
592
635
  ## CLI
593
636
 
594
637
  SwarmClaw ships a built-in CLI for core operational workflows:
package/next.config.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import type { NextConfig } from "next";
2
2
  import { execSync } from "child_process";
3
+ import { networkInterfaces } from "os";
4
+ import { DIRECT_NAV_SEGMENTS } from "./view-route-paths";
3
5
 
4
6
  function getGitSha(): string {
5
7
  try {
@@ -9,6 +11,33 @@ function getGitSha(): string {
9
11
  }
10
12
  }
11
13
 
14
+ function getAllowedDevOrigins(): string[] {
15
+ const allowed = new Set<string>([
16
+ 'localhost',
17
+ '127.0.0.1',
18
+ '0.0.0.0',
19
+ ])
20
+
21
+ // Include all active local IPv4 interfaces so LAN devices can load /_next assets in dev.
22
+ for (const interfaces of Object.values(networkInterfaces())) {
23
+ for (const iface of interfaces ?? []) {
24
+ if ((iface.family === 'IPv4' || (iface.family as string | number) === 4) && !iface.internal) {
25
+ allowed.add(iface.address)
26
+ }
27
+ }
28
+ }
29
+
30
+ // Optional override for custom origins/hosts, e.g. `NEXT_ALLOWED_DEV_ORIGINS=host1,host2`.
31
+ const extra = (process.env.NEXT_ALLOWED_DEV_ORIGINS ?? '')
32
+ .split(',')
33
+ .map((v) => v.trim())
34
+ .filter(Boolean)
35
+ .map((v) => v.replace(/^https?:\/\//, '').replace(/\/$/, ''))
36
+ for (const host of extra) allowed.add(host)
37
+
38
+ return [...allowed]
39
+ }
40
+
12
41
  const nextConfig: NextConfig = {
13
42
  output: 'standalone',
14
43
  turbopack: {
@@ -35,13 +64,9 @@ const nextConfig: NextConfig = {
35
64
  '@whiskeysockets/baileys',
36
65
  'qrcode',
37
66
  ],
38
- allowedDevOrigins: [
39
- 'localhost',
40
- '127.0.0.1',
41
- '0.0.0.0',
42
- ],
67
+ allowedDevOrigins: getAllowedDevOrigins(),
43
68
  async rewrites() {
44
- const views = 'agents|chatrooms|schedules|memory|tasks|secrets|providers|skills|connectors|webhooks|mcp-servers|knowledge|plugins|usage|runs|logs|settings|projects|activity'
69
+ const views = DIRECT_NAV_SEGMENTS.join('|')
45
70
  return [
46
71
  {
47
72
  source: `/:view(${views})`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swarmclawai/swarmclaw",
3
- "version": "0.6.7",
3
+ "version": "0.7.0",
4
4
  "description": "Self-hosted AI agent orchestration dashboard — manage LLM providers, orchestrate agent swarms, schedule tasks, and bridge agents to chat platforms.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -52,7 +52,8 @@
52
52
  "lint:baseline:update": "node ./scripts/lint-baseline.mjs update",
53
53
  "cli": "node ./bin/swarmclaw.js",
54
54
  "test:cli": "node --test src/cli/index.test.js",
55
- "test:openclaw": "tsx --test src/lib/server/connectors/openclaw.test.ts src/lib/openclaw-endpoint.test.ts src/lib/server/gateway/protocol.test.ts src/lib/server/tool-capability-policy.test.ts",
55
+ "test:openclaw": "tsx --test src/lib/server/connectors/openclaw.test.ts src/lib/openclaw-endpoint.test.ts src/lib/server/gateway/protocol.test.ts src/lib/server/tool-capability-policy.test.ts src/lib/server/task-validation.test.ts src/lib/server/task-quality-gate.test.ts src/lib/server/llm-response-cache.test.ts src/lib/server/mcp-conformance.test.ts",
56
+ "test:mcp:conformance": "node --import tsx ./scripts/mcp-conformance-check.ts",
56
57
  "postinstall": "node ./scripts/postinstall.mjs"
57
58
  },
58
59
  "dependencies": {
@@ -49,6 +49,7 @@ export async function POST(req: Request, { params }: { params: Promise<{ id: str
49
49
  createdAt: now,
50
50
  lastActiveAt: now,
51
51
  active: false,
52
+ mainSession: true,
52
53
  sessionType: 'human' as const,
53
54
  agentId,
54
55
  tools: agent.tools || [],
@@ -1,9 +1,9 @@
1
1
  import { NextResponse } from 'next/server'
2
2
  import { genId } from '@/lib/id'
3
- import { loadAgents, saveAgents, logActivity } from '@/lib/server/storage'
3
+ import { loadAgents, loadSessions, loadUsage, saveAgents, logActivity } from '@/lib/server/storage'
4
4
  import { normalizeProviderEndpoint } from '@/lib/openclaw-endpoint'
5
5
  import { notify } from '@/lib/server/ws-hub'
6
- import { getAgentMonthlySpend } from '@/lib/server/cost'
6
+ import { getAgentSpendWindows } from '@/lib/server/cost'
7
7
  import { AgentCreateSchema, formatZodError } from '@/lib/validation/schemas'
8
8
  import { z } from 'zod'
9
9
  export const dynamic = 'force-dynamic'
@@ -11,10 +11,19 @@ export const dynamic = 'force-dynamic'
11
11
 
12
12
  export async function GET(req: Request) {
13
13
  const agents = loadAgents()
14
- // Enrich agents that have a monthly budget with current spend
14
+ const sessions = loadSessions()
15
+ const usage = loadUsage()
16
+ // Enrich agents that have spend limits with current spend windows
15
17
  for (const agent of Object.values(agents)) {
16
- if (typeof agent.monthlyBudget === 'number' && agent.monthlyBudget > 0) {
17
- agent.monthlySpend = getAgentMonthlySpend(agent.id)
18
+ if (
19
+ (typeof agent.monthlyBudget === 'number' && agent.monthlyBudget > 0)
20
+ || (typeof agent.dailyBudget === 'number' && agent.dailyBudget > 0)
21
+ || (typeof agent.hourlyBudget === 'number' && agent.hourlyBudget > 0)
22
+ ) {
23
+ const spend = getAgentSpendWindows(agent.id, Date.now(), { sessions, usage })
24
+ if (typeof agent.monthlyBudget === 'number' && agent.monthlyBudget > 0) agent.monthlySpend = spend.monthly
25
+ if (typeof agent.dailyBudget === 'number' && agent.dailyBudget > 0) agent.dailySpend = spend.daily
26
+ if (typeof agent.hourlyBudget === 'number' && agent.hourlyBudget > 0) agent.hourlySpend = spend.hourly
18
27
  }
19
28
  }
20
29
 
@@ -53,6 +62,11 @@ export async function POST(req: Request) {
53
62
  tools: body.tools,
54
63
  capabilities: body.capabilities,
55
64
  thinkingLevel: body.thinkingLevel || undefined,
65
+ autoRecovery: body.autoRecovery || false,
66
+ monthlyBudget: body.monthlyBudget ?? null,
67
+ dailyBudget: body.dailyBudget ?? null,
68
+ hourlyBudget: body.hourlyBudget ?? null,
69
+ budgetAction: body.budgetAction || 'warn',
56
70
  soul: body.soul || undefined,
57
71
  createdAt: now,
58
72
  updatedAt: now,
@@ -0,0 +1,22 @@
1
+ import { NextResponse } from 'next/server'
2
+ import { listPendingApprovals, submitDecision } from '@/lib/server/approvals'
3
+
4
+ export const dynamic = 'force-dynamic'
5
+
6
+ export async function GET() {
7
+ return NextResponse.json(listPendingApprovals())
8
+ }
9
+
10
+ export async function POST(req: Request) {
11
+ try {
12
+ const body = await req.json()
13
+ const { id, approved } = body
14
+ if (!id || typeof approved !== 'boolean') {
15
+ return NextResponse.json({ error: 'id and approved required' }, { status: 400 })
16
+ }
17
+ await submitDecision(id, approved)
18
+ return NextResponse.json({ ok: true })
19
+ } catch (err: unknown) {
20
+ return NextResponse.json({ error: err instanceof Error ? err.message : String(err) }, { status: 500 })
21
+ }
22
+ }
@@ -18,6 +18,7 @@ import {
18
18
  import { filterHealthyChatroomAgents } from '@/lib/server/chatroom-health'
19
19
  import { evaluateRoutingRules } from '@/lib/server/chatroom-routing'
20
20
  import { markProviderFailure, markProviderSuccess } from '@/lib/server/provider-health'
21
+ import { applyAgentReactionsFromText } from '@/lib/server/chatroom-orchestration'
21
22
  import type { Chatroom, ChatroomMessage, Agent } from '@/types'
22
23
 
23
24
  export const dynamic = 'force-dynamic'
@@ -250,6 +251,9 @@ export async function POST(req: Request, { params }: { params: Promise<{ id: str
250
251
  saveChatrooms(latestChatrooms)
251
252
  notify(`chatroom:${id}`)
252
253
 
254
+ // Extract and apply reactions (e.g. [REACTION]{"emoji":"👍","to":"..."})
255
+ applyAgentReactionsFromText(responseText, id, agent.id)
256
+
253
257
  markProviderSuccess(agent.provider)
254
258
  writeEvent({ t: 'cr_agent_done', agentId: agent.id, agentName: agent.name })
255
259
 
@@ -11,9 +11,9 @@ export async function POST(req: Request) {
11
11
  if (!content) {
12
12
  try {
13
13
  content = await fetchSkillContent(url)
14
- } catch (err: any) {
14
+ } catch (err: unknown) {
15
15
  return NextResponse.json(
16
- { error: err.message || 'Failed to fetch skill content' },
16
+ { error: err instanceof Error ? err.message : 'Failed to fetch skill content' },
17
17
  { status: 502 }
18
18
  )
19
19
  }
@@ -0,0 +1,37 @@
1
+ import { NextResponse } from 'next/server'
2
+ import { z } from 'zod'
3
+ import { runEvalScenario } from '@/lib/server/eval/runner'
4
+ import { listEvalRuns } from '@/lib/server/eval/store'
5
+
6
+ const RunSchema = z.object({
7
+ scenarioId: z.string().min(1),
8
+ agentId: z.string().min(1),
9
+ })
10
+
11
+ export async function POST(req: Request) {
12
+ try {
13
+ const body: unknown = await req.json()
14
+ const parsed = RunSchema.safeParse(body)
15
+ if (!parsed.success) {
16
+ return NextResponse.json(
17
+ { error: parsed.error.issues.map((i) => i.message).join(', ') },
18
+ { status: 400 },
19
+ )
20
+ }
21
+
22
+ const result = await runEvalScenario(parsed.data.scenarioId, parsed.data.agentId)
23
+ return NextResponse.json(result)
24
+ } catch (err: unknown) {
25
+ return NextResponse.json(
26
+ { error: err instanceof Error ? err.message : String(err) },
27
+ { status: 500 },
28
+ )
29
+ }
30
+ }
31
+
32
+ export async function GET(req: Request) {
33
+ const { searchParams } = new URL(req.url)
34
+ const limit = Math.min(parseInt(searchParams.get('limit') || '50', 10), 200)
35
+ const runs = listEvalRuns(limit)
36
+ return NextResponse.json(runs)
37
+ }
@@ -0,0 +1,24 @@
1
+ import { NextResponse } from 'next/server'
2
+ import { EVAL_SCENARIOS } from '@/lib/server/eval/scenarios'
3
+
4
+ export async function GET(req: Request) {
5
+ const { searchParams } = new URL(req.url)
6
+ const category = searchParams.get('category')
7
+
8
+ const scenarios = category
9
+ ? EVAL_SCENARIOS.filter((s) => s.category === category)
10
+ : EVAL_SCENARIOS
11
+
12
+ return NextResponse.json(
13
+ scenarios.map((s) => ({
14
+ id: s.id,
15
+ name: s.name,
16
+ category: s.category,
17
+ description: s.description,
18
+ tools: s.tools,
19
+ timeoutMs: s.timeoutMs,
20
+ criteriaCount: s.scoringCriteria.length,
21
+ maxScore: s.scoringCriteria.reduce((sum, c) => sum + c.weight, 0),
22
+ })),
23
+ )
24
+ }
@@ -0,0 +1,29 @@
1
+ import { NextResponse } from 'next/server'
2
+ import { z } from 'zod'
3
+ import { runEvalSuite } from '@/lib/server/eval/runner'
4
+
5
+ const SuiteSchema = z.object({
6
+ agentId: z.string().min(1),
7
+ categories: z.array(z.string()).optional(),
8
+ })
9
+
10
+ export async function POST(req: Request) {
11
+ try {
12
+ const body: unknown = await req.json()
13
+ const parsed = SuiteSchema.safeParse(body)
14
+ if (!parsed.success) {
15
+ return NextResponse.json(
16
+ { error: parsed.error.issues.map((i) => i.message).join(', ') },
17
+ { status: 400 },
18
+ )
19
+ }
20
+
21
+ const result = await runEvalSuite(parsed.data.agentId, parsed.data.categories)
22
+ return NextResponse.json(result)
23
+ } catch (err: unknown) {
24
+ return NextResponse.json(
25
+ { error: err instanceof Error ? err.message : String(err) },
26
+ { status: 500 },
27
+ )
28
+ }
29
+ }
@@ -0,0 +1,26 @@
1
+ import { NextResponse } from 'next/server'
2
+ import { loadMcpServers } from '@/lib/server/storage'
3
+ import { notFound } from '@/lib/server/collection-helpers'
4
+ import { runMcpConformanceCheck } from '@/lib/server/mcp-conformance'
5
+
6
+ export async function POST(req: Request, { params }: { params: Promise<{ id: string }> }) {
7
+ const { id } = await params
8
+ const servers = loadMcpServers()
9
+ const server = servers[id]
10
+ if (!server) return notFound()
11
+
12
+ const body = await req.json().catch(() => ({}))
13
+ const timeoutMs = typeof body?.timeoutMs === 'number' ? body.timeoutMs : undefined
14
+ const smokeToolName = typeof body?.smokeToolName === 'string' ? body.smokeToolName : undefined
15
+ const smokeToolArgs = body?.smokeToolArgs && typeof body.smokeToolArgs === 'object' && !Array.isArray(body.smokeToolArgs)
16
+ ? body.smokeToolArgs
17
+ : undefined
18
+
19
+ const result = await runMcpConformanceCheck(server, {
20
+ timeoutMs,
21
+ smokeToolName,
22
+ smokeToolArgs,
23
+ })
24
+
25
+ return NextResponse.json(result, { status: result.ok ? 200 : 502 })
26
+ }
@@ -0,0 +1,81 @@
1
+ import { NextResponse } from 'next/server'
2
+ import { loadMcpServers } from '@/lib/server/storage'
3
+ import { notFound } from '@/lib/server/collection-helpers'
4
+ import { connectMcpServer, disconnectMcpServer } from '@/lib/server/mcp-client'
5
+
6
+ function parseArgs(value: unknown): Record<string, unknown> {
7
+ if (value && typeof value === 'object' && !Array.isArray(value)) {
8
+ return value as Record<string, unknown>
9
+ }
10
+ if (typeof value === 'string' && value.trim()) {
11
+ try {
12
+ const parsed = JSON.parse(value)
13
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
14
+ return parsed as Record<string, unknown>
15
+ }
16
+ } catch {
17
+ // Handled by caller via fallback validation error.
18
+ }
19
+ }
20
+ return {}
21
+ }
22
+
23
+ export async function POST(req: Request, { params }: { params: Promise<{ id: string }> }) {
24
+ const { id } = await params
25
+ const servers = loadMcpServers()
26
+ const server = servers[id]
27
+ if (!server) return notFound()
28
+
29
+ const body = await req.json().catch(() => null)
30
+ const toolName = typeof body?.toolName === 'string' ? body.toolName.trim() : ''
31
+ if (!toolName) {
32
+ return NextResponse.json({ error: 'toolName is required' }, { status: 400 })
33
+ }
34
+
35
+ const argsRaw = body?.args
36
+ if (
37
+ argsRaw !== undefined
38
+ && typeof argsRaw !== 'string'
39
+ && (typeof argsRaw !== 'object' || Array.isArray(argsRaw))
40
+ ) {
41
+ return NextResponse.json({ error: 'args must be an object or JSON string' }, { status: 400 })
42
+ }
43
+ const args = parseArgs(argsRaw)
44
+
45
+ let client: unknown
46
+ let transport: unknown
47
+ try {
48
+ const conn = await connectMcpServer(server)
49
+ client = conn.client
50
+ transport = conn.transport
51
+ const result = await (client as { callTool: (opts: { name: string; arguments: Record<string, unknown> }) => Promise<Record<string, unknown>> }).callTool({
52
+ name: toolName,
53
+ arguments: args,
54
+ })
55
+ const textParts = Array.isArray(result?.content)
56
+ ? (result.content as Array<Record<string, unknown>>)
57
+ .filter((part) => part?.type === 'text' && typeof part?.text === 'string')
58
+ .map((part) => part.text as string)
59
+ : []
60
+ const text = textParts.join('\n').trim() || '(no text output)'
61
+
62
+ return NextResponse.json({
63
+ ok: true,
64
+ toolName,
65
+ args,
66
+ text,
67
+ result,
68
+ isError: result?.isError === true,
69
+ })
70
+ } catch (err: unknown) {
71
+ return NextResponse.json(
72
+ { ok: false, error: err instanceof Error ? err.message : 'MCP tool invocation failed' },
73
+ { status: 500 },
74
+ )
75
+ } finally {
76
+ if (client && transport) {
77
+ await disconnectMcpServer(client, transport)
78
+ }
79
+ }
80
+ }
81
+
@@ -0,0 +1,46 @@
1
+ import { NextResponse } from 'next/server'
2
+ import { getMemoryDb } from '@/lib/server/memory-db'
3
+ import type { MemoryEntry } from '@/types'
4
+
5
+ export const dynamic = 'force-dynamic'
6
+
7
+ /** GET /api/memory/graph — returns a node-link structure of the memory graph */
8
+ export async function GET(req: Request) {
9
+ const { searchParams } = new URL(req.url)
10
+ const agentId = searchParams.get('agentId')
11
+ const limit = Math.min(1000, Math.max(1, Number(searchParams.get('limit')) || 200))
12
+
13
+ const db = getMemoryDb()
14
+ const entries: MemoryEntry[] = db.list(agentId || undefined, limit)
15
+
16
+ const nodes = entries.map(e => ({
17
+ id: e.id,
18
+ title: e.title,
19
+ category: e.category,
20
+ agentId: e.agentId,
21
+ contentPreview: e.content.slice(0, 100) + (e.content.length > 100 ? '...' : ''),
22
+ createdAt: e.createdAt,
23
+ updatedAt: e.updatedAt,
24
+ pinned: e.pinned
25
+ }))
26
+
27
+ const links: Array<{ source: string; target: string; type: string }> = []
28
+ const entryIds = new Set(entries.map(e => e.id))
29
+
30
+ for (const entry of entries) {
31
+ if (entry.linkedMemoryIds && Array.isArray(entry.linkedMemoryIds)) {
32
+ for (const targetId of entry.linkedMemoryIds) {
33
+ // Only include links where both nodes are in the current set (or could fetch more if needed)
34
+ if (entryIds.has(targetId)) {
35
+ links.push({
36
+ source: entry.id,
37
+ target: targetId,
38
+ type: 'linked'
39
+ })
40
+ }
41
+ }
42
+ }
43
+ }
44
+
45
+ return NextResponse.json({ nodes, links })
46
+ }