@swarmclawai/swarmclaw 1.1.0 → 1.1.3

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 (292) hide show
  1. package/README.md +178 -1
  2. package/package.json +1 -1
  3. package/src/app/api/agents/[id]/route.ts +6 -0
  4. package/src/app/api/agents/route.ts +2 -0
  5. package/src/app/api/agents/thread-route.test.ts +3 -3
  6. package/src/app/api/autonomy/estop/route.ts +1 -1
  7. package/src/app/api/chatrooms/[id]/chat/route.test.ts +6 -6
  8. package/src/app/api/chatrooms/route.ts +6 -1
  9. package/src/app/api/chats/[id]/chat/route.test.ts +23 -23
  10. package/src/app/api/chats/[id]/messages/route.ts +4 -5
  11. package/src/app/api/chats/[id]/route.ts +5 -0
  12. package/src/app/api/chats/messages-route.test.ts +65 -0
  13. package/src/app/api/chats/route.ts +1 -0
  14. package/src/app/api/credentials/[id]/route.ts +2 -3
  15. package/src/app/api/daemon/route.ts +1 -1
  16. package/src/app/api/extensions/builtins/route.ts +40 -0
  17. package/src/app/api/extensions/dependencies/route.ts +3 -3
  18. package/src/app/api/extensions/install/route.ts +19 -19
  19. package/src/app/api/extensions/marketplace/route.ts +18 -22
  20. package/src/app/api/extensions/route.ts +13 -13
  21. package/src/app/api/extensions/settings/route.ts +19 -19
  22. package/src/app/api/extensions/ui/route.ts +3 -3
  23. package/src/app/api/memory/maintenance/route.ts +51 -41
  24. package/src/app/api/protocols/runs/[id]/actions/route.ts +26 -0
  25. package/src/app/api/protocols/runs/[id]/events/route.ts +16 -0
  26. package/src/app/api/protocols/runs/[id]/route.ts +10 -0
  27. package/src/app/api/protocols/runs/route.test.ts +173 -0
  28. package/src/app/api/protocols/runs/route.ts +51 -0
  29. package/src/app/api/protocols/templates/[id]/route.ts +50 -0
  30. package/src/app/api/protocols/templates/route.test.ts +109 -0
  31. package/src/app/api/protocols/templates/route.ts +30 -0
  32. package/src/app/api/providers/[id]/discover-models/route.ts +1 -0
  33. package/src/app/api/schedules/[id]/route.test.ts +1 -1
  34. package/src/app/api/schedules/route.test.ts +1 -1
  35. package/src/app/api/setup/check-provider/route.ts +27 -9
  36. package/src/app/api/setup/doctor/route.ts +7 -7
  37. package/src/app/api/tasks/[id]/route.ts +9 -7
  38. package/src/app/api/tasks/claim/route.test.ts +58 -0
  39. package/src/app/api/tasks/claim/route.ts +18 -0
  40. package/src/app/api/tasks/route.ts +5 -5
  41. package/src/app/api/usage/route.ts +16 -16
  42. package/src/app/api/webhooks/[id]/helpers.ts +4 -3
  43. package/src/app/chatrooms/[id]/page.tsx +49 -0
  44. package/src/app/extensions/layout.tsx +3 -3
  45. package/src/app/extensions/page.tsx +3 -3
  46. package/src/app/missions/page.tsx +53 -0
  47. package/src/app/protocols/page.tsx +1284 -0
  48. package/src/app/usage/page.tsx +8 -8
  49. package/src/cli/index.js +20 -2
  50. package/src/cli/spec.js +2 -2
  51. package/src/components/agents/agent-sheet.tsx +83 -39
  52. package/src/components/chat/chat-area.tsx +10 -10
  53. package/src/components/chat/chat-header.tsx +67 -1
  54. package/src/components/chat/chat-tool-toggles.tsx +80 -39
  55. package/src/components/chat/file-path-chip.tsx +4 -3
  56. package/src/components/chat/message-bubble.test.ts +160 -2
  57. package/src/components/chat/message-bubble.tsx +208 -108
  58. package/src/components/chat/message-list.tsx +111 -80
  59. package/src/components/chat/tool-request-banner.tsx +17 -17
  60. package/src/components/chatrooms/breakout-command.test.ts +86 -0
  61. package/src/components/chatrooms/breakout-command.ts +119 -0
  62. package/src/components/chatrooms/chatroom-input.tsx +218 -71
  63. package/src/components/chatrooms/chatroom-view.tsx +247 -25
  64. package/src/components/connectors/connector-sheet.tsx +5 -5
  65. package/src/components/{plugins/plugin-list.tsx → extensions/extension-list.tsx} +84 -84
  66. package/src/components/{plugins/plugin-sheet.tsx → extensions/extension-sheet.tsx} +59 -59
  67. package/src/components/input/chat-input.tsx +81 -78
  68. package/src/components/input/composer-shell.tsx +44 -0
  69. package/src/components/layout/dashboard-shell.tsx +4 -4
  70. package/src/components/layout/sheet-layer.tsx +2 -2
  71. package/src/components/layout/sidebar-rail.tsx +6 -0
  72. package/src/components/mcp-servers/mcp-server-list.tsx +1 -1
  73. package/src/components/protocols/structured-session-launcher.tsx +491 -0
  74. package/src/components/schedules/schedule-sheet.tsx +57 -8
  75. package/src/components/shared/model-combobox.tsx +6 -3
  76. package/src/components/tasks/task-sheet.tsx +65 -0
  77. package/src/instrumentation.ts +1 -1
  78. package/src/lib/agent-default-tools.test.ts +24 -30
  79. package/src/lib/app/api-client.test.ts +3 -3
  80. package/src/lib/app/navigation.ts +1 -0
  81. package/src/lib/app/view-constants.ts +9 -1
  82. package/src/lib/canvas-content.test.ts +1 -1
  83. package/src/lib/capability-selection.test.ts +1 -1
  84. package/src/lib/chat/chat-streaming-state.test.ts +3 -6
  85. package/src/lib/chat/chat-streaming-state.ts +6 -6
  86. package/src/lib/chat/message-list-utils.test.ts +31 -0
  87. package/src/lib/chat/message-list-utils.ts +5 -0
  88. package/src/lib/chat/queued-message-queue.ts +12 -1
  89. package/src/lib/{plugin-install-cors.ts → extension-install-cors.ts} +4 -4
  90. package/src/lib/{plugin-sources.ts → extension-sources.ts} +17 -17
  91. package/src/lib/ollama-mode.test.ts +33 -0
  92. package/src/lib/ollama-mode.ts +28 -0
  93. package/src/lib/plugin-sources.test.ts +18 -18
  94. package/src/lib/provider-model-discovery-client.ts +2 -0
  95. package/src/lib/providers/anthropic.ts +5 -4
  96. package/src/lib/providers/index.ts +43 -9
  97. package/src/lib/providers/ollama.ts +7 -5
  98. package/src/lib/providers/openai.ts +51 -25
  99. package/src/lib/server/agents/agent-runtime-config.test.ts +37 -1
  100. package/src/lib/server/agents/agent-runtime-config.ts +9 -0
  101. package/src/lib/server/agents/agent-thread-session.test.ts +9 -5
  102. package/src/lib/server/agents/agent-thread-session.ts +1 -0
  103. package/src/lib/server/agents/main-agent-loop.ts +15 -0
  104. package/src/lib/server/agents/subagent-runtime.test.ts +57 -53
  105. package/src/lib/server/agents/subagent-runtime.ts +19 -19
  106. package/src/lib/server/approval-match.ts +4 -4
  107. package/src/lib/server/approvals.ts +3 -2
  108. package/src/lib/server/autonomy/supervisor-reflection.ts +17 -0
  109. package/src/lib/server/build-llm.test.ts +65 -2
  110. package/src/lib/server/build-llm.ts +11 -3
  111. package/src/lib/server/{builtin-plugins.ts → builtin-extensions.ts} +2 -9
  112. package/src/lib/server/capability-router.ts +9 -9
  113. package/src/lib/server/chat-execution/chat-execution-advanced.test.ts +5 -5
  114. package/src/lib/server/chat-execution/chat-execution-disabled.test.ts +1 -1
  115. package/src/lib/server/chat-execution/chat-execution-eval-history.test.ts +1 -1
  116. package/src/lib/server/chat-execution/chat-execution-heartbeat.test.ts +1 -1
  117. package/src/lib/server/chat-execution/chat-execution-session-sync.test.ts +15 -15
  118. package/src/lib/server/chat-execution/chat-execution-tool-events.test.ts +4 -4
  119. package/src/lib/server/chat-execution/chat-execution-utils.ts +3 -9
  120. package/src/lib/server/chat-execution/chat-execution.ts +264 -184
  121. package/src/lib/server/chat-execution/chat-streaming-utils.ts +8 -7
  122. package/src/lib/server/chat-execution/chat-turn-tool-routing.test.ts +144 -10
  123. package/src/lib/server/chat-execution/chat-turn-tool-routing.ts +152 -18
  124. package/src/lib/server/chat-execution/direct-memory-intent.test.ts +15 -0
  125. package/src/lib/server/chat-execution/direct-memory-intent.ts +6 -3
  126. package/src/lib/server/chat-execution/exact-output-contract.test.ts +91 -0
  127. package/src/lib/server/chat-execution/exact-output-contract.ts +220 -0
  128. package/src/lib/server/chat-execution/memory-mutation-tools.ts +2 -2
  129. package/src/lib/server/chat-execution/situational-awareness.test.ts +318 -0
  130. package/src/lib/server/chat-execution/situational-awareness.ts +273 -0
  131. package/src/lib/server/chat-execution/stream-agent-chat.test.ts +99 -11
  132. package/src/lib/server/chat-execution/stream-agent-chat.ts +166 -84
  133. package/src/lib/server/chat-execution/stream-continuation.ts +4 -4
  134. package/src/lib/server/chatrooms/chatroom-helpers.test.ts +1 -0
  135. package/src/lib/server/chatrooms/chatroom-helpers.ts +3 -1
  136. package/src/lib/server/chatrooms/chatroom-session-persistence.test.ts +3 -3
  137. package/src/lib/server/chatrooms/mailbox-utils.ts +5 -5
  138. package/src/lib/server/chatrooms/session-mailbox.test.ts +6 -0
  139. package/src/lib/server/connectors/connector-routing.test.ts +59 -1
  140. package/src/lib/server/connectors/inbound-audio-transcription.test.ts +5 -5
  141. package/src/lib/server/connectors/manager-roundtrip.test.ts +14 -14
  142. package/src/lib/server/connectors/manager.test.ts +142 -142
  143. package/src/lib/server/connectors/manager.ts +8 -8
  144. package/src/lib/server/connectors/outbox.test.ts +11 -11
  145. package/src/lib/server/connectors/response-media.ts +3 -0
  146. package/src/lib/server/connectors/session.test.ts +10 -10
  147. package/src/lib/server/cost.ts +9 -9
  148. package/src/lib/server/dag-validation.ts +10 -0
  149. package/src/lib/server/data-dir.test.ts +8 -8
  150. package/src/lib/server/eval/agent-regression-advanced.test.ts +66 -66
  151. package/src/lib/server/eval/agent-regression.test.ts +7 -7
  152. package/src/lib/server/eval/agent-regression.ts +104 -103
  153. package/src/lib/server/{plugins-approval-guidance.ts → extensions-approval-guidance.ts} +31 -31
  154. package/src/lib/server/{plugins.test.ts → extensions.test.ts} +104 -105
  155. package/src/lib/server/{plugins.ts → extensions.ts} +501 -483
  156. package/src/lib/server/integrity-monitor.test.ts +21 -21
  157. package/src/lib/server/integrity-monitor.ts +10 -6
  158. package/src/lib/server/knowledge-db.test.ts +4 -0
  159. package/src/lib/server/langgraph-checkpoint.test.ts +6 -6
  160. package/src/lib/server/langgraph-checkpoint.ts +6 -1
  161. package/src/lib/server/memory/memory-consolidation.test.ts +89 -0
  162. package/src/lib/server/memory/memory-consolidation.ts +143 -3
  163. package/src/lib/server/memory/memory-db.ts +17 -5
  164. package/src/lib/server/memory/memory-integration.test.ts +11 -14
  165. package/src/lib/server/missions/mission-service.ts +53 -1
  166. package/src/lib/server/native-capabilities.test.ts +7 -7
  167. package/src/lib/server/native-capabilities.ts +85 -85
  168. package/src/lib/server/ollama-runtime.test.ts +41 -0
  169. package/src/lib/server/ollama-runtime.ts +13 -18
  170. package/src/lib/server/openclaw/gateway.test.ts +157 -1
  171. package/src/lib/server/openclaw/gateway.ts +55 -34
  172. package/src/lib/server/openclaw/sync.ts +15 -15
  173. package/src/lib/server/plugins-advanced.test.ts +86 -87
  174. package/src/lib/server/protocols/dag-scheduler.ts +103 -0
  175. package/src/lib/server/protocols/protocol-service.test.ts +585 -0
  176. package/src/lib/server/protocols/protocol-service.ts +3937 -0
  177. package/src/lib/server/protocols/step-dag-validation.ts +86 -0
  178. package/src/lib/server/protocols/step-outputs.ts +57 -0
  179. package/src/lib/server/provider-endpoint.ts +2 -0
  180. package/src/lib/server/provider-health.ts +2 -0
  181. package/src/lib/server/provider-model-discovery.test.ts +23 -0
  182. package/src/lib/server/provider-model-discovery.ts +23 -5
  183. package/src/lib/server/resolve-image.ts +41 -9
  184. package/src/lib/server/runtime/daemon-guards.test.ts +74 -0
  185. package/src/lib/server/runtime/daemon-state-connectors.test.ts +10 -10
  186. package/src/lib/server/runtime/daemon-state.test.ts +18 -18
  187. package/src/lib/server/runtime/daemon-state.ts +188 -24
  188. package/src/lib/server/runtime/heartbeat-service-timer.test.ts +16 -13
  189. package/src/lib/server/runtime/heartbeat-service.ts +100 -42
  190. package/src/lib/server/runtime/idle-window.ts +84 -0
  191. package/src/lib/server/runtime/process-manager.ts +45 -2
  192. package/src/lib/server/runtime/queue-followups.test.ts +2 -2
  193. package/src/lib/server/runtime/queue.test.ts +28 -3
  194. package/src/lib/server/runtime/queue.ts +104 -12
  195. package/src/lib/server/runtime/scheduler.test.ts +79 -0
  196. package/src/lib/server/runtime/scheduler.ts +26 -39
  197. package/src/lib/server/runtime/session-run-manager.test.ts +9 -0
  198. package/src/lib/server/runtime/session-run-manager.ts +36 -3
  199. package/src/lib/server/runtime/wake-dispatcher.ts +9 -9
  200. package/src/lib/server/runtime/watch-jobs.ts +5 -3
  201. package/src/lib/server/schedules/schedule-lifecycle.test.ts +1 -1
  202. package/src/lib/server/schedules/schedule-normalization.ts +23 -3
  203. package/src/lib/server/session-tools/autonomy-tools.test.ts +11 -33
  204. package/src/lib/server/session-tools/canvas.ts +7 -7
  205. package/src/lib/server/session-tools/chatroom.ts +7 -7
  206. package/src/lib/server/session-tools/connector.test.ts +2 -2
  207. package/src/lib/server/session-tools/connector.ts +7 -7
  208. package/src/lib/server/session-tools/context-mgmt.ts +7 -7
  209. package/src/lib/server/session-tools/context.ts +5 -5
  210. package/src/lib/server/session-tools/crud.test.ts +5 -5
  211. package/src/lib/server/session-tools/crud.ts +13 -244
  212. package/src/lib/server/session-tools/delegate-fallback.test.ts +16 -16
  213. package/src/lib/server/session-tools/delegate.ts +14 -14
  214. package/src/lib/server/session-tools/discovery-approvals.test.ts +2 -2
  215. package/src/lib/server/session-tools/discovery.ts +43 -45
  216. package/src/lib/server/session-tools/edit_file.ts +8 -8
  217. package/src/lib/server/session-tools/email.ts +11 -11
  218. package/src/lib/server/session-tools/{plugin-creator.ts → extension-creator.ts} +61 -60
  219. package/src/lib/server/session-tools/file.ts +9 -9
  220. package/src/lib/server/session-tools/google-workspace.test.ts +5 -5
  221. package/src/lib/server/session-tools/google-workspace.ts +11 -10
  222. package/src/lib/server/session-tools/http.ts +9 -9
  223. package/src/lib/server/session-tools/human-loop.ts +7 -7
  224. package/src/lib/server/session-tools/image-gen.ts +20 -20
  225. package/src/lib/server/session-tools/index.test.ts +8 -0
  226. package/src/lib/server/session-tools/index.ts +48 -65
  227. package/src/lib/server/session-tools/mailbox.ts +7 -7
  228. package/src/lib/server/session-tools/manage-connectors.test.ts +2 -2
  229. package/src/lib/server/session-tools/manage-schedules-advanced.test.ts +1 -1
  230. package/src/lib/server/session-tools/manage-schedules.test.ts +11 -11
  231. package/src/lib/server/session-tools/manage-skills.test.ts +5 -5
  232. package/src/lib/server/session-tools/manage-tasks-advanced.test.ts +3 -3
  233. package/src/lib/server/session-tools/manage-tasks.test.ts +1 -1
  234. package/src/lib/server/session-tools/memory.ts +41 -11
  235. package/src/lib/server/session-tools/monitor.ts +14 -77
  236. package/src/lib/server/session-tools/openclaw-nodes.ts +8 -8
  237. package/src/lib/server/session-tools/openclaw-workspace.ts +7 -7
  238. package/src/lib/server/session-tools/platform-access.test.ts +4 -4
  239. package/src/lib/server/session-tools/platform.ts +19 -19
  240. package/src/lib/server/session-tools/primitive-tools.test.ts +3 -124
  241. package/src/lib/server/session-tools/replicate.ts +9 -9
  242. package/src/lib/server/session-tools/sandbox.ts +6 -86
  243. package/src/lib/server/session-tools/schedule.ts +10 -9
  244. package/src/lib/server/session-tools/session-info.ts +16 -20
  245. package/src/lib/server/session-tools/session-tools-wiring.test.ts +13 -38
  246. package/src/lib/server/session-tools/shell.ts +37 -13
  247. package/src/lib/server/session-tools/skill-runtime.test.ts +3 -3
  248. package/src/lib/server/session-tools/skill-runtime.ts +9 -9
  249. package/src/lib/server/session-tools/skills.ts +4 -4
  250. package/src/lib/server/session-tools/subagent.ts +7 -7
  251. package/src/lib/server/session-tools/wallet-tool.test.ts +4 -4
  252. package/src/lib/server/session-tools/wallet.ts +9 -9
  253. package/src/lib/server/session-tools/web-browser-config.test.ts +4 -3
  254. package/src/lib/server/session-tools/web-utils.ts +4 -2
  255. package/src/lib/server/session-tools/web.ts +68 -15
  256. package/src/lib/server/skills/clawhub-client.test.ts +2 -2
  257. package/src/lib/server/skills/discovered-skill-prompt.test.ts +10 -10
  258. package/src/lib/server/skills/discovered-skill-prompt.ts +10 -10
  259. package/src/lib/server/skills/learned-skills.ts +54 -2
  260. package/src/lib/server/skills/runtime-skill-resolver.test.ts +3 -3
  261. package/src/lib/server/skills/runtime-skill-resolver.ts +24 -24
  262. package/src/lib/server/skills/skill-suggestions.test.ts +1 -1
  263. package/src/lib/server/storage-item-access.test.ts +109 -1
  264. package/src/lib/server/storage.ts +91 -8
  265. package/src/lib/server/tasks/task-followups.test.ts +21 -11
  266. package/src/lib/server/tool-aliases.ts +37 -43
  267. package/src/lib/server/tool-capability-policy-advanced.test.ts +108 -108
  268. package/src/lib/server/tool-capability-policy.test.ts +13 -13
  269. package/src/lib/server/tool-capability-policy.ts +32 -37
  270. package/src/lib/server/tool-loop-detection.ts +9 -9
  271. package/src/lib/server/tool-planning.test.ts +8 -8
  272. package/src/lib/server/tool-planning.ts +21 -21
  273. package/src/lib/server/universal-tool-access.ts +11 -18
  274. package/src/lib/setup-defaults.ts +38 -0
  275. package/src/lib/tool-definitions.ts +16 -16
  276. package/src/lib/validation/schemas.ts +263 -0
  277. package/src/proxy.test.ts +2 -2
  278. package/src/proxy.ts +12 -12
  279. package/src/stores/slices/data-slice.ts +4 -4
  280. package/src/stores/slices/ui-slice.ts +8 -8
  281. package/src/stores/use-app-store.test.ts +2 -2
  282. package/src/stores/use-chat-store.test.ts +3 -3
  283. package/src/stores/use-chat-store.ts +22 -8
  284. package/src/stores/use-chatroom-store.ts +19 -1
  285. package/src/types/index.ts +526 -65
  286. package/src/views/settings/{plugin-manager.tsx → extension-manager.tsx} +36 -36
  287. package/src/lib/server/session-tools/calendar.ts +0 -367
  288. package/src/lib/server/session-tools/crawl.ts +0 -449
  289. package/src/lib/server/session-tools/document.ts +0 -285
  290. package/src/lib/server/session-tools/extract.ts +0 -139
  291. package/src/lib/server/session-tools/git.ts +0 -111
  292. package/src/lib/server/session-tools/table.ts +0 -589
