npx zavudev agents
Create, test, connect and inspect agents from the terminal
An agent is a standalone object with its own id. It can answer on several senders, and it can exist with none while you build it.
terminalnpx zavudev agents list
Codeid name kind enabled senders model qd75detym58c6has4sfrye3vws8b6ygt Atlas voice yes 1 openai/gpt-5.6-luna qd7ck28evcskdc3xx4wzevtmeh8b6bcm Pizza Desk text no 0 gpt-4o-mini
kind reflects what the agent actually is — an agent named for voice whose
voice block is missing reads as text, which is usually the thing you wanted
to know.
Test an agent
The command you will use most. It runs the real prompt, model and knowledge
base and prints what the agent would reply. Nothing is delivered and no
execution is logged. The run is billed like a live reply: on the zavu
provider its tokens are charged to your balance (the cost is in the output), on
your own provider key it costs your provider.
terminalnpx zavudev agents test --agent <agentId> --message "where is order ORD-001?"
CodeYour order ORD-001 shipped on Tuesday and arrives Thursday. 1418→40 tokens · 5249ms · 1 knowledge chunk(s)
| Flag | |
|---|---|
--agent <agentId> | The agent. Get it from agents list. |
--sender <senderId> | Address it by sender instead (resolves to that sender's one agent) |
--message <text> | What to say |
--turn <text...> | Prior turns, alternating user/assistant, oldest first. Repeatable. |
--no-knowledge | Skip retrieval, to isolate prompt behaviour from the knowledge base |
--execute-tools | Run the tools the agent calls, for real, and reply with their results. Without it the run stops at the tool the agent chose. |
--json | Raw response |
Multi-turn:
terminalnpx zavudev agents test --agent <agentId> \ --turn "I need to change my booking" --turn "Sure — which one?" \ --message "the one on Friday"
Start from a factory agent
terminalnpx zavudev agents catalog # what is available npx zavudev agents pull kepler --dir kepler # scaffold it locally cd kepler && npm install npx zavudev deploy
pull writes a real TypeScript project: index.ts with the agent and its
skills, a tsconfig.json, and @zavudev/functions as a dependency so your
editor and tsc resolve it. Edit it freely — the code is the source of truth
and npx zavudev deploy reconciles whatever it declares.
| Flag | |
|---|---|
--dir <path> | Where to scaffold. Defaults to ./<id>. |
--sender <senderId> | Sets the SENDER_ID secret for you |
--calendar <provider> | For booking agents: webhook (default) or calcom |
Booking agents
Kepler, Hopper and Fermi book meetings. By default they scaffold against a generic webhook — an HTTPS endpoint you build and host that returns open slots and books them. Until it exists the agent cannot book, and says so rather than inventing a time.
Point it at Cal.com instead and it works with an API key:
terminalnpx zavudev agents pull kepler --calendar calcom
pull then lists every secret the agent needs, with a hint for each:
Codenpx zavudev fn secrets set CALCOM_API_KEY <value> ↳ Cal.com API key (Settings → Developer → API keys). npx zavudev fn secrets set CALCOM_EVENT_TYPE_ID <value> ↳ Numeric id of the Cal.com event type to book.
The Cal.com client only reports a booking when Cal.com accepted it. An event type that requires confirmation comes back pending, and telling a caller they are booked at that point is the worst outcome on that line.
npx zavudev agents init walks the same path interactively: pick or create a sender,
pick an agent, get ready to deploy.
Create one from scratch
terminalnpx zavudev agents create --sender <senderId> \ --name "Order desk" --provider zavu --model openai/gpt-5.6-luna \ --channels whatsapp,sms \ --prompt "You are the order desk for Tony's Pizza. Answer in one or two sentences."
Use --prompt-file for anything longer than a line — terminal paste mangles
newlines. --data / --file take a full JSON body for advanced fields.
Connect senders
A sender is the identity a conversation happens on. An agent can answer on several; a sender answers with at most one agent.
terminalnpx zavudev agents senders connect --agent <agentId> --sender <senderId> npx zavudev agents senders disconnect --agent <agentId> --sender <senderId>
Connecting a sender that is already in use is rejected, naming the agent that holds it — otherwise you would have an agent that looks connected and never receives a message.
Tools
terminalnpx zavudev agents tools list --sender <senderId> npx zavudev agents tools test --sender <senderId> <toolId> --params '{"orderId":"ORD-1"}'
Tools declared in code with defineTool are managed by their function: they
appear here read-only and are changed by redeploying.
To run a handler in isolation, without deploying:
terminalnpx zavudev fn invoke --tool get_order --args '{"orderId":"ORD-1"}'
Knowledge bases
terminalnpx zavudev agents knowledge-bases create --sender <senderId> --name "Product docs" npx zavudev agents knowledge-bases documents add --sender <senderId> --kb <kbId> \ --title "Pricing" --content-file ./pricing.md npx zavudev agents knowledge-bases documents list --sender <senderId> --kb <kbId>
Processing takes a few seconds. Verify retrieval actually fires with agents test — it reports how many chunks the answer used.
Flows
terminalnpx zavudev agents flows list --sender <senderId> npx zavudev agents flows create --sender <senderId> --file flow.json
A flow is a deterministic sequence with a trigger. Steps take a config
object; a tool step names its tool through config.toolName, or the older
config.toolId. Either field is accepted, and either one may hold the tool's
name or its id. A flow naming a tool the agent does not have is rejected at
creation, listing the ones it does have.
Flows are not evaluated by agents test — they run on a real message, and
agents test says so when the agent has any.
Inspect what happened
terminalnpx zavudev agents stats --sender <senderId> npx zavudev agents executions list --sender <senderId> npx zavudev agents executions get --sender <senderId> <executionId> npx zavudev agents reset-thread --sender <senderId>
executions get carries errorMessage and responseText — the place to look
when a live reply went wrong. reset-thread closes the conversation and any
flow session for one contact so the next inbound starts fresh.
Dry runs do not appear in stats or executions; they are not executions.
Calls
terminalnpx zavudev calls list --status completed npx zavudev calls get <callId> # includes the transcript, turn by turn npx zavudev calls hangup <callId>
calls get prints the conversation including tool calls. It is the only record
of what the agent actually said, and the first place to look after a call went
wrong.
terminalnpx zavudev calls create --to +14155551234