尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证制作者的介绍仅有上面的一句话。 更多详情请阅读仓库 README。 仓库 README ↗
正在加载完整 README。
查看完整 README ↗A Vaani voice agent answers every inbound call. After the call, Gemini judges whether the caller is a lead worth a designer's time, using the studio's own rubric. Every call is logged in HubSpot. Real leads become deals and are posted to a Telegram group with one I'll take it button. A Cal.com booking moves the deal forward.
Metrics:
flowchart LR
caller([Caller]) -->|phone, any hour| vaani[Vaani agent<br/>prompt v2]
vaani -->|end-of-call webhook| wh[/api/webhooks/vaani/]
wh -->|store once, 200 fast| neon[(Neon Postgres)]
wh -.after().-> q[Gemini qualification<br/>rules + services, never pricing]
q --> neon
q --> hs[HubSpot<br/>contact + call, deal if lead]
q -->|qualified / needs review| tg[Telegram group<br/>card + claim button]
tg -->|first tap wins| twh[/api/webhooks/telegram/]
twh --> neon
twh -->|assigned designer, stage DESIGNER_CONTACTED| hs
designer([Designer]) -->|calls back, sends Cal.com link| cal[Cal.com]
cal -->|BOOKING_CREATED / CANCELLED| cwh[/api/webhooks/calcom/]
cwh -->|stage CONSULT_BOOKED, meeting| hs
cwh --> tg
gha[GitHub Actions hourly] --> cron[/api/cron/sync/]
cron -->|deal stages, won values| neon
cron -->|retry failed calls| q
neon --> dash[/dashboard/]
Everything after the Vaani adapter works on a channel-agnostic Enquiry (lib/enquiry.ts), so
WhatsApp or a web form can be added later as new adapters.
| Path | What it does |
|---|---|
config/qualification_rules.md | The approved rubric (v1, with a decision log). Fed to Gemini. |
agent/vaani_prompt_v2.md | The voice agent's prompt (current). v1 is kept for comparison. |
agent/vaani_config_notes.md | Settings to enter in the Vaani dashboard |
knowledge/ | services.md, qualified.md, pricing.md (internal; never sent to any model or message), September transcripts |
lib/ | vaani adapter, pipeline, qualify (Gemini), hubspot, telegram, calcom, claim, price guard |
app/api/webhooks/{vaani,telegram,calcom} | Webhooks |
app/api/cron/sync | Hourly HubSpot stage sync and retry of failed calls |
app/dashboard | Dashboard (metrics, recent calls) and per-call transcript pages. Public by studio decision, hidden from search engines. |
migrations/ | SQL migrations (npm run migrate) |
tests/ | Unit tests, call simulations, 40-transcript qualification run |
APPROVED_PRICE_BAND_TEXT is empty by studio
decision). AI summaries are sanitised. Every Telegram message passes assertNoPrice or isn't sent.
The caller's own budget is stored in Neon but shown in Telegram only as "mentioned / not mentioned".(channel, external_id) key is unique, and each step stores its ids before moving on.robots.txt + noindex). Re-adding a login later is a small change.DRY_RUN=true (the default) skips HubSpot writes and sends Telegram cards only to the admin chat.| Service | What you need |
|---|---|
| Gemini | API key from Google AI Studio |
| Neon | A project (Singapore/Mumbai region) and its pooled connection string |
| HubSpot | Free CRM, super-admin user, a private app (see below) |
| Telegram | A bot from @BotFather, the designers' group with the bot added, your private chat with the bot |
| Cal.com | An event type with a required phone number question, and its public link |
| GitHub + Vercel | A private repo and a Vercel Hobby project importing it |
Copy .env.example to .env and fill it in. Set the same values in Vercel (Project → Settings →
Environment Variables; you can paste the whole file).
| Variable | Notes |
|---|---|
GEMINI_API_KEY, GEMINI_MODEL | Default model gemini-3.8-flash (2.5-flash isn't offered to new keys) |
GEMINI_FALLBACK_MODELS | Tried in order when the free daily quota of the main model runs out |
GEMINI_INPUT_PRICE, GEMINI_OUTPUT_PRICE | USD per 1M tokens, for the cost panel (0 on the free tier) |
CALLBACK_WINDOW_BUSINESS_HOURS / _AFTER_HOURS | within an hour / by eleven a.m. on the next working day |
APPROVED_PRICE_BAND_TEXT(_OFFICE) | Keep empty (no figures) |
VAANI_API_KEY, VAANI_WEBHOOK_SECRET, VAANI_COST_PER_MIN | Secret goes in the webhook URL or header |
DATABASE_URL | Neon pooled connection string |
HUBSPOT_TOKEN, HUBSPOT_PORTAL_ID, HUBSPOT_PIPELINE_ID, HUBSPOT_STAGE_* | npm run hubspot-setup prints these |
TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID, TELEGRAM_ADMIN_CHAT_ID, TELEGRAM_WEBHOOK_SECRET, SEND_NEEDS_REVIEW | npm run telegram-setup -- chats lists chat ids |
CALCOM_WEBHOOK_SECRET, CALCOM_LINK | Secret is pasted into Cal.com's webhook form |
APP_BASE_URL | e.g. https://aangan-intake.vercel.app |
CRON_SECRET | Protects the hourly job endpoint |
FIXED_MONTHLY_COSTS_JSON | e.g. {"vaani_plan": 1500} (INR) |
DRY_RUN | true until you've tested end to end |
npm install
npm run migrate
crm.objects.contacts.read/writecrm.objects.deals.read/writecrm.schemas.contacts.read/writecrm.schemas.deals.read/writenpm run hubspot-setup. It checks the scopes, creates the 6 custom deal properties (the free
CRM allows 10 in total), and prints the HUBSPOT_* values for .env.knowledge/ holds internal pricing).APP_BASE_URL to the deployment URL and redeploy.APP_BASE_URL and CRON_SECRET. This enables
.github/workflows/hourly-sync.yml. Vercel Hobby cron can only run daily, so GitHub Actions runs it instead.npm run telegram-setup -- chats and put the group id in TELEGRAM_CHAT_ID and your private
chat id in TELEGRAM_ADMIN_CHAT_ID.npm run telegram-setup -- webhook. After this, typing /chatid in any chat shows its id.Follow agent/vaani_config_notes.md. In short:
npm run render-prompt.https://<app>/api/webhooks/vaani?token=<VAANI_WEBHOOK_SECRET>.https://<app>/api/webhooks/calcomCALCOM_WEBHOOK_SECRETCALCOM_LINK. It appears on every Telegram card.(The brief named Calendly, but Calendly webhooks need a paid plan. Cal.com's free plan includes signed webhooks, so no registration script is needed: the webhook is created in the Cal.com UI above.)
| Command | What |
|---|---|
npm test | Unit tests: phone normalisation, after-hours, idempotency and claim race (against Neon), HubSpot dedupe, Telegram formatting, Cal.com signature and matching, no-rupee assertion |
npm run simulate | Gemini plays 32 callers (17 from real phone transcripts, plus edge cases) against the agent prompt. An LLM judge scores each against the checklist. Report: tests/output/simulations/report_v2.md. Resumable across days on the free tier. |
npm run qualify-all | Runs all 40 September enquiries through qualification and writes tests/output/qualification_results.csv |
DRY_RUN=true./dashboard with its transcript.DRY_RUN=false in Vercel and redeploy. Cards then go to the group and
HubSpot gets written.handoffs WHERE claimed_at IS NULL AND posted_at < now() - interval '15 minutes',
posting reminders and DMing the owner. /api/cron/sync already runs hourly and is the place for it,
or run it more often from the same GitHub Action.claimed_by (with an audit row)
and re-posts.designers table (areas, project types, load), plus a function between
qualification and ensureTelegramPost that picks a designer and mentions them on the card.Enquiry. Everything
downstream already works per channel (calls.channel).