ESP32-S3 Bench Guide
ESP32-S3 N16R8 bench guide: live pinout, lessons, simulators and the bench log.
Turn your lessons into live, spoken sessions that actually answer questions — no rebuild, no recorded video.
EducationTypeScript · HTMLGitHub @juggernaut2k4OnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
project distill
Translation pending
Taken from the repository README
Loading the full README. The saved excerpt is shown below.
Personalized AI coaching for executives. A voice AI (powered by ElevenLabs) joins your Google Meet, teaches a structured session on a topic from your learning plan, and adapts to your role, industry, and experience level.
• Node.js 18+
• npm 9+
Edit .env.local and replace all PLACEHOLDER_* values with real credentials. See the full variable reference below.
Run the latest migration in your Supabase SQL editor (all files in supabase/migrations/ in order).
Open http://localhost:3000.Read the full README ↗Personalized AI coaching for executives. A voice AI (powered by ElevenLabs) joins your Google Meet, teaches a structured session on a topic from your learning plan, and adapts to your role, industry, and experience level.
| Layer | Technology |
|---|---|
| Framework | Next.js 14 (App Router, TypeScript) |
| Styling | Tailwind CSS + Framer Motion |
| Auth | Clerk |
| Database | Supabase (PostgreSQL) |
| Resend | |
| SMS | Twilio |
| Payments | Stripe |
| AI | Anthropic Claude (claude-sonnet-4-6) |
| Voice AI | ElevenLabs Conversational AI (@11labs/client) |
| Meeting Bot | Attendee.dev (default) or Recall.ai (legacy) |
| Scheduling | Inngest |
git clone <repo-url>
cd distill
npm install --legacy-peer-deps
cp .env.local.example .env.local
Edit .env.local and replace all PLACEHOLDER_* values with real credentials. See the full variable reference below.
Run the latest migration in your Supabase SQL editor (all files in supabase/migrations/ in order).
npm run dev
Open http://localhost:3000.
npx inngest-cli@latest dev
| Variable | Description |
|---|---|
NEXT_PUBLIC_SUPABASE_URL | Supabase project URL |
NEXT_PUBLIC_SUPABASE_ANON_KEY | Supabase anon key |
SUPABASE_SERVICE_ROLE_KEY | Supabase service role key (server-only) |
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY | Clerk publishable key |
CLERK_SECRET_KEY | Clerk secret key |
ANTHROPIC_API_KEY | Anthropic API key |
STRIPE_SECRET_KEY | Stripe secret key |
STRIPE_WEBHOOK_SECRET | Stripe webhook signing secret |
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY | Stripe publishable key |
RESEND_API_KEY | Resend API key |
TWILIO_ACCOUNT_SID | Twilio account SID |
TWILIO_AUTH_TOKEN | Twilio auth token |
INNGEST_EVENT_KEY | Inngest event key |
INNGEST_SIGNING_KEY | Inngest signing key |
| Variable | Description |
|---|---|
NEXT_PUBLIC_ELEVENLABS_AGENT_ID | ElevenLabs conversational agent ID |
NEXT_PUBLIC_ELEVENLABS_VOICE_ID | ElevenLabs voice ID (Siren by default) |
ELEVENLABS_API_KEY | ElevenLabs API key — server-only, required for relay mode (AUD-01) |
| Variable | Values | Description |
|---|---|---|
MEETING_BOT_PROVIDER | attendee (default) · recall · agentcall | Which bot service joins the Google Meet |
ATTENDEE_API_KEY | — | Attendee.dev API key (when MEETING_BOT_PROVIDER=attendee) |
RECALL_API_KEY | — | Recall.ai API key (when MEETING_BOT_PROVIDER=recall) |
ATTENDEE_WEBHOOK_SECRET | — | Attendee.dev webhook signing secret (HMAC-SHA256) |
Controls how Attendee routes audio to ElevenLabs. Browser mode is the default (Vercel-compatible). Relay mode streams raw PCM16 audio server-side, cutting latency from ~800ms to ~150ms.
| Variable | Values | Description |
|---|---|---|
MEETING_BOT_AUDIO_MODE | browser (default) · relay | Server-side: whether Attendee uses the relay or the headless browser |
NEXT_PUBLIC_MEETING_BOT_AUDIO_MODE | browser (default) · relay | Client-side mirror — must match MEETING_BOT_AUDIO_MODE |
AUDIO_RELAY_WS_URL | — | Full WebSocket URL of the relay endpoint, e.g. wss://your-server.railway.app/api/audio-relay |
Both mode variables must match. A mismatch causes the relay to run server-side while WalkthroughClient also tries to start ElevenLabs in the browser — two simultaneous sessions.
Relay mode requires a persistent server (Railway, Render, fly.io). Browser mode works on Vercel with no changes.
Controls when per-tab scripts are injected into the ElevenLabs voice session.
| Variable | Values | Description |
|---|---|---|
CLIO_CONTEXT_MODE | all-upfront (default) · split | Server-side: whether session_script is included in the system prompt at session start |
NEXT_PUBLIC_CLIO_CONTEXT_MODE | all-upfront (default) · split | Client-side mirror — must match CLIO_CONTEXT_MODE |
Both variables must be set to the same value. A mismatch means the server omits the script but the client never injects it — Clio will coach from the knowledge base only.
| Mode | Behaviour |
|---|---|
all-upfront | Full session brief + topic KB + all tab scripts sent as system prompt at session start. Zero behaviour change from pre-CTX-01. |
split | Session brief + topic KB sent at start. Each tab's script injected via sendContextualUpdate at tab-advance time inside show_visual. Tab 1 injected immediately after session connects. |
| Variable | Values | Description |
|---|---|---|
CLIO_VOICE_PROVIDER | elevenlabs (default) · deepgram | Which voice SDK handles the session. deepgram is a POC stub only — no real session is established. |
The meeting bot system uses a pluggable provider pattern. All providers share the same MeetingBotProvider interface (lib/meeting-bot/types.ts).
MEETING_BOT_PROVIDER=attendee
ATTENDEE_API_KEY=<your-attendee-api-key>
ATTENDEE_WEBHOOK_SECRET=<your-webhook-signing-secret>
Attendee.dev joins the meeting, loads the walkthrough page in headless Chromium, and routes meeting audio through ElevenLabs via the page's microphone/speaker. Webhooks are verified with HMAC-SHA256.
Webhook endpoint: POST /api/attendee/webhook
Attendee fires:
bot.state_change — bot joining/leaving (used to update session status)transcript.update — participant speech (forwarded to ElevenLabs via sendUserMessage)participant_events.join_leave — join/leave events (used to greet participants by name)MEETING_BOT_PROVIDER=recall
RECALL_API_KEY=<your-recall-api-key>
Webhook endpoint: POST /api/webhooks/recall (legacy, maintained for backwards compat)
If ATTENDEE_API_KEY starts with PLACEHOLDER or is empty, the provider logs what it would send and returns a mock bot ID. The rest of the session flow continues normally.
Clio's voice session uses a custom LLM endpoint (/api/clio/llm) that fetches context from the database on every turn. The CLIO_CONTEXT_MODE toggle controls whether tab scripts are also included in the initial system prompt or injected on-demand.
POST /api/recall/bot
└── buildAllClioDocs() → session_brief + topic_KB + all_tab_scripts
→ stored in walkthrough_state.clio_session_context
└── ElevenLabs session starts
→ /api/clio/llm fetches full context on every turn
→ show_visual fires → scroll_to → returns script content to LLM
POST /api/recall/bot
└── buildAllClioDocs(mode='split') → session_brief + topic_KB only
→ stored in walkthrough_state.clio_session_context
└── ElevenLabs session starts
→ Tab 1 script injected immediately via sendContextualUpdate
└── show_visual({ section_index: N }) fires
→ training_scripts[N-1] injected via sendContextualUpdate before scroll_to
→ if script not ready: fallback string injected, Clio coaches from KB
Index mapping in split mode:
section_index: 0 = Session Overview — no training script, injection skippedsection_index: 1 = first content tab → training_scripts[0]section_index: N (N ≥ 1) → training_scripts[N-1]Server-side relay mode streams raw PCM16 audio between Attendee and ElevenLabs, cutting latency from ~800ms to ~150ms by eliminating the transcript webhook chain.
Browser mode (default):
Participant speaks → Attendee Whisper STT → transcript.update webhook
→ DB write → WalkthroughClient poll → sendUserMessage → ElevenLabs
(~800ms, 13 hops)
Relay mode (MEETING_BOT_AUDIO_MODE=relay):
Participant speaks → Attendee PCM16 → relay WS → ElevenLabs STT+LLM+TTS
→ relay WS → Attendee speaker
(~150ms, 3 hops)
server.ts Custom Next.js server — WebSocket upgrade at /api/audio-relay
lib/voice/relay-handler.ts AudioRelayHandler — Attendee ↔ ElevenLabs audio bridge
Handles show_visual (DB write) and end_session server-side
lib/meeting-bot/attendee.ts Browser mode: voice_agent_settings (headless browser)
Relay mode: websocket_settings.audio (no headless browser)
All direct calls on the ElevenLabs Conversation object are wrapped behind a VoiceSessionAdapter interface. This makes it straightforward to test an alternative provider (e.g. Deepgram) by swapping the adapter without touching WalkthroughClient.
lib/voice/
adapter.ts VoiceSessionAdapter interface
elevenlabs-adapter.ts Wraps @11labs/client Conversation
deepgram-adapter.ts POC stub (logs + no-ops)
relay-handler.ts Server-side Attendee ↔ ElevenLabs bridge (AUD-01)
index.ts createVoiceAdapter(provider, conversation) factory
sendUserMessage (transcript forwarding, ElevenLabs-specific) is kept on a separate elevenLabsConvRef — it is not part of the adapter interface because it has no Deepgram equivalent in this build. In relay mode, ElevenLabs handles STT directly from the audio stream, so sendUserMessage is not called at all.
app/
api/
recall/bot/ POST: create bot, build context docs, write to walkthrough_state
attendee/webhook/ POST: Attendee.dev webhook (state, transcript, participants)
clio/llm/ POST: custom LLM endpoint — fetches context + handles tool calls
walkthrough-state/ GET/POST/PATCH: session state polling
inngest/ Inngest function registry
dashboard/
walkthrough/ WalkthroughClient — ElevenLabs session, split-mode injection
plan/ Learning plan approval + session start
knowledge-base/ Per-session KB viewer
walkthrough/[userId]/ Public walkthrough page (loaded by the meeting bot)
lib/
voice/ VoiceSessionAdapter + ElevenLabs/Deepgram adapters (CTX-01)
meeting-bot/ Provider abstraction (Recall, Attendee, AgentCall)
clio-context-builder.ts Builds session_brief, topic_context, session_script
content/ AI content generation pipeline
learning/ User learning profile + profile context builder
session-plan.ts Session plan helpers
supabase.ts Server + browser Supabase clients
inngest/
session-content-pipeline.ts Generates tab content (visuals + scripts) per session
session-designer-auto.ts Auto-fires content generation after plan approval
daily-delivery.ts Daily email/SMS delivery cron
feedback-processor.ts Processes feedback, updates AI Readiness Score
docs/specs/ BA requirement documents for all shipped features
# Unit + integration (Vitest)
npm test
# E2E (Playwright — app must be running on port 3000)
npm run dev # terminal 1
npm run test:e2e # terminal 2
The project deploys to Vercel. Push to main.
Set all environment variables in the Vercel project dashboard under Settings → Environment Variables.
| Service | Endpoint |
|---|---|
| Attendee.dev | https://yourdomain.com/api/attendee/webhook |
| Stripe | https://yourdomain.com/api/webhooks/stripe |
| Clerk | https://yourdomain.com/api/webhooks/clerk |
| Inngest | Auto-configured via Inngest dashboard |
To switch from Recall to Attendee (or back) in Vercel:
MEETING_BOT_PROVIDER in Vercel environment variablesATTENDEE_API_KEY or RECALL_API_KEY)To enable split context mode:
CLIO_CONTEXT_MODE=split and NEXT_PUBLIC_CLIO_CONTEXT_MODE=splitRelay mode streams raw PCM16 audio directly from Attendee to ElevenLabs server-side, bypassing the transcript webhook chain. Latency drops from ~800ms to ~150ms.
Relay mode cannot run on Vercel — it requires a persistent WebSocket server. Deploy to Railway, Render, or fly.io instead.
# On your persistent server (Railway / Render / fly.io)
MEETING_BOT_AUDIO_MODE=relay
NEXT_PUBLIC_MEETING_BOT_AUDIO_MODE=relay
ELEVENLABS_API_KEY=sk_...
AUDIO_RELAY_WS_URL=wss://your-server.railway.app/api/audio-relay
# Start with the custom server (not `next start`)
npm run start:relay
How it works (hybrid design):
voice_agent_settings) — screen share works as normal in the meetingAUDIO_RELAY_WS_URL (websocket_settings.audio)/api/clio/llm) + TTS runs server-siderealtime_audio.bot_output — injected directly into the meetingshow_visual tool calls update walkthrough_state in the DB → the headless browser page polls and scrolls → bot screen-shares the updated visualTo revert to browser mode: unset MEETING_BOT_AUDIO_MODE (or set to browser) and redeploy.
| Plan | Price | Features |
|---|---|---|
| Free Trial | $0 / 7 days | 1 email/day, onboarding, learning plan |
| Starter | $12/mo or $99/yr | Daily email, weekly digest, AI Readiness Score |
| Pro | $25/mo or $199/yr | Email + SMS, Ask Anything, voice sessions |
| Executive | $49/mo or $399/yr | Everything + dedicated number, priority onboarding |