Production-style harness for deploying enterprise voice agents into real business workflows safely.
Voice agents are easy to demo and hard to deploy safely. The hard part is not getting a model to talk. The hard part is turning a call into a typed backend action only after identity, permissions, policy, budget, duplication, and audit checks pass.
This project focuses on that deployment layer.
- not a voice model
- not a telephony platform
- not a Bland replacement
- not a production deployment claim
This project does not try to build a voice model or replace Bland. It focuses on the layer around a voice agent that enterprise deployments need: typed actions, policy enforcement, audit logs, and regression testing.
transcript -> extraction -> caller -> policy -> action -> audit -> evalThat loop is shared across:
- simulator runs
POST /api/demo/run-scenarioPOST /api/bland/webhooknpm run evalnpm run verify:trace
lib/actions/action-gateway.ts: shared orchestration entrypointlib/extraction/*: deterministic, optional LLM, and Bland-variable extraction modeslib/policies/*: data-driven policy engine and RBAC checkslib/actions/*: typed backend service layerlib/db/repository.ts: repository abstraction with Postgres or in-memory fallbackapp/*: simulator, dashboard, call detail, approvals, and eval UIeval/scenarios/*: regression suitepathways/*: pathway-as-code handoff configs
- valid material request creates a requisition
- missing required field returns clarification
- over-budget request routes to approval
- unknown caller cannot mutate data
- duplicate request does not create duplicate state
- non-finance caller cannot view vendor payment data
- finance-authorized caller can view vendor payment data
- PO status lookup stays read-only
- urgent site issue creates an escalation
- approval bypass attempt is blocked and logged
- default mode
- used by evals and trace verification
- stable and reproducible
- no external APIs
- optional mode
- enabled only when
OPENAI_API_KEYis set - returns the same typed schema as deterministic extraction
- fails closed into clarification rather than unsafe action
- used by
POST /api/bland/webhook - validates and normalizes pathway variables
- still relies on the policy engine for authorization
The project includes a Bland-style ingestion endpoint:
POST /api/bland/webhook
It accepts transcript plus extracted variables, normalizes them into the shared extraction schema, and then calls the same runVoiceAction path used by simulator and evals.
See docs/Bland-integration.md.
The pathways/ directory contains handoff-style configs for:
- material request
- stock check
- PO status
- vendor payment
- site issue escalation
These are not auto-imports. They are artifacts that show how a deployment engineer could define the pathway contract next to the action gateway.
The policy engine is data-driven, not scenario-driven.
Checks include:
- caller known
- site access allowed
- role can create requisition
- role can view finance
- required fields present
- budget within limit
- duplicate request check
- approval bypass attempt
- emergency escalation
- read-only access allowed
- authorized caller required for mutation
Guardrails include:
unknown_callersite_access_deniedmissing_required_fieldsbudget_exceededrestricted_finance_accessduplicate_requestapproval_bypass_attemptemergency_escalationextraction_failed
runVoiceAction is the center of the system.
It:
- accepts transcript or normalized variables
- extracts intent and fields
- resolves caller from phone number
- evaluates policy
- executes, blocks, clarifies, routes, or escalates
- writes audit and webhook trace records
- stores follow-up text
- returns a typed response with a decision timeline
Every orchestration run records:
- interaction record
- action log
- webhook event
- guardrail events when triggered
- follow-up record
Write-side sub-actions also record their own trace rows.
The call detail page and trace report expose a full timeline:
- transcript received
- extraction completed
- caller resolved
- policy evaluated
- action attempted
- audit log written
- follow-up generated
npm run eval runs 50 scenarios through the shared action gateway and writes:
Current proof lines:
npm run evalnpm run verify:tracenpm test- eval scenario count:
50 - unsafe action count:
0 - audit coverage:
100% - readiness status:
ready
Readiness is ready only if:
- unsafe action count is
0 - critical scenarios pass
- audit coverage is
100%
It becomes conditionally_ready when non-critical failures remain, and not_ready if unsafe mutations or audit gaps appear.
Screenshot placeholders live in docs/screenshots:
- simulator
- call detail
- policy trace
- approvals
- eval results
npm install
cp .env.example .env.local
npm run devPostgres is optional:
npm run db:push
npm run db:seedIf DATABASE_URL is not set, the app uses the seeded in-memory repository so simulator, eval, trace verification, and tests still run.
npm install
npm run dev
npm run build
npm run eval
npm run eval:llm
npm run verify:trace
npm run verify:db
npm test
npm run db:seedEXTRACTION_MODE=deterministic
OPENAI_API_KEY=
OPENAI_MODEL=gpt-4.1-mini
OPENAI_BASE_URL=https://api.openai.com/v1
DATABASE_URL=
BLAND_WEBHOOK_SECRET=
NEXT_PUBLIC_APP_URL=http://localhost:3000
ENABLE_BLAND_INTEGRATION=false- Say this is not a voice bot demo, it is the deployment harness around a voice agent.
- Show the simulator and run the valid material request.
- Show extraction, caller resolution, policy, created requisition, and timeline.
- Run a bypass or over-budget case and show safe blocking or approval routing.
- Open call detail and point to audit logs, webhook trace, and guardrails.
- End on the eval page and readiness report.
More detailed walkthrough notes are in docs/demo-script.md.
See docs/security-and-safety.md.
The short version:
- extraction does not authorize actions
- Bland variables are validated but not trusted for permissions
- unknown callers cannot mutate state
- finance access is role-gated
- over-budget actions route to approval
- duplicate actions are blocked
- all write actions are idempotent
- deterministic extraction is still the default review path
- optional LLM extraction is supported but may not be exercised locally without an API key
- Bland compatibility is webhook-level, not a full telecom deployment
- follow-ups are stored as records, not sent through a live messaging provider
- Postgres proof mode depends on
DATABASE_URL
- add real screenshots or a stable screenshot script
- run LLM extraction eval in an environment with
OPENAI_API_KEY - verify Postgres mode in a live configured environment
- add customer-specific tenant config around the pathway contracts