Interactive AI-driven onboarding interview and room promotion lifecycle
The Agent Setup Onboarding flow (/onboarding) is Zooly's primary onboarding experience. It replaces static multi-step forms with an interactive conversation where a prospective user talks directly with an AI agent to establish their representation terms, floor pricing, and partnership preferences.
The flow operates as a stateful, progressive funnel:
distilMemory: true), recording terms and evaluating the talent-setup goal.runOnboardingPromotion runs full planning and Arvist promotion to seed durable account memory.talent-home)When a visitor lands on /onboarding and selects an audience, ensureOnboardingRoom creates or resumes a room of kind talent-home:
guestKey stored in an HttpOnly cookie.autoPlan: false to keep guest turns fast and inexpensive, avoiding full multi-agent orchestration on initial messages.talent-setup goal (GOAL_KEY_TALENT_SETUP).onboarding: { audience, setupStarted: false, skipped: false }.const room = await createZAgentRoom({
title: "Your agent",
kind: ROOM_KIND_TALENT_HOME,
plannerPrompt: DEFAULT_PLANNER_GUIDE_PROMPT,
plannerModel: DEFAULT_PLANNER_GUIDE_MODEL,
recallPrompt: DEFAULT_RECALL_PROMPT,
recallModel: DEFAULT_RECALL_MODEL,
autoPlan: false,
goalConfig: DEFAULT_GOAL_CONFIG.filter(
(goal) => goal.key === GOAL_KEY_TALENT_SETUP,
),
memory: {
onboarding: {
audience: input.audience,
setupStarted: false,
skipped: false,
},
},
});
When the user clicks "Build my agent", startOnboardingSetup records setupStarted: true and posts the first scripted interview question. The 7 questions (TALENT_BUILDER_QUESTIONS) are:
| # | Key | Question | Purpose |
|---|---|---|---|
| 1 | wantedOffers | What offers would you like to receive? | Preferred collaboration formats, deal structures |
| 2 | unwantedOffers | What offers would you not like to receive? | Hard refusals, no-go categories |
| 3 | wantedBrands | What brands, or types of brands, would you want to work with? | Target brand partners and verticals |
| 4 | unwantedBrands | What brands, or types of brands, would you not want to work with? | Excluded brands and competitors |
| 5 | minimumOffer | What is the minimum offer you would entertain? | Pricing floor (parsed into minor units) |
| 6 | offerMustInclude | What information do you want an offer to include? | Mandatory proposal requirements |
| 7 | other | Anything else that is important for you, or that you want me to know? | Scheduling, usage rights, personal notes |
During guest turns, autoPlan is disabled so the blocking PlannerGuidAg stage is bypassed. Instead, buildOnboardingPersonaDirective computes the system directive for the persona on the fly based on:
answeredCount and setupGoalMet.distilMemory: trueAlthough autoPlan is false, every turn in the onboarding chat route passes distilMemory: true to streamRoomChat. This invokes PlannerDistilMemAg in the background (parallel to the persona's streaming reply):
room.memory.talent-setup goal description:
"The talent has said enough to be represented properly: what they accept, what they refuse, what an offer must contain, and what they charge. Not when the questions have been asked — when the answers are actually in the memory."
met: When satisfied, PlannerDistilMemAg writes goals.talent-setup.status = "met".// apps/zooly-app/app/api/z-agent/onboarding/rooms/[roomId]/chat/route.ts
return await streamRoomChat({
roomId,
participantId: view.participantId,
messages: body.messages,
personaDirective: buildOnboardingPersonaDirective(view, {
pendingUserAnswer,
}),
distilMemory: true,
});
The client determines when to show the sign-in prompt using signInSuggested:
signInSuggested =
onboarding.skipped === true ||
setupGoalMet ||
answeredCount >= ONBOARDING_ANSWER_CAP; // 11
This ensures the user is prompted to save their agent in any of three cases:
ONBOARDING_ANSWER_CAP (11) messages since the interview started. The cap is deliberately higher than the 7 questions because user turns also include side questions, so the count is a loose backstop rather than a script position.The sign-in card renders non-blockingly inside the transcript using AgentChat's signInPrompt prop, supporting Google OAuth popup login and email authentication without page redirection.
When the user finishes authentication, claimOnboardingRoom binds the guest seat to their new accountId and triggers finalizeOnboardingRoom:
who field (TALENT, BRAND, or AGENCY) based on the onboarding audience.runOnboardingPromotion):
autoPlan claim lock (claimZAgentRoomAutoPlan).planFromRoom (full PlannerGuide + PlannerDistilMem) to compile room memory.runRoomArvist to extract standing rules, preferences, and identity into the account's durable memory (z_agent_object_memory).autoPlan: true on the room, enabling full live mediator capabilities for future sessions.export async function runOnboardingPromotion(roomId: string) {
const room = await getZAgentRoom(roomId);
const participants = await listZAgentParticipants(roomId);
const talent = participants.find((p) => p.key === "talent");
if (!(await claimZAgentRoomAutoPlan(roomId))) {
return { planned: false, arvist: null };
}
await planFromRoom(room, talent);
const arvist = talent.accountId
? await runRoomArvist(roomId, talent.id)
: null;
return { planned: true, arvist };
}
After claiming, the onboarding SPA advances through three final steps before opening the dashboard:
ProfileStep.tsx):
fetchUploadImageAssets and saves imageUrl to the account.SocialsStep.tsx):
BuildingStep.tsx):
markAgentSetupComplete: true (if interview completed) or markAgentSetupSkipped: true (if skipped).DoneStep.tsx):
/dashboard/agent/links.| Area | File / Endpoint | Purpose |
|---|---|---|
| Frontend Page | packages/offers/client/src/app/components/onboarding/OnboardingPage.tsx | Main onboarding orchestrator component |
| API Client | packages/offers/client/src/app/components/onboarding/onboardingApi.ts | Onboarding API calls (fetchOnboardingRoom, startSetup, claim) |
| Server Logic | packages/z-agent/srv/src/onboarding.ts | Room lifecycle, guest claiming, and view projection |
| Interview Spec | packages/z-agent/srv/src/talent-onboarding.ts | 7 builder questions and currency parsing logic |
| Room Route | GET /api/z-agent/onboarding/room | Find-or-create guest onboarding room |
| Chat Route | POST /api/z-agent/onboarding/rooms/[roomId]/chat | Streams onboarding reply with distilMemory: true |
| Start Setup | POST /api/z-agent/onboarding/rooms/[roomId]/start-setup | Marks setup started and sends first question |
| Skip Route | POST /api/z-agent/onboarding/rooms/[roomId]/skip | Marks setup skipped |
| Claim Route | POST /api/z-agent/onboarding/rooms/[roomId]/claim | Binds guest participant to account and runs promotion |
On This Page
OverviewRoom Architecture & Guest IdentityLanding Room (,[object Object],)The 7 Setup QuestionsPersona Directive & Background DistillationDirective-Steered Reply[object Object]The Sign-In Gate & State MachinePromotion to Durable Account MemoryPost-Authentication Setup StepsKey Files & Endpoints