Skip to content
View pinecall's full-sized avatar

Block or report pinecall

Block user

Prevent this user from interacting with your repositories and sending you notifications. Learn more about blocking users.

You must be logged in to block users.

Content in all repositories owned by your account will be closed.
Maximum 250 characters. Please don’t include any personal information such as legal names or email addresses. This note will only be visible to you.
Report abuse

Contact GitHub support about this user’s behavior. Learn more about reporting abuse.

Report abuse
pinecall/README.md

Pinecall: the harness for voice AI — voice agents and voice apps, from Studio, code, the CLI or MCP

check release npm pypi

Open the console · Quickstart · Docs · Studio · CLI · MCP · Self-hosting


Pinecall is the open-source harness for voice AI. Build a voice agent in a form, write a voice app in code, or ask your coding assistant to do it. Put it on a phone number, a web page or WhatsApp, test it before it goes live, and read every call it takes.

Pinecall takes care of the phone line, speech in and out, turn-taking, memory, retrieval, judges at hang-up, the console and the bill. This repository holds all of it, and you can run it on your own machine or use Pinecall Cloud.

The Pinecall console: a dental clinic's agents, the calls live now, what the judges held, and the week's numbers

▸ Four ways in, one platform

you work in best when
Agent Studio the console: a form and a live chat the agent is instructions, a few API calls and some data to collect
A voice app your editor, in TypeScript, Python or Ruby it has logic of its own: stages, state, tools in your code
The CLI a terminal or a CI job you want it scripted: tests, deploys, numbers, settings
MCP Claude Code, Codex, Cursor and others you would rather say what you want and watch it get built

They are not separate products. Every agent, however it was made, runs on the same runtime, takes the same calls, is tested the same way and shows up in the same console. A Studio agent can leave the form whenever it outgrows it: pinecall studio code turns it into a TypeScript project.

▸ Agent Studio: no code

Open the sandbox's Agents, press New, and you are talking to your agent a minute later. There is nothing to install and nothing to host.

  • Instructions, with the call's own values filled in: {{call.from}}, {{caller.name}}, {{call.today}}.
  • A greeting: a set line, an instruction the model opens with, or the caller speaks first.
  • Tools: HTTP requests to your API that the model may make, each one tried from the page. A secret is named as {{secrets.AGENDA_KEY}}, never written in the form.
  • Data collection: fields gathered from the caller and posted where you say when the call ends.
  • A voice, a knowledge base, a phone number, in tabs of the same page, with Talk always one click away, as a chat or as a call.

Every save is a numbered release. The sandbox is where you build; Deploy copies a release to production, where your callers reach it. The same agent can be driven from a terminal:

$ pinecall studio new front-desk
$ pinecall studio pull front-desk            # the definition, as a file you can review
$ pinecall studio push front-desk --note "books the visit"
$ pinecall chat front-desk                   # nothing to start: Pinecall holds it
$ pinecall studio deploy front-desk

Studio, field by field

▸ A voice app: all the code you want

A voice app is code in your repository: an app.ts that says which number is answered by whom, and a class per agent. @agent({ … }) says what the agent is; the class's fields are the call's state, its @tool methods are what the model may do, and its docstrings and render() are the prompt.

// agents/front-desk/agent.tsx
import { Agent, agent, tool, type Call } from "@pinecall/agents";

/**
 * You are the front desk of Brightside Dental. You book, move and explain visits.
 * Short sentences. Offer at most two times. Never say a time the agenda did not give you.
 */
@agent({
  stt: "deepgram/flux-general-en",                    // what it hears, thinks and speaks with
  llm: "google/gemini-3.5-flash-lite",
  voice: "cartesia/f786b574-daa5-4673-aa0c-cbe3e8534c02",  // Katie
  takes: "booking, moving or asking about a visit",   // what a teammate may pass it
  judges: ["consent", "grounded", offersARealTime],   // who reads every call: Pinecall's, and its own
  personas: [hurried, chargedTwice],                  // the test callers that phone it
})
export default class FrontDesk extends Agent {
  slots: Slot[] = [];                                 // state: plain fields, kept per call

  override onCall(call: Call) {
    this.call.shared["patient"] = clinic.patientByPhone(call.contact);
  }

  /** The open times of one day. `day` is YYYY-MM-DD. */
  @tool({ announce: "Let me check the agenda." })     // a verb the model may call
  freeSlots(day: string) {
    return (this.slots = clinic.free(day));
  }

  /** Book one of the times freeSlots returned, once the caller said yes to it. */
  @tool()
  book(day: string, time: string, name: string) {
    const visit = clinic.book(day, time, name);
    this.log("visit.booked", { ...visit, phone: this.call.contact });   // the app hears it, below
    return visit;
  }

  override render() {                                 // the prompt's last block, a function of state
    if (this.slots.length === 0) return null;
    return <p>Offer only these times: {this.slots.map((slot) => slot.time).join(", ")}.</p>;
  }
}
// app.ts
import { flow, voiceApp } from "@pinecall/agents";

