Skip to content

Repository files navigation

LineOne

Inbound voice for a one-person shop. A caller talks; LineOne answers out loud on the AssemblyAI Voice Agent API, runs JSON-schema tools, and Maya watches the ticket board.

This is original work for the AssemblyAI Voice Agent Hackathon on lablab.ai (1–30 Sep 2026). It is not UVAI, ClipToAction, or EventRelay.

Contest AssemblyAI account: connectkk11. LineOne never looks that name up. The only secret it reads is ASSEMBLYAI_API_KEY from the process environment.

The key stays in env

Do not paste the API key into git, chat, README, screenshots, or the browser.

Where How
Local gitignored .env.local (copy from .env.example)
Cursor Cloud environment secret named ASSEMBLYAI_API_KEY
Vercel Project Settings → Environment Variables → ASSEMBLYAI_API_KEY

Restart the Node process after the variable is set. GET /api/status reports handshake: "ok" when GET https://agents.assemblyai.com/v1/agents succeeds. That probe does not mint a voice session. Starting a call mints a single-use token via GET /v1/token on the server; the permanent key never goes to the client.

Optional: LINEONE_AGENT_ID to bind a stored agent. If unset, the browser sends an inline session.update (prompt, Mary voice, VAD, tools).

.env* is gitignored except .env.example, which is empty on purpose.

What it does

Northline Repair is a solo appliance shop. Maya Chen is usually on a job, so the one shop line is the business. LineOne is that line:

  1. Browser asks this app for a temporary Voice Agent token. This server calls GET /v1/token with ASSEMBLYAI_API_KEY.
  2. Browser opens wss://agents.assemblyai.com/v1/ws?token=… and sends one session.update with prompt, Mary voice, VAD, Universal-3 Pro max_accuracy STT, and four function tools.
  3. Microphone PCM16 @ 24 kHz streams as input.audio. Agent speech comes back as reply.audio.
  4. When the model needs shop data it emits tool.call. LineOne executes the tool against an in-memory board, then sends tool.result after reply.done so turn-taking stays intact.
Tool What it does
lookup_hours Weekday hours for Northline Repair (Sun closed, Sat 9–1)
create_ticket Opens a numbered job on the board
schedule_callback Queues a call-back window for Maya
read_open_tickets Reads tickets already on the board

Run locally

Node 20+. Chrome or Edge recommended (Safari needs the worklet resample path, which is included).

cp .env.example .env.local
# set ASSEMBLYAI_API_KEY in that file from the AssemblyAI dashboard.
# Do not commit .env.local.

npm install
npm test
npm run dev

Open http://127.0.0.1:4317. Click Start call, allow the microphone, wait for the greeting, then talk. Try:

  • “Are you open today?”
  • “My fridge is warm and the compressor keeps clicking. I’m Jordan Hale, 503-555-0199.”
  • “Can Maya call me back tomorrow after 3?”
  • “Do you already have a ticket for Elena Vargas?”

Without the env var the board still loads (two seed jobs). Start call returns HTTP 503 from /api/voice-token. That is intentional.

Hosted demo (Vercel)

Judges: https://lineone-two.vercel.app

lablab wants a URL judges can open. That production deploy is it.

To recreate the deploy:

  1. Use this public repo: https://github.com/kk-agent/lineone
  2. Import it in Vercel.
  3. Set one secret in the Vercel project: ASSEMBLYAI_API_KEY.
  4. Deploy. The production URL is the demo.

Anyone with the URL can start sessions billed to that key. Use the contest key with a spend cap.

Render also works (npm run start after npm run build, bind 0.0.0.0:$PORT). Vercel is the path this app is built for.

How the Voice Agent wiring works

Server, src/lib/assemblyai.ts:

GET https://agents.assemblyai.com/v1/agents     # handshake, not a call
GET https://agents.assemblyai.com/v1/token      # one-time browser token
Authorization: Bearer $ASSEMBLYAI_API_KEY

Client, src/lib/voice-session.ts:

  • session.update from src/lib/voice-agent-config.ts — inline config, or agent_id when LINEONE_AGENT_ID is set
  • input.audio — base64 PCM16 from /pcm-processor.js
  • tool.result only when the latest event is reply.done
  • session.end on hang-up and pagehide so you are not billed for the 30s resume window

Docs used while building:

Limits of this prototype

  • Shop state is in-process memory. Serverless instances do not share it; Reset board restores the two seed tickets.
  • No Twilio number in this slice. The Voice Agent API can attach SIP later; the browser call is the judge path.
  • No wallet seeds, Stripe, or checkout.

License

MIT. See LICENSE.

About

LineOne — AssemblyAI Voice Agent Hackathon: inbound voice for a one-person shop

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages