@swarmclawai/swarmclaw 1.2.3 → 1.2.5

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 (273) hide show
  1. package/README.md +20 -0
  2. package/bin/daemon-cmd.js +169 -0
  3. package/bin/server-cmd.js +3 -0
  4. package/bin/swarmclaw.js +11 -0
  5. package/package.json +17 -16
  6. package/src/app/api/agents/[id]/clone/route.ts +3 -32
  7. package/src/app/api/agents/[id]/route.ts +6 -158
  8. package/src/app/api/agents/[id]/status/route.ts +2 -3
  9. package/src/app/api/agents/[id]/thread/route.ts +4 -17
  10. package/src/app/api/agents/bulk/route.ts +5 -47
  11. package/src/app/api/agents/route.ts +5 -119
  12. package/src/app/api/agents/trash/route.ts +13 -24
  13. package/src/app/api/auth/route.ts +3 -9
  14. package/src/app/api/autonomy/estop/route.ts +5 -5
  15. package/src/app/api/chatrooms/[id]/chat/route.ts +11 -5
  16. package/src/app/api/chatrooms/[id]/route.ts +23 -2
  17. package/src/app/api/chatrooms/route.ts +13 -2
  18. package/src/app/api/chats/[id]/clear/route.ts +2 -13
  19. package/src/app/api/chats/[id]/deploy/route.ts +2 -3
  20. package/src/app/api/chats/[id]/edit-resend/route.ts +7 -13
  21. package/src/app/api/chats/[id]/mailbox/route.ts +6 -8
  22. package/src/app/api/chats/[id]/queue/route.ts +17 -64
  23. package/src/app/api/chats/[id]/retry/route.ts +4 -22
  24. package/src/app/api/chats/[id]/route.ts +10 -138
  25. package/src/app/api/chats/heartbeat/route.ts +2 -1
  26. package/src/app/api/chats/migrate-messages/route.ts +7 -0
  27. package/src/app/api/chats/route.ts +13 -134
  28. package/src/app/api/connectors/[id]/access/route.ts +12 -229
  29. package/src/app/api/connectors/[id]/doctor/route.ts +1 -1
  30. package/src/app/api/connectors/[id]/health/route.ts +12 -39
  31. package/src/app/api/connectors/[id]/route.ts +14 -122
  32. package/src/app/api/connectors/[id]/webhook/route.ts +1 -1
  33. package/src/app/api/connectors/doctor/route.ts +1 -1
  34. package/src/app/api/connectors/route.ts +12 -70
  35. package/src/app/api/credentials/[id]/route.ts +2 -4
  36. package/src/app/api/credentials/route.ts +10 -19
  37. package/src/app/api/daemon/health-check/route.ts +3 -4
  38. package/src/app/api/daemon/route.ts +10 -8
  39. package/src/app/api/documents/route.ts +11 -10
  40. package/src/app/api/external-agents/route.ts +3 -3
  41. package/src/app/api/gateways/[id]/health/route.ts +2 -3
  42. package/src/app/api/gateways/[id]/route.ts +7 -122
  43. package/src/app/api/gateways/route.ts +3 -103
  44. package/src/app/api/mcp-servers/[id]/tools/route.ts +5 -5
  45. package/src/app/api/openclaw/dashboard-url/route.ts +8 -16
  46. package/src/app/api/openclaw/directory/route.ts +2 -2
  47. package/src/app/api/openclaw/history/route.ts +3 -5
  48. package/src/app/api/providers/[id]/models/route.test.ts +60 -0
  49. package/src/app/api/providers/[id]/models/route.ts +33 -1
  50. package/src/app/api/providers/[id]/route.test.ts +49 -0
  51. package/src/app/api/providers/[id]/route.ts +30 -1
  52. package/src/app/api/providers/ollama/route.ts +6 -5
  53. package/src/app/api/schedules/[id]/route.ts +14 -108
  54. package/src/app/api/schedules/[id]/run/route.ts +6 -67
  55. package/src/app/api/schedules/route.ts +9 -51
  56. package/src/app/api/settings/route.ts +4 -3
  57. package/src/app/api/setup/check-provider/route.ts +15 -1
  58. package/src/app/api/setup/openclaw-device/route.ts +2 -2
  59. package/src/app/api/system/status/route.ts +2 -2
  60. package/src/app/api/tasks/[id]/route.ts +16 -202
  61. package/src/app/api/tasks/bulk/route.ts +5 -86
  62. package/src/app/api/tasks/metrics/route.ts +2 -1
  63. package/src/app/api/tasks/route.ts +11 -171
  64. package/src/app/api/upload/route.ts +1 -1
  65. package/src/app/api/uploads/[filename]/route.ts +1 -1
  66. package/src/app/api/uploads/route.ts +1 -1
  67. package/src/app/api/webhooks/[id]/history/route.ts +2 -2
  68. package/src/app/layout.tsx +9 -6
  69. package/src/app/protocols/page.tsx +71 -89
  70. package/src/app/tasks/page.tsx +32 -32
  71. package/src/cli/index.js +1 -0
  72. package/src/cli/spec.js +1 -0
  73. package/src/components/agents/agent-sheet.tsx +51 -25
  74. package/src/components/agents/inspector-panel.tsx +15 -4
  75. package/src/components/auth/setup-wizard/index.tsx +27 -18
  76. package/src/components/auth/setup-wizard/shared.tsx +2 -2
  77. package/src/components/auth/setup-wizard/step-agents.tsx +51 -38
  78. package/src/components/auth/setup-wizard/step-connect.tsx +48 -17
  79. package/src/components/auth/setup-wizard/types.ts +6 -4
  80. package/src/components/auth/setup-wizard/utils.test.ts +38 -8
  81. package/src/components/auth/setup-wizard/utils.ts +14 -8
  82. package/src/components/chatrooms/chatroom-sheet.tsx +16 -276
  83. package/src/components/connectors/connector-list.tsx +26 -40
  84. package/src/components/connectors/connector-sheet.tsx +95 -149
  85. package/src/components/gateways/gateway-sheet.tsx +61 -110
  86. package/src/components/layout/live-query-sync.tsx +121 -0
  87. package/src/components/protocols/structured-session-launcher.tsx +24 -45
  88. package/src/components/providers/app-query-provider.tsx +17 -0
  89. package/src/components/providers/provider-list.tsx +150 -77
  90. package/src/components/providers/provider-sheet.tsx +102 -77
  91. package/src/components/shared/model-combobox.tsx +5 -4
  92. package/src/components/skills/skill-list.tsx +5 -18
  93. package/src/components/skills/skill-sheet.tsx +21 -20
  94. package/src/components/skills/skills-workspace.tsx +48 -87
  95. package/src/components/tasks/task-card.tsx +20 -13
  96. package/src/components/tasks/task-column.tsx +22 -7
  97. package/src/components/tasks/task-list.tsx +8 -11
  98. package/src/components/tasks/task-sheet.tsx +111 -103
  99. package/src/features/agents/queries.ts +20 -0
  100. package/src/features/chatrooms/queries.ts +20 -0
  101. package/src/features/chats/queries.ts +27 -0
  102. package/src/features/connectors/queries.ts +145 -0
  103. package/src/features/credentials/queries.ts +37 -0
  104. package/src/features/extensions/queries.ts +26 -0
  105. package/src/features/external-agents/queries.ts +36 -0
  106. package/src/features/gateways/queries.ts +274 -0
  107. package/src/features/missions/queries.ts +23 -0
  108. package/src/features/projects/queries.ts +20 -0
  109. package/src/features/protocols/queries.ts +149 -0
  110. package/src/features/providers/queries.ts +142 -0
  111. package/src/features/settings/queries.ts +20 -0
  112. package/src/features/skills/queries.ts +182 -0
  113. package/src/features/tasks/queries.ts +189 -0
  114. package/src/hooks/use-ws.ts +3 -2
  115. package/src/lib/agent-provider-options.test.ts +152 -0
  116. package/src/lib/agent-provider-options.ts +84 -0
  117. package/src/lib/app/api-client.ts +2 -2
  118. package/src/lib/providers/index.test.ts +78 -0
  119. package/src/lib/providers/index.ts +13 -10
  120. package/src/lib/query/client.ts +17 -0
  121. package/src/lib/server/agents/agent-runtime-config.ts +6 -6
  122. package/src/lib/server/agents/agent-service.ts +429 -0
  123. package/src/lib/server/agents/agent-thread-session.ts +6 -5
  124. package/src/lib/server/agents/autonomy-contract.ts +1 -4
  125. package/src/lib/server/agents/delegation-advisory.test.ts +206 -0
  126. package/src/lib/server/agents/delegation-advisory.ts +251 -0
  127. package/src/lib/server/agents/main-agent-loop.ts +98 -40
  128. package/src/lib/server/agents/subagent-runtime.ts +12 -0
  129. package/src/lib/server/autonomy/supervisor-reflection.test.ts +20 -1
  130. package/src/lib/server/autonomy/supervisor-reflection.ts +39 -19
  131. package/src/lib/server/build-llm.ts +7 -15
  132. package/src/lib/server/capability-router.test.ts +70 -1
  133. package/src/lib/server/capability-router.ts +24 -99
  134. package/src/lib/server/chat-execution/chat-execution-utils.ts +0 -15
  135. package/src/lib/server/chat-execution/chat-streaming-utils.ts +2 -4
  136. package/src/lib/server/chat-execution/chat-turn-finalization.ts +77 -12
  137. package/src/lib/server/chat-execution/chat-turn-partial-persistence.ts +4 -4
  138. package/src/lib/server/chat-execution/chat-turn-preflight.ts +2 -2
  139. package/src/lib/server/chat-execution/chat-turn-preparation.ts +41 -17
  140. package/src/lib/server/chat-execution/chat-turn-stream-execution.ts +4 -2
  141. package/src/lib/server/chat-execution/chat-turn-tool-routing.test.ts +45 -0
  142. package/src/lib/server/chat-execution/chat-turn-tool-routing.ts +48 -17
  143. package/src/lib/server/chat-execution/continuation-evaluator.ts +4 -1
  144. package/src/lib/server/chat-execution/direct-memory-intent.test.ts +9 -0
  145. package/src/lib/server/chat-execution/direct-memory-intent.ts +12 -2
  146. package/src/lib/server/chat-execution/message-classifier.test.ts +35 -23
  147. package/src/lib/server/chat-execution/message-classifier.ts +74 -32
  148. package/src/lib/server/chat-execution/prompt-builder.test.ts +29 -0
  149. package/src/lib/server/chat-execution/prompt-builder.ts +37 -2
  150. package/src/lib/server/chat-execution/prompt-sections.test.ts +56 -0
  151. package/src/lib/server/chat-execution/prompt-sections.ts +193 -0
  152. package/src/lib/server/chat-execution/stream-agent-chat.ts +63 -7
  153. package/src/lib/server/chat-execution/stream-continuation.test.ts +36 -0
  154. package/src/lib/server/chat-execution/stream-continuation.ts +28 -13
  155. package/src/lib/server/chatrooms/chatroom-agent-signals.ts +26 -18
  156. package/src/lib/server/chatrooms/chatroom-helpers.ts +19 -18
  157. package/src/lib/server/chatrooms/chatroom-repository.ts +16 -0
  158. package/src/lib/server/chatrooms/chatroom-routing.test.ts +96 -0
  159. package/src/lib/server/chatrooms/chatroom-routing.ts +207 -53
  160. package/src/lib/server/chatrooms/mailbox-utils.ts +4 -2
  161. package/src/lib/server/chatrooms/session-mailbox.ts +50 -40
  162. package/src/lib/server/chats/chat-session-service.ts +410 -0
  163. package/src/lib/server/connectors/access.ts +1 -1
  164. package/src/lib/server/connectors/commands.ts +7 -6
  165. package/src/lib/server/connectors/connector-inbound.ts +14 -7
  166. package/src/lib/server/connectors/connector-outbound.ts +16 -11
  167. package/src/lib/server/connectors/connector-service.ts +453 -0
  168. package/src/lib/server/connectors/delivery.ts +17 -12
  169. package/src/lib/server/connectors/inbound-audio-transcription.ts +5 -14
  170. package/src/lib/server/connectors/media.ts +1 -1
  171. package/src/lib/server/connectors/response-media.ts +1 -1
  172. package/src/lib/server/connectors/session-consolidation.ts +11 -7
  173. package/src/lib/server/connectors/session.ts +9 -7
  174. package/src/lib/server/connectors/voice-note.ts +2 -1
  175. package/src/lib/server/context-manager.ts +20 -1
  176. package/src/lib/server/cost.ts +2 -3
  177. package/src/lib/server/credentials/credential-repository.ts +43 -4
  178. package/src/lib/server/credentials/credential-service.ts +112 -0
  179. package/src/lib/server/daemon/admin-metadata.ts +64 -0
  180. package/src/lib/server/daemon/controller.ts +577 -0
  181. package/src/lib/server/daemon/daemon-runtime.ts +352 -0
  182. package/src/lib/server/daemon/daemon-status-repository.ts +63 -0
  183. package/src/lib/server/daemon/types.ts +101 -0
  184. package/src/lib/server/embeddings.ts +3 -9
  185. package/src/lib/server/eval/agent-regression.ts +3 -2
  186. package/src/lib/server/eval/runner.ts +2 -2
  187. package/src/lib/server/execution-brief.test.ts +167 -0
  188. package/src/lib/server/execution-brief.ts +295 -0
  189. package/src/lib/server/execution-engine/chat-turn.ts +9 -0
  190. package/src/lib/server/execution-engine/import-boundary.test.ts +44 -0
  191. package/src/lib/server/execution-engine/index.ts +35 -0
  192. package/src/lib/server/execution-engine/task-attempt.ts +303 -0
  193. package/src/lib/server/execution-engine/types.ts +33 -0
  194. package/src/lib/server/gateways/gateway-profile-repository.ts +47 -3
  195. package/src/lib/server/gateways/gateway-profile-service.ts +200 -0
  196. package/src/lib/server/memory/session-archive-memory.ts +12 -10
  197. package/src/lib/server/messages/message-repository.ts +330 -0
  198. package/src/lib/server/missions/mission-service/core.ts +8 -6
  199. package/src/lib/server/openclaw/agent-resolver.ts +2 -3
  200. package/src/lib/server/openclaw/doctor.ts +1 -1
  201. package/src/lib/server/openclaw/gateway.test.ts +10 -1
  202. package/src/lib/server/openclaw/gateway.ts +5 -14
  203. package/src/lib/server/openclaw/health.ts +3 -11
  204. package/src/lib/server/openclaw/sync.ts +8 -6
  205. package/src/lib/server/persistence/storage-context.ts +3 -0
  206. package/src/lib/server/protocols/protocol-agent-turn.ts +25 -17
  207. package/src/lib/server/protocols/protocol-normalization.ts +1 -1
  208. package/src/lib/server/protocols/protocol-queries.ts +13 -7
  209. package/src/lib/server/protocols/protocol-run-lifecycle.ts +16 -20
  210. package/src/lib/server/protocols/protocol-run-repository.ts +81 -0
  211. package/src/lib/server/protocols/protocol-step-processors.ts +23 -31
  212. package/src/lib/server/protocols/protocol-swarm.ts +8 -8
  213. package/src/lib/server/protocols/protocol-template-repository.ts +42 -0
  214. package/src/lib/server/protocols/protocol-templates.ts +4 -2
  215. package/src/lib/server/protocols/protocol-types.ts +10 -7
  216. package/src/lib/server/provider-endpoint.ts +7 -12
  217. package/src/lib/server/provider-model-discovery.ts +2 -11
  218. package/src/lib/server/query-expansion.ts +5 -6
  219. package/src/lib/server/run-context.test.ts +365 -0
  220. package/src/lib/server/run-context.ts +367 -0
  221. package/src/lib/server/runtime/heartbeat-service.ts +7 -5
  222. package/src/lib/server/runtime/queue/core.ts +61 -190
  223. package/src/lib/server/runtime/run-ledger.ts +8 -0
  224. package/src/lib/server/runtime/session-run-manager/drain.ts +2 -2
  225. package/src/lib/server/runtime/session-run-manager/enqueue.ts +6 -0
  226. package/src/lib/server/runtime/session-run-manager/state.ts +4 -0
  227. package/src/lib/server/schedules/schedule-route-service.ts +230 -0
  228. package/src/lib/server/service-result.ts +16 -0
  229. package/src/lib/server/session-note.ts +2 -3
  230. package/src/lib/server/session-reset-policy.ts +4 -3
  231. package/src/lib/server/session-tools/connector.ts +9 -6
  232. package/src/lib/server/session-tools/context-mgmt.ts +58 -9
  233. package/src/lib/server/session-tools/crud.ts +162 -10
  234. package/src/lib/server/session-tools/delegate.ts +1 -1
  235. package/src/lib/server/session-tools/manage-tasks.test.ts +152 -0
  236. package/src/lib/server/session-tools/memory.ts +6 -4
  237. package/src/lib/server/session-tools/session-info.test.ts +56 -0
  238. package/src/lib/server/session-tools/session-info.ts +119 -12
  239. package/src/lib/server/session-tools/skill-runtime.ts +3 -1
  240. package/src/lib/server/session-tools/skills.ts +15 -15
  241. package/src/lib/server/session-tools/subagent.test.ts +115 -1
  242. package/src/lib/server/session-tools/subagent.ts +125 -7
  243. package/src/lib/server/session-tools/team-context.ts +4 -3
  244. package/src/lib/server/session-tools/wallet.ts +0 -58
  245. package/src/lib/server/sessions/session-lineage.ts +55 -0
  246. package/src/lib/server/sessions/session-repository.ts +2 -2
  247. package/src/lib/server/skills/learned-skills.ts +24 -23
  248. package/src/lib/server/skills/runtime-skill-resolver.ts +2 -1
  249. package/src/lib/server/skills/skill-repository.ts +136 -13
  250. package/src/lib/server/skills/skill-suggestions.ts +25 -28
  251. package/src/lib/server/storage-normalization.test.ts +42 -215
  252. package/src/lib/server/storage-normalization.ts +98 -0
  253. package/src/lib/server/storage.ts +19 -0
  254. package/src/lib/server/structured-extract.ts +3 -14
  255. package/src/lib/server/tasks/task-followups.ts +16 -11
  256. package/src/lib/server/tasks/task-result.test.ts +25 -29
  257. package/src/lib/server/tasks/task-result.ts +5 -9
  258. package/src/lib/server/tasks/task-route-service.ts +449 -0
  259. package/src/lib/server/text-normalization.ts +41 -0
  260. package/src/lib/server/tool-planning.ts +6 -42
  261. package/src/lib/server/upload-path.ts +5 -0
  262. package/src/lib/server/working-state/extraction.ts +614 -0
  263. package/src/lib/server/working-state/normalization.ts +866 -0
  264. package/src/lib/server/working-state/prompt.ts +60 -0
  265. package/src/lib/server/working-state/repository.ts +38 -0
  266. package/src/lib/server/working-state/service.test.ts +253 -0
  267. package/src/lib/server/working-state/service.ts +293 -0
  268. package/src/lib/validation/schemas.ts +1 -0
  269. package/src/lib/ws-client.ts +3 -3
  270. package/src/stores/slices/task-slice.ts +1 -4
  271. package/src/stores/use-chatroom-store.ts +2 -2
  272. package/src/types/index.ts +288 -22
  273. package/src/views/settings/section-providers.tsx +2 -2