package/README.md CHANGED
@@ -15,8 +15,182 @@ Docs: https://swarmclaw.ai/docs
15
15
  Website: https://swarmclaw.ai
16
16
  Extension tutorial: https://swarmclaw.ai/docs/extension-tutorial
17
17
 
18
+ <div align="center">
19
+ <table>
20
+ <tr>
21
+ <td align="center"><strong>Works<br>with</strong></td>
22
+ <td align="center"><img src="doc/assets/logos/openclaw.svg" width="32" alt="OpenClaw"><br><sub>OpenClaw</sub></td>
23
+ <td align="center"><img src="doc/assets/logos/claude-code.svg" width="32" alt="Claude Code"><br><sub>Claude Code</sub></td>
24
+ <td align="center"><img src="doc/assets/logos/codex.svg" width="32" alt="Codex"><br><sub>Codex</sub></td>
25
+ <td align="center"><img src="doc/assets/logos/gemini-cli.svg" width="32" alt="Gemini CLI"><br><sub>Gemini CLI</sub></td>
26
+ <td align="center"><img src="doc/assets/logos/opencode.svg" width="32" alt="OpenCode"><br><sub>OpenCode</sub></td>
27
+ <td align="center"><img src="doc/assets/logos/anthropic.svg" width="32" alt="Anthropic"><br><sub>Anthropic</sub></td>
28
+ <td align="center"><img src="doc/assets/logos/openai.svg" width="32" alt="OpenAI"><br><sub>OpenAI</sub></td>
29
+ <td align="center"><img src="doc/assets/logos/google.svg" width="32" alt="Google Gemini"><br><sub>Gemini</sub></td>
30
+ <td align="center"><img src="doc/assets/logos/ollama.svg" width="32" alt="Ollama"><br><sub>Ollama</sub></td>
31
+ <td align="center"><img src="doc/assets/logos/deepseek.svg" width="32" alt="DeepSeek"><br><sub>DeepSeek</sub></td>
32
+ <td align="center"><img src="doc/assets/logos/groq.svg" width="32" alt="Groq"><br><sub>Groq</sub></td>
33
+ <td align="center"><img src="doc/assets/logos/together.svg" width="32" alt="Together AI"><br><sub>Together</sub></td>
34
+ <td align="center"><img src="doc/assets/logos/mistral.svg" width="32" alt="Mistral AI"><br><sub>Mistral</sub></td>
35
+ <td align="center"><img src="doc/assets/logos/xai.svg" width="32" alt="xAI"><br><sub>xAI</sub></td>
36
+ <td align="center"><img src="doc/assets/logos/fireworks.svg" width="32" alt="Fireworks AI"><br><sub>Fireworks</sub></td>
37
+ <td align="center"><img src="doc/assets/logos/nebius.svg" width="32" alt="Nebius"><br><sub>Nebius</sub></td>
38
+ <td align="center"><img src="doc/assets/logos/deepinfra.svg" width="32" alt="DeepInfra"><br><sub>DeepInfra</sub></td>
39
+ </tr>
40
+ </table>
41
+ </div>
42
+
43
+ ## Use Cases
44
+
45
+ SwarmClaw is a general-purpose agent runtime. Here are some of the ways people use it.
46
+
47
+ ---
48
+
49
+ ### Personal Assistant
50
+
51
+ A single agent with memory, web access, scheduling, and file tools — your always-available copilot.
52
+
53
+ > *"Remember that I prefer window seats. Book research time every Monday at 9am. Summarize the articles I saved last week."*
54
+
55
+ - Remembers preferences, contacts, and decisions across conversations
56
+ - Schedules reminders, recurring check-ins, and follow-ups
57
+ - Researches, drafts, plans, and manages your day-to-day
58
+ - Bridges to WhatsApp or Telegram so you can message your agent on the go
59
+
60
+ **Starter kit:** Personal Assistant &rarr; 1 agent, ready in under a minute.
61
+
62
+ ---
63
+
64
+ ### Virtual Company
65
+
66
+ Build a full org chart of specialized agents that collaborate, delegate, and report up — a lightweight simulation of a real company.
67
+
68
+ | Role | Agent | Responsibilities |
69
+ |------|-------|-----------------|
70
+ | **CEO** | Strategist | Sets objectives, reviews progress, delegates to department heads |
71
+ | **CTO** | Builder | Owns technical execution, code reviews, architecture decisions |
72
+ | **CFO** | Analyst | Tracks budgets, monitors token spend, produces cost reports |
73
+ | **CMO** | Marketer | Drafts campaigns, manages content calendar, monitors channels |
74
+ | **COO** | Operator | Coordinates cross-agent work, manages schedules, unblocks tasks |
75
+
76
+ - Each agent has its own provider, model, personality (soul), and tool access
77
+ - The CEO delegates via the task board; department heads pick up work autonomously
78
+ - Heartbeat loops let agents check in on their own, surface blockers, and request approvals
79
+ - Memory means every agent remembers past decisions and context
80
+ - Connect the CMO to Discord/Slack so it can post updates directly
81
+
82
+ ---
83
+
84
+ ### Development Team
85
+
86
+ A squad of agents mirroring a real engineering team — planning, building, reviewing, and testing in parallel.
87
+
88
+ | Role | Agent | Tools |
89
+ |------|-------|-------|
90
+ | **Lead** | Architect | Delegation, tasks, schedules, missions |
91
+ | **Dev** | Builder | Shell, files, Claude Code / Codex / OpenCode |
92
+ | **QA** | Tester | Shell, browser, files, web search |
93
+ | **Designer** | Creative | Image generation, browser, web search, files |
94
+ | **Reviewer** | Critic | Files, web search, memory |
95
+
96
+ - The Lead creates missions and breaks them into tasks on the board
97
+ - Dev agents pick up tasks and delegate to Claude Code, Codex, or OpenCode for implementation
98
+ - QA runs tests, takes screenshots, and files bugs back on the task board
99
+ - The Reviewer audits PRs and flags regressions
100
+ - Structured Sessions let you run a bounded sprint — plan → build → test → review — with durable transcripts
101
+
102
+ **Starter kit:** Builder Studio &rarr; pre-configured Builder + Reviewer pair.
103
+
104
+ ---
105
+
106
+ ### Research Bureau
107
+
108
+ Multiple research agents working in parallel, each with different search strategies, then synthesizing findings.
109
+
110
+ - Spawn a swarm of researchers across different topics or sources
111
+ - Each agent searches, fetches, reads, and summarizes independently
112
+ - A lead agent collects outputs into a structured report with citations
113
+ - Memory stores findings for future reference across conversations
114
+ - Schedule recurring research runs (daily digest, weekly competitive scan)
115
+
116
+ **Starter kit:** Research Copilot &rarr; 1 focused researcher, scale up with subagents.
117
+
118
+ ---
119
+
120
+ ### OpenClaw Fleet
121
+
122
+ Distribute autonomous agents across multiple machines using OpenClaw gateways — one control plane, many runtimes.
123
+
124
+ - Deploy OpenClaw runtimes on local machines, VPS nodes, or Tailnet peers
125
+ - Each agent targets a different gateway profile (one for code, one for research, one for ops)
126
+ - The operator agent coordinates work across the fleet via delegation and the task board
127
+ - Gateway health, runtime state, and version info visible from the Providers screen
128
+ - Import `SKILL.md` files from any OpenClaw instance into SwarmClaw's skill library
129
+
130
+ **Starter kit:** OpenClaw Fleet &rarr; Operator + Remote Builder + Remote Researcher.
131
+
132
+ ---
133
+
134
+ ### Content Studio
135
+
136
+ A writer/editor pipeline for blogs, docs, newsletters, marketing copy, or social posts.
137
+
138
+ - **Writer** drafts content based on briefs, outlines, and style guides
139
+ - **Editor** tightens structure, fixes tone, and flags missing evidence
140
+ - Schedule daily or weekly content runs with automatic handoff
141
+ - Connect to Slack or Discord to publish directly from the pipeline
142
+ - Image generation agent produces visuals alongside copy
143
+
144
+ **Starter kit:** Content Studio &rarr; Writer + Editor pair.
145
+
146
+ ---
147
+
148
+ ### Customer Support Desk
149
+
150
+ Agents answering questions on every platform your users are on, with shared memory and escalation paths.
151
+
152
+ - Bridge a support agent to Discord, Slack, Telegram, WhatsApp, and Teams simultaneously
153
+ - The agent remembers each sender's history, preferences, and open issues
154
+ - Unanswerable questions escalate via `ask_human` or get routed to a specialist agent
155
+ - Schedule a nightly agent to review open threads, follow up on stale conversations, and summarize trends
156
+ - Skills let you codify common support workflows so the agent improves over time
157
+
158
+ ---
159
+
160
+ ### Crypto Operations
161
+
162
+ Agents with linked wallets for on-chain work — monitoring, trading, signing, and reporting.
163
+
164
+ - Attach Solana or Ethereum wallets to any agent
165
+ - Agents can check balances, simulate transactions, and execute swaps
166
+ - Approval gates require human sign-off before spending above a threshold
167
+ - Schedule periodic balance checks or price-alert sweeps
168
+ - The operator agent coordinates across multiple wallet-holding agents
169
+
170
+ ---
171
+
172
+ ### Mix and Match
173
+
174
+ These aren't exclusive templates — they're patterns you combine. A virtual company can have a dev team inside it. A personal assistant can spin up a research swarm on demand. An OpenClaw fleet can run your customer support desk.
175
+
176
+ The building blocks are the same: **agents, tools, memory, delegation, schedules, connectors, and skills**. SwarmClaw just gives you the control plane to wire them together.
177
+
18
178
  ## Release Notes
