Compare commits

...

13 Commits

Author SHA1 Message Date
Nicholas Tindle
ec5a5a4dfd fix(frontend): refresh credits for all onboarding step completions
Move fetchCredits() before the task group check so credits refresh
for hidden steps like VISIT_COPILOT. Confetti still only triggers
for steps displayed in the wallet checklist.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 22:53:21 -06:00
Nicholas Tindle
a79acf25d1 fix(backend): default chat flag to false when LaunchDarkly unavailable
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 22:39:56 -06:00
Nicholas Tindle
6e119f32f3 refactor(platform): move chat flag check to backend onboarding endpoint
Backend /api/onboarding/enabled now checks Flag.CHAT for the user:
- If chat enabled → return false (skip legacy onboarding)
- If chat disabled → return original logic (check store agent count)

Frontend shouldShowOnboarding() restored to original logic since
backend handles the chat flag check server-side with LaunchDarkly.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 22:36:54 -06:00
Nicholas Tindle
cc7d0abf31 Merge branch 'dev' into open-2967-disable-onboarding-redirects 2026-01-27 21:11:35 -06:00
Nicholas Tindle
fa85255ece Add VISIT_COPILOT to OnboardingStep enum
Added 'VISIT_COPILOT' value to the OnboardingStep enum.
2026-01-27 21:10:12 -06:00
Nicholas Tindle
db71e10d9a Add 'VISIT_COPILOT' value to OnboardingStep enum 2026-01-27 21:09:56 -06:00
Nicholas Tindle
9b8d11dfb0 Merge branch 'dev' into open-2967-disable-onboarding-redirects 2026-01-27 20:53:08 -06:00
claude[bot]
efbf61dc06 fix(frontend): add URL type annotation to waitForURL callbacks
Fixes TypeScript implicit 'any' type error in signup.ts and login.page.ts
by adding explicit URL type annotation to the callback parameters.

Co-authored-by: Nicholas Tindle <ntindle@users.noreply.github.com>
2026-01-28 02:27:39 +00:00
claude[bot]
479412eb64 fix(frontend): fix e2e test signup/login redirect URL handling
- Replace Promise.race() with single waitForURL callback in signup.ts
  to avoid race conditions with multiple independent timeouts
- Add /library and /copilot to login.page.ts URL pattern to handle
  new post-login redirect destinations

Co-authored-by: Nicholas Tindle <ntindle@users.noreply.github.com>
2026-01-28 02:10:03 +00:00
claude[bot]
1b0271e912 fix(frontend): update e2e signup tests to handle new post-signup landing pages
After disabling onboarding redirects, users now land on /copilot or /library
instead of /onboarding. This updates the test utility to:
- Wait for /copilot and /library in addition to /onboarding and /marketplace
- Verify authentication on copilot/library pages without requiring marketplace text

Co-authored-by: Nicholas Tindle <ntindle@users.noreply.github.com>
2026-01-28 01:08:41 +00:00
Nicholas Tindle
4f627f500e Update autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotPage.ts
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-27 17:26:51 -06:00
Nicholas Tindle
8cf19af359 Merge branch 'dev' into open-2967-disable-onboarding-redirects 2026-01-27 17:06:01 -06:00
Nicholas Tindle
dc7b489374 feat(platform): disable onboarding redirects and add $5 signup bonus
- Disable automatic onboarding redirects by making shouldShowOnboarding() return false
- Users now land directly on /copilot after signup/login
- Add VISIT_COPILOT onboarding step with 500 credit ($5) reward
- Complete VISIT_COPILOT step automatically when user visits /copilot
- Checklist/wallet remains functional, reward is granted invisibly

OPEN-2967

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 16:53:57 -06:00
10 changed files with 55 additions and 17 deletions

View File

@@ -265,9 +265,13 @@ async def get_onboarding_agents(
"/onboarding/enabled",
summary="Is onboarding enabled",
tags=["onboarding", "public"],
dependencies=[Security(requires_user)],
)
async def is_onboarding_enabled() -> bool:
async def is_onboarding_enabled(
user_id: Annotated[str, Security(get_user_id)],
) -> bool:
# If chat is enabled for user, skip legacy onboarding
if await is_feature_enabled(Flag.CHAT, user_id, False):
return False
return await onboarding_enabled()

View File

@@ -41,6 +41,7 @@ FrontendOnboardingStep = Literal[
OnboardingStep.AGENT_NEW_RUN,
OnboardingStep.AGENT_INPUT,
OnboardingStep.CONGRATS,
OnboardingStep.VISIT_COPILOT,
OnboardingStep.MARKETPLACE_VISIT,
OnboardingStep.BUILDER_OPEN,
]
@@ -122,6 +123,9 @@ async def update_user_onboarding(user_id: str, data: UserOnboardingUpdate):
async def _reward_user(user_id: str, onboarding: UserOnboarding, step: OnboardingStep):
reward = 0
match step:
# Welcome bonus for visiting copilot ($5 = 500 credits)
case OnboardingStep.VISIT_COPILOT:
reward = 500
# Reward user when they clicked New Run during onboarding
# This is because they need credits before scheduling a run (next step)
# This is seen as a reward for the GET_RESULTS step in the wallet

View File

@@ -0,0 +1,2 @@
-- AlterEnum
ALTER TYPE "OnboardingStep" ADD VALUE 'VISIT_COPILOT';

View File