@@ -5,7 +5,7 @@ import { api } from '@/lib/app/api-client'
5
5
  import { dedup, errorMessage } from '@/lib/shared-utils'
6
6
  import { getDefaultModelForProvider } from '@/lib/setup-defaults'
7
7
  import { OpenClawDeployPanel } from '@/components/openclaw/openclaw-deploy-panel'
8
- import type { Credential, Credentials, GatewayProfile, ProviderConfig } from '@/types'
8
+ import type { Credential, Credentials, GatewayProfile, ProviderId, ProviderConfig } from '@/types'
9
9
  import type { StepConnectProps, CheckState, ProviderCheckResponse, ConfiguredProvider } from './types'
10
10
  import {
11
11
  formatEndpointHost,
@@ -35,7 +35,9 @@ export function StepConnect({
35
35
  const [checkMessage, setCheckMessage] = useState('')
36
36
  const [checkErrorCode, setCheckErrorCode] = useState<string | null>(null)
37
37
  const [openclawDeviceId, setOpenclawDeviceId] = useState<string | null>(null)
38
- const [providerSuggestedModel, setProviderSuggestedModel] = useState(provider === 'custom' ? '' : getDefaultModelForProvider(provider))
38
+ const [providerSuggestedModel, setProviderSuggestedModel] = useState(
39
+ editingProvider?.defaultModel || (provider === 'custom' ? '' : getDefaultModelForProvider(provider)),
40
+ )
39
41
  const [saving, setSaving] = useState(false)
40
42
  const [error, setError] = useState('')
41
43
  const [existingCredentials, setExistingCredentials] = useState<Credential[]>([])
@@ -63,6 +65,7 @@ export function StepConnect({
63
65
  const supportsEndpoint = selectedProvider.supportsEndpoint || isCustom
64
66
  const keyIsOptional = (selectedProvider.optionalKey && !isOllamaCloud) || isCustom
65
67
  const requiresVerifiedConnection = provider === 'openclaw'
68
+ const canCheckConnection = !isCustom
66
69
  const openClawEndpointValue = provider === 'openclaw'
67
70
  ? (endpoint.trim() || selectedProvider.defaultEndpoint || 'http://localhost:18789/v1')
68
71
  : null
@@ -162,6 +165,11 @@ export function StepConnect({
162
165
  return
163
166
  }
164
167
 
168
+ if (isCustom && !providerSuggestedModel.trim()) {
169
+ setError('Custom providers need a default model ID.')
170
+ return
171
+ }
172
+
165
173
  setSaving(true)
166
174
  setError('')
167
175
  try {
@@ -187,17 +195,17 @@ export function StepConnect({
187
195
  }
188
196
 
189
197
  // Custom providers: create a ProviderConfig in the DB so agents can reference it
190
- let resolvedProvider = provider
198
+ let resolvedProvider: ProviderId = provider
191
199
  if (isCustom) {
192
200
  const customConfig = await api<ProviderConfig>('POST', '/providers', {
193
201
  name: providerLabel.trim() || 'Custom Provider',
194
202
  baseUrl: endpoint.trim(),
195
- models: providerSuggestedModel ? [providerSuggestedModel] : [],
196
- requiresApiKey: !!apiKey.trim(),
203
+ models: providerSuggestedModel.trim() ? [providerSuggestedModel.trim()] : [],
204
+ requiresApiKey: hasKeyOrCredential,
197
205
  credentialId: nextCredentialId || null,
198
206
  isEnabled: true,
199
207
  })
200
- resolvedProvider = customConfig.id as typeof provider
208
+ resolvedProvider = customConfig.id as ProviderId
201
209
  }
202
210
 
203
211
  // Build a tokenized dashboard URL for OpenClaw so step-agents can link to it
@@ -208,12 +216,13 @@ export function StepConnect({
208
216
  }
209
217
 
210
218
  const configured: ConfiguredProvider = {
211
- id: crypto.randomUUID(),
219
+ id: Math.random().toString(16).slice(2, 10),
220
+ setupProvider: provider,
212
221
  provider: resolvedProvider,
213
222
  name: providerLabel.trim() || selectedProvider.name,
214
223
  credentialId: nextCredentialId || null,
215
224
  endpoint: supportsEndpoint ? (endpoint.trim() || selectedProvider.defaultEndpoint || null) : null,
216
- defaultModel: providerSuggestedModel || (isCustom ? '' : getDefaultModelForProvider(provider)),
225
+ defaultModel: providerSuggestedModel.trim() || (isCustom ? '' : getDefaultModelForProvider(provider)),
217
226
  gatewayProfileId: null,
218
227
  notes: providerNotes.trim() || null,
219
228
  tags: providerTags,
@@ -323,6 +332,26 @@ export function StepConnect({
323
332
  </div>
324
333
  )}
325
334
 
335
+ {isCustom && (
336
+ <div>
337
+ <label className="block text-[12px] text-text-3 font-500 mb-1.5 ml-1">
338
+ Default model
339
+ </label>
340
+ <input
341
+ type="text"
342
+ value={providerSuggestedModel}
343
+ onChange={(e) => setProviderSuggestedModel(e.target.value)}
344
+ placeholder="e.g. gpt-4o-mini"
345
+ className="w-full px-4 py-3 rounded-[12px] border border-white/[0.08] bg-surface
346
+ text-text text-[14px] font-mono outline-none transition-all duration-200
347
+ focus:border-accent-bright/30 focus:shadow-[0_0_30px_rgba(99,102,241,0.1)]"
348
+ />
349
+ <p className="mt-1.5 text-[11px] text-text-3">
350
+ Save the model ID you want starter agents to use with this provider. You can change it later per agent.
351
+ </p>
352
+ </div>
353
+ )}
354
+
326
355
  {provider === 'openclaw' && (
327
356
  <div className="rounded-[14px] border border-white/[0.08] bg-surface p-4 space-y-4">
328
357
  <OpenClawDeployPanel
@@ -536,17 +565,19 @@ export function StepConnect({
536
565
  >
537
566
  Back
538
567
  </button>
539
- <button
540
- onClick={runConnectionCheck}
541
- disabled={checkState === 'checking' || saving}
542
- className="px-6 py-3.5 rounded-[14px] border border-white/[0.08] bg-white/[0.03] text-text text-[14px]
543
- font-display font-600 cursor-pointer hover:bg-white/[0.06] transition-all duration-200 disabled:opacity-40"
544
- >
545
- {checkState === 'checking' ? 'Checking...' : 'Check Connection'}
546
- </button>
568
+ {canCheckConnection && (
569
+ <button
570
+ onClick={runConnectionCheck}
571
+ disabled={checkState === 'checking' || saving}
572
+ className="px-6 py-3.5 rounded-[14px] border border-white/[0.08] bg-white/[0.03] text-text text-[14px]
573
+ font-display font-600 cursor-pointer hover:bg-white/[0.06] transition-all duration-200 disabled:opacity-40"
574
+ >
575
+ {checkState === 'checking' ? 'Checking...' : 'Check Connection'}
576
+ </button>
577
+ )}
547
578
  <button
548
579
  onClick={saveProvider}
549
- disabled={(requiresKey && !hasKeyOrCredential) || saving}
580
+ disabled={(requiresKey && !hasKeyOrCredential) || (isCustom && !providerSuggestedModel.trim()) || saving}
550
581
  className="px-8 py-3.5 rounded-[14px] border-none bg-accent-bright text-white text-[15px] font-display font-600
551
582
  cursor-pointer hover:brightness-110 active:scale-[0.97] transition-all duration-200
552
583
  shadow-[0_6px_28px_rgba(99,102,241,0.3)] disabled:opacity-30"
@@ -1,4 +1,4 @@
1
- import type { GatewayProfile } from '@/types'
1
+ import type { GatewayProfile, ProviderId } from '@/types'
2
2
  import type { SetupProvider } from '@/lib/setup-defaults'
3
3
 
4
4
  export type SetupStep = 'profile' | 'providers' | 'connect' | 'agents' | 'next' | 'done'
@@ -34,7 +34,8 @@ export interface SetupWizardProps {
34
34
 
35
35
  export interface ConfiguredProvider {
36
36
  id: string
37
- provider: SetupProvider
37
+ setupProvider: SetupProvider
38
+ provider: ProviderId
38
39
  name: string
39
40
  credentialId: string | null
40
41
  endpoint: string | null
@@ -56,7 +57,8 @@ export interface StarterDraftAgent {
56
57
  systemPrompt: string
57
58
  soul: string
58
59
  providerConfigId: string | null
59
- provider: SetupProvider | null
60
+ setupProvider: SetupProvider | null
61
+ provider: ProviderId | null
60
62
  model: string
61
63
  credentialId: string | null
62
64
  apiEndpoint: string | null
@@ -77,7 +79,7 @@ export interface StarterDraftAgent {
77
79
  export interface CreatedAgentSummary {
78
80
  id: string
79
81
  name: string
80
- provider: SetupProvider
82
+ provider: ProviderId
81
83
  providerName: string
82
84
  }
83
85
 
@@ -6,6 +6,7 @@ import {
6
6
  isLocalOpenClawEndpoint,
7
7
  resolveOpenClawDashboardUrl,
8
8
  getOpenClawErrorHint,
9
+ requiresSetupProviderVerification,
9
10
  withHttpScheme,
10
11
  buildStarterDrafts,
11
12
  preferredConfiguredProvider,
@@ -165,21 +166,24 @@ test('withHttpScheme preserves wss://', () => {
165
166
  // buildStarterDrafts — OpenClaw provider handling
166
167
  // ---------------------------------------------------------------------------
167
168
 
168
- function makeConfiguredProvider(overrides: Partial<ConfiguredProvider> & { provider: ConfiguredProvider['provider'] }): ConfiguredProvider {
169
+ function makeConfiguredProvider(overrides: Partial<ConfiguredProvider> & { setupProvider: ConfiguredProvider['setupProvider']; provider?: ConfiguredProvider['provider'] }): ConfiguredProvider {
170
+ const { setupProvider, provider = setupProvider, ...rest } = overrides
169
171
  return {
170
172
  id: 'cp-1',
173
+ setupProvider,
174
+ provider,
171
175
  name: 'Test Provider',
172
176
  credentialId: null,
173
177
  endpoint: null,
174
178
  defaultModel: '',
175
179
  gatewayProfileId: null,
176
180
  verified: true,
177
- ...overrides,
181
+ ...rest,
178
182
  }
179
183
  }
180
184
 
181
185
  test('buildStarterDrafts assigns OpenClaw provider to drafts', () => {
182
- const cp = makeConfiguredProvider({ provider: 'openclaw', endpoint: 'http://localhost:18789' })
186
+ const cp = makeConfiguredProvider({ setupProvider: 'openclaw', endpoint: 'http://localhost:18789' })
183
187
  const drafts = buildStarterDrafts({
184
188
  starterKitId: 'personal_assistant',
185
189
  intentText: '',
@@ -188,12 +192,13 @@ test('buildStarterDrafts assigns OpenClaw provider to drafts', () => {
188
192
  assert.ok(drafts.length > 0, 'should produce at least one draft')
189
193
  for (const d of drafts) {
190
194
  assert.equal(d.provider, 'openclaw')
195
+ assert.equal(d.setupProvider, 'openclaw')
191
196
  assert.equal(d.providerConfigId, cp.id)
192
197
  }
193
198
  })
194
199
 
195
200
  test('buildStarterDrafts OpenClaw drafts use empty model (not "default")', () => {
196
- const cp = makeConfiguredProvider({ provider: 'openclaw', defaultModel: '' })
201
+ const cp = makeConfiguredProvider({ setupProvider: 'openclaw', defaultModel: '' })
197
202
  const drafts = buildStarterDrafts({
198
203
  starterKitId: 'personal_assistant',
199
204
  intentText: '',
@@ -206,7 +211,7 @@ test('buildStarterDrafts OpenClaw drafts use empty model (not "default")', () =>
206
211
  })
207
212
 
208
213
  test('buildStarterDrafts OpenClaw drafts inherit endpoint from provider', () => {
209
- const cp = makeConfiguredProvider({ provider: 'openclaw', endpoint: 'http://10.0.0.5:18789' })
214
+ const cp = makeConfiguredProvider({ setupProvider: 'openclaw', endpoint: 'http://10.0.0.5:18789' })
210
215
  const drafts = buildStarterDrafts({
211
216
  starterKitId: 'personal_assistant',
212
217
  intentText: '',
@@ -219,7 +224,7 @@ test('buildStarterDrafts OpenClaw drafts inherit endpoint from provider', () =>
219
224
 
220
225
  test('buildStarterDrafts carries dashboardUrl through from ConfiguredProvider', () => {
221
226
  const cp = makeConfiguredProvider({
222
- provider: 'openclaw',
227
+ setupProvider: 'openclaw',
223
228
  endpoint: 'http://localhost:18789',
224
229
  dashboardUrl: 'http://localhost:18789?token=my-secret',
225
230
  })
@@ -228,11 +233,36 @@ test('buildStarterDrafts carries dashboardUrl through from ConfiguredProvider',
228
233
  })
229
234
 
230
235
  test('preferredConfiguredProvider picks openclaw provider for openclaw template', () => {
231
- const openclawCp = makeConfiguredProvider({ id: 'oc-1', provider: 'openclaw' })
232
- const openaiCp = makeConfiguredProvider({ id: 'oai-1', provider: 'openai' })
236
+ const openclawCp = makeConfiguredProvider({ id: 'oc-1', setupProvider: 'openclaw' })
237
+ const openaiCp = makeConfiguredProvider({ id: 'oai-1', setupProvider: 'openai' })
233
238
  const result = preferredConfiguredProvider(
234
239
  { id: 'tmpl-1', name: 'Test', description: '', systemPrompt: '', tools: [], recommendedProviders: ['openclaw'] },
235
240
  [openaiCp, openclawCp],
236
241
  )
237
242
  assert.equal(result?.id, 'oc-1')
238
243
  })
244
+
245
+ test('buildStarterDrafts carries custom runtime provider ids alongside custom setup provider state', () => {
246
+ const cp = makeConfiguredProvider({
247
+ setupProvider: 'custom',
248
+ provider: 'custom-openrouter',
249
+ defaultModel: 'openai/gpt-4.1',
250
+ })
251
+ const drafts = buildStarterDrafts({
252
+ starterKitId: 'personal_assistant',
253
+ intentText: '',
254
+ configuredProviders: [cp],
255
+ })
256
+
257
+ for (const draft of drafts) {
258
+ assert.equal(draft.setupProvider, 'custom')
259
+ assert.equal(draft.provider, 'custom-openrouter')
260
+ assert.equal(draft.model, 'openai/gpt-4.1')
261
+ }
262
+ })
263
+
264
+ test('requiresSetupProviderVerification skips custom providers', () => {
265
+ assert.equal(requiresSetupProviderVerification('custom'), false)
266
+ assert.equal(requiresSetupProviderVerification('openclaw'), false)
267
+ assert.equal(requiresSetupProviderVerification('openai'), true)
268
+ })
@@ -91,6 +91,10 @@ export function getOpenClawErrorHint(message: string): string | null {
91
91
  return null
92
92
  }
93
93
 
94
+ export function requiresSetupProviderVerification(provider: SetupProvider | null | undefined): boolean {
95
+ return provider != null && provider !== 'openclaw' && provider !== 'custom'
96
+ }
97
+
94
98
  export function preferredConfiguredProvider(
95
99
  template: StarterKitAgentTemplate,
96
100
  configuredProviders: ConfiguredProvider[],
@@ -103,12 +107,12 @@ export function preferredConfiguredProvider(
103
107
  }
104
108
 
105
109
  if (fallbackProvider) {
106
- const exact = configuredProviders.find((candidate) => candidate.provider === fallbackProvider)
110
+ const exact = configuredProviders.find((candidate) => candidate.setupProvider === fallbackProvider)
107
111
  if (exact) return exact
108
112
  }
109
113
 
110
114
  for (const provider of template.recommendedProviders || []) {
111
- const exact = configuredProviders.find((candidate) => candidate.provider === provider)
115
+ const exact = configuredProviders.find((candidate) => candidate.setupProvider === provider)
112
116
  if (exact) return exact
113
117
  }
114
118
 
@@ -134,14 +138,15 @@ export function buildStarterDrafts(args: {
134
138
  template,
135
139
  configuredProviders,
136
140
  previous?.providerConfigId,
137
- previous?.provider,
141
+ previous?.setupProvider,
138
142
  )
139
- const oldProvider = previous?.provider || null
143
+ const oldProvider = previous?.setupProvider || null
144
+ const previousModel = previous?.model || ''
140
145
  const oldProviderDefault = oldProvider ? getDefaultModelForProvider(oldProvider) : ''
141
146
  const nextProviderDefault = configuredProvider?.defaultModel || ''
142
147
  const shouldRefreshModel =
143
- !previous?.model
144
- || (oldProvider !== configuredProvider?.provider && previous.model === oldProviderDefault)
148
+ !previousModel.trim()
149
+ || (oldProvider !== configuredProvider?.setupProvider && previousModel === oldProviderDefault)
145
150
 
146
151
  return {
147
152
  id,
@@ -151,8 +156,9 @@ export function buildStarterDrafts(args: {
151
156
  systemPrompt: previous?.systemPrompt || applyIntentContext(template.systemPrompt, intentText),
152
157
  soul: previous?.soul || '',
153
158
  providerConfigId: configuredProvider?.id || null,
159
+ setupProvider: configuredProvider?.setupProvider || null,
154
160
  provider: configuredProvider?.provider || null,
155
- model: shouldRefreshModel ? nextProviderDefault : previous.model,
161
+ model: shouldRefreshModel ? nextProviderDefault : previousModel,
156
162
  credentialId: configuredProvider?.credentialId || null,
157
163
  apiEndpoint: configuredProvider?.endpoint || null,
158
164
  gatewayProfileId: configuredProvider?.gatewayProfileId || null,
@@ -164,7 +170,7 @@ export function buildStarterDrafts(args: {
164
170
  autoDraftSkillSuggestions: previous?.autoDraftSkillSuggestions ?? true,
165
171
  orchestratorEnabled: previous?.orchestratorEnabled ?? false,
166
172
  orchestratorMission: previous?.orchestratorMission || '',
167
- avatarSeed: previous?.avatarSeed || crypto.randomUUID().slice(0, 8),
173
+ avatarSeed: previous?.avatarSeed || Math.random().toString(36).slice(2, 10),
168
174
  avatarUrl: previous?.avatarUrl || null,
169
175
  enabled: previous?.enabled ?? true,
170
176
  }