@swarmclawai/swarmclaw 0.7.2 → 0.7.4

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 (274) hide show
  1. package/README.md +116 -50
  2. package/bin/package-manager.js +157 -0
  3. package/bin/package-manager.test.js +90 -0
  4. package/bin/server-cmd.js +38 -7
  5. package/bin/swarmclaw.js +54 -4
  6. package/bin/update-cmd.js +48 -10
  7. package/bin/update-cmd.test.js +55 -0
  8. package/package.json +8 -3
  9. package/scripts/postinstall.mjs +26 -0
  10. package/src/app/api/agents/[id]/route.ts +43 -0
  11. package/src/app/api/agents/[id]/thread/route.ts +39 -8
  12. package/src/app/api/agents/route.ts +35 -2
  13. package/src/app/api/auth/route.ts +77 -8
  14. package/src/app/api/chatrooms/[id]/chat/route.ts +22 -6
  15. package/src/app/api/chatrooms/[id]/pins/route.ts +2 -1
  16. package/src/app/api/chatrooms/[id]/reactions/route.ts +2 -1
  17. package/src/app/api/chatrooms/[id]/route.ts +6 -0
  18. package/src/app/api/chats/[id]/browser/route.ts +5 -1
  19. package/src/app/api/chats/[id]/chat/route.ts +7 -3
  20. package/src/app/api/chats/[id]/messages/route.ts +19 -13
  21. package/src/app/api/chats/[id]/route.ts +30 -0
  22. package/src/app/api/chats/[id]/stop/route.ts +6 -1
  23. package/src/app/api/chats/heartbeat/route.ts +2 -1
  24. package/src/app/api/chats/route.ts +23 -1
  25. package/src/app/api/connectors/[id]/doctor/route.ts +26 -0
  26. package/src/app/api/connectors/doctor/route.ts +13 -0
  27. package/src/app/api/external-agents/[id]/heartbeat/route.ts +33 -0
  28. package/src/app/api/external-agents/[id]/route.ts +31 -0
  29. package/src/app/api/external-agents/register/route.ts +3 -0
  30. package/src/app/api/external-agents/route.ts +66 -0
  31. package/src/app/api/files/open/route.ts +16 -14
  32. package/src/app/api/gateways/[id]/health/route.ts +28 -0
  33. package/src/app/api/gateways/[id]/route.ts +79 -0
  34. package/src/app/api/gateways/route.ts +57 -0
  35. package/src/app/api/memory/maintenance/route.ts +11 -1
  36. package/src/app/api/openclaw/agent-files/route.ts +27 -4
  37. package/src/app/api/openclaw/gateway/route.ts +10 -7
  38. package/src/app/api/openclaw/skills/route.ts +12 -4
  39. package/src/app/api/plugins/dependencies/route.ts +24 -0
  40. package/src/app/api/plugins/install/route.ts +15 -92
  41. package/src/app/api/plugins/route.ts +3 -26
  42. package/src/app/api/plugins/settings/route.ts +17 -12
  43. package/src/app/api/plugins/ui/route.ts +1 -0
  44. package/src/app/api/providers/[id]/discover-models/route.ts +27 -0
  45. package/src/app/api/schedules/[id]/route.ts +38 -9
  46. package/src/app/api/schedules/route.ts +51 -28
  47. package/src/app/api/settings/route.ts +55 -17
  48. package/src/app/api/setup/doctor/route.ts +6 -4
  49. package/src/app/api/tasks/[id]/route.ts +16 -6
  50. package/src/app/api/tasks/bulk/route.ts +3 -3
  51. package/src/app/api/tasks/route.ts +9 -4
  52. package/src/app/api/webhooks/[id]/route.ts +8 -1
  53. package/src/app/page.tsx +135 -17
  54. package/src/cli/binary.test.js +142 -0
  55. package/src/cli/index.js +38 -11
  56. package/src/cli/index.test.js +195 -0
  57. package/src/cli/index.ts +21 -12
  58. package/src/cli/server-cmd.test.js +59 -0
  59. package/src/cli/spec.js +20 -2
  60. package/src/components/agents/agent-card.tsx +15 -12
  61. package/src/components/agents/agent-chat-list.tsx +101 -1
  62. package/src/components/agents/agent-list.tsx +46 -9
  63. package/src/components/agents/agent-sheet.tsx +456 -23
  64. package/src/components/agents/inspector-panel.tsx +110 -49
  65. package/src/components/agents/sandbox-env-panel.tsx +4 -1
  66. package/src/components/auth/access-key-gate.tsx +36 -97
  67. package/src/components/auth/setup-wizard.tsx +970 -275
  68. package/src/components/chat/chat-area.tsx +70 -27
  69. package/src/components/chat/chat-card.tsx +6 -21
  70. package/src/components/chat/chat-header.tsx +263 -366
  71. package/src/components/chat/chat-list.tsx +62 -26
  72. package/src/components/chat/checkpoint-timeline.tsx +1 -1
  73. package/src/components/chat/message-list.tsx +145 -19
  74. package/src/components/chatrooms/chatroom-input.tsx +96 -33
  75. package/src/components/chatrooms/chatroom-list.tsx +141 -72
  76. package/src/components/chatrooms/chatroom-message.tsx +7 -6
  77. package/src/components/chatrooms/chatroom-sheet.tsx +13 -1
  78. package/src/components/chatrooms/chatroom-tool-request-banner.tsx +5 -2
  79. package/src/components/chatrooms/chatroom-view.tsx +422 -209
  80. package/src/components/chatrooms/reaction-picker.tsx +38 -33
  81. package/src/components/connectors/connector-list.tsx +265 -127
  82. package/src/components/connectors/connector-sheet.tsx +217 -0
  83. package/src/components/gateways/gateway-sheet.tsx +567 -0
  84. package/src/components/home/home-view.tsx +128 -4
  85. package/src/components/input/chat-input.tsx +135 -86
  86. package/src/components/layout/app-layout.tsx +385 -194
  87. package/src/components/layout/mobile-header.tsx +26 -8
  88. package/src/components/memory/memory-browser.tsx +71 -6
  89. package/src/components/memory/memory-card.tsx +18 -0
  90. package/src/components/memory/memory-detail.tsx +58 -31
  91. package/src/components/memory/memory-sheet.tsx +32 -4
  92. package/src/components/plugins/plugin-list.tsx +15 -3
  93. package/src/components/plugins/plugin-sheet.tsx +118 -9
  94. package/src/components/projects/project-detail.tsx +189 -1
  95. package/src/components/providers/provider-list.tsx +158 -2
  96. package/src/components/providers/provider-sheet.tsx +81 -70
  97. package/src/components/shared/agent-picker-list.tsx +2 -2
  98. package/src/components/shared/bottom-sheet.tsx +31 -15
  99. package/src/components/shared/command-palette.tsx +111 -24
  100. package/src/components/shared/confirm-dialog.tsx +45 -30
  101. package/src/components/shared/model-combobox.tsx +90 -8
  102. package/src/components/shared/settings/plugin-manager.tsx +20 -4
  103. package/src/components/shared/settings/section-capability-policy.tsx +105 -0
  104. package/src/components/shared/settings/section-heartbeat.tsx +88 -6
  105. package/src/components/shared/settings/section-orchestrator.tsx +6 -3
  106. package/src/components/shared/settings/section-runtime-loop.tsx +5 -5
  107. package/src/components/shared/settings/section-secrets.tsx +6 -6
  108. package/src/components/shared/settings/section-user-preferences.tsx +1 -1
  109. package/src/components/shared/settings/section-voice.tsx +5 -1
  110. package/src/components/shared/settings/section-web-search.tsx +10 -2
  111. package/src/components/shared/settings/settings-page.tsx +248 -47
  112. package/src/components/tasks/approvals-panel.tsx +211 -18
  113. package/src/components/tasks/task-board.tsx +242 -46
  114. package/src/components/ui/dialog.tsx +2 -2
  115. package/src/components/usage/metrics-dashboard.tsx +74 -1
  116. package/src/components/wallets/wallet-approval-dialog.tsx +59 -54
  117. package/src/components/wallets/wallet-panel.tsx +17 -5
  118. package/src/components/webhooks/webhook-sheet.tsx +7 -7
  119. package/src/lib/auth.ts +17 -0
  120. package/src/lib/chat-streaming-state.test.ts +108 -0
  121. package/src/lib/chat-streaming-state.ts +108 -0
  122. package/src/lib/heartbeat-defaults.ts +48 -0
  123. package/src/lib/memory-presentation.ts +59 -0
  124. package/src/lib/openclaw-agent-id.test.ts +14 -0
  125. package/src/lib/openclaw-agent-id.ts +31 -0
  126. package/src/lib/provider-model-discovery-client.ts +29 -0
  127. package/src/lib/providers/index.ts +12 -5
  128. package/src/lib/runtime-loop.ts +105 -3
  129. package/src/lib/safe-storage.ts +6 -1
  130. package/src/lib/server/agent-assignment.test.ts +112 -0
  131. package/src/lib/server/agent-assignment.ts +169 -0
  132. package/src/lib/server/agent-runtime-config.test.ts +141 -0
  133. package/src/lib/server/agent-runtime-config.ts +277 -0
  134. package/src/lib/server/approval-connector-notify.test.ts +253 -0
  135. package/src/lib/server/approvals-auto-approve.test.ts +264 -0
  136. package/src/lib/server/approvals.ts +483 -75
  137. package/src/lib/server/autonomy-runtime.test.ts +341 -0
  138. package/src/lib/server/browser-state.test.ts +118 -0
  139. package/src/lib/server/browser-state.ts +123 -0
  140. package/src/lib/server/build-llm.test.ts +44 -0
  141. package/src/lib/server/build-llm.ts +11 -4
  142. package/src/lib/server/builtin-plugins.ts +34 -0
  143. package/src/lib/server/chat-execution-heartbeat.test.ts +40 -0
  144. package/src/lib/server/chat-execution-tool-events.test.ts +219 -0
  145. package/src/lib/server/chat-execution.ts +402 -125
  146. package/src/lib/server/chatroom-health.test.ts +26 -0
  147. package/src/lib/server/chatroom-health.ts +2 -3
  148. package/src/lib/server/chatroom-helpers.test.ts +74 -2
  149. package/src/lib/server/chatroom-helpers.ts +144 -11
  150. package/src/lib/server/chatroom-session-persistence.test.ts +87 -0
  151. package/src/lib/server/connectors/discord.ts +175 -11
  152. package/src/lib/server/connectors/doctor.test.ts +80 -0
  153. package/src/lib/server/connectors/doctor.ts +116 -0
  154. package/src/lib/server/connectors/manager.ts +994 -130
  155. package/src/lib/server/connectors/policy.test.ts +222 -0
  156. package/src/lib/server/connectors/policy.ts +452 -0
  157. package/src/lib/server/connectors/slack.ts +189 -10
  158. package/src/lib/server/connectors/telegram.ts +65 -15
  159. package/src/lib/server/connectors/thread-context.test.ts +44 -0
  160. package/src/lib/server/connectors/thread-context.ts +72 -0
  161. package/src/lib/server/connectors/types.ts +41 -11
  162. package/src/lib/server/daemon-state.ts +62 -3
  163. package/src/lib/server/data-dir.ts +13 -0
  164. package/src/lib/server/delegation-jobs.test.ts +140 -0
  165. package/src/lib/server/delegation-jobs.ts +248 -0
  166. package/src/lib/server/document-utils.test.ts +47 -0
  167. package/src/lib/server/document-utils.ts +397 -0
  168. package/src/lib/server/eval/agent-regression.test.ts +47 -0
  169. package/src/lib/server/eval/agent-regression.ts +1742 -0
  170. package/src/lib/server/eval/runner.ts +11 -1
  171. package/src/lib/server/eval/store.ts +2 -1
  172. package/src/lib/server/heartbeat-service.ts +23 -43
  173. package/src/lib/server/heartbeat-source.test.ts +22 -0
  174. package/src/lib/server/heartbeat-source.ts +7 -0
  175. package/src/lib/server/identity-continuity.test.ts +77 -0
  176. package/src/lib/server/identity-continuity.ts +127 -0
  177. package/src/lib/server/mailbox-utils.ts +347 -0
  178. package/src/lib/server/main-agent-loop.ts +31 -964
  179. package/src/lib/server/memory-db.ts +4 -6
  180. package/src/lib/server/memory-tiers.ts +40 -0
  181. package/src/lib/server/openclaw-agent-resolver.test.ts +70 -0
  182. package/src/lib/server/openclaw-agent-resolver.ts +128 -0
  183. package/src/lib/server/openclaw-exec-config.ts +6 -5
  184. package/src/lib/server/openclaw-gateway.ts +123 -36
  185. package/src/lib/server/openclaw-skills-normalize.test.ts +56 -0
  186. package/src/lib/server/openclaw-skills-normalize.ts +136 -0
  187. package/src/lib/server/openclaw-sync.ts +3 -2
  188. package/src/lib/server/orchestrator-lg.ts +18 -8
  189. package/src/lib/server/orchestrator.ts +5 -4
  190. package/src/lib/server/playwright-proxy.mjs +27 -3
  191. package/src/lib/server/plugins.test.ts +215 -0
  192. package/src/lib/server/plugins.ts +832 -69
  193. package/src/lib/server/provider-health.ts +33 -3
  194. package/src/lib/server/provider-model-discovery.ts +481 -0
  195. package/src/lib/server/queue.ts +4 -21
  196. package/src/lib/server/runtime-settings.test.ts +119 -0
  197. package/src/lib/server/runtime-settings.ts +12 -92
  198. package/src/lib/server/schedule-normalization.ts +187 -0
  199. package/src/lib/server/scheduler.ts +2 -0
  200. package/src/lib/server/session-archive-memory.test.ts +85 -0
  201. package/src/lib/server/session-archive-memory.ts +230 -0
  202. package/src/lib/server/session-mailbox.ts +8 -18
  203. package/src/lib/server/session-reset-policy.test.ts +99 -0
  204. package/src/lib/server/session-reset-policy.ts +311 -0
  205. package/src/lib/server/session-run-manager.ts +33 -80
  206. package/src/lib/server/session-tools/autonomy-tools.test.ts +128 -0
  207. package/src/lib/server/session-tools/calendar.ts +2 -12
  208. package/src/lib/server/session-tools/connector.ts +109 -8
  209. package/src/lib/server/session-tools/context.ts +14 -2
  210. package/src/lib/server/session-tools/crawl.ts +447 -0
  211. package/src/lib/server/session-tools/crud.ts +96 -34
  212. package/src/lib/server/session-tools/delegate-fallback.test.ts +219 -0
  213. package/src/lib/server/session-tools/delegate.ts +406 -20
  214. package/src/lib/server/session-tools/discovery-approvals.test.ts +170 -0
  215. package/src/lib/server/session-tools/discovery.ts +40 -12
  216. package/src/lib/server/session-tools/document.ts +283 -0
  217. package/src/lib/server/session-tools/email.ts +1 -3
  218. package/src/lib/server/session-tools/extract.ts +137 -0
  219. package/src/lib/server/session-tools/file-normalize.test.ts +98 -0
  220. package/src/lib/server/session-tools/file-send.test.ts +84 -1
  221. package/src/lib/server/session-tools/file.ts +243 -24
  222. package/src/lib/server/session-tools/http.ts +9 -3
  223. package/src/lib/server/session-tools/human-loop.ts +227 -0
  224. package/src/lib/server/session-tools/image-gen.ts +1 -3
  225. package/src/lib/server/session-tools/index.ts +87 -2
  226. package/src/lib/server/session-tools/mailbox.ts +276 -0
  227. package/src/lib/server/session-tools/manage-schedules.test.ts +137 -0
  228. package/src/lib/server/session-tools/memory.ts +35 -3
  229. package/src/lib/server/session-tools/monitor.ts +162 -12
  230. package/src/lib/server/session-tools/normalize-tool-args.ts +17 -14
  231. package/src/lib/server/session-tools/openclaw-nodes.test.ts +111 -0
  232. package/src/lib/server/session-tools/openclaw-nodes.ts +86 -20
  233. package/src/lib/server/session-tools/platform-normalize.test.ts +142 -0
  234. package/src/lib/server/session-tools/platform.ts +142 -4
  235. package/src/lib/server/session-tools/plugin-creator.ts +95 -25
  236. package/src/lib/server/session-tools/primitive-tools.test.ts +257 -0
  237. package/src/lib/server/session-tools/replicate.ts +1 -3
  238. package/src/lib/server/session-tools/sandbox.ts +51 -92
  239. package/src/lib/server/session-tools/schedule.ts +20 -10
  240. package/src/lib/server/session-tools/session-info.ts +58 -4
  241. package/src/lib/server/session-tools/session-tools-wiring.test.ts +54 -17
  242. package/src/lib/server/session-tools/shell.ts +2 -2
  243. package/src/lib/server/session-tools/subagent.ts +195 -27
  244. package/src/lib/server/session-tools/table.ts +587 -0
  245. package/src/lib/server/session-tools/wallet.ts +13 -10
  246. package/src/lib/server/session-tools/web-browser-config.test.ts +39 -0
  247. package/src/lib/server/session-tools/web.ts +947 -108
  248. package/src/lib/server/storage.ts +255 -10
  249. package/src/lib/server/stream-agent-chat.test.ts +61 -0
  250. package/src/lib/server/stream-agent-chat.ts +185 -25
  251. package/src/lib/server/structured-extract.test.ts +72 -0
  252. package/src/lib/server/structured-extract.ts +373 -0
  253. package/src/lib/server/task-mention.test.ts +16 -2
  254. package/src/lib/server/task-mention.ts +61 -11
  255. package/src/lib/server/tool-aliases.ts +80 -12
  256. package/src/lib/server/tool-capability-policy.ts +7 -1
  257. package/src/lib/server/tool-retry.ts +2 -0
  258. package/src/lib/server/watch-jobs.test.ts +173 -0
  259. package/src/lib/server/watch-jobs.ts +532 -0
  260. package/src/lib/server/ws-hub.ts +5 -3
  261. package/src/lib/setup-defaults.ts +352 -11
  262. package/src/lib/tool-definitions.ts +3 -4
  263. package/src/lib/validation/schemas.test.ts +26 -0
  264. package/src/lib/validation/schemas.ts +62 -1
  265. package/src/lib/ws-client.ts +14 -12
  266. package/src/proxy.ts +5 -5
  267. package/src/stores/use-app-store.ts +43 -7
  268. package/src/stores/use-chat-store.ts +31 -2
  269. package/src/stores/use-chatroom-store.ts +153 -26
  270. package/src/types/index.ts +470 -44
  271. package/src/app/api/chats/[id]/main-loop/route.ts +0 -94
  272. package/src/components/chat/new-chat-sheet.tsx +0 -253
  273. package/src/lib/server/main-session.ts +0 -17
  274. package/src/lib/server/session-run-manager.test.ts +0 -26
