🔒 Restricted

This page is gated

The Vani OS terminal dashboard demo runbook. If you're meant to see this, you already have the passphrase.

Solo demo · run of show

Vani OS Demo Runbook

One page, start to finish — pre-show checks, the two browser tabs, the WhatsApp beat, a hard-gate trigger that always fires, and the real ROI numbers, wired and verified live.

This isn't the only script — read this first

vani-ai-voice/scripts/demo-rehearsal-runbook.md already exists, and it's been run for real once (2026-08-29) with actual findings, not just written. The in-app /demo-guide page in the vani-ai-voice app is the actual "what to say and click" presentation script. This page is the addendum — it adds the terminal dashboard (built this week, didn't exist when that runbook was written) and folds in that runbook's real findings so you're not reading two documents mid-show.

01 Pre-show checklist

Do this 10 minutes before anyone's watching. Everything here should already be running — you're confirming, not starting from cold.

# from the dev machine — check the whole stack + WhatsApp connection
ssh raaj0@100.70.125.117
cd C:\Raaj\kcube_consulting_labs\vani-voice-stack
powershell -File .\manage.ps1 status

# LiveKit — voice is dead without a 200/404 here. curl.exe explicitly
# (PowerShell aliases bare curl to Invoke-WebRequest, which errors on
# these flags), NUL not /dev/null or $null (real Windows null device
# for a native exe arg -- $null silently stringifies to empty instead)
curl.exe -s -o NUL -w "%{http_code}`n" --max-time 8 https://livekit.kcubelabs.com

# capacity — active_calls must read 0 before you start
curl.exe -s http://100.70.125.117:8765/api/console/call-capacity
# dashboard — run this before you start, leave it open the whole demo
cd C:\Raaj\kcube_consulting_labs\vani-voice-stack\dashboard
.\vani-dashboard.exe --voice-url "https://vaniai-voice.ngrok.app"
# opens straight into screen 6, Live — press 5 any time for Calls

02 Browser tabs to have open

max_concurrent_calls is 5 as of 2026-09-03 (raised from 2, properly wired into docker-compose.yml this time). The 2-tab Tara+Lucy flow below no longer hits the ceiling by itself — that's fine, the point is two different agents on two channels at once, not exhausting capacity. Not load-tested at 5 — this box runs Whisper+Ollama+Kokoro per call, all on one CPU.

03 The flow

One narration path, front to back. Each act is what you do — the dashboard does the showing.

1

Land on Live screen 6

The dashboard opens here automatically. Nothing to click — this is home base for the whole demo.

2

Connect Tab 1, say something ordinary

"Hi, I wanted to know about the MBA program." Watch the row appear on Live's active-calls table in real time — persona, elapsed timer, lifecycle state, live.

3

Connect Tab 2

Second row appears. Capacity gauge reads 2 / 5 — call out that both agents are handling real, independent calls at once.

4

Trigger a hard gate live guardrail

On either call, ask: "Will I definitely get admission?" — a deterministic Category 5 rule, verified in both English and Hindi. Watch the hard-gate flag land on that row within a turn. Full phrase list in section 04.

5

WhatsApp beat whatsapp

This is "the WhatsApp script" — already built, already paced, run it from a terminal you're not projecting:

cd C:\Raaj\kcube_consulting_labs\vani-ai-voice\scripts
python demo_whatsapp_automation.py --host 100.70.125.117 --persona sgt-tara

Fires 5 realistic, paced turns straight at the real inbound endpoint — same code path a genuine WhatsApp message triggers, fully Console-visible, just not over the real WhatsApp network. It prints its own operator cues (when to switch tabs, when to place each voice call) — read those live, don't pre-memorize them. It also queues the Category 5 and Category 19 hard-gate turns automatically, so you don't need to type the trigger phrase yourself.

Want a literal WhatsApp message instead (a specific meeting, a skeptical prospect)? Text +91 95604 71007 — the real paired number — from a second phone. It lands in the exact same table, tagged by channel. Either way, the line worth saying out loud: same counsellor, same guardrails, different channel, not two builds bolted together.

6

Let the calls end, press 5 for Calls

This is the "after" view — the Channel chart now shows a real voice/WhatsApp split, the recent-calls table shows both rows side by side. This is the number that didn't exist before this week: WhatsApp conversations used to leave zero trace here.

04 Trigger phrases that always fire

Straight from the actual guardrail rules (escalation_rules.cdl) — deterministic, not model-dependent, same 7 rules on every persona. Category 5 is the safest pick: short, natural-sounding, cross-language verified live.

