feat: improve jtbd agent support (#202)

* feat: termination condition

* chore: minor url change

* feat: added support for install id in the frontend

* fix: pass experiment id from frontend

* chore: remove excessive comments per CLAUDE.md guidelines

Co-authored-by: Felarof <felarof99@users.noreply.github.com>

* fix: add route to survey

* fix: pass install id correctly

* fix: url

---------

Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Felarof <felarof99@users.noreply.github.com>
This commit is contained in:
Felarof
2026-01-10 11:12:10 -08:00
committed by GitHub
parent 27f8598e92
commit 4ef0a99b0d
4 changed files with 43 additions and 8 deletions

View File

@@ -9,12 +9,22 @@ import { LlmHubPage } from './llm-hub/LlmHubPage'
import { MCPSettingsPage } from './mcp-settings/MCPSettingsPage'
import { ScheduledTasksPage } from './scheduled-tasks/ScheduledTasksPage'
// Check query params for direct page navigation
function getInitialRoute(): string {
const params = new URLSearchParams(window.location.search)
const page = params.get('page')
if (page === 'survey') return '/jtbd-agent'
return '/ai'
}
export const App: FC = () => {
const initialRoute = getInitialRoute()
return (
<HashRouter>
<Routes>
<Route element={<DashboardLayout />}>
<Route index element={<Navigate to="/ai" replace />} />
<Route index element={<Navigate to={initialRoute} replace />} />
<Route path="ai" element={<AISettingsPage key="ai" />} />
<Route path="chat" element={<LlmHubPage />} />
<Route path="search" element={null} />

View File

@@ -1,7 +1,22 @@
import { useRef, useState } from 'react'
import { getBrowserOSAdapter } from '@/lib/browseros/adapter'
import { BROWSEROS_PREFS } from '@/lib/browseros/prefs'
const JTBD_API_URL = 'https://jtbd-agent.fly.dev'
const DOMAIN = 'browseros'
const JTBD_API_URL = 'https://jtbd-agent.fly.dev' // 'http://localhost:3001'
const EXPERIMENT_ID = 'jtbd_jan26'
async function getInstallId(): Promise<string> {
try {
const adapter = getBrowserOSAdapter()
const pref = await adapter.getPref(BROWSEROS_PREFS.INSTALL_ID)
if (pref?.value) {
return String(pref.value)
}
} catch {
// BrowserOS API not available
}
return ''
}
export type Message = {
id: string
@@ -11,6 +26,8 @@ export type Message = {
export type Phase = 'idle' | 'active' | 'completed' | 'error'
const INTERVIEW_COMPLETE_MARKER = '__INTERVIEW_COMPLETE__'
async function* streamSSE(
response: Response,
): AsyncGenerator<string, void, unknown> {
@@ -38,6 +55,8 @@ async function* streamSSE(
const event = JSON.parse(data)
if (event.type === 'text-delta' && event.delta) {
yield event.delta
} else if (event.type === 'interview_complete') {
yield INTERVIEW_COMPLETE_MARKER
} else if (event.type === 'error' && event.errorText) {
throw new Error(event.errorText)
}
@@ -91,10 +110,11 @@ export function useJTBDAgentChat() {
abortControllerRef.current = new AbortController()
try {
const installId = await getInstallId()
const response = await fetch(`${JTBD_API_URL}/api/interview/start`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ domain: DOMAIN }),
body: JSON.stringify({ installId, experimentId: EXPERIMENT_ID }),
signal: abortControllerRef.current.signal,
})
@@ -158,15 +178,18 @@ export function useJTBDAgentChat() {
}
let accumulated = ''
let isComplete = false
for await (const chunk of streamSSE(response)) {
if (chunk === INTERVIEW_COMPLETE_MARKER) {
isComplete = true
continue
}
accumulated += chunk
updateLastMessage(accumulated)
}
if (
accumulated.toLowerCase().includes('thank you') &&
accumulated.toLowerCase().includes('valuable insights')
) {
if (isComplete) {
setPhase('completed')
}
} catch (e) {

View File

@@ -9,4 +9,5 @@ export const BROWSEROS_PREFS = {
SHOW_LLM_CHAT: 'browseros.show_llm_chat',
SHOW_LLM_HUB: 'browseros.show_llm_hub',
SHOW_TOOLBAR_LABELS: 'browseros.show_toolbar_labels',
INSTALL_ID: 'browseros.metrics_install_id',
} as const

View File

@@ -42,5 +42,6 @@ export default defineWebExtConfig({
},
}),
chromiumArgs,
startUrls: ['chrome://newtab'],
disabled: !useBrowserOS,
})