package/bin/server-cmd.js CHANGED
@@ -1,10 +1,16 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict'
3
+ /* eslint-disable @typescript-eslint/no-require-imports */
3
4
 
4
5
  const fs = require('node:fs')
5
6
  const path = require('node:path')
6
- const { spawn, execSync } = require('node:child_process')
7
+ const { spawn, execFileSync } = require('node:child_process')
7
8
  const os = require('node:os')
9
+ const {
10
+ LOCKFILE_NAMES,
11
+ detectPackageManager,
12
+ getInstallCommand,
13
+ } = require('./package-manager.js')
8
14
 
9
15
  // ---------------------------------------------------------------------------
10
16
  // Paths
@@ -26,7 +32,7 @@ const BUILD_COPY_ENTRIES = [
26
32
  'tsconfig.json',
27
33
  'postcss.config.mjs',
28
34
  'package.json',
29
- 'package-lock.json',
35
+ ...LOCKFILE_NAMES,
30
36
  ]
31
37
 
32
38
  // ---------------------------------------------------------------------------
@@ -73,13 +79,26 @@ function symlinkPath(src, dest) {
73
79
  fs.symlinkSync(src, dest)
74
80
  }
75
81
 
82
+ function readBuiltInfo() {
83
+ if (!fs.existsSync(BUILT_MARKER)) return null
84
+ try {
85
+ const raw = JSON.parse(fs.readFileSync(BUILT_MARKER, 'utf8'))
86
+ return raw && typeof raw === 'object' ? raw : null
87
+ } catch {
88
+ return null
89
+ }
90
+ }
91
+
76
92
  // ---------------------------------------------------------------------------
