This document summarises how the three projects in the monorepo are wired together and confirms that all demo / mock data has been removed.
| Project | Env variable | Default fallback |
|---|---|---|
| Backend (FastAPI) | DASHBOARD_ORIGIN (CORS only) |
— |
| Overlay (Chrome ext) | VITE_BACKEND_URL |
http://localhost:8000 |
| Frontend (Dashboard) | VITE_API_BASE_URL |
http://localhost:8000 |
Both clients fall back to the same default (DEFAULT_API_BASE_URL in shared/apiConfig.ts). In production, set the appropriate env var to point at the deployed backend origin.
A single anonymous UUID (v4) identifies the learner across overlay and dashboard.
Overlay: generated via crypto.randomUUID() and persisted in chrome.storage.local under the key prosocratic_user_id. On every backend request the UUID is sent as the X-User-Id header.
Dashboard: the overlay mirrors the UUID into localStorage['prosocratic_user_id'] so the dashboard (which runs as a normal web page) can read it. If the dashboard loads without the extension, it generates its own UUID the same way.
The header name is centralised in shared/apiConfig.ts as USER_ID_HEADER.
Located at the monorepo root, shared/ contains two files imported by both projects via the @shared path alias:
apiConfig.ts—DEFAULT_API_BASE_URL,USER_ID_HEADER, and anAPIobject with every backend route path.apiTypes.ts— TypeScript interfaces for every request and response payload, matching the backend Pydantic models exactly.
Both vite.config.ts and tsconfig.json in the overlay and frontend resolve @shared/* to ../shared/*.
shared/apiConfig.tsshared/apiTypes.tsdocs/INTEGRATION.md
vite.config.ts— added@sharedaliastsconfig.json— added@sharedpath + include.env.example— corrected URL fromlocalhost:3001/apitolocalhost:8000src/background/index.ts— imports from@shared; usesAPI.*route constants andUSER_ID_HEADER; removedgetFallbackResponse(hardcoded demo replies); addedMICROASSESS_GENERATE,MICROASSESS_SUBMIT,UNASKED_QUESTIONmessage handlers; addedMICRO_ASSESSsuggestion forwarding to content scriptsrc/shared/types.ts— added newChromeMessagevariants for microassess and unasked-question flowssrc/telemetry/integration.ts— removed commented-out direct-fetch block with wrong URLsrc/overlay/hooks/useBehavioralSensors.ts— rewritten to gate telemetry start/stop behindsessionActivesrc/overlay/store/overlayStore.ts— addedsessionActiveflag,beginSession(),stopSession()actions; gated all data flow behind sessionsrc/overlay/components/ChatPanel.tsx— addedSessionStartScreen("Start Studying" button),SessionControls("End session"), conditional renderingsrc/overlay/hooks/useOriState.ts— ignores backend messages when session is not activesrc/content/index.tsx— removed autoextractAndSendPageContext()on page load
vite.config.ts— added@sharedalias + path importtsconfig.json— added@sharedpath + includesrc/api/client.ts— importsDEFAULT_API_BASE_URLandUSER_ID_HEADERfrom sharedsrc/api/dashboard.ts— usesAPI.*route constants and imports types from@shared/apiTypessrc/api/profiles.ts— usesAPI.*route constants and imports types from@shared/apiTypessrc/types/api.ts— now re-exports from@shared/apiTypes(thin wrapper)src/types/states.ts— now re-exports from@shared/apiTypes(thin wrapper)
No changes. The backend is the source of truth.
The following demo artefacts were removed:
-
getFallbackResponse()inoverlay/src/background/index.ts— hardcoded pattern-matched Socratic replies used when the backend was unreachable. Replaced with honest error messages ("Unable to reach the backend…" / "Session is still starting…"). -
Commented-out direct fetch in
overlay/src/telemetry/integration.ts— referencedlocalhost:3001/apiwith a wrong payload shape. Replaced with a comment noting that all telemetry routes through the background service worker. -
"demo" comment in
overlay/src/overlay/hooks/useBehavioralSensors.ts— thestartBehavioralTimer()call was annotated "for backwards compat during demo". Comment updated to reflect actual purpose (local activity tracking). -
.env.exampleURL mismatch — pointed tolocalhost:3001/apiinstead of the backend's actuallocalhost:8000. Corrected.
All overlay behaviour is now driven exclusively by real backend responses.
The overlay enforces an explicit session start — no telemetry, no nudges, and no backend communication until the user clicks "Start Studying".
How it works:
The Zustand store exposes a sessionActive boolean (default false) with two actions:
beginSession()— sendsPAGE_CONTEXTto the background worker (which callsPOST /v1/session/start), then setssessionActive: true. This triggers theuseBehavioralSensorshook to start the telemetry engine and the 30-second emitter.stopSession()— syncs any pending data viaSESSION_SYNC, then setssessionActive: false. The sensors hook detects this and tears down telemetry.
What is gated:
All of the following are no-ops when sessionActive === false:
surfaceNudge()— nudge cards will not appearhandleTelemetrySnapshot()— telemetry data will not be processedhandleSignalResponse()— backend signal responses are ignoredsendMessage()— chat input is disabledsyncSessionToStorage()— no writes tochrome.storageuseOriStatemessage handler — backend messages are ignoreduseBehavioralSensors— telemetry engine is not running
UI flow:
- Overlay opens → shows
SessionStartScreenwith a "Start Studying" button - User clicks →
beginSession()fires → telemetry starts → full UI appears (topic bar, chat, nudge area) - User clicks "End session" →
stopSession()fires → telemetry stops → back to start screen
No data is collected or transmitted until the user explicitly starts a session.
Overlay UI Content Script / Hooks Background SW Backend API
────────── ────────────────────── ────────────── ───────────
[Start Studying] ──────────> beginSession()
↓ sets sessionActive=true
PAGE_CONTEXT ───────────> startBackendSession() ───> POST /v1/session/start
↓ stores session_id
useBehavioralSensors
starts telemetry engine
↓ (every 30s)
TELEMETRY_WINDOW ────────> sendTelemetryUpdate() ──> POST /v1/session/update
↓ receives policy result
ORI_STATE_CHANGE <──────── ori_state
STATE_CLASSIFIED <──────── state_label + confidence
NUDGE_READY <──────── suggestion (type=TECHNIQUE)
MICROASSESS_SUGGESTED <──── suggestion (type=MICRO_ASSESS)
[User asks question] ──────> CHAT_REQUEST ─────────────> handleChatRequest() ────> POST /v1/questions/answer
CHAT_RESPONSE <────────────── direct_answer + follow_up
MICROASSESS_GENERATE ────> backendPost() ──────────> POST /v1/microassess/generate
MICROASSESS_SUBMIT ──────> backendPost() ──────────> POST /v1/microassess/submit
UNASKED_QUESTION ────────> backendPost() ──────────> POST /v1/unasked-question
[End session] ─────────────> stopSession()
↓ sets sessionActive=false
SESSION_SYNC ────────────> endBackendSession() ───> POST /v1/session/end
Dashboard reads from the same backend via GET endpoints (/v1/dashboard/summary, /v1/dashboard/sessions, /v1/dashboard/session/{id}, /v1/profiles/{id}, etc.), all authenticated with the same X-User-Id header.