19
179
 
180
+ ### v1.1.2 Highlights
181
+
182
+ - **Structured Sessions expanded into richer orchestration**: ProtocolRun-based sessions now support dependency-aware step graphs, reusable step outputs, and a broader advanced execution model on the same durable runtime instead of bringing back a separate orchestrator.
183
+ - **Explicit Ollama local/cloud routing**: agents and sessions now persist the user-selected Ollama mode directly, so local Ollama no longer flips to cloud because of model naming or leftover credentials.
184
+ - **Chat and runtime regression hardening**: live-streamed inline media, stale streaming cleanup, exact-output handling, and chat rendering bugs were tightened again, including the recent message-row and avatar rendering regressions.
185
+ - **Nebius and DeepInfra as built-in providers**: both are now first-class providers with setup wizard entries, model discovery, and pre-configured defaults instead of requiring the custom provider workaround.
186
+ - **`stream_options` resilience**: the OpenAI-compatible streaming handler now retries without `stream_options` if a provider rejects it with 400, fixing connectivity for strict endpoints.
187
+
188
+ ### v1.1.1 Highlights
189
+
190
+ - **Structured Sessions are now contextual**: start bounded structured runs from direct chats, chatrooms, tasks, missions, or schedules, including a new chatroom `/breakout` command that spins up a focused session from the current room with auto-filled participants and kickoff context.
191
+ - **ProtocolRun orchestration matured**: structured sessions now run on the same durable engine for step-based branching, repeat loops, parallel branches, and explicit joins instead of growing a separate orchestration subsystem.
192
+ - **Live-agent runtime hardening**: exact-output contracts, memory preflight behavior, same-channel delivery rendering, inline media, and grounded runtime inspection were all tightened through live-agent validation before release.
193
+
20
194
  ### v1.1.0 Highlights
