BashBrainTeammatesPricingDevelopersGet a test key

Give every user an agent, right inside your app.

Your users ask in plain words. Bash does the job through your own API and checks with them before it books, pays or cancels anything. You write the functions, we run the agent.

npm install https://vestra.ai/developers/kit/vestra-sdk-0.1.8.tgz && npm pkg set type=module
server.ts
import { Vestra, tool } from '@vestra/sdk';

// get_booking is your API. It only reads, so Bash calls it freely.
const get_booking = tool({
  name: 'get_booking',
  description: 'A booking, and the flights it can move to.',
  input: { ref: 'string' },
  read_only: true,
  run: ({ ref }) => api.booking(ref),
});
// book_flight pays, so it waits. Its card shows each input.
const book_flight = tool({
  name: 'book_flight',
  description: 'Books the new flight and charges its fare.',
  input: { flight: 'string', fare: 'string', refund: 'string' },
  run: (agreed, call) => api.rebook(call.user, agreed),
});

const vestra = new Vestra({ tools: [get_booking, book_flight] });
const run = vestra.user('traveller-42')
  .ask('My 6am flight got cancelled. Get me on the next one.');

for await (const event of run) {
  if (event.type !== 'interaction.created') continue;
  if (event.interaction?.type !== 'approval') continue;
  // Bash stops here for your user's yes
  const yes = await app.confirm(event.user, event.interaction);
  await event.answer({ approve: yes, present_user: true });
}
console.log(await run.text());

What traveller-42 sees in your app

Bash asks traveller-42waits for a yes

Books the new flight and charges its fare.

flight
6E 2171, Delhi to Mumbai, 09:15 tomorrow
fare
Rs 5,420
refund
Rs 4,980 for the cancelled 6E 2134

You're on the 09:15 tomorrow, seat 14C. Your refund of Rs 4,980 is on its way.

Four steps from first ask to launch.

One traveller, start to finish. Each step adds a few lines to your code, and you watch what Bash does with them.

Name them by your own id. Their Bash is made the first time you do, and every step streams back.

server.tsvestra.yaml
6 lines
import { Vestra } from '@vestra/sdk';
const vestra = new Vestra();
const run = vestra.user('traveller-42')
.ask('My 6am flight got cancelled. Get me on the next one.');
console.log(await run.text());
name: travel-help
instructions: |
Help travellers fix broken trips. Look bookings up with get_booking.
tools:
- function: get_booking
description: Looks a booking up by its reference.
input: { ref: string }
read_only: true
approvals: [payment, booking]
tests:
- name: Rebook a cancelled flight
ask: My 6am flight got cancelled. Get me on the next one.
expect: Offers the next flight and asks before booking.

# Your tests in vestra.yaml, in test mode.

$ vestra test

# Live once they pass, with your live key.

$ VESTRA_API_KEY=$VESTRA_LIVE_KEY vestra deploy --live

Bash, for traveller-42Their own Bash, made just now
test mode
My 6am flight got cancelled. Get me on the next one.

task.startedRebook the flight
Bash needs traveller-42's okayShown in your app, answered by them
Booking

Book the 09:15 to Mumbai, seat 14C?

The fare difference is ₹1,200. Nothing is booked until they say yes.

Booked. traveller-42 said yes.interaction.resolved, approve: true
Bash called get_booking
{ ref: 'TN-48213' }
Your function answered, on your server
{ from: 'DEL', to: 'BOM', leaves: '06:10', status: 'cancelled' }
Bash carried on
The next flight to Mumbai is the 09:15. Shall I book it?

Bash sees what your function returns, and nothing else from your systems.

travel-help, version 3Tested in test mode, judged against each expect
  • Rebook a cancelled flight
  • Everyday baggage question
  • Flight time with no reference
  • Delayed flight question
  • Pressure to promise a refund
Testing…vestra deploy --live

Guide 1 walks this path with your own key, and it ran end to end on our staging servers on 1 October. Open guide 1

Call it from the stack you already have.

TypeScript, Python, the CLI, plain HTTP or MCP. Each one sends the same ask and gets back the same events.

ask.ts
import { Vestra } from '@vestra/sdk'; const vestra = new Vestra(); const run = vestra.user('traveller-42') .ask('My 6am flight got cancelled. Get me on the next one.'); for await (const event of run) console.log(event.type); console.log(await run.text());

Every step streams back as a typed event, and run.text() is the reply.

What comes back

Bashfor traveller-42

  • conversation.status
  • message.delta
  • task.started
  • interaction.created
The same reply and the same event names, whichever door you use.

Eighteen commands cover the whole API.

Learn these and you can guess the rest. Every guide and reference page is organised around them.

The Vestra cardOpen the card

Follow progress

Apps and memory

Your users

Access and keys

Every command has its own page, with all five doors.

Get a test key.
Build your first agent.

Nothing reaches a real user until you go live.