From b7783d98520aa00585b906b53b42eae0132eb511 Mon Sep 17 00:00:00 2001 From: Brian O'Kelley Date: Sun, 4 Jan 2026 20:45:28 -0500 Subject: [PATCH] Conversational onboarding and fix showContextModal bug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changes: - Fix missing showContextModal function in admin-users.html that was causing "Can't find variable" errors when adding insights - Update Addie's system prompt to use conversational onboarding style instead of survey-style questions - Update suggested prompts to be more casual (e.g., "What brings you here?" instead of "What can you help me with?") - Add migration for new insight types (initial_interest, team_context, perspective_preference) and conversational welcome goal 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .changeset/many-cups-teach.md | 2 + server/public/admin-users.html | 25 ++ server/src/addie/prompts.ts | 78 +++--- .../140_conversational_onboarding.sql | 226 ++++++++++++++++++ 4 files changed, 304 insertions(+), 27 deletions(-) create mode 100644 .changeset/many-cups-teach.md create mode 100644 server/src/db/migrations/140_conversational_onboarding.sql diff --git a/.changeset/many-cups-teach.md b/.changeset/many-cups-teach.md new file mode 100644 index 00000000..a845151c --- /dev/null +++ b/.changeset/many-cups-teach.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/server/public/admin-users.html b/server/public/admin-users.html index 83925059..3668ea80 100644 --- a/server/public/admin-users.html +++ b/server/public/admin-users.html @@ -1802,6 +1802,31 @@

Different Approach

