feat: fixes to the jtbd agent (#231)

* feat: add support for jtbd agent to accept max turns and experiment id as query params

* fix: add jtbd agent integration with workflow

* fix: change message threshold to 5
This commit is contained in:
Felarof
2026-01-14 17:30:39 -08:00
committed by GitHub
parent d9a6bb173f
commit 8d2c70689a
7 changed files with 58 additions and 17 deletions

View File

@@ -19,8 +19,18 @@ function getInitialRoute(): string {
return '/ai'
}
// Get survey params from query string
function getSurveyParams(): { maxTurns?: number; experimentId?: string } {
const params = new URLSearchParams(window.location.search)
const maxTurnsStr = params.get('maxTurns')
const experimentId = params.get('experimentId') ?? 'default'
const maxTurns = maxTurnsStr ? Number.parseInt(maxTurnsStr, 10) : 7
return { maxTurns, experimentId }
}
export const App: FC = () => {
const initialRoute = getInitialRoute()
const surveyParams = getSurveyParams()
return (
<HashRouter>
@@ -40,7 +50,10 @@ export const App: FC = () => {
/>
<Route path="scheduled" element={<ScheduledTasksPage />} />
<Route path="workflows" element={<WorkflowsPage />} />
<Route path="jtbd-agent" element={<SurveyPage />} />
<Route
path="jtbd-agent"
element={<SurveyPage {...surveyParams} />}
/>
</Route>
<Route path="create-graph" element={<CreateGraph />} />
</Routes>

View File

@@ -40,11 +40,14 @@ export const GraphChat: FC<GraphChatProps> = ({
const {
popupVisible,
recordMessageSent,
triggerIfEligible,
onTakeSurvey,
onTakeSurvey: onTakeSurveyBase,
onDismiss: onDismissJtbdPopup,
} = useJtbdPopup()
const onTakeSurvey = () => onTakeSurveyBase(5, 'workflow_survey')
// Trigger JTBD popup when AI finishes responding
const previousChatStatus = useRef(status)
// biome-ignore lint/correctness/useExhaustiveDependencies: intentionally only trigger on status change
@@ -93,6 +96,11 @@ export const GraphChat: FC<GraphChatProps> = ({
}))
}
const handleSubmit: FormEventHandler<HTMLFormElement> = (e) => {
recordMessageSent()
onSubmit(e)
}
const handleKeyDown = (e: KeyboardEvent<HTMLTextAreaElement>) => {
if (
e.key === 'Enter' &&
@@ -128,7 +136,7 @@ export const GraphChat: FC<GraphChatProps> = ({
{chatError && <ChatError error={chatError} />}
<div className="shrink-0 border-border/40 border-t bg-background/80 p-2 backdrop-blur-md">
<form
onSubmit={onSubmit}
onSubmit={handleSubmit}
className="relative flex w-full items-end gap-2"
>
<textarea

View File

@@ -7,6 +7,11 @@ import { Header } from './SurveyHeader'
import { Welcome } from './SurveyWelcome'
import { useChat } from './useSurveyChat'
interface SurveyPageProps {
maxTurns?: number
experimentId?: string
}
const ThankYouCard: FC<{ onReset: () => void }> = ({ onReset }) => (
<div className="rounded-xl border border-border bg-card p-8 text-center shadow-sm">
<div className="mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-green-500/10">
@@ -43,8 +48,8 @@ const ErrorCard: FC<{ error: Error; onRetry: () => void }> = ({
</div>
)
export const SurveyPage: FC = () => {
const chat = useChat()
export const SurveyPage: FC<SurveyPageProps> = ({ maxTurns, experimentId }) => {
const chat = useChat({ maxTurns, experimentId })
const handleStart = async () => {
const current = await jtbdPopupStorage.getValue()

View File

@@ -4,7 +4,13 @@ import { BROWSEROS_PREFS } from '@/lib/browseros/prefs'
const JTBD_API_URL = 'https://jtbd-agent.fly.dev'
// const LOCAL_JTBD_API_URL = 'http://localhost:3001'
const EXPERIMENT_ID = 'jtbd_jan26'
const DEFAULT_MAX_TURNS = 7
const DEFAULT_EXPERIMENT_ID = 'default'
export interface SurveyChatOptions {
maxTurns?: number
experimentId?: string
}
async function getInstallId(): Promise<string> {
try {
@@ -77,7 +83,10 @@ async function* streamSSE(
}
}
export function useChat() {
export function useChat(options: SurveyChatOptions = {}) {
const maxTurns = options.maxTurns ?? DEFAULT_MAX_TURNS
const experimentId = options.experimentId ?? DEFAULT_EXPERIMENT_ID
const [phase, setPhase] = useState<Phase>('idle')
const [messages, setMessages] = useState<Message[]>([])
const [isStreaming, setIsStreaming] = useState(false)
@@ -116,7 +125,7 @@ export function useChat() {
const response = await fetch(`${JTBD_API_URL}/api/interview/start`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ installId, experimentId: EXPERIMENT_ID }),
body: JSON.stringify({ installId, experimentId, maxTurns }),
signal: abortControllerRef.current.signal,
})
@@ -170,7 +179,7 @@ export function useChat() {
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ response: text }),
body: JSON.stringify({ response: text, maxTurns }),
signal: abortControllerRef.current.signal,
},
)

View File

@@ -61,7 +61,7 @@ export const ChatError: FC<ChatErrorProps> = ({ error, onRetry }) => {
</a>
{' or '}
<a
href="/options.html?page=survey"
href="/options.html?page=survey&maxTurns=5&experimentId=daily_limit"
target="_blank"
rel="noopener noreferrer"
className="underline hover:text-foreground"

View File

@@ -1,6 +1,6 @@
export const JTBD_POPUP_CONSTANTS = {
// Show popup after this many messages
MESSAGE_THRESHOLD: 15,
MESSAGE_THRESHOLD: 5,
// Show to 1 in N users (samplingId % N === 0)
// Set to 1 to show to everyone
SAMPLING_DIVISOR: 1,

View File

@@ -44,12 +44,18 @@ export function useJtbdPopup() {
}
}, [])
const onTakeSurvey = useCallback(async () => {
const current = await jtbdPopupStorage.getValue()
track(JTBD_POPUP_CLICKED_EVENT, { messageCount: current.messageCount })
setPopupVisible(false)
window.open('/options.html?page=survey', '_blank')
}, [])
const onTakeSurvey = useCallback(
async (maxTurns = 15, experimentId = 'popup_survey') => {
const current = await jtbdPopupStorage.getValue()
track(JTBD_POPUP_CLICKED_EVENT, { messageCount: current.messageCount })
setPopupVisible(false)
window.open(
`/options.html?page=survey&maxTurns=${maxTurns}&experimentId=${experimentId}`,
'_blank',
)
},
[],
)
const onDismiss = useCallback(async () => {
const current = await jtbdPopupStorage.getValue()