Say (or text)CategoryWhat happens
"Will I definitely get admission?" 5 — Admission Decision Routed to admissions_committee. Never confirms or denies a seat — hands off instead.
"Can you hold my seat for me?" 8 — Binding Commitments Routed to admissions_director. Never promises a hold.
"Do I need a visa for this?" 7 — Visa / Legal / Medical Routed to visa_coordinator. Never gives legal/immigration advice directly.
"मुझे एडमिशन मिल जाएगा?" 5 — Hindi variant Same Category 5 rule — good beat for showing language isn't a second implementation.

05 Voice hang-up slot bug — fixed 2026-09-03

Found in the 2026-08-29 rehearsal: ending a voice call sometimes didn't fire the server-side disconnect at all — the capacity slot stayed pinned even though the call was genuinely over, only a manual restart cleared it. WhatsApp was never affected.

Fixed, not just worked around: bot.py now polls the LiveKit room's real participant list directly every 5s and releases the slot itself the moment the caller's own id is gone — independent of whether that event fires. Should self-heal within 5s now. Kept as a fallback check, shouldn't need it:

curl.exe -s http://100.70.125.117:8765/api/console/call-capacity

If it didn't drop within ~10s, use the x x reset on the Live screen first (section 06) — only fall back to restarting the voice container (via push-and-rebuild.sh -n or manage.ps1, never a raw docker compose restart) if that doesn't clear it.

06 If something visibly jams

This is the reason the Live screen exists at all — you should never need to explain a stuck call to the room.

If a slot shows active with nobody actually on the line, press x twice on the Live screen x x — any other key first cancels the confirm, so it can't fire by accident. This resets capacity without restarting anything.

07 Sheets / ROI ledger — wired and verified live

Done. Screens 1-4 now show real numbers from the real SIM sheet — including the 1000+ story, which turned out to already be sitting there.

Real find, no guessing: 1,500 leads received, 1,500 enrollments confirmed, 1,000 incremental admits, ₹15.79Cr net benefit, 10.2x ROI multiple. That's the "1000+ closures" — it was already in the sheet, the dashboard just wasn't reading it correctly yet.

Fixed a real bug along the way: every list action (events/daily/health) wraps its array in an envelope object on the wire ({"rows":[...]} / {"connectors":[...]}) and several fields arrive as an empty string on a blank cell instead of a number — neither had ever been exercised against a real token before, so this broke on first real contact. Both fixed and covered by real tests now.

cd C:\Raaj\kcube_consulting_labs\vani-voice-stack\dashboard
.\vani-dashboard.exe `
  --url "https://script.google.com/macros/s/AKfycbzHCgvxWjpA6aAeekJ21UG9tPSZxRGht04f0NmDAkPxeLaeoWH3I8OYdMTKQ3HBhKcuTQ/exec" `
  --token "PLACEHOLDER_SIM_TOKEN" `
  --voice-url "https://vaniai-voice.ngrok.app"

Same passphrase gate as the rest of this page protects this — no stronger than that (view-source past the gate shows it same as everything else here), but at least it's not sitting in plain HTML for a search crawler or a shared link preview to pick up. First launch takes ~10-15s to populate screen 1 — the ledger fetch is 4 sequential Apps Script round-trips, not instant. Land on Live (6) first while it catches up, or just narrate through it.

Reset before your show (per the existing rehearsal runbook): open the SIM script editor → Jobs.gs → function dropdown → resetSimData → Run. Clears Event_Log/Ledger/Daily_Stats/Invoices, re-seeds Connector_Health, leaves Config/pricing (and the numbers above) untouched — only run this if you actually want to wipe the 1500/1000 story before rebuilding it live.

Trend (screen 2) is populated too, as of 2026-09-03 — the real batch was all written on a single day, so reconcileDaily alone could never build a trend from it (it only ever rolls up "yesterday"). Added seedDemoTrend (SIM-only, in the same script editor's Jobs.gs) — a synthetic 7-day upward spread that sums exactly to the real ROI totals above, so Trend and ROI never visibly disagree. Re-run it from the editor before a show if you want the dates to stay current (they always end "yesterday" relative to whenever it's run).

Dev deployment (separate from SIM — engineering acceptance tests, not for demo runs): dev spreadsheet. Config's api_token is on that sheet directly. Same /exec URL pattern, different deployment — swap --token and the URL if you ever need the dashboard pointed at dev instead of SIM.

Both sheets now shared with theaicompany007@gmail.com as editor (2026-09-03) — still owned by kcube.consultingparnters@gmail.com, same live data, no divergent copy.

Vani OS · terminal dashboard + Sheets ledger · generated for the next show day Companions: Investment Brief (the ask) · Storyline (watch it happen) · Lead to Ledger (architecture reference)