@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
@@ -1,6 +1,7 @@
1
1
  import { WebSocketServer, WebSocket } from 'ws'
2
2
  import type { IncomingMessage } from 'http'
3
3
  import { validateAccessKey } from './storage'
4
+ import { AUTH_COOKIE_NAME, getCookieValue } from '@/lib/auth'
4
5
 
5
6
  interface WsClient {
6
7
  ws: WebSocket
@@ -29,9 +30,10 @@ export function initWsServer() {
29
30
  ;(globalThis as any)[GK] = hub
30
31
 
31
32
  wss.on('connection', (ws: WebSocket, req: IncomingMessage) => {
32
- // Auth: validate ?key= from upgrade URL
33
- const url = new URL(req.url || '/', `http://${req.headers.host || 'localhost'}`)
34
- const key = url.searchParams.get('key') || ''
33
+ const headerKey = req.headers['x-access-key']
34
+ const key = (Array.isArray(headerKey) ? headerKey[0] : headerKey)
35
+ || getCookieValue(req.headers.cookie, AUTH_COOKIE_NAME)
36
+ || ''
35
37
  if (!validateAccessKey(key)) {
36
38
  ws.close(4001, 'Unauthorized')
37
39
  return
@@ -22,6 +22,7 @@ export interface SetupProviderOption {
22
22
  description: string
23
23
  requiresKey: boolean
24
24
  supportsEndpoint: boolean
25
+ allowMultiple?: boolean
25
26
  defaultEndpoint?: string
26
27
  keyUrl?: string
27
28
  keyLabel?: string
@@ -44,6 +45,18 @@ export const SETUP_PROVIDERS: SetupProviderOption[] = [
44
45
  badge: 'Recommended',
45
46
  icon: 'O',
46
47
  },
48
+ {
49
+ id: 'openclaw',
50
+ name: 'OpenClaw',
51
+ description: 'Connect one or more local or remote OpenClaw gateways and map different starter agents to each one.',
52
+ requiresKey: false,
53
+ supportsEndpoint: true,
54
+ allowMultiple: true,
55
+ defaultEndpoint: 'http://localhost:18789/v1',
56
+ optionalKey: true,
57
+ badge: 'First-Tier',
58
+ icon: 'C',
59
+ },
47
60
  {
48
61
  id: 'anthropic',
49
62
  name: 'Anthropic',
@@ -125,17 +138,6 @@ export const SETUP_PROVIDERS: SetupProviderOption[] = [
125
138
  keyLabel: 'fireworks.ai',
126
139
  icon: 'F',
127
140
  },
128
- {
129
- id: 'openclaw',
130
- name: 'OpenClaw',
131
- description: 'Connect to your local or remote OpenClaw gateway (multi-OpenClaw ready).',
132
- requiresKey: false,
133
- supportsEndpoint: true,
134
- defaultEndpoint: 'http://localhost:18789/v1',
135
- optionalKey: true,
136
- badge: 'OpenClaw',
137
- icon: 'C',
138
- },
139
141
  {
140
142
  id: 'ollama',
141
143
  name: 'Ollama',
@@ -189,6 +191,341 @@ Behavior:
189
191
  - When the user asks for direct execution (for example browsing, screenshots, research, or file edits), use available tools and return real results instead of only describing what to do.
190
192
  - If a capability depends on provider/tool configuration, call that out explicitly.`
191
193
 
194
+ const PERSONAL_ASSISTANT_PROMPT = `You are a personal AI copilot inside SwarmClaw.
195
+
196
+ Primary objective:
197
+ - Help the user make progress on whatever matters to them, whether that is research, planning, writing, building, organizing life admin, or running a business.
198
+
199
+ Behavior:
200
+ - Start from the user's intent, not from the tooling.
201
+ - Turn vague goals into concrete next steps.
202
+ - When useful, suggest tasks, schedules, or specialist agents, but do not force control-plane workflow on the user.
203
+ - Stay concise, practical, and execution-oriented.`
204
+
205
+ const RESEARCH_PROMPT = `You are a research copilot inside SwarmClaw.
206
+
207
+ Primary objective:
208
+ - Gather facts, compare options, summarize findings, and keep the user's work organized.
209
+
210
+ Behavior:
211
+ - Clarify the research question when needed.
212
+ - Prefer structured findings, tradeoffs, and source-backed summaries.
213
+ - Capture useful outputs in files or tasks when that helps the user continue.`
214
+
215
+ const BUILDER_PROMPT = `You are a builder agent inside SwarmClaw.
216
+
217
+ Primary objective:
218
+ - Help the user design, implement, debug, and ship software or technical projects.
219
+
220
+ Behavior:
221
+ - Move from goal to concrete implementation steps quickly.
222
+ - Use code, files, browser, and task tooling when helpful.
223
+ - Surface blockers, assumptions, and verification clearly.`
224
+
225
+ const REVIEWER_PROMPT = `You are a reviewer agent inside SwarmClaw.
226
+
227
+ Primary objective:
228
+ - Review plans, code, documents, and outputs for quality, correctness, and risk.
229
+
230
+ Behavior:
231
+ - Focus first on bugs, regressions, gaps, and unclear assumptions.
232
+ - Be direct and specific.
233
+ - Offer concrete follow-up actions when you find issues.`
234
+
235
+ const WRITER_PROMPT = `You are a writing copilot inside SwarmClaw.
236
+
237
+ Primary objective:
238
+ - Help the user draft, refine, and structure written work for clarity and impact.
239
+
240
+ Behavior:
241
+ - Adapt tone, format, and level of detail to the user's context.
242
+ - Suggest outlines, drafts, revisions, and packaging for different channels.
243
+ - Keep momentum high and avoid generic filler.`
244
+
245
+ const EDITOR_PROMPT = `You are an editor inside SwarmClaw.
246
+
247
+ Primary objective:
248
+ - Improve drafts for clarity, structure, tone, and quality.
249
+
250
+ Behavior:
251
+ - Tighten weak writing, call out inconsistencies, and preserve the intended voice.
252
+ - Give concise, high-signal edits and rationale.
253
+ - Flag missing evidence or unclear claims when relevant.`
254
+
255
+ const OPERATOR_PROMPT = `You are an operations-focused SwarmClaw operator.
256
+
257
+ Primary objective:
258
+ - Keep work moving across agents, tasks, schedules, and approvals without losing sight of the user's real goals.
259
+
260
+ Behavior:
261
+ - Monitor progress, surface bottlenecks, and delegate when appropriate.
262
+ - Be explicit about what is blocked, what is running, and what should happen next.
263
+ - Treat the control plane as a means to an end, not the end itself.`
264
+
265
+ export type OnboardingPath = 'quick' | 'intent' | 'manual'
266
+
267
+ export interface OnboardingPathOption {
268
+ id: OnboardingPath
269
+ title: string
270
+ description: string
271
+ detail: string
272
+ badge?: string
273
+ }
274
+
275
+ export const ONBOARDING_PATHS: OnboardingPathOption[] = [
276
+ {
277
+ id: 'quick',
278
+ title: 'Quick Start',
279
+ description: 'Provider first, one starter kit, fastest path into chat.',
280
+ detail: 'Best when you already know which provider you want and want to get moving quickly.',
281
+ badge: 'Fastest',
282
+ },
283
+ {
284
+ id: 'intent',
285
+ title: 'Intent Guided',
286
+ description: 'Start from what you want to do, then shape a starter workspace around it.',
287
+ detail: 'Best for open-ended use cases like research, building, writing, planning, or mixed personal work.',
288
+ badge: 'Recommended',
289
+ },
290
+ {
291
+ id: 'manual',
292
+ title: 'Custom Setup',
293
+ description: 'Configure providers first and choose whether to start blank or from a template.',
294
+ detail: 'Best for advanced users who want control over the initial setup and agent mix.',
295
+ },
296
+ ]
297
+
298
+ export interface StarterKitAgentTemplate {
299
+ id: string
300
+ name: string
301
+ description: string
302
+ systemPrompt: string
303
+ tools: string[]
304
+ capabilities?: string[]
305
+ recommendedProviders?: SetupProvider[]
306
+ platformAssignScope?: 'self' | 'all'
307
+ }
308
+
309
+ export interface StarterKit {
310
+ id: string
311
+ name: string
312
+ description: string
313
+ detail: string
314
+ badge?: string
315
+ recommendedFor?: OnboardingPath[]
316
+ agents: StarterKitAgentTemplate[]
317
+ }
318
+
319
+ const PERSONAL_AGENT_TOOLS = [
320
+ 'memory',
321
+ 'files',
322
+ 'web_search',
323
+ 'web_fetch',
324
+ 'browser',
325
+ 'manage_tasks',
326
+ 'manage_schedules',
327
+ 'manage_documents',
328
+ ]
329
+
330
+ const RESEARCH_AGENT_TOOLS = [
331
+ 'memory',
332
+ 'files',
333
+ 'web_search',
334
+ 'web_fetch',
335
+ 'browser',
336
+ 'manage_tasks',
337
+ 'manage_documents',
338
+ ]
339
+
340
+ const BUILDER_AGENT_TOOLS = [
341
+ 'memory',
342
+ 'files',
343
+ 'web_search',
344
+ 'web_fetch',
345
+ 'browser',
346
+ 'manage_tasks',
347
+ 'claude_code',
348
+ 'codex_cli',
349
+ 'opencode_cli',
350
+ ]
351
+
352
+ const OPERATOR_AGENT_TOOLS = STARTER_AGENT_TOOLS
353
+ const OPENCLAW_AGENT_TOOLS = [
354
+ 'memory',
355
+ 'files',
356
+ 'web_search',
357
+ 'web_fetch',
358
+ 'browser',
359
+ 'manage_tasks',
360
+ 'manage_schedules',
361
+ 'manage_sessions',
362
+ 'openclaw_workspace',
363
+ ]
364
+
365
+ export const STARTER_KITS: StarterKit[] = [
366
+ {
367
+ id: 'personal_assistant',
368
+ name: 'Personal Assistant',
369
+ description: 'One flexible agent for open-ended work.',
370
+ detail: 'A strong default for general planning, research, writing, and day-to-day execution.',
371
+ badge: 'Recommended',
372
+ recommendedFor: ['quick', 'intent'],
373
+ agents: [
374
+ {
375
+ id: 'sidekick',
376
+ name: 'Sidekick',
377
+ description: 'A versatile assistant for everyday work, planning, and follow-through.',
378
+ systemPrompt: PERSONAL_ASSISTANT_PROMPT,
379
+ tools: PERSONAL_AGENT_TOOLS,
380
+ capabilities: ['planning', 'research', 'writing', 'coordination'],
381
+ },
382
+ ],
383
+ },
384
+ {
385
+ id: 'research_copilot',
386
+ name: 'Research Copilot',
387
+ description: 'A focused setup for investigation and synthesis.',
388
+ detail: 'Useful for market scans, comparisons, technical investigation, and source-backed summaries.',
389
+ recommendedFor: ['intent', 'manual'],
390
+ agents: [
391
+ {
392
+ id: 'researcher',
393
+ name: 'Researcher',
394
+ description: 'Collects facts, compares options, and produces structured findings.',
395
+ systemPrompt: RESEARCH_PROMPT,
396
+ tools: RESEARCH_AGENT_TOOLS,
397
+ capabilities: ['research', 'analysis', 'summarization'],
398
+ },
399
+ ],
400
+ },
401
+ {
402
+ id: 'builder_studio',
403
+ name: 'Builder Studio',
404
+ description: 'Start with a builder and a reviewer.',
405
+ detail: 'Good for coding, prototyping, product work, and technical iteration.',
406
+ recommendedFor: ['intent', 'manual'],
407
+ agents: [
408
+ {
409
+ id: 'builder',
410
+ name: 'Builder',
411
+ description: 'Implements ideas, ships changes, and drives technical execution.',
412
+ systemPrompt: BUILDER_PROMPT,
413
+ tools: BUILDER_AGENT_TOOLS,
414
+ capabilities: ['coding', 'debugging', 'implementation'],
415
+ recommendedProviders: ['anthropic', 'openai', 'google', 'openclaw', 'ollama'],
416
+ },
417
+ {
418
+ id: 'reviewer',
419
+ name: 'Reviewer',
420
+ description: 'Reviews plans and outputs for bugs, regressions, and quality gaps.',
421
+ systemPrompt: REVIEWER_PROMPT,
422
+ tools: RESEARCH_AGENT_TOOLS,
423
+ capabilities: ['review', 'testing', 'risk assessment'],
424
+ recommendedProviders: ['anthropic', 'openai', 'google', 'openclaw'],
425
+ },
426
+ ],
427
+ },
428
+ {
429
+ id: 'content_studio',
430
+ name: 'Content Studio',
431
+ description: 'A writer and editor working together.',
432
+ detail: 'Useful for blogs, marketing copy, docs, newsletters, and publishing workflows.',
433
+ recommendedFor: ['intent', 'manual'],
434
+ agents: [
435
+ {
436
+ id: 'writer',
437
+ name: 'Writer',
438
+ description: 'Drafts content, outlines, and messaging in the user’s preferred style.',
439
+ systemPrompt: WRITER_PROMPT,
440
+ tools: PERSONAL_AGENT_TOOLS,
441
+ capabilities: ['writing', 'messaging', 'structuring'],
442
+ },
443
+ {
444
+ id: 'editor',
445
+ name: 'Editor',
446
+ description: 'Improves structure, tone, and quality before publishing.',
447
+ systemPrompt: EDITOR_PROMPT,
448
+ tools: RESEARCH_AGENT_TOOLS,
449
+ capabilities: ['editing', 'quality control', 'review'],
450
+ },
451
+ ],
452
+ },
453
+ {
454
+ id: 'operator_swarm',
455
+ name: 'Operator Swarm',
456
+ description: 'A coordination-heavy setup for multi-agent work.',
457
+ detail: 'Closest to the current SwarmClaw operator workflow, with an orchestrator plus an execution agent.',
458
+ recommendedFor: ['manual'],
459
+ agents: [
460
+ {
461
+ id: 'operator',
462
+ name: 'Operator',
463
+ description: 'Coordinates tasks, delegates work, and keeps the workspace moving.',
464
+ systemPrompt: OPERATOR_PROMPT,
465
+ tools: OPERATOR_AGENT_TOOLS,
466
+ capabilities: ['coordination', 'delegation', 'operations'],
467
+ platformAssignScope: 'all',
468
+ recommendedProviders: ['openclaw', 'anthropic', 'openai'],
469
+ },
470
+ {
471
+ id: 'maker',
472
+ name: 'Maker',
473
+ description: 'Executes focused work items assigned by the user or other agents.',
474
+ systemPrompt: BUILDER_PROMPT,
475
+ tools: BUILDER_AGENT_TOOLS,
476
+ capabilities: ['execution', 'implementation', 'research'],
477
+ },
478
+ ],
479
+ },
480
+ {
481
+ id: 'openclaw_fleet',
482
+ name: 'OpenClaw Fleet',
483
+ description: 'An OpenClaw-first starter setup for local or remote gateways.',
484
+ detail: 'Designed for users who want multiple OpenClaw-backed agents right away, including remote endpoint assignments.',
485
+ recommendedFor: ['manual'],
486
+ badge: 'OpenClaw',
487
+ agents: [
488
+ {
489
+ id: 'openclaw_operator',
490
+ name: 'OpenClaw Operator',
491
+ description: 'Coordinates OpenClaw-backed execution and keeps distributed agents aligned.',
492
+ systemPrompt: OPERATOR_PROMPT,
493
+ tools: OPERATOR_AGENT_TOOLS,
494
+ capabilities: ['coordination', 'delegation', 'openclaw'],
495
+ platformAssignScope: 'all',
496
+ recommendedProviders: ['openclaw'],
497
+ },
498
+ {
499
+ id: 'openclaw_builder',
500
+ name: 'Remote Builder',
501
+ description: 'A build-focused OpenClaw agent for implementation work on a chosen gateway.',
502
+ systemPrompt: BUILDER_PROMPT,
503
+ tools: OPENCLAW_AGENT_TOOLS,
504
+ capabilities: ['coding', 'implementation', 'openclaw'],
505
+ recommendedProviders: ['openclaw'],
506
+ },
507
+ {
508
+ id: 'openclaw_researcher',
509
+ name: 'Remote Researcher',
510
+ description: 'A research-focused OpenClaw agent for browser and knowledge work on a chosen gateway.',
511
+ systemPrompt: RESEARCH_PROMPT,
512
+ tools: OPENCLAW_AGENT_TOOLS,
513
+ capabilities: ['research', 'analysis', 'openclaw'],
514
+ recommendedProviders: ['openclaw'],
515
+ },
516
+ ],
517
+ },
518
+ {
519
+ id: 'blank_workspace',
520
+ name: 'Blank Workspace',
521
+ description: 'Finish setup without starter agents.',
522
+ detail: 'Use this if you want to land in the app first and create providers, agents, and workflows yourself.',
523
+ recommendedFor: ['manual'],
524
+ badge: 'Blank',
525
+ agents: [],
526
+ },
527
+ ]
528
+
192
529
  export interface DefaultAgentConfig {
193
530
  name: string
194
531
  description: string
@@ -276,3 +613,7 @@ export const DEFAULT_AGENTS: Record<SetupProvider, DefaultAgentConfig> = {
276
613
  tools: STARTER_AGENT_TOOLS,
277
614
  },
278
615
  }
616
+
617
+ export function getDefaultModelForProvider(provider: SetupProvider): string {
618
+ return DEFAULT_AGENTS[provider].model
619
+ }
@@ -16,14 +16,14 @@ export const AVAILABLE_TOOLS: ToolDefinition[] = [
16
16
  { id: 'delegate', label: 'Delegate', description: 'Delegate complex tasks to specialized backends (Claude Code, Codex, OpenCode)' },
17
17
  { id: 'browser', label: 'Browser', description: 'Playwright — browse, scrape, interact with web pages' },
18
18
  { id: 'memory', label: 'Memory', description: 'Store and retrieve long-term memories across conversations' },
19
- { id: 'sandbox', label: 'Sandbox', description: 'Secure isolated code execution for JS, TS, and Python' },
19
+ { id: 'sandbox', label: 'Sandbox', description: 'Deno-based isolated JS/TS execution for cases where custom code is necessary' },
20
20
  { id: 'create_document', label: 'Create Document', description: 'Render markdown to PDF, HTML, or image' },
21
21
  { id: 'create_spreadsheet', label: 'Create Spreadsheet', description: 'Create Excel or CSV files from structured data' },
22
- { id: 'http_request', label: 'HTTP Request', description: 'Make direct HTTP API calls with custom methods, headers, and bodies' },
22
+ { id: 'http_request', label: 'HTTP Request', description: 'Make direct HTTP API calls without generating throwaway code' },
23
23
  { id: 'git', label: 'Git', description: 'Run structured git operations (status, commit, push, diff, etc.)' },
24
24
  { id: 'wallet', label: 'Wallet', description: 'Manage agent crypto wallet — check balance, send SOL, view transactions' },
25
25
  { id: 'monitor', label: 'Monitor', description: 'System observability: check resource usage, watch logs, and ping endpoints' },
26
- { id: 'plugin_creator', label: 'Plugin Creator', description: 'Design, write, and test custom SwarmClaw plugins dynamically' },
26
+ { id: 'plugin_creator', label: 'Plugin Creator', description: 'Design focused plugins for durable capabilities and recurring automations' },
27
27
  { id: 'sample_ui', label: 'Sample UI', description: 'Demonstration of dynamic UI injection into Sidebar and Chat Header' },
28
28
  { id: 'image_gen', label: 'Image Generation', description: 'Generate images from text prompts using OpenAI, Stability AI, Replicate, fal.ai, and more' },
29
29
  { id: 'email', label: 'Email', description: 'Send emails via SMTP with plain text and HTML support' },
@@ -49,4 +49,3 @@ export const ALL_TOOLS: ToolDefinition[] = [...AVAILABLE_TOOLS, ...PLATFORM_TOOL
49
49
  export const TOOL_LABELS: Record<string, string> = Object.fromEntries(
50
50
  ALL_TOOLS.map((t) => [t.id, t.label]),
51
51
  )
52
-
@@ -0,0 +1,26 @@
1
+ import { describe, it } from 'node:test'
2
+ import assert from 'node:assert/strict'
3
+ import { AgentCreateSchema } from './schemas'
4
+
5
+ describe('AgentCreateSchema', () => {
6
+ it('defaults platformAssignScope to self', () => {
7
+ const parsed = AgentCreateSchema.parse({
8
+ name: 'Solo Agent',
9
+ provider: 'openai',
10
+ })
11
+
12
+ assert.equal(parsed.platformAssignScope, 'self')
13
+ })
14
+
15
+ it('accepts explicit all-scope delegation without relying on legacy orchestrator flags', () => {
16
+ const parsed = AgentCreateSchema.parse({
17
+ name: 'Coordinator',
18
+ provider: 'openai',
19
+ platformAssignScope: 'all',
20
+ isOrchestrator: false,
21
+ })
22
+
23
+ assert.equal(parsed.platformAssignScope, 'all')
24
+ assert.equal(parsed.isOrchestrator, false)
25
+ })
26
+ })
@@ -1,5 +1,18 @@
1
1
  import { z } from 'zod'
2
2
 
3
+ const AgentRoutingTargetSchema = z.object({
4
+ id: z.string().min(1),
5
+ label: z.string().optional(),
6
+ role: z.enum(['primary', 'economy', 'premium', 'reasoning', 'backup']).optional(),
7
+ provider: z.string().min(1),
8
+ model: z.string().optional().default(''),
9
+ credentialId: z.string().nullable().optional().default(null),
10
+ fallbackCredentialIds: z.array(z.string()).optional().default([]),
11
+ apiEndpoint: z.string().nullable().optional().default(null),
12
+ gatewayProfileId: z.string().nullable().optional().default(null),
13
+ priority: z.number().int().optional(),
14
+ })
15
+
3
16
  export const AgentCreateSchema = z.object({
4
17
  name: z.string().min(1, 'Agent name is required'),
5
18
  provider: z.string().min(1, 'Provider is required'),
@@ -7,15 +20,41 @@ export const AgentCreateSchema = z.object({
7
20
  systemPrompt: z.string().optional().default(''),
8
21
  model: z.string().optional().default(''),
9
22
  credentialId: z.string().nullable().optional().default(null),
23
+ fallbackCredentialIds: z.array(z.string()).optional().default([]),
10
24
  apiEndpoint: z.string().nullable().optional().default(null),
25
+ gatewayProfileId: z.string().nullable().optional().default(null),
26
+ routingStrategy: z.enum(['single', 'balanced', 'economy', 'premium', 'reasoning']).nullable().optional().default(null),
27
+ routingTargets: z.array(AgentRoutingTargetSchema).optional().default([]),
11
28
  isOrchestrator: z.boolean().optional().default(false),
29
+ platformAssignScope: z.enum(['self', 'all']).optional().default('self'),
12
30
  subAgentIds: z.array(z.string()).optional().default([]),
13
31
  plugins: z.array(z.string()).optional().default([]),
14
32
  /** @deprecated Use plugins */
15
33
  tools: z.array(z.string()).optional(),
34
+ skills: z.array(z.string()).optional().default([]),
35
+ skillIds: z.array(z.string()).optional().default([]),
36
+ mcpServerIds: z.array(z.string()).optional().default([]),
37
+ mcpDisabledTools: z.array(z.string()).optional().default([]),
16
38
  capabilities: z.array(z.string()).optional().default([]),
17
39
  thinkingLevel: z.string().optional(),
18
40
  soul: z.string().optional(),
41
+ identityState: z.record(z.string(), z.unknown()).nullable().optional().default(null),
42
+ heartbeatEnabled: z.boolean().optional().default(false),
43
+ heartbeatInterval: z.union([z.string(), z.number()]).nullable().optional().default(null),
44
+ heartbeatIntervalSec: z.number().int().nonnegative().nullable().optional().default(null),
45
+ heartbeatModel: z.string().nullable().optional().default(null),
46
+ heartbeatPrompt: z.string().nullable().optional().default(null),
47
+ elevenLabsVoiceId: z.string().nullable().optional().default(null),
48
+ sessionResetMode: z.enum(['idle', 'daily']).nullable().optional().default(null),
49
+ sessionIdleTimeoutSec: z.number().int().nonnegative().nullable().optional().default(null),
50
+ sessionMaxAgeSec: z.number().int().nonnegative().nullable().optional().default(null),
51
+ sessionDailyResetAt: z.string().nullable().optional().default(null),
52
+ sessionResetTimezone: z.string().nullable().optional().default(null),
53
+ memoryScopeMode: z.enum(['auto', 'all', 'global', 'agent', 'session', 'project']).nullable().optional().default(null),
54
+ memoryTierPreference: z.enum(['working', 'durable', 'archive', 'blended']).nullable().optional().default(null),
55
+ projectId: z.string().optional(),
56
+ avatarSeed: z.string().optional(),
57
+ avatarUrl: z.string().nullable().optional().default(null),
19
58
  autoRecovery: z.boolean().optional().default(false),
20
59
  monthlyBudget: z.number().positive().nullable().optional().default(null),
21
60
  dailyBudget: z.number().positive().nullable().optional().default(null),
@@ -36,6 +75,28 @@ export const ConnectorCreateSchema = z.object({
36
75
  autoStart: z.boolean().optional(),
37
76
  })
38
77
 
78
+ export const ExternalAgentRegisterSchema = z.object({
79
+ id: z.string().optional(),
80
+ name: z.string().min(1, 'External agent name is required'),
81
+ sourceType: z.enum(['codex', 'claude', 'opencode', 'openclaw', 'custom']).default('custom'),
82
+ status: z.enum(['online', 'idle', 'offline', 'stale']).optional().default('online'),
83
+ provider: z.string().nullable().optional().default(null),
84
+ model: z.string().nullable().optional().default(null),
85
+ workspace: z.string().nullable().optional().default(null),
86
+ transport: z.enum(['http', 'ws', 'cli', 'gateway', 'custom']).nullable().optional().default(null),
87
+ endpoint: z.string().nullable().optional().default(null),
88
+ agentId: z.string().nullable().optional().default(null),
89
+ gatewayProfileId: z.string().nullable().optional().default(null),
90
+ capabilities: z.array(z.string()).optional().default([]),
91
+ labels: z.array(z.string()).optional().default([]),
92
+ metadata: z.record(z.string(), z.unknown()).nullable().optional().default(null),
93
+ tokenStats: z.object({
94
+ inputTokens: z.number().nonnegative().optional(),
95
+ outputTokens: z.number().nonnegative().optional(),
96
+ totalTokens: z.number().nonnegative().optional(),
97
+ }).nullable().optional().default(null),
98
+ })
99
+
39
100
  export const TaskCreateSchema = z.object({
40
101
  title: z.string().min(1, 'Task title is required'),
41
102
  description: z.string().optional().default(''),
@@ -64,7 +125,7 @@ export const TaskCreateSchema = z.object({
64
125
 
65
126
  export const ChatroomCreateSchema = z.object({
66
127
  name: z.string().min(1, 'Chatroom name is required'),
67
- agentIds: z.array(z.string()).default([]),
128
+ agentIds: z.array(z.string()).min(1, 'Select at least one agent').default([]),
68
129
  description: z.string().optional().default(''),
69
130
  chatMode: z.enum(['sequential', 'parallel']).optional(),
70
131
  autoAddress: z.boolean().optional(),
@@ -1,15 +1,15 @@
1
1
  type WsCallback = () => void
2
2
 
3
3
  let ws: WebSocket | null = null
4
- let accessKey = ''
4
+ let wsEnabled = false
5
5
  let reconnectTimer: ReturnType<typeof setTimeout> | null = null
6
6
  let reconnectDelay = 1000
7
7
  const MAX_RECONNECT_DELAY = 30_000
8
8
  const listeners = new Map<string, Set<WsCallback>>()
9
9
  let connected = false
10
10
 
11
- function getWsUrl(key: string): string {
12
- if (typeof window === 'undefined') return `ws://localhost:3457/ws?key=${encodeURIComponent(key)}`
11
+ function getWsUrl(): string {
12
+ if (typeof window === 'undefined') return 'ws://localhost:3457/ws'
13
13
 
14
14
  const protocol = window.location.protocol === 'https:' ? 'wss' : 'ws'
15
15
  const pagePort = window.location.port
@@ -22,7 +22,7 @@ function getWsUrl(key: string): string {
22
22
  const behindProxy = !pagePort || pagePort === '80' || pagePort === '443' || pagePort !== appPort
23
23
  const wsHost = behindProxy ? window.location.host : `${window.location.hostname}:${buildPort}`
24
24
 
25
- return `${protocol}://${wsHost}/ws?key=${encodeURIComponent(key)}`
25
+ return `${protocol}://${wsHost}/ws`
26
26
  }
27
27
 
28
28
  function handleMessage(event: MessageEvent) {
@@ -44,17 +44,18 @@ function scheduleReconnect() {
44
44
  const jitter = Math.random() * 2000
45
45
  reconnectTimer = setTimeout(() => {
46
46
  reconnectTimer = null
47
- if (!accessKey) return
48
- connect(accessKey)
47
+ if (!wsEnabled) return
48
+ connect()
49
49
  }, reconnectDelay + jitter)
50
50
  reconnectDelay = Math.min(reconnectDelay * 2, MAX_RECONNECT_DELAY)
51
51
  }
52
52
 
53
- function connect(key: string) {
53
+ function connect() {
54
+ if (!wsEnabled) return
54
55
  if (ws && (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING)) return
55
56
 
56
57
  try {
57
- ws = new WebSocket(getWsUrl(key))
58
+ ws = new WebSocket(getWsUrl())
58
59
  } catch {
59
60
  scheduleReconnect()
60
61
  return
@@ -75,7 +76,7 @@ function connect(key: string) {
75
76
  ws.onclose = () => {
76
77
  connected = false
77
78
  ws = null
78
- if (accessKey) scheduleReconnect()
79
+ if (wsEnabled) scheduleReconnect()
79
80
  }
80
81
 
81
82
  ws.onerror = () => {
@@ -84,13 +85,14 @@ function connect(key: string) {
84
85
  }
85
86
 
86
87
  export function connectWs(key: string) {
87
- accessKey = key
88
+ void key
89
+ wsEnabled = true
88
90
  reconnectDelay = 1000
89
- connect(key)
91
+ connect()
90
92
  }
91
93
 
92
94
  export function disconnectWs() {
93
- accessKey = ''
95
+ wsEnabled = false
94
96
  if (reconnectTimer) {
95
97
  clearTimeout(reconnectTimer)
96
98
  reconnectTimer = null
package/src/proxy.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { NextResponse } from 'next/server'
2
2
  import type { NextRequest } from 'next/server'
3
+ import { AUTH_COOKIE_NAME } from '@/lib/auth'
3
4
 
4
5
  /* ------------------------------------------------------------------ */
5
6
  /* Rate-limit state — HMR-safe via globalThis */
@@ -44,7 +45,7 @@ function getClientIp(request: NextRequest): string {
44
45
  /* ------------------------------------------------------------------ */
45
46
 
46
47
  /** Access key auth proxy with brute-force rate limiting.
47
- * Checks X-Access-Key header or ?key= param on all /api/ routes except /api/auth.
48
+ * Checks X-Access-Key header or auth cookie on all /api/ routes except /api/auth.
48
49
  * The key is validated against the ACCESS_KEY env var.
49
50
  * After 5 failed attempts from a single IP the client is locked out for 15 minutes.
50
51
  */
@@ -55,11 +56,10 @@ export function proxy(request: NextRequest) {
55
56
  const isConnectorWebhook = request.method === 'POST'
56
57
  && /^\/api\/connectors\/[^/]+\/webhook\/?$/.test(pathname)
57
58
 
58
- // Only protect API routes (not auth, uploads served as static assets, or inbound webhooks)
59
+ // Only protect API routes (not auth or inbound webhooks)
59
60
  if (
60
61
  !pathname.startsWith('/api/')
61
62
  || pathname === '/api/auth'
62
- || pathname.startsWith('/api/uploads/')
63
63
  || isWebhookTrigger
64
64
  || isConnectorWebhook
65
65
  ) {
@@ -88,8 +88,8 @@ export function proxy(request: NextRequest) {
88
88
  }
89
89
 
90
90
  const providedKey =
91
- request.headers.get('x-access-key')
92
- || request.nextUrl.searchParams.get('key')
91
+ request.headers.get('x-access-key')?.trim()
92
+ || request.cookies.get(AUTH_COOKIE_NAME)?.value?.trim()
93
93
  || ''
94
94
 
95
95
  if (providedKey !== accessKey) {