const app = voiceApp("brightside", { timezone: "America/Los_Angeles" });

// One number and a team behind it: the front desk answers, and the call may pass to billing.
app.line("+1 415 555 0134", { greeting: "Brightside Dental. How can I help?", answers: FrontDesk, team: [Billing] });
app.rule("Never say an amount of money that a tool did not give you.");

// A flow outlives the call that started it: the day before the visit, the agent calls the patient.
const remindVisit = flow("remind-visit", async (booked, step) => {
  const visit = booked as unknown as Visit & { phone: string };
  await step.sleepUntil(startsAt(visit), { before: "24h" });
  await step.call(visit.phone, FrontDesk, { instruction: `Remind ${visit.patient} of the visit.` });
});
app.on("visit.booked", remindVisit);

export default app;
An agent in Python
from typing import Literal

from pinecall import Agent, tool


class FrontDesk(Agent):
    """You answer the phone for the business as its automated assistant, and your first sentence says so.
    Short sentences: everything you say is read aloud.
    """

    stage: Literal["ask", "done"] = "ask"
    message: dict[str, str] | None = None

    @tool(stage="ask", pii=("name",))
    def take_message(self, name: str, about: str) -> dict[str, str]:
        """Writes down the caller's message, once they have said their name and what it is about."""
        self.message = {"name": name, "about": about}
        self.stage = "done"
        return self.message
An agent in Ruby
require "pinecall"

# You answer the phone for the business as its automated assistant, and your first sentence says so.
# Short sentences: everything you say is read aloud.
class FrontDesk < Pinecall::Agent
  stage :ask, :done
  state :message

  # Writes down the caller's message, once they have said their name and what it is about.
  tool stage: :ask, pii: %i[name]
  def take_message(name:, about:)
    self.message = { name:, about: }
    self.stage = :done
    message
  end
end

What the agent runs on — its voice, its models, its greeting, the documents it searches — is declared with the class, and an app written as code is set by its code: the console shows it, read-only. An agent built in Studio has no code, and there the same settings are tuned in the console, per environment and versioned.

From an empty folder to a call:

$ npm i -g pinecall
$ pinecall login                              # or run the runtime yourself, below
$ pinecall new acme --agent front-desk        # --python or --ruby for the other SDKs
$ cd acme && npm install
$ pinecall chat                               # a written call against your class, here
$ pinecall test                               # the goldens, judged by a model
$ pinecall start                              # hold the agent: the console's calls reach your process
$ pinecall deploy up                          # run it on the runtime, without your laptop

▸ The CLI: the whole platform from a terminal

One pinecall, the same for every language. What the console can do, a script can do.

stage verbs
start login · new · generate · whoami
hold and talk start · chat · prompt · console · supervise
Agent Studio studio new · pull · push · try · deploy · code
the agent's settings agent · lexicon · voices · pipeline · providers
testing test · simulate · eval · runs · cases · personas · judges · judging
knowledge and memory docs · memory · remember
the phone numbers · carriers · line · callbacks
production deploy · secrets · sessions · monitors · webhook · telemetry · limits · data
assistants mcp · mcp install

pinecall <verb> --help explains each one with examples, and the CLI reference has a page per verb.

▸ MCP: let your assistant build it

pinecall mcp is the same CLI as an MCP server. One command adds it to Claude Code, Claude Desktop, Codex, Cursor, Windsurf, Antigravity and Gemini CLI:

$ npm i -g pinecall
$ pinecall mcp install

Then ask for what you want. Your assistant has the whole journey as tools: make a project, write the agent, hold it while it changes, chat with it, run its goldens, play a caller against it, read a call, buy a number, and deploy.

"Make me a front-desk agent for a dental clinic that takes messages, test it with an impatient caller, and tell me what went wrong."

Every tool acts in the sandbox unless it is asked for production, and your org's switch still decides whether it may. No tool takes or returns a key. What cannot be undone (erasing data, dropping a number, removing a deployed app) is not a tool at all: those stay commands a person types.

The MCP server

▸ The console

Every org gets one page with both worlds in it: a sandbox, where nothing reaches a real caller, and production. It is where a team that does not write code lives.

Build Agents and Apps, each with its configuration, knowledge, tools, channels and tests. Numbers, with a test line to try one before buying
Observe Calls live and past, each with its transcript, tools, latency and cost. Quality, Observability, and Monitors with alerts per agent
Harness Goldens, Simulations, Personas, Judges, and Cases: a call that broke, kept until it is fixed
Manage Team, Usage, Billing, Privacy, Settings
Talk A side panel on every page: chat with the agent, or call it from the browser

A supervisor can listen to a live call, whisper to the agent, take the line and hand it back.

A tour of the console

▸ Test it like software