21
195
 
22
196
  - **Mission controller and Missions UI**: SwarmClaw now tracks durable multi-step objectives as missions with status, phase, linked tasks, queued turns, recent runs, event history, and operator actions from the new **Missions** surface.
@@ -34,6 +208,7 @@ Extension tutorial: https://swarmclaw.ai/docs/extension-tutorial
34
208
  ## What SwarmClaw Focuses On
35
209
 
36
210
  - **Delegation and background execution**: delegated work, subagents, durable jobs, checkpointing, and background task execution.
211
+ - **Structured Sessions and orchestration**: temporary bounded runs for one agent or many, launched from context and backed by durable templates, branching, loops, parallel joins, transcripts, outputs, operator controls, and chatroom breakout flows.
37
212
  - **Autonomy and memory**: heartbeats, schedules, long-running execution, durable memory, reflection memory, human-context learning, document recall, and project-aware context.
38
213
  - **OpenClaw integration**: named gateway profiles, external runtimes, deploy helpers, config sync, approval handling, and OpenClaw agent file editing.
39
214
  - **Runtime skills**: pinned skills, OpenClaw-compatible `SKILL.md` import, on-demand skill execution, and configurable keyword or embedding-based recommendation.
@@ -113,9 +288,11 @@ Then open `http://localhost:3456`.
113
288
 