77
93
  // Build
78
94
  // ---------------------------------------------------------------------------
79
95
 
80
96
  function needsBuild(forceBuild) {
81
97
  if (forceBuild) return true
82
- if (!fs.existsSync(BUILT_MARKER)) return true
98
+ const info = readBuiltInfo()
99
+ if (!info) return true
100
+ if (info.version !== getVersion()) return true
101
+ if (!findStandaloneServer()) return true
83
102
  return false
84
103
  }
85
104
 
@@ -110,15 +129,18 @@ function runBuild() {
110
129
  symlinkPath(nmSrc, nmDest)
111
130
  } else {
112
131
  // If node_modules doesn't exist at PKG_ROOT, install
113
- log('Installing dependencies...')
114
- execSync('npm install', { cwd: SWARMCLAW_HOME, stdio: 'inherit' })
132
+ const packageManager = detectPackageManager(SWARMCLAW_HOME, process.env)
133
+ const install = getInstallCommand(packageManager)
134
+ log(`Installing dependencies with ${packageManager}...`)
135
+ execFileSync(install.command, install.args, { cwd: SWARMCLAW_HOME, stdio: 'inherit' })
115
136
  }
116
137
 
117
138
  // Run Next.js build
118
139
  log('Building Next.js application (this may take a minute)...')