ring what runs how
0 the class as plain code: tools, state, the prompt, no network npm test
1 goldens: a conversation the model must hold, judged turn by turn pinecall test, or the agent's Test tab
2 a persona calls the agent over a real line, scored at hang-up pinecall simulate --voice, or Simulations
3 one real call re-checked: latency, errors, consent, talk share pinecall eval <call>
4 every finished call, judged at hang-up: the verdict is an entry in its log nothing to run

A broken call becomes a case: kept, fixed, then approved or dismissed. Judges, monitors and alerts watch production.

▸ Your machine or Pinecall Cloud

The runtime is open source, and the agent is the same either way.

on your machineon Pinecall Cloud
$ uv tool install "pinecall-runtime[voice]"
$ pinecall-runtime setup

Reads your machine and runs the stack that fits it, and asks your email and a password for the console. No account, and no login. On your machine

$ pinecall login

Signs this machine in through the browser, once. Or open the console and start in Studio, with nothing installed.

▸ How a call moves

Phone, browser, chat and WhatsApp reach the Pinecall runtime. Its worker runs LiveKit's AgentSession, speech to text, the model and text to speech; its gateway asks the agent's class to run a tool and tells app.ts what happened on the call; a booked visit, an entry in the call's log, starts a flow that outlives the call; the log is kept in Postgres, with Redis as the signal; the console, the CLI and the MCP server read the log live

  1. A caller comes in by a carrier, the browser widget, a written chat or WhatsApp. The app's line says which agent answers the number and who it may pass the call to.
  2. A worker takes the call. It runs LiveKit's AgentSession, with speech-to-text, the model and text-to-speech built from what the agent declares, or from the org's providers.
  3. The agent acts. A class's tool runs in your own process, asked over its app socket. A Studio tool is an HTTP request the gateway makes to your API.
  4. Everything is an entry in the call's log. Turns, tool calls, latencies, costs and scores are appended, never rewritten. The console, the CLI, the MCP server, the bill and the judges all read that log.
  5. The app hears the log. app.on("visit.booked", …) runs your code on an entry, or starts a flow: steps the gateway keeps in a journal, so a reminder a day later outlives the call and a deploy.

The runtime is one Python package shaped like a Rails app. The architecture page walks every folder.

▸ Many orgs, one runtime

One runtime serves many businesses, and each one is an org: its own people and roles, keys, numbers and carriers, vendor keys, quotas, usage and data.

  • Your code never runs inside the runtime. A class runs in the org's own process, or in a gVisor-sandboxed pod of its own when the runtime hosts it. No two orgs' code share a process.
  • The database holds the line. Every request is scoped to its key's org, and Postgres row-level security shows each connection that org's rows alone, a query that forgot its WHERE included.
  • Two environments per org. The sandbox and production share the gateway and the database, never the workers. In the sandbox every person gets a copy of the agents of their own.
  • Settings live per org, per environment, versioned. Quotas say what an org may use, and an org may bring its own vendor keys.

Pinecall stands on LiveKit: its media server, its SIP bridge and its Agents framework. It adds the platform around them. Orgs, keys and the two environments has the whole model.

▸ What is inside

folder what it is published as
runtime/ the gateway that answers every door of the API, and the worker that runs the calls on LiveKit pinecall-runtime · PyPI
cli/ the pinecall CLI and its MCP server, the same for every language pinecall · npm
console/ the page the gateway serves, both environments in it, Agent Studio included inside pinecall-runtime
widget/ one agent as one button on any page inside pinecall-runtime
sdk/typescript/ the TypeScript framework an agent is written in @pinecall/agents · npm
sdk/python/ the Python SDK pinecall · PyPI
sdk/ruby/ the Ruby SDK pinecall · RubyGems
packages/room/ the caller's side of a call, for a page's own UI @pinecall/room · npm
packages/dot/ the talking dot, and the tutor that walks a page with a pointer of its own @pinecall/dot · npm
packages/core/ what the console and Pinecall's supervisor app stand on taken as source
examples/ Brightside Dental, the one example app, in each language read and copied, never published
infra/ the charts, the images and the Terraform a runtime is deployed with make deploy from runtime/

▸ Contributing

$ git clone https://github.com/pinecall/pinecall && cd pinecall
$ pnpm install          # every TypeScript package, one workspace and one lockfile (Node 24, pnpm 11)
$ cd runtime && uv sync # each Python package is a project of its own, with its own uv.lock

Each package's own check is what CI runs for it, only when its folder changed. Every published package carries the same version, and a v* tag publishes them all. Report a vulnerability as SECURITY.md says.


$ pinecall --help · docs.pinecall.io · pinecall.io

Popular repositories Loading

  1. pinecall pinecall Public

    Build, test and run voice and chat agents on the phone, the web and WhatsApp: the runtime, the console, the SDKs and the CLI.

    Python 1

  2. .github .github Public

    Pinecall's profile on GitHub