document.getElementById('contextModal').classList.add('hidden'); } + // Wrapper to show context modal with automatic type detection + // Called after adding insights or sending outreach + function showContextModal(userId, userName) { + // Find user in allUsers to determine the appropriate type + const user = allUsers.find(u => + u.id === userId || + u.workos_user_id === userId || + u.slack_user_id === userId + ); + + if (user) { + const contextUserId = user.workos_user_id || user.slack_user_id; + if (!contextUserId) { + // User found but has no identifiers - use original userId + showUserContext(userId, 'slack', userName); + return; + } + const contextType = user.workos_user_id ? 'workos' : 'slack'; + showUserContext(contextUserId, contextType, userName); + } else { + // Fallback: assume slack type if we can't find the user + showUserContext(userId, 'slack', userName); + } + } + async function loadAddieHomePreview(workosUserId, slackUserId) { const container = document.getElementById('addieHomePreviewContainer'); const loading = document.getElementById('addieHomePreviewLoading'); diff --git a/server/src/addie/prompts.ts b/server/src/addie/prompts.ts index 5d411755..831d938d 100644 --- a/server/src/addie/prompts.ts +++ b/server/src/addie/prompts.ts @@ -38,6 +38,29 @@ CRITICAL: Always use "AgenticAdvertising.org" (NOT "Alliance for Agentic Adverti - **Knowledgeable but Humble**: Deep expertise, but always cite sources. Say "I don't know" rather than guess - **Connector**: Route people to working groups, chapters, and community members who can help - **Question-First**: Ask questions to understand user perspective and knowledge level before answering +- **Casual & Conversational**: Talk like a helpful colleague, not a survey bot. Use contractions, be warm, skip the corporate speak + +## Conversational Onboarding + +When meeting someone new, have a NATURAL conversation - don't interrogate them with a list of questions. + +**The opener**: Start with something like "Hey! Glad you're here - what brings you to AgenticAdvertising.org?" This one question tells you a lot. + +**Follow up based on their answer**, not a fixed script: + +- If they're **building something**: "Nice! Are you driving this yourself or working with a team? Looking for more of a technical deep-dive or the business perspective?" +- If they're **exploring/curious**: "Makes sense - anything specific that sparked your interest? Or just getting a lay of the land?" +- If they were **sent by someone**: "Got it. Do you have a specific question they wanted answered, or more of a general 'figure out what this is about'?" +- If they want **technical specs**: "Straight to the technical stuff - I like it. What are you trying to integrate with?" +- If they want **connections**: "The community is definitely a big part of what we do. Anyone specific you're hoping to connect with?" + +**What NOT to do**: +- Don't ask "What's your role?" - you can infer this from what they're working on +- Don't ask "What are your 2026 plans?" out of the blue - let it come up naturally +- Don't rapid-fire multiple questions - one at a time, like a real conversation +- Don't be overly formal or use phrases like "I'd be happy to assist you with..." + +**Extracting insights naturally**: As you chat, you'll learn things about them (what they're building, their challenges, their team situation). Record these as insights when relevant, but don't make the conversation feel like a data collection exercise. ## Domain Expertise @@ -364,31 +387,32 @@ Use capability questions as an opportunity to **nudge users toward valuable acti /** * Suggested prompts shown when user opens Assistant + * Keep these casual and conversational - like things a person would actually say */ export const SUGGESTED_PROMPTS: SuggestedPrompt[] = [ { - title: 'What can you help me with?', - message: 'What can you do? What kinds of things can I ask you about?', + title: 'What brings you here?', + message: "Hey! I'm curious what brought you to AgenticAdvertising.org", }, { - title: 'Learn about AdCP', - message: 'What is AdCP and how does it work?', + title: 'Help me build something', + message: "I'm trying to build an agent - where do I start?", }, { - title: 'Get started building', - message: 'How do I set up a sales agent with AdCP?', + title: 'What is this anyway?', + message: "I keep hearing about agentic advertising but I'm not sure what it actually is", }, { - title: 'Become a member', - message: 'I want to join AgenticAdvertising.org. Can you help me find the right membership?', + title: 'Connect me with people', + message: 'Who else is working on this stuff? I want to meet people in the space', }, { - title: 'AdCP vs programmatic', - message: 'How is agentic advertising different from programmatic, and why is it better for sustainability?', + title: 'Show me the specs', + message: 'Where can I find the technical documentation?', }, { - title: 'Get involved', - message: 'What working groups can I join and how do I become more active in AgenticAdvertising.org?', + title: 'What can you do?', + message: 'What kinds of things can you help me with?', }, ]; @@ -430,16 +454,16 @@ export async function buildDynamicSuggestedPrompts( logger.warn({ error }, 'Failed to fetch insight goals for suggested prompts'); } - // Not linked - prioritize account setup and discovery + // Not linked - prioritize casual discovery if (!isMapped) { const prompts: SuggestedPrompt[] = [ { - title: 'What can you help me with?', - message: 'What can you do? What kinds of things can I ask you about?', + title: 'What brings you here?', + message: "Hey! I'm curious what brought you to AgenticAdvertising.org", }, { - title: 'Link my account', - message: 'Help me link my Slack account to AgenticAdvertising.org', + title: 'Help me get set up', + message: 'I want to link my account and get started', }, ]; @@ -447,8 +471,8 @@ export async function buildDynamicSuggestedPrompts( prompts.push(...goalPrompts); prompts.push({ - title: 'Learn about AdCP', - message: 'What is AdCP and how does it work?', + title: 'What is this anyway?', + message: "I keep hearing about agentic advertising but I'm not sure what it actually is", }); return prompts.slice(0, 4); // Slack limits to 4 prompts @@ -486,28 +510,28 @@ export async function buildDynamicSuggestedPrompts( if (memberContext.working_groups && memberContext.working_groups.length > 0) { prompts.push({ title: 'My working groups', - message: "What's happening in my working groups?", + message: "What's been happening in my working groups?", }); } else { prompts.push({ - title: 'Find a working group', - message: 'What working groups can I join based on my interests?', + title: 'Find my people', + message: 'What working groups would be a good fit for me?', }); } prompts.push({ title: 'Test my agent', - message: 'Help me verify my AdCP agent is working correctly', + message: 'Can you check if my agent is set up correctly?', }); prompts.push({ - title: 'What can you help me with?', - message: 'What can you do? What kinds of things can I ask you about?', + title: 'What can you do?', + message: 'What kinds of things can you help me with?', }); prompts.push({ - title: 'Learn about AdCP', - message: 'What is AdCP and how does it work?', + title: 'Explain it to me', + message: "I'm still wrapping my head around agentic advertising - can you give me the quick version?", }); return prompts.slice(0, 4); // Slack limits to 4 prompts diff --git a/server/src/db/migrations/140_conversational_onboarding.sql b/server/src/db/migrations/140_conversational_onboarding.sql new file mode 100644 index 00000000..b82204fd --- /dev/null +++ b/server/src/db/migrations/140_conversational_onboarding.sql @@ -0,0 +1,226 @@ +-- ============================================================================ +-- Migration: 140_conversational_onboarding.sql +-- Description: Shift from survey-style questions to natural conversation +-- +-- Philosophy: Instead of asking "What's your role?" or "What are your 2026 plans?", +-- we ask one casual opener and let the conversation flow naturally. Addie infers +-- the structured insights from what people share organically. +-- ============================================================================ + +-- ============================================================================ +-- NEW INSIGHT TYPE: What brings them here +-- ============================================================================ + +INSERT INTO member_insight_types (name, description, example_values, is_active, created_by) +VALUES + ( + 'initial_interest', + 'What brought them to AgenticAdvertising.org - their opening statement', + ARRAY[ + 'Building a sales agent', + 'Exploring agentic advertising', + 'Sent by colleague/boss', + 'Saw news coverage', + 'Looking for technical specs', + 'Want to connect with others in the space' + ], + TRUE, + 'system' + ) +ON CONFLICT (name) DO UPDATE SET + description = EXCLUDED.description, + example_values = EXCLUDED.example_values, + is_active = EXCLUDED.is_active; + +-- ============================================================================ +-- NEW INSIGHT TYPE: Working solo or with team +-- ============================================================================ + +INSERT INTO member_insight_types (name, description, example_values, is_active, created_by) +VALUES + ( + 'team_context', + 'Whether they are driving this alone or working with others', + ARRAY[ + 'Solo/individual contributor', + 'Leading a team', + 'Part of a team', + 'Evaluating for leadership', + 'Cross-functional initiative' + ], + TRUE, + 'system' + ) +ON CONFLICT (name) DO UPDATE SET + description = EXCLUDED.description, + example_values = EXCLUDED.example_values, + is_active = EXCLUDED.is_active; + +-- ============================================================================ +-- NEW INSIGHT TYPE: Perspective preference +-- ============================================================================ + +INSERT INTO member_insight_types (name, description, example_values, is_active, created_by) +VALUES + ( + 'perspective_preference', + 'Whether they want business or technical perspective', + ARRAY[ + 'Technical/developer', + 'Business/strategy', + 'Both/hybrid', + 'Operations/execution' + ], + TRUE, + 'system' + ) +ON CONFLICT (name) DO UPDATE SET + description = EXCLUDED.description, + example_values = EXCLUDED.example_values, + is_active = EXCLUDED.is_active; + +-- ============================================================================ +-- UPDATE EXISTING OUTREACH GOAL: Make it conversational +-- ============================================================================ + +-- First, let's create a new conversational welcome goal (if it doesn't exist) +INSERT INTO outreach_goals (name, category, description, success_insight_type, requires_mapped, requires_min_engagement, requires_insights, excludes_insights, message_template, follow_up_on_question, base_priority, created_by) +SELECT + 'Welcome - What Brings You', + 'information', + 'Casual opener to understand what brought them here', + 'initial_interest', + FALSE, -- Works for unmapped users too + 0, + '{}', + '{"initial_interest": "any"}', -- Skip if we already know + E'Hey {{user_name}}! Glad you''re here.\n\nCurious - what brings you to AgenticAdvertising.org? Are you building something, exploring the space, or something else entirely?', + E'No wrong answers - just helps me point you in the right direction.', + 95, -- High priority for new users + 'system' +WHERE NOT EXISTS ( + SELECT 1 FROM outreach_goals WHERE name = 'Welcome - What Brings You' +); + +-- Update if it already exists +UPDATE outreach_goals +SET description = 'Casual opener to understand what brought them here', + message_template = E'Hey {{user_name}}! Glad you''re here.\n\nCurious - what brings you to AgenticAdvertising.org? Are you building something, exploring the space, or something else entirely?', + follow_up_on_question = E'No wrong answers - just helps me point you in the right direction.', + base_priority = 95 +WHERE name = 'Welcome - What Brings You'; + +-- ============================================================================ +-- OUTCOMES FOR WELCOME GOAL +-- These trigger contextual follow-ups based on what they share +-- ============================================================================ + +-- Building something specific +INSERT INTO goal_outcomes (goal_id, trigger_type, trigger_value, outcome_type, response_message, insight_to_record, insight_value, priority) +SELECT g.id, 'keyword', 'build,building,implement,ship,launch,deploy,agent,sales agent,buyer agent', 'success', + E'Oh nice - you''re building something! Are you driving this yourself or working with a team? And are you looking for more of a technical deep-dive or the business angle?', + 'initial_interest', 'building', 90 +FROM outreach_goals g WHERE g.name = 'Welcome - What Brings You' +AND NOT EXISTS ( + SELECT 1 FROM goal_outcomes o WHERE o.goal_id = g.id AND o.trigger_value LIKE '%build%' +); + +-- Exploring/learning +INSERT INTO goal_outcomes (goal_id, trigger_type, trigger_value, outcome_type, response_message, insight_to_record, insight_value, priority) +SELECT g.id, 'keyword', 'explore,exploring,curious,learn,learning,understand,figure out,check out', 'success', + E'Makes sense - it''s a new space. Anything specific that sparked your interest? Or just getting a lay of the land?', + 'initial_interest', 'exploring', 85 +FROM outreach_goals g WHERE g.name = 'Welcome - What Brings You' +AND NOT EXISTS ( + SELECT 1 FROM goal_outcomes o WHERE o.goal_id = g.id AND o.trigger_value LIKE '%explore%' +); + +-- Sent by someone +INSERT INTO goal_outcomes (goal_id, trigger_type, trigger_value, outcome_type, response_message, insight_to_record, insight_value, priority) +SELECT g.id, 'keyword', 'boss,manager,colleague,team,company,told,sent,asked', 'success', + E'Got it - someone pointed you this way. Do you have a specific question they wanted answered, or more of a general "go figure out what this is about"?', + 'initial_interest', 'sent_by_others', 80 +FROM outreach_goals g WHERE g.name = 'Welcome - What Brings You' +AND NOT EXISTS ( + SELECT 1 FROM goal_outcomes o WHERE o.goal_id = g.id AND o.trigger_value LIKE '%boss%' +); + +-- Looking for specs/technical +INSERT INTO goal_outcomes (goal_id, trigger_type, trigger_value, outcome_type, response_message, insight_to_record, insight_value, priority) +SELECT g.id, 'keyword', 'spec,specs,technical,protocol,api,documentation,docs,integrate,integration', 'success', + E'Straight to the technical stuff - I like it. What are you trying to integrate with? I can point you to the right part of the docs.', + 'initial_interest', 'technical_specs', 85 +FROM outreach_goals g WHERE g.name = 'Welcome - What Brings You' +AND NOT EXISTS ( + SELECT 1 FROM goal_outcomes o WHERE o.goal_id = g.id AND o.trigger_value LIKE '%spec%' +); + +-- Networking/connections +INSERT INTO goal_outcomes (goal_id, trigger_type, trigger_value, outcome_type, response_message, insight_to_record, insight_value, priority) +SELECT g.id, 'keyword', 'connect,network,meet,people,community,others,who else', 'success', + E'The community is definitely a big part of what we do. Anyone specific you''re hoping to connect with? Or a type of company/role?', + 'initial_interest', 'networking', 80 +FROM outreach_goals g WHERE g.name = 'Welcome - What Brings You' +AND NOT EXISTS ( + SELECT 1 FROM goal_outcomes o WHERE o.goal_id = g.id AND o.trigger_value LIKE '%connect%' +); + +-- Default/unclear +INSERT INTO goal_outcomes (goal_id, trigger_type, outcome_type, response_message, priority) +SELECT g.id, 'default', 'clarify', + E'All good - we get all kinds here. Are you more on the technical side (building, integrating) or the business side (strategy, partnerships)?', + 50 +FROM outreach_goals g WHERE g.name = 'Welcome - What Brings You' +AND NOT EXISTS ( + SELECT 1 FROM goal_outcomes o WHERE o.goal_id = g.id AND o.trigger_type = 'default' +); + +-- Timeout (168 hours = 7 days) +INSERT INTO goal_outcomes (goal_id, trigger_type, trigger_value, outcome_type, defer_days, priority) +SELECT g.id, 'timeout', '168', 'defer', 7, 10 +FROM outreach_goals g WHERE g.name = 'Welcome - What Brings You' +AND NOT EXISTS ( + SELECT 1 FROM goal_outcomes o WHERE o.goal_id = g.id AND o.trigger_type = 'timeout' +); + +-- ============================================================================ +-- LOWER PRIORITY OF OLD SURVEY-STYLE GOALS +-- Keep them for passive extraction but don't proactively ask them +-- ============================================================================ + +UPDATE outreach_goals +SET base_priority = base_priority - 30, + description = description || ' (passive extraction - don''t ask directly)' +WHERE name IN ('Learn 2026 Plans', 'Learn AAO Goals') +AND description NOT LIKE '%passive extraction%'; + +-- ============================================================================ +-- ADD INSIGHT GOAL FOR PASSIVE EXTRACTION +-- Addie should infer these from natural conversation, not ask directly +-- ============================================================================ + +INSERT INTO insight_goals ( + name, + question, + insight_type_id, + target_unmapped_only, + target_mapped_only, + priority, + is_enabled, + suggested_prompt_title, + suggested_prompt_message +) +SELECT + 'Learn Initial Interest', + 'What brought you to AgenticAdvertising.org?', + id, + FALSE, + FALSE, + 95, + TRUE, + 'What brings you here?', + 'I''m curious what brought you to AgenticAdvertising.org' +FROM member_insight_types WHERE name = 'initial_interest' +AND NOT EXISTS ( + SELECT 1 FROM insight_goals WHERE name = 'Learn Initial Interest' +);