@swarmclawai/swarmclaw 1.4.8 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -215,6 +215,16 @@ SwarmClaw agents can join [SwarmFeed](https://swarmfeed.ai) — a social network
215
215
 
216
216
  Read the docs at [swarmclaw.ai/docs/swarmfeed](https://swarmclaw.ai/docs/swarmfeed) and visit [swarmfeed.ai](https://swarmfeed.ai) for the platform itself.
217
217
 
218
+ ### v1.5.0 Highlights
219
+
220
+ - **First-run activation refresh**: setup now includes a dedicated start-path step, broad starter shapes instead of niche presets, and draft agents generated directly from the chosen setup shape.
221
+ - **Guided post-setup launchpad**: finishing setup now routes through action-oriented next steps such as opening the first agent chat, launching a structured session, connecting platforms, or reviewing usage.
222
+ - **State-aware home and protocols**: fresh workspaces now open on a launchpad instead of a sparse ops dashboard, and the Protocols page now surfaces the visual builder and template gallery directly.
223
+
224
+ ### v1.4.9 Highlights
225
+
226
+ - **Standalone build reliability**: `public/`, `.next/static/`, and `css-tree/data/` are now automatically copied into the standalone build output, fixing runtime crashes and missing assets when running the standalone bundle. (Community contribution by [@borislavnnikolov](https://github.com/borislavnnikolov) — PR #34)
227
+
218
228
  ### v1.4.8 Highlights
219
229
 
220
230
  - **Agent-scoped SwarmFeed dashboard**: the in-app feed now has an explicit acting-agent model so humans can direct social actions without ever posting as a separate user identity.
@@ -351,7 +361,7 @@ Then open `http://localhost:3456`.
351
361
  - **Delegation**: built-in delegation to Claude Code, Codex CLI, OpenCode CLI, Gemini CLI, and native SwarmClaw subagents.
352
362
  - **Autonomy**: heartbeat loops, schedules, background jobs, task execution, supervisor recovery, and agent wakeups.
353
363
  - **Orchestration**: durable structured execution with branching, repeat loops, parallel branches, explicit joins, restart-safe run state, and contextual launch from chats, chatrooms, tasks, schedules, and API flows.
354
- - **Structured Sessions**: reusable bounded runs with templates, facilitators, participants, hidden live rooms, chatroom `/breakout`, durable transcripts, outputs, and operator controls.
364
+ - **Structured Sessions**: reusable bounded runs with templates, facilitators, participants, hidden live rooms, chatroom `/breakout`, durable transcripts, outputs, operator controls, and a visible protocols template gallery plus visual builder.
355
365
  - **Memory**: hybrid recall, graph traversal, journaling, durable documents, project-scoped context, automatic reflection memory, communication preferences, profile and boundary memory, significant events, and open follow-up loops.
356
366
  - **Wallets**: linked Base wallet generation, address management, approval-oriented limits, and agent payout identity.
357
367
  - **Connectors**: Discord, Slack, Telegram, WhatsApp, Teams, Matrix, OpenClaw, SwarmDock, SwarmFeed, and more.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swarmclawai/swarmclaw",
3
- "version": "1.4.8",
3
+ "version": "1.5.0",
4
4
  "description": "Self-hosted AI runtime for OpenClaw, delegation, autonomy, runtime skills, crypto wallets, and chat platform connectors.",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
@@ -56,6 +56,44 @@ function hasRequiredNextMetadataFiles(dir) {
56
56
  return REQUIRED_NEXT_METADATA_FILES.every((fileName) => fs.existsSync(path.join(dir, fileName)))
57
57
  }
58
58
 
59
+ export function repairStandalonePublicAndStatic(cwd = process.cwd()) {
60
+ const standaloneDir = path.join(cwd, '.next', 'standalone')
61
+ if (!fs.existsSync(standaloneDir)) return false
62
+
63
+ let repaired = false
64
+
65
+ // Next.js standalone does not copy public/ or .next/static/ automatically.
66
+ const publicSrc = path.join(cwd, 'public')
67
+ const publicDst = path.join(standaloneDir, 'public')
68
+ if (fs.existsSync(publicSrc) && !fs.existsSync(publicDst)) {
69
+ fs.cpSync(publicSrc, publicDst, { recursive: true, force: true })
70
+ repaired = true
71
+ }
72
+
73
+ const staticSrc = path.join(cwd, '.next', 'static')
74
+ const staticDst = path.join(standaloneDir, '.next', 'static')
75
+ if (fs.existsSync(staticSrc) && !fs.existsSync(staticDst)) {
76
+ fs.cpSync(staticSrc, staticDst, { recursive: true, force: true })
77
+ repaired = true
78
+ }
79
+
80
+ return repaired
81
+ }
82
+
83
+ export function repairStandaloneCssTreeData(cwd = process.cwd()) {
84
+ const standaloneDir = path.join(cwd, '.next', 'standalone')
85
+ if (!fs.existsSync(standaloneDir)) return false
86
+
87
+ const dataDst = path.join(standaloneDir, 'node_modules', 'css-tree', 'data')
88
+ if (fs.existsSync(dataDst)) return false
89
+
90
+ const dataSrc = path.join(cwd, 'node_modules', 'css-tree', 'data')
91
+ if (!fs.existsSync(dataSrc)) return false
92
+
93
+ fs.cpSync(dataSrc, dataDst, { recursive: true, force: true })
94
+ return true
95
+ }
96
+
59
97
  export function repairStandaloneNextMetadata(cwd = process.cwd()) {
60
98
  const standaloneDir = path.join(cwd, '.next', 'standalone')
61
99
  if (!fs.existsSync(standaloneDir)) return false
@@ -106,6 +144,12 @@ function main() {
106
144
  if (result.status === 0 && repairStandaloneNextMetadata(process.cwd())) {
107
145
  console.error('Repaired missing Next metadata runtime files in the standalone build output.')
108
146
  }
147
+ if (result.status === 0 && repairStandalonePublicAndStatic(process.cwd())) {
148
+ console.error('Copied public/ and .next/static/ into standalone build output.')
149
+ }
150
+ if (result.status === 0 && repairStandaloneCssTreeData(process.cwd())) {
151
+ console.error('Copied css-tree/data/ into standalone build output.')
152
+ }
109
153
  process.exit(result.status)
110
154
  }
111
155
  if (result.signal) {
@@ -1,16 +1,20 @@
1
1
  'use client'
2
2
 
3
3
  import { useEffect, useMemo, useState } from 'react'
4
+ import { useRouter } from 'next/navigation'
4
5
  import { AreaChart, Area, ResponsiveContainer, Tooltip } from 'recharts'
5
6
  import { useAppStore } from '@/stores/use-app-store'
6
7
  import { useChatStore } from '@/stores/use-chat-store'
7
8
  import { AgentAvatar } from '@/components/agents/agent-avatar'
9
+ import { HomeLaunchpad } from '@/components/home/home-launchpad'
8
10
  import { useMountedRef } from '@/hooks/use-mounted-ref'
9
11
  import { useNow } from '@/hooks/use-now'
10
12
  import { api } from '@/lib/app/api-client'
11
13
  import { useNavigate } from '@/lib/app/navigation'
14
+ import { safeStorageGet, safeStorageRemove } from '@/lib/app/safe-storage'
12
15
  import { isLocalhostBrowser, isVisibleSessionForViewer } from '@/lib/observability/local-observability'
13
16
  import { getSessionLastMessage } from '@/lib/chat/session-summary'
17
+ import { DEFAULT_BUILDER_ROUTE, deriveHomeMode, HOME_LAUNCHPAD_AFTER_SETUP_KEY } from '@/lib/home-launchpad'
14
18
  import { getNotificationActivityAt, getNotificationOccurrenceCount } from '@/lib/notifications/notification-utils'
15
19
  import { timeAgo, timeUntil } from '@/lib/time-format'
16
20
  import type { Agent, Session, BoardTask, AppNotification, ActivityEntry } from '@/types'
@@ -79,6 +83,7 @@ const PLATFORM_LABELS: Record<string, string> = {
79
83
  }
80
84
 
81
85
  export default function HomePage() {
86
+ const router = useRouter()
82
87
  const now = useNow()
83
88
  const agents = useAppStore((s) => s.agents)
84
89
  const sessions = useAppStore((s) => s.sessions)
@@ -103,14 +108,22 @@ export default function HomePage() {
103
108
  const [costTrend, setCostTrend] = useState<{ cost: number; bucket: string }[]>([])
104
109
  const [localhostBrowser, setLocalhostBrowser] = useState(false)
105
110
  const [pageReady, setPageReady] = useState(false)
111
+ const [launchpadFlag, setLaunchpadFlag] = useState(false)
106
112
  const mountedRef = useMountedRef()
107
113
 
108
114
  useEffect(() => {
109
115
  setLocalhostBrowser(isLocalhostBrowser())
110
116
  }, [])
111
117
 
118
+ useEffect(() => {
119
+ const hasFlag = safeStorageGet(HOME_LAUNCHPAD_AFTER_SETUP_KEY) === '1'
120
+ setLaunchpadFlag(hasFlag)
121
+ if (hasFlag) safeStorageRemove(HOME_LAUNCHPAD_AFTER_SETUP_KEY)
122
+ }, [])
123
+
112
124
  const allAgents = Object.values(agents).filter((a) => !a.trashedAt)
113
125
  const pinnedAgents = allAgents.filter((a) => a.pinned)
126
+ const firstAgent = allAgents[0] || null
114
127
 
115
128
  const recentChats = useMemo(
116
129
  () =>
@@ -123,10 +136,14 @@ export default function HomePage() {
123
136
 
124
137
  // Quick stats
125
138
  const agentCount = allAgents.length
139
+ const sessionCount = Object.keys(sessions).length
126
140
  const allTasks = Object.values(tasks)
141
+ const totalTaskCount = allTasks.length
127
142
  const activeTaskCount = allTasks.filter((t) => t.status === 'running' || t.status === 'queued').length
128
143
  const allConnectors = Object.values(connectors)
129
144
  const activeConnectorCount = allConnectors.filter((c) => c.status === 'running').length
145
+ const connectorCount = allConnectors.length
146
+ const scheduleCount = Object.keys(schedules).length
130
147
 
131
148
  // Agents with running tasks
132
149
  const runningAgentIds = useMemo(() => {
@@ -223,6 +240,51 @@ export default function HomePage() {
223
240
  )
224
241
  }
225
242
 
243
+ const homeMode = deriveHomeMode({
244
+ hasLaunchpadFlag: launchpadFlag,
245
+ agentCount,
246
+ sessionCount,
247
+ taskCount: totalTaskCount,
248
+ scheduleCount,
249
+ connectorCount,
250
+ todayCost,
251
+ })
252
+
253
+ const openFirstAgent = () => {
254
+ if (firstAgent) {
255
+ navigateTo('agents', firstAgent.id)
256
+ return
257
+ }
258
+ navigateTo('agents')
259
+ }
260
+
261
+ const openBuilder = () => {
262
+ router.push(DEFAULT_BUILDER_ROUTE)
263
+ }
264
+
265
+ if (homeMode === 'launchpad') {
266
+ return (
267
+ <MainContent>
268
+ <div className="flex-1 overflow-y-auto">
269
+ <HomeLaunchpad
270
+ firstAgent={firstAgent}
271
+ agentCount={agentCount}
272
+ sessionCount={sessionCount}
273
+ taskCount={totalTaskCount}
274
+ scheduleCount={scheduleCount}
275
+ connectorCount={connectorCount}
276
+ todayCost={todayCost}
277
+ onOpenFirstAgent={openFirstAgent}
278
+ onOpenProtocols={() => navigateTo('protocols')}
279
+ onOpenBuilder={openBuilder}
280
+ onOpenConnectors={() => navigateTo('connectors')}
281
+ onOpenUsage={() => navigateTo('usage')}
282
+ />
283
+ </div>
284
+ </MainContent>
285
+ )
286
+ }
287
+
226
288
  return (
227
289
  <MainContent>
228
290
  <div className="flex-1 overflow-y-auto">
@@ -16,7 +16,9 @@ import {
16
16
  import { useTasksQuery } from '@/features/tasks/queries'
17
17
  import { MainContent } from '@/components/layout/main-content'
18
18
  import { StructuredSessionLauncher } from '@/components/protocols/structured-session-launcher'
19
+ import { TemplateGallery } from '@/components/protocols/builder/template-gallery'
19
20
  import { GroundingPanel } from '@/components/knowledge/grounding-panel'
21
+ import { DEFAULT_BUILDER_ROUTE } from '@/lib/home-launchpad'
20
22
  import { timeAgo } from '@/lib/time-format'
21
23
  import type {
22
24
  BoardTask,
@@ -390,6 +392,13 @@ export default function ProtocolsPage() {
390
392
  >
391
393
  New template
392
394
  </button>
395
+ <button
396
+ type="button"
397
+ onClick={() => router.push(DEFAULT_BUILDER_ROUTE)}
398
+ className="rounded-[10px] border border-white/[0.08] bg-white/[0.04] px-3 py-2 text-[12px] font-700 text-text-2 transition-all hover:bg-white/[0.08] cursor-pointer"
399
+ >
400
+ Open visual builder
401
+ </button>
393
402
  </div>
394
403
  </div>
395
404
 
@@ -417,6 +426,28 @@ export default function ProtocolsPage() {
417
426
  </div>
418
427
  </div>
419
428
 
429
+ <div className="mt-4 rounded-[16px] border border-white/[0.06] bg-white/[0.03] p-4">
430
+ <div className="flex flex-col gap-4 md:flex-row md:items-start md:justify-between">
431
+ <div className="max-w-[720px]">
432
+ <div className="text-[11px] font-700 uppercase tracking-[0.12em] text-text-3/55">Visual Workflow Templates</div>
433
+ <div className="mt-2 text-[15px] font-700 text-text">Pick a built-in protocol and open it in the builder.</div>
434
+ <div className="mt-2 text-[13px] leading-relaxed text-text-3/72">
435
+ Use the gallery for the fastest path into structured workflows. Custom templates stay here too, so teams can refine reusable runs without dropping straight into JSON first.
436
+ </div>
437
+ </div>
438
+ <button
439
+ type="button"
440
+ onClick={() => router.push(DEFAULT_BUILDER_ROUTE)}
441
+ className="rounded-[10px] bg-accent-bright px-3 py-2 text-[12px] font-700 text-black transition-all hover:opacity-90 cursor-pointer"
442
+ >
443
+ Open builder
444
+ </button>
445
+ </div>
446
+ <div className="mt-4">
447
+ <TemplateGallery templates={templates} />
448
+ </div>
449
+ </div>
450
+
420
451
  <details className="mt-4 rounded-[16px] border border-white/[0.06] bg-white/[0.03] p-4">
421
452
  <summary className="cursor-pointer list-none text-[11px] font-700 uppercase tracking-[0.12em] text-text-3/55">
422
453
  Advanced Manual Run Builder
@@ -2,16 +2,18 @@
2
2
 
3
3
  import { useRouter } from 'next/navigation'
4
4
  import { safeStorageSet } from '@/lib/app/safe-storage'
5
+ import { HOME_LAUNCHPAD_AFTER_SETUP_KEY } from '@/lib/home-launchpad'
5
6
  import { SetupWizard } from '@/components/auth/setup-wizard'
6
7
 
7
8
  export default function SetupPage() {
8
9
  const router = useRouter()
9
10
  return (
10
11
  <SetupWizard
11
- onComplete={() => {
12
+ onComplete={(destination) => {
12
13
  safeStorageSet('sc_setup_done', '1')
14
+ safeStorageSet(HOME_LAUNCHPAD_AFTER_SETUP_KEY, '1')
13
15
  window.dispatchEvent(new Event('sc:setup-complete'))
14
- router.replace('/home')
16
+ router.replace(destination || '/home')
15
17
  }}
16
18
  />
17
19
  )
@@ -7,11 +7,11 @@ import { dedup, errorMessage } from '@/lib/shared-utils'
7
7
  import type { ProviderId, GatewayProfile } from '@/types'
8
8
  import {
9
9
  SETUP_PROVIDERS,
10
- SWARMCLAW_ASSISTANT_PROMPT,
11
10
  getDefaultModelForProvider,
11
+ type OnboardingPath,
12
12
  type SetupProvider,
13
13
  } from '@/lib/setup-defaults'
14
- import { getDefaultAgentToolIds } from '@/lib/agent-default-tools'
14
+ import { DEFAULT_BUILDER_ROUTE } from '@/lib/home-launchpad'
15
15
  import type {
16
16
  SetupStep,
17
17
  SetupWizardProps,
@@ -21,9 +21,10 @@ import type {
21
21
  ProviderCheckResponse,
22
22
  } from './types'
23
23
  import { STEP_ORDER } from './types'
24
- import { requiresSetupProviderVerification, stepIndex } from './utils'
24
+ import { buildStarterDrafts, defaultKitForPath, getStarterKitsForPath, requiresSetupProviderVerification, stepIndex } from './utils'
25
25
  import { SparkleIcon } from './shared'
26
26
  import { StepProgress } from './step-progress'
27
+ import { StepPath } from './step-path'
27
28
  import { StepProviders } from './step-providers'
28
29
  import { StepConnect } from './step-connect'
29
30
  import { StepAgents } from './step-agents'
@@ -34,6 +35,9 @@ export function SetupWizard({ onComplete }: SetupWizardProps) {
34
35
  const setUser = useAppStore((s) => s.setUser)
35
36
  const loadSettings = useAppStore((s) => s.loadSettings)
36
37
  const [step, setStep] = useState<SetupStep>('profile')
38
+ const [onboardingPath, setOnboardingPath] = useState<OnboardingPath>('quick')
39
+ const [starterKitId, setStarterKitId] = useState<string>(defaultKitForPath('quick'))
40
+ const [intentText, setIntentText] = useState('')
37
41
 
38
42
  const [activeProvider, setActiveProvider] = useState<SetupProvider | null>(null)
39
43
  const [editingProviderId, setEditingProviderId] = useState<string | null>(null)
@@ -41,6 +45,7 @@ export function SetupWizard({ onComplete }: SetupWizardProps) {
41
45
 
42
46
  const [configuredProviders, setConfiguredProviders] = useState<ConfiguredProvider[]>([])
43
47
  const [draftAgents, setDraftAgents] = useState<StarterDraftAgent[]>([])
48
+ const [createdAgents, setCreatedAgents] = useState<CreatedAgentSummary[]>([])
44
49
  const [saving, setSaving] = useState(false)
45
50
  const [error, setError] = useState('')
46
51
 
@@ -56,13 +61,23 @@ export function SetupWizard({ onComplete }: SetupWizardProps) {
56
61
  const configuredProviderIds = new Set(configuredProviders.map((cp) => cp.setupProvider))
57
62
  const canContinueFromProviders = configuredProviders.length > 0
58
63
 
64
+ const ensureStarterDrafts = (providers: ConfiguredProvider[], previousDrafts: StarterDraftAgent[] = draftAgents) => {
65
+ if (previousDrafts.length > 0) return previousDrafts
66
+ return buildStarterDrafts({
67
+ starterKitId,
68
+ intentText,
69
+ configuredProviders: providers,
70
+ previousDrafts,
71
+ })
72
+ }
73
+
59
74
  const skip = async () => {
60
75
  try {
61
76
  await api('PUT', '/settings', { setupCompleted: true })
62
77
  } catch {
63
78
  // Continue anyway.
64
79
  }
65
- onComplete()
80
+ onComplete('/home')
66
81
  }
67
82
 
68
83
  const handleProfileContinue = async (userName: string, avatarSeed: string) => {
@@ -71,6 +86,20 @@ export function SetupWizard({ onComplete }: SetupWizardProps) {
71
86
  } catch { /* still set locally */ }
72
87
  setUser(userName)
73
88
  loadSettings()
89
+ setStep('path')
90
+ }
91
+
92
+ const handlePathChange = (path: OnboardingPath) => {
93
+ setOnboardingPath(path)
94
+ const visibleKits = getStarterKitsForPath(path)
95
+ const currentVisible = visibleKits.some((kit) => kit.id === starterKitId)
96
+ if (!currentVisible) {
97
+ setStarterKitId(defaultKitForPath(path))
98
+ }
99
+ }
100
+
101
+ const handlePathContinue = () => {
102
+ setError('')
74
103
  setStep('providers')
75
104
  }
76
105
 
@@ -117,33 +146,8 @@ export function SetupWizard({ onComplete }: SetupWizardProps) {
117
146
 
118
147
  // If this is the first provider and there are no agents yet, create a default agent
119
148
  if (!editingProviderId && draftAgents.length === 0) {
120
- const cp = configured
121
- setDraftAgents([{
122
- id: `auto:${Math.random().toString(36).slice(2, 10)}`,
123
- templateId: 'auto',
124
- name: 'Assistant',
125
- description: 'A helpful assistant.',
126
- systemPrompt: SWARMCLAW_ASSISTANT_PROMPT,
127
- soul: '',
128
- providerConfigId: cp.id,
129
- setupProvider: cp.setupProvider,
130
- provider: cp.provider,
131
- model: cp.defaultModel,
132
- credentialId: cp.credentialId,
133
- apiEndpoint: cp.endpoint,
134
- gatewayProfileId: cp.gatewayProfileId,
135
- tools: getDefaultAgentToolIds(),
136
- capabilities: [],
137
- delegationEnabled: false,
138
- delegationTargetMode: 'all',
139
- delegationTargetAgentIds: [],
140
- autoDraftSkillSuggestions: true,
141
- orchestratorEnabled: false,
142
- orchestratorMission: '',
143
- avatarSeed: Math.random().toString(36).slice(2, 10),
144
- avatarUrl: null,
145
- enabled: true,
146
- }])
149
+ const starterDrafts = ensureStarterDrafts(nextConfigured, [])
150
+ setDraftAgents(starterDrafts)
147
151
  } else {
148
152
  // Update existing agents that reference the edited provider
149
153
  if (editingProviderId) {
@@ -175,41 +179,13 @@ export function SetupWizard({ onComplete }: SetupWizardProps) {
175
179
  }
176
180
 
177
181
  const goToAgentReview = () => {
182
+ if (draftAgents.length === 0) {
183
+ setDraftAgents(ensureStarterDrafts(configuredProviders, []))
184
+ }
178
185
  setError('')
179
186
  setStep('agents')
180
187
  }
181
188
 
182
- const addBlankAgent = () => {
183
- const defaultProvider = configuredProviders[0] || null
184
- const newAgent: StarterDraftAgent = {
185
- id: `custom:${Math.random().toString(36).slice(2, 10)}`,
186
- templateId: 'custom',
187
- name: `Agent ${draftAgents.length + 1}`,
188
- description: '',
189
- systemPrompt: '',
190
- soul: '',
191
- providerConfigId: defaultProvider?.id || null,
192
- setupProvider: defaultProvider?.setupProvider || null,
193
- provider: defaultProvider?.provider || null,
194
- model: defaultProvider?.defaultModel || '',
195
- credentialId: defaultProvider?.credentialId || null,
196
- apiEndpoint: defaultProvider?.endpoint || null,
197
- gatewayProfileId: defaultProvider?.gatewayProfileId || null,
198
- tools: getDefaultAgentToolIds(),
199
- capabilities: [],
200
- delegationEnabled: false,
201
- delegationTargetMode: 'all',
202
- delegationTargetAgentIds: [],
203
- autoDraftSkillSuggestions: true,
204
- orchestratorEnabled: false,
205
- orchestratorMission: '',
206
- avatarSeed: Math.random().toString(36).slice(2, 10),
207
- avatarUrl: null,
208
- enabled: true,
209
- }
210
- setDraftAgents((current) => [...current, newAgent])
211
- }
212
-
213
189
  const removeAgent = (id: string) => {
214
190
  setDraftAgents((current) => current.filter((draft) => draft.id !== id))
215
191
  }
@@ -393,6 +369,7 @@ export function SetupWizard({ onComplete }: SetupWizardProps) {
393
369
  await appState.setCurrentAgent(created[0].id)
394
370
  }
395
371
 
372
+ setCreatedAgents(created)
396
373
  setStep('next')
397
374
  } catch (err: unknown) {
398
375
  setError(errorMessage(err))
@@ -405,24 +382,20 @@ export function SetupWizard({ onComplete }: SetupWizardProps) {
405
382
  const enabledDrafts = draftAgents.filter((draft) => draft.enabled)
406
383
  if (enabledDrafts.length === 0) {
407
384
  // No agents — go straight to "next" step
385
+ setCreatedAgents([])
408
386
  setStep('next')
409
387
  return
410
388
  }
411
389
  await createAgentsAndFinish()
412
390
  }
413
391
 
414
- const finishSetup = async () => {
392
+ const finishSetup = async (destination = '/home') => {
415
393
  try {
416
394
  await api('PUT', '/settings', { setupCompleted: true })
417
395
  } catch {
418
396
  // Continue anyway
419
397
  }
420
- onComplete()
421
- }
422
-
423
- const handleNextAddAgent = () => {
424
- addBlankAgent()
425
- setStep('agents')
398
+ onComplete(destination)
426
399
  }
427
400
 
428
401
  return (
@@ -447,12 +420,27 @@ export function SetupWizard({ onComplete }: SetupWizardProps) {
447
420
  />
448
421
  )}
449
422
 
423
+ {step === 'path' && (
424
+ <StepPath
425
+ onboardingPath={onboardingPath}
426
+ starterKitId={starterKitId}
427
+ intentText={intentText}
428
+ onPathChange={handlePathChange}
429
+ onStarterKitChange={setStarterKitId}
430
+ onIntentTextChange={setIntentText}
431
+ onContinue={handlePathContinue}
432
+ onBack={() => setStep('profile')}
433
+ onSkip={skip}
434
+ />
435
+ )}
436
+
450
437
  {step === 'providers' && (
451
438
  <StepProviders
452
439
  configuredProviders={configuredProviders}
453
440
  configuredProviderIds={configuredProviderIds}
454
441
  error={error}
455
442
  canContinue={canContinueFromProviders}
443
+ onBack={() => setStep('path')}
456
444
  onSelectProvider={selectProvider}
457
445
  onRemoveProvider={removeProvider}
458
446
  onContinue={goToAgentReview}
@@ -467,8 +455,8 @@ export function SetupWizard({ onComplete }: SetupWizardProps) {
467
455
  initialLabel={activeProviderLabel}
468
456
  editingProvider={editingProvider}
469
457
  configuredProviders={configuredProviders}
470
- starterKitId={null}
471
- intentText=""
458
+ starterKitId={starterKitId}
459
+ intentText={intentText}
472
460
  onSaveProvider={handleSaveProvider}
473
461
  onBack={handleBackFromConnect}
474
462
  onSkip={skip}
@@ -492,9 +480,13 @@ export function SetupWizard({ onComplete }: SetupWizardProps) {
492
480
 
493
481
  {step === 'next' && (
494
482
  <StepNext
495
- onAddProvider={() => setStep('providers')}
496
- onAddAgent={handleNextAddAgent}
497
- onContinueToDashboard={finishSetup}
483
+ createdAgents={createdAgents}
484
+ onContinueToDashboard={() => finishSetup('/home')}
485
+ onOpenFirstAgent={() => finishSetup(createdAgents[0]?.id ? `/agents/${encodeURIComponent(createdAgents[0].id)}` : '/agents')}
486
+ onOpenProtocols={() => finishSetup('/protocols')}
487
+ onOpenBuilder={() => finishSetup(DEFAULT_BUILDER_ROUTE)}
488
+ onOpenConnectors={() => finishSetup('/connectors')}
489
+ onOpenUsage={() => finishSetup('/usage')}
498
490
  />
499
491
  )}
500
492