114
289
  ## Core Capabilities
115
290
 
116
- - **Providers**: OpenClaw, OpenAI, Anthropic, Ollama, Google, DeepSeek, Groq, Together, Mistral, xAI, Fireworks, plus compatible custom endpoints.
291
+ - **Providers**: OpenClaw, OpenAI, Anthropic, Ollama, Google, DeepSeek, Groq, Together, Mistral, xAI, Fireworks, Nebius, DeepInfra, plus compatible custom endpoints.
117
292
  - **Delegation**: built-in delegation to Claude Code, Codex CLI, OpenCode CLI, Gemini CLI, and native SwarmClaw subagents.
118
293
  - **Autonomy**: heartbeat loops, schedules, background jobs, task execution, supervisor recovery, mission control, and agent wakeups.
294
+ - **Orchestration**: durable structured execution with branching, repeat loops, parallel branches, explicit joins, restart-safe run state, and contextual launch from chats, chatrooms, tasks, missions, schedules, and API flows.
295
+ - **Structured Sessions**: reusable bounded runs with templates, facilitators, participants, hidden live rooms, chatroom `/breakout`, durable transcripts, outputs, and operator controls.
119
296
  - **Memory**: hybrid recall, graph traversal, journaling, durable documents, project-scoped context, automatic reflection memory, communication preferences, profile and boundary memory, significant events, and open follow-up loops.
120
297
  - **Wallets**: balances, transfers, signatures, EVM call/quote/swap flows, and approval-gated execution.
121
298
  - **Connectors**: Discord, Slack, Telegram, WhatsApp, Teams, Matrix, OpenClaw, and more.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swarmclawai/swarmclaw",
3
- "version": "1.1.0",
3
+ "version": "1.1.3",
4
4
  "description": "Self-hosted AI runtime for OpenClaw, delegation, autonomy, runtime skills, crypto wallets, and chat platform connectors.",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