@@ -81,6 +81,7 @@ enum OnboardingStep {
AGENT_INPUT
CONGRATS
// First Wins
VISIT_COPILOT
GET_RESULTS
MARKETPLACE_VISIT
MARKETPLACE_ADD_AGENT

View File

@@ -5,6 +5,7 @@ import {
import { useToast } from "@/components/molecules/Toast/use-toast";
import { getHomepageRoute } from "@/lib/constants";
import { useSupabase } from "@/lib/supabase/hooks/useSupabase";
import { useOnboarding } from "@/providers/onboarding/onboarding-provider";
import {
Flag,
type FlagValues,
@@ -25,12 +26,20 @@ export function useCopilotPage() {
const queryClient = useQueryClient();
const { user, isLoggedIn, isUserLoading } = useSupabase();
const { toast } = useToast();
const { completeStep } = useOnboarding();
const { urlSessionId, setUrlSessionId } = useCopilotSessionId();
const setIsStreaming = useCopilotStore((s) => s.setIsStreaming);
const isCreating = useCopilotStore((s) => s.isCreatingSession);
const setIsCreating = useCopilotStore((s) => s.setIsCreatingSession);
// Complete VISIT_COPILOT onboarding step to grant $5 welcome bonus
useEffect(() => {
if (isLoggedIn) {
completeStep("VISIT_COPILOT");
}
}, [completeStep, isLoggedIn]);
const isChatEnabled = useGetFlag(Flag.CHAT);
const flags = useFlags<FlagValues>();
const homepageRoute = getHomepageRoute(isChatEnabled);

View File

@@ -4594,6 +4594,7 @@
"AGENT_NEW_RUN",
"AGENT_INPUT",
"CONGRATS",
"VISIT_COPILOT",
"MARKETPLACE_VISIT",
"BUILDER_OPEN"
],
@@ -8754,6 +8755,7 @@
"AGENT_NEW_RUN",
"AGENT_INPUT",
"CONGRATS",
"VISIT_COPILOT",
"GET_RESULTS",
"MARKETPLACE_VISIT",
"MARKETPLACE_ADD_AGENT",

View File

@@ -255,13 +255,18 @@ export function Wallet() {
(notification: WebSocketNotification) => {
if (
notification.type !== "onboarding" ||
notification.event !== "step_completed" ||
!walletRef.current
notification.event !== "step_completed"
) {
return;
}
// Only trigger confetti for tasks that are in groups
// Always refresh credits when any onboarding step completes
fetchCredits();
// Only trigger confetti for tasks that are in displayed groups
if (!walletRef.current) {
return;
}
const taskIds = groups
.flatMap((group) => group.tasks)
.map((task) => task.id);
@@ -274,7 +279,6 @@ export function Wallet() {
return;
}
fetchCredits();
party.confetti(walletRef.current, {
count: 30,
spread: 120,
@@ -284,7 +288,7 @@ export function Wallet() {
modules: [fadeOut],
});
},
[fetchCredits, fadeOut],
[fetchCredits, fadeOut, groups],
);
// WebSocket setup for onboarding notifications

View File

@@ -1003,6 +1003,7 @@ export type OnboardingStep =
| "AGENT_INPUT"
| "CONGRATS"
// First Wins
| "VISIT_COPILOT"
| "GET_RESULTS"
| "MARKETPLACE_VISIT"
| "MARKETPLACE_ADD_AGENT"

View File

@@ -37,9 +37,13 @@ export class LoginPage {
this.page.on("load", (page) => console.log(` Now at URL: ${page.url()}`));
// Start waiting for navigation before clicking
// Wait for redirect to marketplace, onboarding, library, or copilot (new landing pages)
const leaveLoginPage = this.page
.waitForURL(
(url) => /^\/(marketplace|onboarding(\/.*)?)?$/.test(url.pathname),
(url: URL) =>
/^\/(marketplace|onboarding(\/.*)?|library|copilot)?$/.test(
url.pathname,
),
{ timeout: 10_000 },
)
.catch((reason) => {

View File

@@ -36,14 +36,16 @@ export async function signupTestUser(
const signupButton = getButton("Sign up");
await signupButton.click();
// Wait for successful signup - could redirect to onboarding or marketplace
// Wait for successful signup - could redirect to various pages depending on onboarding state
try {
// Wait for either onboarding or marketplace redirect
await Promise.race([
page.waitForURL(/\/onboarding/, { timeout: 15000 }),
page.waitForURL(/\/marketplace/, { timeout: 15000 }),
]);
// Wait for redirect to onboarding, marketplace, copilot, or library
// Use a single waitForURL with a callback to avoid Promise.race race conditions
await page.waitForURL(
(url: URL) =>
/\/(onboarding|marketplace|copilot|library)/.test(url.pathname),
{ timeout: 15000 },
);
} catch (error) {
console.error(
"❌ Timeout waiting for redirect, current URL:",
@@ -54,14 +56,19 @@ export async function signupTestUser(
const currentUrl = page.url();
// Handle onboarding or marketplace redirect
// Handle onboarding redirect if needed
if (currentUrl.includes("/onboarding") && ignoreOnboarding) {
await page.goto("http://localhost:3000/marketplace");
await page.waitForLoadState("domcontentloaded", { timeout: 10000 });
}
// Verify we're on the expected final page
if (ignoreOnboarding || currentUrl.includes("/marketplace")) {
// Verify we're on an expected final page and user is authenticated
if (currentUrl.includes("/copilot") || currentUrl.includes("/library")) {
// For copilot/library landing pages, just verify user is authenticated
await page
.getByTestId("profile-popout-menu-trigger")
.waitFor({ state: "visible", timeout: 10000 });
} else if (ignoreOnboarding || currentUrl.includes("/marketplace")) {
// Verify we're on marketplace
await page
.getByText(