mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-22 09:34:53 +02:00
[eric] onboarding: agentic cursor auto-opens the chat on first run and asks 'what do you want done?' (static, no LLM); user types their own thing
This commit is contained in:
@@ -18,7 +18,12 @@ import {
|
||||
import AgenticCursor, { type AgenticCursorHandle } from './ac/AgenticCursor';
|
||||
import { onboardingDirector } from './OnboardingDirector';
|
||||
import { STEPS } from './steps';
|
||||
import { hasAnyAgentCompleted } from './steps/skipPredicates';
|
||||
import {
|
||||
hasAnyAgentCompleted,
|
||||
hasAnyAgentLaunched,
|
||||
hasFreeTrialActive,
|
||||
hasModelConnected,
|
||||
} from './steps/skipPredicates';
|
||||
import OnboardingPanel from './OnboardingPanel';
|
||||
import { onboardingBus } from './eventBus';
|
||||
import { report } from './telemetry';
|
||||
@@ -33,6 +38,15 @@ const OnboardingRoot: React.FC = () => {
|
||||
const progress = useAppSelector((s) => s.onboardingProgress);
|
||||
const settingsLoaded = useAppSelector((s) => s.settings.loaded);
|
||||
const firstAgentDone = useAppSelector(hasAnyAgentCompleted);
|
||||
const autoOpenedRef = useRef(false);
|
||||
// Ready to auto-open the chat: a way to run exists, nothing launched yet, and the
|
||||
// first step isn't already done.
|
||||
const firstRunReady = useAppSelector(
|
||||
(s) =>
|
||||
(hasFreeTrialActive(s) || hasModelConnected(s)) &&
|
||||
!hasAnyAgentLaunched(s) &&
|
||||
!s.onboardingProgress.completedSteps.includes('launch_agent'),
|
||||
);
|
||||
|
||||
// The one gentle nudge: open the quiet pill ONCE, but only AFTER the first agent
|
||||
// actually finishes, so it never pops mid-run. Respects a panel they've hidden or
|
||||
@@ -50,6 +64,22 @@ const OnboardingRoot: React.FC = () => {
|
||||
dispatch,
|
||||
]);
|
||||
|
||||
// First-run assist: the agentic cursor opens the chat and asks "what do you want
|
||||
// done?" on its own, so the user never hunts for the input. Fires once, only on the
|
||||
// dashboard with the AC idle. Fail-safe: if it can't run, the empty state + chat
|
||||
// still work by hand, so a missed auto-open never blocks anything.
|
||||
useEffect(() => {
|
||||
if (autoOpenedRef.current || !progress.initialized || !firstRunReady) return;
|
||||
if (onboardingDirector.isRunning()) return;
|
||||
if (!window.location.hash.includes('/dashboard/')) return;
|
||||
autoOpenedRef.current = true;
|
||||
const t = window.setTimeout(() => {
|
||||
if (onboardingDirector.isRunning()) return;
|
||||
onboardingDirector.startStep('launch_agent', { x: window.innerWidth - 80, y: 110 });
|
||||
}, 1400); // let the dashboard + cursor settle before it drives
|
||||
return () => window.clearTimeout(t);
|
||||
}, [progress.initialized, firstRunReady]);
|
||||
|
||||
useEffect(() => {
|
||||
if (progress.initialized) return;
|
||||
if (!settingsLoaded) return;
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
import type { OnboardingStep } from './types';
|
||||
import { S } from '../selectors';
|
||||
import { hasAnyAgentLaunched, isYoutubeEnabled, hasModelConnected, hasFreeTrialActive } from './skipPredicates';
|
||||
|
||||
// Primary: YouTube summary (needs MCP from step 2). Fallback uses built-in web tools (no MCP).
|
||||
const YOUTUBE_PROMPT =
|
||||
'What is this youtube video about: https://youtu.be/_NKj8KQMY-k?si=rEk4KO2bOpa5Vo0z. Do not use browser agents.';
|
||||
const FALLBACK_PROMPT =
|
||||
'Find the latest news about AI from the web and give me a short summary.';
|
||||
import { hasAnyAgentLaunched, hasModelConnected, hasFreeTrialActive } from './skipPredicates';
|
||||
|
||||
export const step03: OnboardingStep = {
|
||||
id: 'launch_agent',
|
||||
@@ -16,31 +10,24 @@ export const step03: OnboardingStep = {
|
||||
// instead, which restores today's flow exactly (no trial = no regression).
|
||||
index: 1,
|
||||
title: 'Launch your first Agent',
|
||||
description: 'Click the chat bubble to fire up a new Agent in a dashboard.',
|
||||
description: 'Tell the chat what you want done and a team gets to work.',
|
||||
videoSrc: './onboarding-videos/v2/03.mp4',
|
||||
videoDurationLabel: '0:24',
|
||||
skipIf: (s) => hasAnyAgentLaunched(s) || (!hasModelConnected(s) && !hasFreeTrialActive(s)),
|
||||
requiresDashboard: true,
|
||||
// The cursor opens the chat FOR the user, then asks what they want. No canned
|
||||
// prompt and no LLM here: it's a static move + simulated click + a hardcoded
|
||||
// line; the user types their own thing and their team runs.
|
||||
ops: [
|
||||
{ kind: 'move_to', target: S.newAgentButton },
|
||||
{ kind: 'popup', text: 'Tap the chat bubble to start a fresh chat.' },
|
||||
{
|
||||
kind: 'wait_user',
|
||||
condition: { kind: 'click_target', target: S.newAgentButton },
|
||||
},
|
||||
{
|
||||
kind: 'type_into',
|
||||
target: S.chatInput,
|
||||
// YouTube prompt bans browser agents (MCP handles it); fallback uses web tools by design.
|
||||
text: (state) => (isYoutubeEnabled(state) ? YOUTUBE_PROMPT : FALLBACK_PROMPT),
|
||||
speedMs: 12,
|
||||
},
|
||||
{ kind: 'move_to', target: S.chatSendButton },
|
||||
{ kind: 'click', target: S.chatSendButton, simulate: true },
|
||||
{ kind: 'popup', text: 'Let me open a chat for you.' },
|
||||
{ kind: 'click', target: S.newAgentButton, simulate: true },
|
||||
{ kind: 'move_to', target: S.chatInput },
|
||||
{ kind: 'popup', text: "What do you want done? Type it here and I'll put a team on it." },
|
||||
{
|
||||
kind: 'wait_user',
|
||||
condition: { kind: 'event_bus', event: 'chat:message_sent' },
|
||||
timeoutMs: 30000,
|
||||
timeoutMs: 180000,
|
||||
},
|
||||
{ kind: 'outro' },
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user