@@ -44,6 +44,9 @@ export async function PUT(req: Request, { params }: { params: Promise<{ id: stri
44
44
  body.apiEndpoint,
45
45
  )
46
46
  }
47
+ if (body.provider !== undefined && body.provider !== 'ollama' && body.ollamaMode === undefined) {
48
+ agent.ollamaMode = null
49
+ }
47
50
  if (body.sandboxConfig !== undefined) {
48
51
  agent.sandboxConfig = normalizeAgentSandboxConfig(body.sandboxConfig)
49
52
  }
@@ -64,6 +67,9 @@ export async function PUT(req: Request, { params }: { params: Promise<{ id: stri
64
67
  role: target.role,
65
68
  provider: (typeof target.provider === 'string' && target.provider.trim() ? target.provider : agent.provider),
66
69
  model: typeof target.model === 'string' ? target.model : '',
70
+ ollamaMode: (typeof target.provider === 'string' ? target.provider : agent.provider) === 'ollama'
71
+ ? (target.ollamaMode === 'cloud' ? 'cloud' : 'local')
72
+ : null,
67
73
  credentialId: target.credentialId ?? null,
68
74
  fallbackCredentialIds: Array.isArray(target.fallbackCredentialIds) ? target.fallbackCredentialIds : [],
69
75
  apiEndpoint: normalizeProviderEndpoint(
@@ -79,6 +79,7 @@ export async function POST(req: Request) {
79
79
  systemPrompt: body.systemPrompt,
80
80
  provider: body.provider,
81
81
  model: body.model,
82
+ ollamaMode: body.provider === 'ollama' ? (body.ollamaMode || 'local') : null,
82
83
  credentialId: body.credentialId,
83
84
  fallbackCredentialIds: body.fallbackCredentialIds,
84
85
  apiEndpoint: normalizeProviderEndpoint(body.provider, body.apiEndpoint || null),
@@ -88,6 +89,7 @@ export async function POST(req: Request) {
88
89
  routingStrategy: body.routingStrategy,
89
90
  routingTargets: body.routingTargets?.map((target) => ({
90
91
  ...target,
92
+ ollamaMode: target.provider === 'ollama' ? (target.ollamaMode || 'local') : null,
91
93
  apiEndpoint: normalizeProviderEndpoint(target.provider, target.apiEndpoint || null),
92
94
  })),
93
95
  delegationEnabled: body.delegationEnabled ?? false,
@@ -25,7 +25,7 @@ function seedAgent(id: string, overrides: Record<string, unknown> = {}) {
25
25
  fallbackCredentialIds: [],
26
26
  apiEndpoint: null,
27
27
  gatewayProfileId: null,
28
- plugins: ['memory'],
28
+ extensions: ['memory'],
29
29
  createdAt: now,
30
30
  updatedAt: now,
31
31
  ...overrides,
@@ -84,7 +84,7 @@ test('POST /api/agents/[id]/thread reuses an existing thread for a disabled agen
84
84
  sessionType: 'human',
85
85
  agentId: 'agent-thread-disabled-existing',
86
86
  parentSessionId: null,
87
- plugins: ['memory'],
87
+ extensions: ['memory'],
88
88
  tools: ['memory'],
89
89
  heartbeatEnabled: false,
90
90
  heartbeatIntervalSec: null,
@@ -104,7 +104,7 @@ test('POST /api/agents/[id]/thread reuses an existing thread for a disabled agen
104
104
  connectorIdleTimeoutSec: null,
105
105
  connectorMaxAgeSec: null,
106
106
  mailbox: null,
107
- connectorContext: null,
107
+ connectorContext: undefined,
108
108
  lastAutoMemoryAt: null,
109
109
  lastHeartbeatText: null,
110
110
  lastHeartbeatSentAt: null,
@@ -37,7 +37,7 @@ export async function POST(req: Request) {
37
37
  reason: typeof body.reason === 'string' ? body.reason : null,
38
38
  engagedBy: typeof body.engagedBy === 'string' ? body.engagedBy : 'user',
39
39
  })
40
- stopDaemon({ source: `api/autonomy/estop:${level}` })
40
+ await stopDaemon({ source: `api/autonomy/estop:${level}` })
41
41
  const cancelled = level === 'all'
42
42
  ? cancelAllRuns('Cancelled because all estop is engaged.')
43
43
  : { cancelledQueued: 0, abortedRunning: 0 }
@@ -35,7 +35,7 @@ test('chatroom route prevents duplicate chained replies when an already-queued a
35
35
  name: 'Alpha',
36
36
  provider: 'chatroom-provider',
37
37
  model: 'room-model',
38
- plugins: [],
38
+ extensions: [],
39
39
  createdAt: now,
40
40
  updatedAt: now,
41
41
  },
@@ -44,7 +44,7 @@ test('chatroom route prevents duplicate chained replies when an already-queued a
44
44
  name: 'Beta',
45
45
  provider: 'chatroom-provider',
46
46
  model: 'room-model',
47
- plugins: [],
47
+ extensions: [],
48
48
  createdAt: now,
49
49
  updatedAt: now,
50
50
  },
@@ -183,7 +183,7 @@ test('chatroom route forwards tool activity and records one reply per participat
183
183
  name: 'Alpha',
184
184
  provider: 'chatroom-tool-provider',
185
185
  model: 'room-tool-model',
186
- plugins: ['shell'],
186
+ extensions: ['shell'],
187
187
  createdAt: now,
188
188
  updatedAt: now,
189
189
  },
@@ -192,7 +192,7 @@ test('chatroom route forwards tool activity and records one reply per participat
192
192
  name: 'Beta',
193
193
  provider: 'chatroom-tool-provider',
194
194
  model: 'room-tool-model',
195
- plugins: ['shell'],
195
+ extensions: ['shell'],
196
196
  createdAt: now,
197
197
  updatedAt: now,
198
198
  },
@@ -256,7 +256,7 @@ test('chatroom route forwards tool activity and records one reply per participat
256
256
  return { fullText: reply, finalResponse: reply }
257
257
  }
258
258
  if (agentId === 'beta') {
259
- const reply = 'Beta reviewed the plugin path and agrees with Alpha.'
259
+ const reply = 'Beta reviewed the extension path and agrees with Alpha.'
260
260
  opts.write('data: ' + JSON.stringify({ t: 'r', text: reply }) + '\\n')
261
261
  return { fullText: reply, finalResponse: reply }
262
262
  }
@@ -268,7 +268,7 @@ test('chatroom route forwards tool activity and records one reply per participat
268
268
  new Request('http://local/api/chatrooms/room_1/chat', {
269
269
  method: 'POST',
270
270
  headers: { 'content-type': 'application/json' },
271
- body: JSON.stringify({ senderId: 'user', text: 'Please inspect the workspace and plugin path.' }),
271
+ body: JSON.stringify({ senderId: 'user', text: 'Please inspect the workspace and extension path.' }),
272
272
  }),
273
273
  { params: Promise.resolve({ id: 'room_1' }) },
274
274
  )
@@ -10,7 +10,12 @@ export const dynamic = 'force-dynamic'
10
10
 
11
11
  export async function GET() {
12
12
  const chatrooms = loadChatrooms()
13
- return NextResponse.json(chatrooms)
13
+ const filtered: typeof chatrooms = {}
14
+ for (const [id, chatroom] of Object.entries(chatrooms)) {
15
+ if (chatroom.hidden === true || chatroom.archivedAt) continue
16
+ filtered[id] = chatroom
17
+ }
18
+ return NextResponse.json(filtered)
14
19
  }
15
20
 
16
21
  export async function POST(req: Request) {
@@ -48,7 +48,7 @@ test('chat route keeps long-lived user runs alive after stream disconnect and re
48
48
  name: 'Workbench Agent',
49
49
  provider: 'workbench-provider',
50
50
  model: 'wb-model',
51
- plugins: [],
51
+ extensions: [],
52
52
  createdAt: now,
53
53
  updatedAt: now,
54
54
  },
@@ -67,7 +67,7 @@ test('chat route keeps long-lived user runs alive after stream disconnect and re
67
67
  lastActiveAt: now,
68
68
  sessionType: 'human',
69
69
  agentId: 'agent_1',
70
- plugins: [],
70
+ extensions: [],
71
71
  },
72
72
  })
73
73
 
@@ -159,7 +159,7 @@ test('chat route heartbeat runs stay internal and do not persist terminal ack te
159
159
  name: 'Heartbeat Agent',
160
160
  provider: 'heartbeat-provider',
161
161
  model: 'hb-model',
162
- plugins: [],
162
+ extensions: [],
163
163
  heartbeatEnabled: true,
164
164
  createdAt: now,
165
165
  updatedAt: now,
@@ -180,7 +180,7 @@ test('chat route heartbeat runs stay internal and do not persist terminal ack te
180
180
  sessionType: 'human',
181
181
  agentId: 'agent_1',
182
182
  heartbeatEnabled: true,
183
- plugins: [],
183
+ extensions: [],
184
184
  },
185
185
  })
186
186
 
@@ -279,7 +279,7 @@ test('chat route queues a second user message behind the first run and completes
279
279
  name: 'Queue Agent',
280
280
  provider: 'queue-provider',
281
281
  model: 'queue-model',
282
- plugins: [],
282
+ extensions: [],
283
283
  createdAt: now,
284
284
  updatedAt: now,
285
285
  },
@@ -298,7 +298,7 @@ test('chat route queues a second user message behind the first run and completes
298
298
  lastActiveAt: now,
299
299
  sessionType: 'human',
300
300
  agentId: 'agent_1',
301
- plugins: [],
301
+ extensions: [],
302
302
  },
303
303
  })
304
304
 
@@ -356,7 +356,7 @@ test('chat route queues a second user message behind the first run and completes
356
356
  assistantReplies,
357
357
  runStatuses: runs.listRuns({ sessionId: 'sess_1' }).map((entry) => entry.status),
358
358
  }))
359
- `, { prefix: 'swarmclaw-chat-route-plugins-' })
359
+ `, { prefix: 'swarmclaw-chat-route-extensions-' })
360
360
 
361
361
  assert.match(output.firstRunMeta, /"position":0/)
362
362
  assert.match(output.secondRunMeta, /"position":1/)
@@ -367,7 +367,7 @@ test('chat route queues a second user message behind the first run and completes
367
367
  assert.deepEqual(output.runStatuses, ['completed', 'completed'])
368
368
  })
369
369
 
370
- test('chat route forwards plugin-path tool activity when a plugin-enabled run uses streamAgentChat', () => {
370
+ test('chat route forwards extension-path tool activity when an extension-enabled run uses streamAgentChat', () => {
371
371
  const output = runWithTempDataDir<{
372
372
  toolCalls: string[]
373
373
  toolResults: string[]
@@ -382,10 +382,10 @@ test('chat route forwards plugin-path tool activity when a plugin-enabled run us
382
382
  const route = routeMod.default || routeMod
383
383
  const stream = streamMod.default || streamMod
384
384
 
385
- providers.PROVIDERS['plugin-provider'] = {
386
- id: 'plugin-provider',
387
- name: 'Plugin Provider',
388
- models: ['plugin-model'],
385
+ providers.PROVIDERS['extension-provider'] = {
386
+ id: 'extension-provider',
387
+ name: 'Extension Provider',
388
+ models: ['extension-model'],
389
389
  requiresApiKey: false,
390
390
  requiresEndpoint: false,
391
391
  handler: { streamChat: async () => '' },
@@ -395,10 +395,10 @@ test('chat route forwards plugin-path tool activity when a plugin-enabled run us
395
395
  storage.saveAgents({
396
396
  agent_1: {
397
397
  id: 'agent_1',
398
- name: 'Plugin Agent',
399
- provider: 'plugin-provider',
400
- model: 'plugin-model',
401
- plugins: ['web'],
398
+ name: 'Extension Agent',
399
+ provider: 'extension-provider',
400
+ model: 'extension-model',
401
+ extensions: ['web'],
402
402
  createdAt: now,
403
403
  updatedAt: now,
404
404
  },
@@ -406,18 +406,18 @@ test('chat route forwards plugin-path tool activity when a plugin-enabled run us
406
406
  storage.saveSessions({
407
407
  sess_1: {
408
408
  id: 'sess_1',
409
- name: 'Plugin Session',
409
+ name: 'Extension Session',
410
410
  cwd: process.env.WORKSPACE_DIR,
411
411
  user: 'workbench',
412
- provider: 'plugin-provider',
413
- model: 'plugin-model',
412
+ provider: 'extension-provider',
413
+ model: 'extension-model',
414
414
  claudeSessionId: null,
415
415
  messages: [],
416
416
  createdAt: now,
417
417
  lastActiveAt: now,
418
418
  sessionType: 'human',
419
419
  agentId: 'agent_1',
420
- plugins: ['web'],
420
+ extensions: ['web'],
421
421
  },
422
422
  })
423
423
 
@@ -460,7 +460,7 @@ test('chat route forwards plugin-path tool activity when a plugin-enabled run us
460
460
  toolOutput: 'Fetched queue health summary',
461
461
  toolCallId: 'tool-1',
462
462
  }) + '\\n')
463
- const reply = 'Queue looks healthy and no plugin errors were observed.'
463
+ const reply = 'Queue looks healthy and no extension errors were observed.'
464
464
  opts.write('data: ' + JSON.stringify({ t: 'r', text: reply }) + '\\n')
465
465
  return { fullText: reply, finalResponse: reply }
466
466
  })
@@ -488,9 +488,9 @@ test('chat route forwards plugin-path tool activity when a plugin-enabled run us
488
488
  } finally {
489
489
  stream.setStreamAgentChatForTest(null)
490
490
  }
491
- `, { prefix: 'swarmclaw-chat-route-plugin-events-' })
491
+ `, { prefix: 'swarmclaw-chat-route-extension-events-' })
492
492
 
493
493
  assert.deepEqual(output.toolCalls, ['web'])
494
494
  assert.deepEqual(output.toolResults, ['Fetched queue health summary'])
495
- assert.deepEqual(output.assistantReplies, ['Queue looks healthy and no plugin errors were observed.'])
495
+ assert.deepEqual(output.assistantReplies, ['Queue looks healthy and no extension errors were observed.'])
496
496
  })
@@ -1,7 +1,6 @@
1
1
  import { NextResponse } from 'next/server'
2
- import { active, loadStoredItem, upsertStoredItem } from '@/lib/server/storage'
2
+ import { loadStoredItem, upsertStoredItem } from '@/lib/server/storage'
3
3
  import { notFound } from '@/lib/server/collection-helpers'
4
- import { getSessionRunState } from '@/lib/server/runtime/session-run-manager'
5
4
  import { materializeStreamingAssistantArtifacts } from '@/lib/chat/chat-streaming-state'
6
5
  import { appendSessionNote } from '@/lib/server/session-note'
7
6
  import type { Message, Session } from '@/types'
@@ -12,9 +11,9 @@ export async function GET(req: Request, { params }: { params: Promise<{ id: stri
12
11
  if (!session) return notFound()
13
12
  session.messages = Array.isArray(session.messages) ? session.messages : []
14
13
 
15
- const run = getSessionRunState(id)
16
- const hasLiveRun = active.has(id) || !!run.runningRunId
17
- if (!hasLiveRun && materializeStreamingAssistantArtifacts(session.messages)) {
14
+ const sessionClaimsActive = session.active === true
15
+ || (typeof session.currentRunId === 'string' && session.currentRunId.trim().length > 0)
16
+ if (!sessionClaimsActive && materializeStreamingAssistantArtifacts(session.messages)) {
18
17
  upsertStoredItem('sessions', id, session)
19
18
  }
20
19
 
@@ -64,6 +64,11 @@ export async function PUT(req: Request, { params }: { params: Promise<{ id: stri
64
64
  else if (agentIdUpdateProvided && linkedRoute?.model) session.model = linkedRoute.model
65
65
  else if (agentIdUpdateProvided && linkedAgent?.model !== undefined) session.model = linkedAgent.model
66
66
 
67
+ if (updates.ollamaMode !== undefined) session.ollamaMode = updates.ollamaMode
68
+ else if (updates.provider !== undefined && updates.provider !== 'ollama') session.ollamaMode = null
69
+ else if (agentIdUpdateProvided && linkedRoute) session.ollamaMode = linkedRoute.ollamaMode ?? null
70
+ else if (agentIdUpdateProvided && linkedAgent) session.ollamaMode = linkedAgent.ollamaMode ?? null
71
+
67
72
  if (updates.credentialId !== undefined) session.credentialId = updates.credentialId
68
73
  else if (agentIdUpdateProvided && linkedRoute) session.credentialId = linkedRoute.credentialId ?? null
69
74
  else if (agentIdUpdateProvided && linkedAgent) session.credentialId = linkedAgent.credentialId ?? null
@@ -0,0 +1,65 @@
1
+ import assert from 'node:assert/strict'
2
+ import test from 'node:test'
3
+
4
+ import { runWithTempDataDir } from '@/lib/server/test-utils/run-with-temp-data-dir'
5
+
6
+ test('chat messages route materializes stale streaming artifacts even if runtime memory is stale', () => {
7
+ const output = runWithTempDataDir<{
8
+ status: number
9
+ returnedStreaming: boolean | null
10
+ returnedText: string | null
11
+ persistedStreaming: boolean | null
12
+ persistedText: string | null
13
+ }>(`
14
+ const storageMod = await import('./src/lib/server/storage')
15
+ const routeMod = await import('./src/app/api/chats/[id]/messages/route')
16
+ const storage = storageMod.default || storageMod
17
+ const route = routeMod.default || routeMod
18
+
19
+ storage.upsertStoredItem('sessions', 'session-stale', {
20
+ id: 'session-stale',
21
+ name: 'Stale session',
22
+ provider: 'ollama',
23
+ model: 'test-model',
24
+ createdAt: 1,
25
+ updatedAt: 1,
26
+ active: false,
27
+ currentRunId: null,
28
+ messages: [
29
+ { role: 'user', text: 'hello', time: 1 },
30
+ {
31
+ role: 'assistant',
32
+ text: 'partial reply',
33
+ time: 2,
34
+ streaming: true,
35
+ toolEvents: [{ name: 'http_request', input: '{}', output: '{"ok":true}' }],
36
+ },
37
+ ],
38
+ })
39
+
40
+ storage.active.set('session-stale', { kill() {} })
41
+
42
+ const response = await route.GET(
43
+ new Request('http://local/api/chats/session-stale/messages'),
44
+ { params: Promise.resolve({ id: 'session-stale' }) },
45
+ )
46
+ const payload = await response.json()
47
+ const persisted = storage.loadSession('session-stale')
48
+ const returned = Array.isArray(payload) ? payload[payload.length - 1] : null
49
+ const saved = Array.isArray(persisted?.messages) ? persisted.messages[persisted.messages.length - 1] : null
50
+
51
+ console.log(JSON.stringify({
52
+ status: response.status,
53
+ returnedStreaming: returned?.streaming === true,
54
+ returnedText: returned?.text || null,
55
+ persistedStreaming: saved?.streaming === true,
56
+ persistedText: saved?.text || null,
57
+ }))
58
+ `, { prefix: 'swarmclaw-chat-messages-route-' })
59
+
60
+ assert.equal(output.status, 200)
61
+ assert.equal(output.returnedStreaming, false)
62
+ assert.equal(output.persistedStreaming, false)
63
+ assert.equal(output.returnedText, 'partial reply')
64
+ assert.equal(output.persistedText, 'partial reply')
65
+ })
@@ -131,6 +131,7 @@ export async function POST(req: Request) {
131
131
  user: body.user || 'user',
132
132
  provider: body.provider || agent?.provider || 'claude-cli',
133
133
  model: body.model || agent?.model || '',
134
+ ollamaMode: body.ollamaMode ?? agent?.ollamaMode ?? ((body.provider || agent?.provider) === 'ollama' ? 'local' : null),
134
135
  credentialId: body.credentialId || agent?.credentialId || null,
135
136
  fallbackCredentialIds: body.fallbackCredentialIds || agent?.fallbackCredentialIds || [],
136
137
  apiEndpoint: normalizeProviderEndpoint(
@@ -1,5 +1,5 @@
1
1
  import { NextResponse } from 'next/server'
2
- import { loadCredentials, saveCredentials } from '@/lib/server/storage'
2
+ import { loadCredentials, deleteCredential } from '@/lib/server/storage'
3
3
  import { notFound } from '@/lib/server/collection-helpers'
4
4
 
5
5
  export async function DELETE(_req: Request, { params }: { params: Promise<{ id: string }> }) {
@@ -8,8 +8,7 @@ export async function DELETE(_req: Request, { params }: { params: Promise<{ id:
8
8
  if (!creds[credId]) {
9
9
  return notFound()
10
10
  }
11
- delete creds[credId]
12
- saveCredentials(creds)
11
+ deleteCredential(credId)
13
12
  console.log(`[credentials] deleted ${credId}`)
14
13
  return new NextResponse('OK')
15
14
  }
@@ -19,7 +19,7 @@ export async function POST(req: Request) {
19
19
  return NextResponse.json({ ok: true, status: 'running' })
20
20
  } else if (action === 'stop') {
21
21
  const { stopDaemon } = await import('@/lib/server/runtime/daemon-state')
22
- stopDaemon({ source: 'api/daemon:post:stop', manualStop: true })
22
+ await stopDaemon({ source: 'api/daemon:post:stop', manualStop: true })
23
23
  notify('daemon')
24
24
  return NextResponse.json({ ok: true, status: 'stopped' })
25
25
  }