@swarmclawai/swarmclaw 1.4.9 → 1.5.1

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.1 Highlights
219
+
220
+ - **Standalone connector lifecycle**: connector start, stop, status, and repair now work correctly in standalone production builds (`npm start` / pm2) where the daemon runs in-process. Previously these operations silently failed because the controller assumed a daemon subprocess was always present. (Community contribution by [@borislavnnikolov](https://github.com/borislavnnikolov) -- PR #35)
221
+
222
+ ### v1.5.0 Highlights
223
+
224
+ - **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.
225
+ - **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.
226
+ - **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.
227
+
218
228
  ### v1.4.9 Highlights
219
229
 
220
230
  - **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)
@@ -355,7 +365,7 @@ Then open `http://localhost:3456`.
355
365
  - **Delegation**: built-in delegation to Claude Code, Codex CLI, OpenCode CLI, Gemini CLI, and native SwarmClaw subagents.
356
366
  - **Autonomy**: heartbeat loops, schedules, background jobs, task execution, supervisor recovery, and agent wakeups.
357
367
  - **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.
358
- - **Structured Sessions**: reusable bounded runs with templates, facilitators, participants, hidden live rooms, chatroom `/breakout`, durable transcripts, outputs, and operator controls.
368
+ - **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.
359
369
  - **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.
360
370
  - **Wallets**: linked Base wallet generation, address management, approval-oriented limits, and agent payout identity.
361
371
  - **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.9",
3
+ "version": "1.5.1",
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": {
@@ -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
 
@@ -1,68 +1,80 @@
1
1
  'use client'
2
2
 
3
+ import { LaunchActionCard } from '@/components/shared/launch-action-card'
3
4
  import type { StepNextProps } from './types'
4
5
  import { StepShell } from './shared'
5
6
 
6
7
  export function StepNext({
7
- onAddProvider,
8
- onAddAgent,
8
+ createdAgents,
9
9
  onContinueToDashboard,
10
+ onOpenFirstAgent,
11
+ onOpenProtocols,
12
+ onOpenBuilder,
13
+ onOpenConnectors,
14
+ onOpenUsage,
10
15
  }: StepNextProps) {
16
+ const firstAgent = createdAgents[0] || null
17
+
11
18
  return (
12
- <StepShell>
19
+ <StepShell wide>
13
20
  <h1 className="font-display text-[36px] font-800 leading-[1.05] tracking-[-0.04em] mb-3">
14
- What&apos;s Next?
21
+ Launch Your Workspace
15
22
  </h1>
16
- <p className="text-[15px] text-text-2 mb-8">
17
- Your agents are saved. You can keep building or head to the dashboard.
23
+ <p className="text-[15px] text-text-2 mb-2">
24
+ Setup is complete. Start from the path that gets you to an actual result fastest.
25
+ </p>
26
+ <p className="text-[13px] text-text-3 mb-7">
27
+ {firstAgent
28
+ ? `${firstAgent.name} is ready to use.`
29
+ : 'You finished setup without starter agents, so the launch options below focus on wiring up the rest of the workspace.'}
18
30
  </p>
19
31
 
20
- <div className="flex flex-col gap-3 max-w-[480px] mx-auto mb-8">
21
- <button
22
- onClick={onAddProvider}
23
- className="w-full px-5 py-4 rounded-[14px] border border-white/[0.08] bg-surface text-left
24
- transition-all duration-200 flex items-start gap-4 cursor-pointer
25
- hover:border-accent-bright/30 hover:bg-surface-hover"
26
- >
27
- <div className="w-10 h-10 rounded-[10px] border border-white/[0.06] bg-white/[0.04] flex items-center justify-center shrink-0 mt-0.5">
28
- <svg width="18" height="18" viewBox="0 0 18 18" fill="none" className="text-accent-bright">
29
- <path d="M9 3V15M3 9H15" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" />
30
- </svg>
31
- </div>
32
- <div>
33
- <div className="text-[15px] font-display font-600 text-text mb-1">Add Another Provider</div>
34
- <div className="text-[13px] text-text-3 leading-relaxed">
35
- Connect a different LLM provider for more model options.
36
- </div>
37
- </div>
38
- </button>
39
-
40
- <button
41
- onClick={onAddAgent}
42
- className="w-full px-5 py-4 rounded-[14px] border border-white/[0.08] bg-surface text-left
43
- transition-all duration-200 flex items-start gap-4 cursor-pointer
44
- hover:border-accent-bright/30 hover:bg-surface-hover"
45
- >
46
- <div className="w-10 h-10 rounded-[10px] border border-white/[0.06] bg-white/[0.04] flex items-center justify-center shrink-0 mt-0.5">
47
- <svg width="18" height="18" viewBox="0 0 18 18" fill="none" className="text-accent-bright">
48
- <circle cx="9" cy="6" r="3" stroke="currentColor" strokeWidth="1.5" />
49
- <path d="M3 15C3 12.2386 5.68629 10 9 10C12.3137 10 15 12.2386 15 15" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" />
50
- </svg>
51
- </div>
52
- <div>
53
- <div className="text-[15px] font-display font-600 text-text mb-1">Add Another Agent</div>
54
- <div className="text-[13px] text-text-3 leading-relaxed">
55
- Create another agent using your connected providers.
56
- </div>
57
- </div>
58
- </button>
32
+ <div className="grid gap-3 md:grid-cols-2 xl:grid-cols-3 mb-8">
33
+ <LaunchActionCard
34
+ title={firstAgent ? 'Open First Agent Chat' : 'Open Agents'}
35
+ description={firstAgent
36
+ ? `Jump straight into ${firstAgent.name} and start working from the workspace you just created.`
37
+ : 'Open the agents workspace so you can create or tune the first agent manually.'}
38
+ actionLabel={firstAgent ? 'Open Chat' : 'Open Agents'}
39
+ onClick={onOpenFirstAgent}
40
+ tone="primary"
41
+ />
42
+ <LaunchActionCard
43
+ title="Start Structured Session"
44
+ description="Open bounded collaboration runs for reviews, planning rounds, decision-making, or focused multi-agent work."
45
+ actionLabel="Open Protocols"
46
+ onClick={onOpenProtocols}
47
+ />
48
+ <LaunchActionCard
49
+ title="Open Workflow Builder"
50
+ description="Jump into the visual protocol builder if you want a reusable orchestration graph instead of a one-off run."
51
+ actionLabel="Open Builder"
52
+ onClick={onOpenBuilder}
53
+ />
54
+ <LaunchActionCard
55
+ title="Connect a Platform"
56
+ description="Bridge agents into Discord, Slack, Telegram, WhatsApp, or other runtime connectors."
57
+ actionLabel="Open Connectors"
58
+ onClick={onOpenConnectors}
59
+ />
60
+ <LaunchActionCard
61
+ title="Review Usage"
62
+ description="Inspect cost, provider health, and agent activity so the workspace stays observable from day one."
63
+ actionLabel="Open Usage"
64
+ onClick={onOpenUsage}
65
+ />
66
+ <LaunchActionCard
67
+ title="Go to Dashboard"
68
+ description="Land on the main home view. Fresh workspaces open in guided launch mode before switching to the normal ops dashboard."
69
+ actionLabel="Open Home"
70
+ onClick={onContinueToDashboard}
71
+ />
59
72
  </div>
60
73
 
61
74
  <button
75
+ type="button"
62
76
  onClick={onContinueToDashboard}
63
- className="px-10 py-3.5 rounded-[14px] border-none bg-accent-bright text-white text-[15px] font-display font-600
64
- cursor-pointer hover:brightness-110 active:scale-[0.97] transition-all duration-200
65
- shadow-[0_6px_28px_rgba(99,102,241,0.3)]"
77
+ className="px-10 py-3.5 rounded-[14px] border-none bg-accent-bright text-white text-[15px] font-display font-600 cursor-pointer hover:brightness-110 active:scale-[0.97] transition-all duration-200 shadow-[0_6px_28px_rgba(99,102,241,0.3)]"
66
78
  >
67
79
  Continue to Dashboard
68
80
  </button>