119
140
  // Use webpack for production build reliability in packaged/fresh-install
120
141
  // environments (Turbopack has intermittently failed during prerender).
121
- execSync('npx next build --webpack', {
142
+ const nextCli = path.join(SWARMCLAW_HOME, 'node_modules', 'next', 'dist', 'bin', 'next')
143
+ execFileSync(process.execPath, [nextCli, 'build', '--webpack'], {
122
144
  cwd: SWARMCLAW_HOME,
123
145
  stdio: 'inherit',
124
146
  env: {
@@ -373,4 +395,13 @@ function main() {
373
395
  startServer({ port, wsPort, host, detach })
374
396
  }
375
397
 
376
- main()
398
+ if (require.main === module) {
399
+ main()
400
+ }
401
+
402
+ module.exports = {
403
+ getVersion,
404
+ main,
405
+ needsBuild,
406
+ readBuiltInfo,
407
+ }
package/bin/swarmclaw.js CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict'
3
+ /* eslint-disable @typescript-eslint/no-require-imports */
3
4
 
4
5
  const path = require('node:path')
5
6
  const { spawnSync } = require('node:child_process')
@@ -34,12 +35,45 @@ function shouldUseLegacyTsCli(argv) {
34
35
  return actions.has(action)
35
36
  }
36
37
 
38
+ function supportsStripTypes() {
39
+ return process.allowedNodeEnvironmentFlags.has('--experimental-strip-types')
40
+ }
41
+
42
+ function hasTsxRuntime() {
43
+ try {
44
+ require.resolve('tsx/package.json')
45
+ return true
46
+ } catch {
47
+ return false
48
+ }
49
+ }
50
+
51
+ function buildLegacyTsCliArgs(cliPath, argv, options = {}) {
52
+ const stripTypesSupported = options.supportsStripTypes ?? supportsStripTypes()
53
+ if (stripTypesSupported) {
54
+ return ['--no-warnings', '--experimental-strip-types', cliPath, ...argv]
55
+ }
56
+
57
+ const tsxAvailable = options.hasTsxRuntime ?? hasTsxRuntime()
58
+ if (tsxAvailable) {
59
+ return ['--no-warnings', '--import', 'tsx', cliPath, ...argv]
60
+ }
61
+
62
+ return null
63
+ }
64
+
37
65
  function runLegacyTsCli(argv) {
38
66
  const cliPath = path.join(__dirname, '..', 'src', 'cli', 'index.ts')
67
+ const args = buildLegacyTsCliArgs(cliPath, argv)
68
+ const env = normalizeLegacyCliEnv(process.env)
69
+ if (!args) {
70
+ process.stderr.write('Legacy CLI commands require Node 22.6+ or an available local tsx runtime.\n')
71
+ return 1
72
+ }
39
73
  const child = spawnSync(
40
74
  process.execPath,
41
- ['--no-warnings', '--experimental-strip-types', cliPath, ...argv],
42
- { stdio: 'inherit' },
75
+ args,
76
+ { stdio: 'inherit', env },
43
77
  )
44
78
 
45
79
  if (child.error) {
@@ -50,6 +84,18 @@ function runLegacyTsCli(argv) {
50
84
  return 1
51
85
  }
52
86
 
87
+ function normalizeLegacyCliEnv(env) {
88
+ const nextEnv = { ...env }
89
+ if (!nextEnv.SWARMCLAW_URL && nextEnv.SWARMCLAW_BASE_URL) {
90
+ nextEnv.SWARMCLAW_URL = nextEnv.SWARMCLAW_BASE_URL
91
+ }
92
+ if (!nextEnv.SWARMCLAW_ACCESS_KEY) {
93
+ const key = nextEnv.SWARMCLAW_API_KEY || nextEnv.SC_ACCESS_KEY || ''
94
+ if (key) nextEnv.SWARMCLAW_ACCESS_KEY = key
95
+ }
96
+ return nextEnv
97
+ }
98
+
53
99
  async function runMappedCli(argv) {
54
100
  const cliPath = path.join(__dirname, '..', 'src', 'cli', 'index.js')
55
101
  const cliModule = await import(cliPath)
@@ -66,11 +112,11 @@ async function main() {
66
112
 
67
113
  // Route 'server' and 'update' subcommands to CJS scripts (no TS dependency).
68
114
  if (top === 'server') {
69
- require('./server-cmd.js')
115
+ require('./server-cmd.js').main()
70
116
  return
71
117
  }
72
118
  if (top === 'update') {
73
- require('./update-cmd.js')
119
+ require('./update-cmd.js').main()
74
120
  return
75
121
  }
76
122
 
@@ -86,6 +132,10 @@ if (require.main === module) {
86
132
  }
87
133
 
88
134
  module.exports = {
135
+ buildLegacyTsCliArgs,
136
+ hasTsxRuntime,
89
137
  TS_CLI_ACTIONS,
138
+ normalizeLegacyCliEnv,
139
+ supportsStripTypes,
90
140
  shouldUseLegacyTsCli,
91
141
  }
package/bin/update-cmd.js CHANGED
@@ -1,11 +1,20 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict'
3
+ /* eslint-disable @typescript-eslint/no-require-imports */
3
4
 
4
- const { execSync } = require('node:child_process')
5
+ const { execSync, execFileSync } = require('node:child_process')
5
6
  const path = require('node:path')
7
+ const {
8
+ dependenciesChanged,
9
+ detectPackageManager,
10
+ getGlobalUpdateSpec,
11
+ getInstallCommand,
12
+ } = require('./package-manager.js')
6
13
 
7
14
  const PKG_ROOT = path.resolve(__dirname, '..')
15
+ const PACKAGE_NAME = '@swarmclawai/swarmclaw'
8
16
  const RELEASE_TAG_RE = /^v\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/
17
+ const PACKAGE_MANAGER = detectPackageManager(PKG_ROOT)
9
18
 
10
19
  function run(cmd) {
11
20
  return execSync(cmd, { encoding: 'utf-8', cwd: PKG_ROOT, timeout: 60_000 }).trim()
@@ -27,15 +36,37 @@ function getLatestStableTag() {
27
36
  return tags.find((t) => RELEASE_TAG_RE.test(t)) || null
28
37
  }
29
38
 
39
+ function runRegistrySelfUpdate(
40
+ packageManager = PACKAGE_MANAGER,
41
+ execImpl = execFileSync,
42
+ logger = { log, logError },
43
+ ) {
44
+ const update = getGlobalUpdateSpec(packageManager, PACKAGE_NAME)
45
+ logger.log(`No git checkout detected. Updating the global ${PACKAGE_NAME} install via ${packageManager}...`)
46
+ try {
47
+ execImpl(update.command, update.args, {
48
+ cwd: PKG_ROOT,
49
+ stdio: 'inherit',
50
+ timeout: 120_000,
51
+ })
52
+ logger.log(`Global update complete via ${packageManager}.`)
53
+ logger.log('Restart the server to apply changes: swarmclaw server stop && swarmclaw server start')
54
+ return 0
55
+ } catch (err) {
56
+ logger.logError(`Registry update failed: ${err.message}`)
57
+ logger.logError(`Retry manually with: ${update.display}`)
58
+ return 1
59
+ }
60
+ }
61
+
30
62
  function main() {
31
63
  const args = process.argv.slice(3)
32
64
  if (args.includes('-h') || args.includes('--help')) {
33
65
  console.log(`
34
66
  Usage: swarmclaw update
35
67
 
36
- Pull the latest SwarmClaw release via git.
37
- Prefers stable release tags (v*); falls back to origin/main.
38
- Runs npm install if package files changed.
68
+ If running from a git checkout, pull the latest SwarmClaw release tag.
69
+ If running from a registry install, update the global package with ${PACKAGE_MANAGER}.
39
70
  `.trim())
40
71
  process.exit(0)
41
72
  }
@@ -44,8 +75,7 @@ Runs npm install if package files changed.
44
75
  try {
45
76
  run('git rev-parse --git-dir')
46
77
  } catch {
47
- logError('Not a git repository. Cannot update.')
48
- process.exit(1)
78
+ process.exit(runRegistrySelfUpdate(PACKAGE_MANAGER))
49
79
  }
50
80
 
51
81
  const beforeRef = run('git rev-parse HEAD')
@@ -105,9 +135,10 @@ Runs npm install if package files changed.
105
135
  // Install deps if package files changed
106
136
  try {
107
137
  const diff = run(`git diff --name-only ${beforeSha}..HEAD`)
108
- if (diff.includes('package-lock.json') || diff.includes('package.json')) {
109
- log('Package files changed running npm install...')
110
- execSync('npm install --omit=dev', { cwd: PKG_ROOT, stdio: 'inherit', timeout: 120_000 })
138
+ if (dependenciesChanged(diff)) {
139
+ const install = getInstallCommand(PACKAGE_MANAGER, true)
140
+ log(`Package files changed running ${PACKAGE_MANAGER} install...`)
141
+ execFileSync(install.command, install.args, { cwd: PKG_ROOT, stdio: 'inherit', timeout: 120_000 })
111
142
  }
112
143
  } catch {
113
144
  // If diff fails, skip install check
@@ -117,4 +148,11 @@ Runs npm install if package files changed.
117
148
  log('Restart the server to apply changes: swarmclaw server stop && swarmclaw server start')
118
149
  }
119
150
 
120
- main()
151
+ if (require.main === module) {
152
+ main()
153
+ }
154
+
155
+ module.exports = {
156
+ main,
157
+ runRegistrySelfUpdate,
158
+ }
@@ -0,0 +1,55 @@
1
+ 'use strict'
2
+ /* eslint-disable @typescript-eslint/no-require-imports */
3
+
4
+ const test = require('node:test')
5
+ const assert = require('node:assert/strict')
6
+
7
+ const { runRegistrySelfUpdate } = require('./update-cmd.js')
8
+
9
+ test('runRegistrySelfUpdate executes the manager-specific global update command', () => {
10
+ const messages = []
11
+ let captured = null
12
+
13
+ const exitCode = runRegistrySelfUpdate(
14
+ 'pnpm',
15
+ (command, args, options) => {
16
+ captured = { command, args, options }
17
+ },
18
+ {
19
+ log: (message) => messages.push(`log:${message}`),
20
+ logError: (message) => messages.push(`err:${message}`),
21
+ },
22
+ )
23
+
24
+ assert.equal(exitCode, 0)
25
+ assert.deepEqual(captured, {
26
+ command: 'pnpm',
27
+ args: ['add', '-g', '@swarmclawai/swarmclaw@latest'],
28
+ options: {
29
+ cwd: process.cwd(),
30
+ stdio: 'inherit',
31
+ timeout: 120_000,
32
+ },
33
+ })
34
+ assert.match(messages.join('\n'), /updating the global @swarmclawai\/swarmclaw install via pnpm/i)
35
+ assert.match(messages.join('\n'), /global update complete via pnpm/i)
36
+ })
37
+
38
+ test('runRegistrySelfUpdate reports a manual retry command when the registry update fails', () => {
39
+ const messages = []
40
+
41
+ const exitCode = runRegistrySelfUpdate(
42
+ 'bun',
43
+ () => {
44
+ throw new Error('spawn bun ENOENT')
45
+ },
46
+ {
47
+ log: (message) => messages.push(`log:${message}`),
48
+ logError: (message) => messages.push(`err:${message}`),
49
+ },
50
+ )
51
+
52
+ assert.equal(exitCode, 1)
53
+ assert.match(messages.join('\n'), /registry update failed: spawn bun ENOENT/i)
54
+ assert.match(messages.join('\n'), /retry manually with: bun add -g @swarmclawai\/swarmclaw@latest/i)
55
+ })
package/package.json CHANGED
@@ -1,8 +1,12 @@
1
1
  {
2
2
  "name": "@swarmclawai/swarmclaw",
3
- "version": "0.7.2",
3
+ "version": "0.7.4",
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
+ "publishConfig": {
7
+ "access": "public",
8
+ "registry": "https://registry.npmjs.org/"
9
+ },
6
10
  "repository": {
7
11
  "type": "git",
8
12
  "url": "https://github.com/swarmclawai/swarmclaw.git"
@@ -46,13 +50,14 @@
46
50
  "start": "next start",
47
51
  "start:standalone": "node .next/standalone/server.js",
48
52
  "benchmark:autonomy": "node ./scripts/benchmark-autonomy-harness.mjs",
53
+ "benchmark:agent-regression": "node --import tsx ./scripts/run-agent-regression-suite.ts",
49
54
  "lint": "eslint",
50
55
  "lint:fix": "eslint --fix",
51
56
  "lint:baseline": "node ./scripts/lint-baseline.mjs check",
52
57
  "lint:baseline:update": "node ./scripts/lint-baseline.mjs update",
53
58
  "cli": "node ./bin/swarmclaw.js",
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 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",
59
+ "test:cli": "node --test src/cli/*.test.js bin/*.test.js",
60
+ "test:openclaw": "tsx --test src/lib/openclaw-agent-id.test.ts src/lib/openclaw-endpoint.test.ts src/lib/server/agent-runtime-config.test.ts src/lib/server/build-llm.test.ts src/lib/server/connectors/connector-routing.test.ts src/lib/server/connectors/openclaw.test.ts src/lib/server/gateway/protocol.test.ts src/lib/server/llm-response-cache.test.ts src/lib/server/mcp-conformance.test.ts src/lib/server/openclaw-agent-resolver.test.ts src/lib/server/openclaw-skills-normalize.test.ts src/lib/server/session-tools/openclaw-nodes.test.ts src/lib/server/task-quality-gate.test.ts src/lib/server/task-validation.test.ts src/lib/server/tool-capability-policy.test.ts",
56
61
  "test:mcp:conformance": "node --import tsx ./scripts/mcp-conformance-check.ts",
57
62
  "postinstall": "node ./scripts/postinstall.mjs"
58
63
  },
@@ -1,6 +1,32 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ import { writeFileSync } from 'node:fs'
3
4
  import { spawnSync } from 'node:child_process'
5
+ const INSTALL_METADATA_FILE = '.swarmclaw-install.json'
6
+
7
+ function detectPackageManagerFromUserAgent(userAgent) {
8
+ const normalized = String(userAgent || '').toLowerCase()
9
+ if (normalized.startsWith('pnpm/')) return 'pnpm'
10
+ if (normalized.startsWith('yarn/')) return 'yarn'
11
+ if (normalized.startsWith('bun/')) return 'bun'
12
+ if (normalized.startsWith('npm/')) return 'npm'
13
+ return null
14
+ }
15
+
16
+ const installedWith = detectPackageManagerFromUserAgent(process.env.npm_config_user_agent) || 'npm'
17
+
18
+ try {
19
+ writeFileSync(
20
+ new URL(`../${INSTALL_METADATA_FILE}`, import.meta.url),
21
+ JSON.stringify({
22
+ packageManager: installedWith,
23
+ installedAt: new Date().toISOString(),
24
+ }, null, 2),
25
+ 'utf8',
26
+ )
27
+ } catch {
28
+ // Ignore metadata write failures for install resilience.
29
+ }
4
30
 
5
31
  const result = spawnSync('npm', ['rebuild', 'better-sqlite3', '--silent'], {
6
32
  stdio: 'ignore',
@@ -11,17 +11,60 @@ export async function PUT(req: Request, { params }: { params: Promise<{ id: stri
11
11
  const body = await req.json()
12
12
  const result = mutateItem(ops, id, (agent) => {
13
13
  Object.assign(agent, body, { updatedAt: Date.now() })
14
+ if (body.platformAssignScope === 'all' || body.platformAssignScope === 'self') {
15
+ agent.platformAssignScope = body.platformAssignScope
16
+ agent.isOrchestrator = body.platformAssignScope === 'all'
17
+ } else if (agent.platformAssignScope === 'all' || agent.platformAssignScope === 'self') {
18
+ agent.isOrchestrator = agent.platformAssignScope === 'all'
19
+ }
14
20
  if (body.apiEndpoint !== undefined) {
15
21
  agent.apiEndpoint = normalizeProviderEndpoint(
16
22
  body.provider || agent.provider,
17
23
  body.apiEndpoint,
18
24
  )
19
25
  }
26
+ if (body.routingTargets !== undefined && Array.isArray(body.routingTargets)) {
27
+ agent.routingTargets = body.routingTargets.map((target: Record<string, unknown>, index: number) => ({
28
+ id: typeof target.id === 'string' && target.id.trim() ? target.id.trim() : `route-${index + 1}`,
29
+ label: typeof target.label === 'string' ? target.label : undefined,
30
+ role: target.role,
31
+ provider: (typeof target.provider === 'string' && target.provider.trim() ? target.provider : agent.provider),
32
+ model: typeof target.model === 'string' ? target.model : '',
33
+ credentialId: target.credentialId ?? null,
34
+ fallbackCredentialIds: Array.isArray(target.fallbackCredentialIds) ? target.fallbackCredentialIds : [],
35
+ apiEndpoint: normalizeProviderEndpoint(
36
+ typeof target.provider === 'string' ? target.provider : agent.provider,
37
+ typeof target.apiEndpoint === 'string' ? target.apiEndpoint : null,
38
+ ),
39
+ gatewayProfileId: target.gatewayProfileId ?? null,
40
+ priority: typeof target.priority === 'number' ? target.priority : index + 1,
41
+ }))
42
+ }
43
+ delete (agent as Record<string, unknown>).isOrchestrator
44
+ agent.isOrchestrator = agent.platformAssignScope === 'all'
20
45
  delete (agent as Record<string, unknown>).id
21
46
  agent.id = id
22
47
  return agent
23
48
  })
24
49
  if (!result) return notFound()
50
+
51
+ if (result.threadSessionId) {
52
+ const sessions = loadSessions()
53
+ const shortcut = sessions[result.threadSessionId]
54
+ if (shortcut) {
55
+ let changed = false
56
+ if (shortcut.name !== result.name) {
57
+ shortcut.name = result.name
58
+ changed = true
59
+ }
60
+ if (shortcut.shortcutForAgentId !== id) {
61
+ shortcut.shortcutForAgentId = id
62
+ changed = true
63
+ }
64
+ if (changed) saveSessions(sessions)
65
+ }
66
+ }
67
+
25
68
  logActivity({ entityType: 'agent', entityId: id, action: 'updated', actor: 'user', summary: `Agent updated: "${result.name}"` })
26
69
  return NextResponse.json(result)
27
70
  }
@@ -2,6 +2,7 @@ import { NextResponse } from 'next/server'
2
2
  import { genId } from '@/lib/id'
3
3
  import { loadAgents, saveAgents, loadSessions, saveSessions } from '@/lib/server/storage'
4
4
  import { WORKSPACE_DIR } from '@/lib/server/data-dir'
5
+ import { applyResolvedRoute, resolvePrimaryAgentRoute } from '@/lib/server/agent-runtime-config'
5
6
 
6
7
  export async function POST(req: Request, { params }: { params: Promise<{ id: string }> }) {
7
8
  const { id: agentId } = await params
@@ -15,28 +16,57 @@ export async function POST(req: Request, { params }: { params: Promise<{ id: str
15
16
  const user = body.user || 'default'
16
17
  const sessions = loadSessions()
17
18
 
18
- // If agent already has a thread session that exists, return it
19
+ // If the agent already has a shortcut chat session, return it.
19
20
  if (agent.threadSessionId && sessions[agent.threadSessionId]) {
20
- return NextResponse.json(sessions[agent.threadSessionId])
21
+ const existing = sessions[agent.threadSessionId] as Record<string, unknown>
22
+ let changed = false
23
+ if (existing.shortcutForAgentId !== agentId) {
24
+ existing.shortcutForAgentId = agentId
25
+ changed = true
26
+ }
27
+ if (existing.name !== agent.name) {
28
+ existing.name = agent.name
29
+ changed = true
30
+ }
31
+ if (changed) saveSessions(sessions)
32
+ return NextResponse.json(existing)
21
33
  }
22
34
 
23
- // Check if an existing session is already linked to this agent as a thread
35
+ // Legacy fallback for older shortcut sessions that were named using the
36
+ // old agent-thread convention before the explicit link was persisted.
24
37
  const existing = Object.values(sessions).find(
25
- (s: Record<string, unknown>) => s.name === `agent-thread:${agentId}` && s.user === user
38
+ (s: Record<string, unknown>) =>
39
+ (
40
+ s.shortcutForAgentId === agentId
41
+ || s.name === `agent-thread:${agentId}`
42
+ )
43
+ && s.user === user
26
44
  )
27
45
  if (existing) {
28
46
  agent.threadSessionId = (existing as Record<string, unknown>).id as string
29
47
  agent.updatedAt = Date.now()
30
48
  saveAgents(agents)
49
+ let changed = false
50
+ const existingRecord = existing as Record<string, unknown>
51
+ if (existingRecord.shortcutForAgentId !== agentId) {
52
+ existingRecord.shortcutForAgentId = agentId
53
+ changed = true
54
+ }
55
+ if (existingRecord.name !== agent.name) {
56
+ existingRecord.name = agent.name
57
+ changed = true
58
+ }
59
+ if (changed) saveSessions(sessions)
31
60
  return NextResponse.json(existing)
32
61
  }
33
62
 
34
- // Create a new thread session
35
- const sessionId = `agent-thread-${agentId}-${genId()}`
63
+ // Create a new shortcut chat session for this agent.
64
+ const sessionId = `agent-chat-${agentId}-${genId()}`
36
65
  const now = Date.now()
37
- const session = {
66
+ const baseSession = {
38
67
  id: sessionId,
39
- name: `agent-thread:${agentId}`,
68
+ name: agent.name,
69
+ shortcutForAgentId: agentId,
40
70
  cwd: WORKSPACE_DIR,
41
71
  user: user,
42
72
  provider: agent.provider,
@@ -55,6 +85,7 @@ export async function POST(req: Request, { params }: { params: Promise<{ id: str
55
85
  heartbeatEnabled: agent.heartbeatEnabled || false,
56
86
  heartbeatIntervalSec: agent.heartbeatIntervalSec || null,
57
87
  }
88
+ const session = applyResolvedRoute(baseSession, resolvePrimaryAgentRoute(agent))
58
89
 
59
90
  sessions[sessionId] = session as Record<string, unknown>
60
91
  saveSessions(sessions)
@@ -13,6 +13,9 @@ export async function GET(req: Request) {
13
13
  const agents = loadAgents()
14
14
  const sessions = loadSessions()
15
15
  const usage = loadUsage()
16
+ for (const agent of Object.values(agents)) {
17
+ agent.isOrchestrator = agent.platformAssignScope === 'all'
18
+ }
16
19
  // Enrich agents that have spend limits with current spend windows
17
20
  for (const agent of Object.values(agents)) {
18
21
  if (
@@ -48,26 +51,56 @@ export async function POST(req: Request) {
48
51
  const id = genId()
49
52
  const now = Date.now()
50
53
  const agents = loadAgents()
54
+ const platformAssignScope = body.platformAssignScope
51
55
  agents[id] = {
52
56
  id,
53
57
  name: body.name,
54
58
  description: body.description,
59
+ soul: body.soul || undefined,
55
60
  systemPrompt: body.systemPrompt,
56
61
  provider: body.provider,
57
62
  model: body.model,
58
63
  credentialId: body.credentialId,
64
+ fallbackCredentialIds: body.fallbackCredentialIds,
59
65
  apiEndpoint: normalizeProviderEndpoint(body.provider, body.apiEndpoint || null),
60
- isOrchestrator: body.isOrchestrator,
66
+ gatewayProfileId: body.gatewayProfileId,
67
+ routingStrategy: body.routingStrategy,
68
+ routingTargets: body.routingTargets?.map((target) => ({
69
+ ...target,
70
+ apiEndpoint: normalizeProviderEndpoint(target.provider, target.apiEndpoint || null),
71
+ })),
72
+ isOrchestrator: platformAssignScope === 'all',
73
+ platformAssignScope,
61
74
  subAgentIds: body.subAgentIds,
62
75
  plugins: body.plugins?.length ? body.plugins : (body.tools || []),
76
+ skills: body.skills,
77
+ skillIds: body.skillIds,
78
+ mcpServerIds: body.mcpServerIds,
79
+ mcpDisabledTools: body.mcpDisabledTools?.length ? body.mcpDisabledTools : undefined,
63
80
  capabilities: body.capabilities,
64
81
  thinkingLevel: body.thinkingLevel || undefined,
65
82
  autoRecovery: body.autoRecovery || false,
83
+ heartbeatEnabled: body.heartbeatEnabled || false,
84
+ heartbeatInterval: body.heartbeatInterval,
85
+ heartbeatIntervalSec: body.heartbeatIntervalSec,
86
+ heartbeatModel: body.heartbeatModel,
87
+ heartbeatPrompt: body.heartbeatPrompt,
88
+ elevenLabsVoiceId: body.elevenLabsVoiceId,
66
89
  monthlyBudget: body.monthlyBudget ?? null,
67
90
  dailyBudget: body.dailyBudget ?? null,
68
91
  hourlyBudget: body.hourlyBudget ?? null,
69
92
  budgetAction: body.budgetAction || 'warn',
70
- soul: body.soul || undefined,
93
+ identityState: body.identityState ?? null,
94
+ memoryScopeMode: body.memoryScopeMode,
95
+ memoryTierPreference: body.memoryTierPreference,
96
+ projectId: body.projectId,
97
+ avatarSeed: body.avatarSeed,
98
+ avatarUrl: body.avatarUrl,
99
+ sessionResetMode: body.sessionResetMode ?? null,
100
+ sessionIdleTimeoutSec: body.sessionIdleTimeoutSec ?? null,
101
+ sessionMaxAgeSec: body.sessionMaxAgeSec ?? null,
102
+ sessionDailyResetAt: body.sessionDailyResetAt ?? null,
103
+ sessionResetTimezone: body.sessionResetTimezone ?? null,
71
104
  createdAt: now,
72
105
  updatedAt: now,
73
106
  }