The call
this.call: who is on the line, on which channel, and what the agent can do to the call.
this.call is the one call this instance serves: who is on the line, on which channel, what has
been said, and the commands that act on the call. Tools, hooks and render() all read it.
What is on this.call?#
| property | what it is |
|---|---|
id |
the call's id |
contact |
the other party: a phone number, a contact id, or a web visitor's id |
from |
the number or identity the call came from, when there is one |
channel |
phone, web or whatsapp |
medium |
voice or text. A web call can be either; WhatsApp is text |
today |
the day the call opened, YYYY-MM-DD. Count "next Tuesday" from this, never from the machine's clock |
history |
the finished turns: turns, last and length. Each turn has who (user or agent), text and interrupted |
room |
who is in the call: participants, caller, has(kind), get(identity) |
claimed |
the page code this call claimed, or null |
this.call exists only while a call is being served. Read outside one, it throws.
Speaking#
await this.say("One moment, let me check."); // these words, as written
await this.reply("Apologise and offer another time."); // the model speaks, guided by words the caller never hears
await this.say("Your code is 4 4 1 9.", { allowInterruptions: false });
await self.say("One moment, let me check.") # these words, as written
await self.reply("Apologise and offer another time.") # the model speaks, guided by words the caller never hears
await self.say("Your code is 4 4 1 9.", allow_interruptions=False)
A verb that waits answers with an Answer: await it in an async def tool, or call .result()
in a def one, which runs on a thread of its own.
say "One moment, let me check." # these words, as written; blocks until spoken
reply "Apologise and offer another time." # the model speaks, guided by words the caller never hears
say "Your code is 4 4 1 9.", allow_interruptions: false
Both resolve true once the turn has been spoken, and false if it was not within 30 seconds or the
call ended first. allowInterruptions: false keeps the caller from cutting the sentence short.
The room#
this.call.room.has("supervisor"); // caller, agent, supervisor, listener or sip
this.call.participant(identity).mute(); // for the rest of the call
this.call.participant(identity).remove(); // removing the caller ends the call
this.call.invite("+34910000099", { kind: "sip" }); // dial a number into the call
this.call.send("cart", { total: 42 }, { to: identity }); // a payload to a browser in the call
this.log("appointment.booked", booking); // one named entry in the call's log
self.call.room.has("supervisor") # caller, agent, supervisor, listener or sip
self.call.participant(identity).mute() # for the rest of the call
self.call.participant(identity).remove() # removing the caller ends the call
self.call.invite("+34910000099", "sip") # dial a number into the call
self.call.send("cart", {"total": 42}, to=identity) # a payload to a browser in the call
self.log("appointment.booked", booking) # one named entry in the call's log
call.participants.any? { |one| one.kind == "supervisor" } # caller, agent, supervisor, listener or sip
call.participant(identity).mute # for the rest of the call
call.participant(identity).remove # removing the caller ends the call
call.invite("+34910000099", kind: :sip) # dial a number into the call
call.send_to("cart", { total: 42 }, to: identity) # a payload to a browser in the call
log "appointment.booked", booking # one named entry in the call's log
Searching from a tool#
this.knowledge.search(question, { k }) searches the knowledge bases attached to the agent and
returns { path, heading, text } per chunk. this.call.search does the same.
/** Looks up what the patient asks in the clinic's documents. */
@tool()
async lookUp(question: string): Promise<Found[]> {
return await this.knowledge.search(question, { k: 3 });
}
A class that reads this.knowledge is refused at registration when no base is attached to it, so it
never meets a call where the search finds nothing. See Knowledge bases.
Handing the call on#
These verbs act on the call itself. Each one that can fail answers with what really happened, so the tool decides what the caller is told.
| verb | does |
|---|---|
transfer(to, { mode }) |
sends the caller to a number. Answers { ok, to, mode, error? }; ok: false means the caller is still with the agent. Python: transfer(to, mode=…); Ruby: transfer(to, mode:) |
attention(reason, { waitS }) |
asks your team to take the line, and waits up to waitS seconds. Answers { ok, by, error? }. Python and Ruby: attention(reason, wait_s=…) |
hold() · unhold() |
puts the caller on hold music, and takes them off it |
dtmf(digits) |
sends touch tones: 0-9, *, #, and , for a pause |
callback(number, { when, note }) |
records that the caller wants a call back (callback(number, when=…, note=…) in Python, callback(number, at:, note:) in Ruby) |
optOut(note) |
adds the caller to the org's do-not-call list (opt_out in Python and Ruby) |
hangup(reason) |
ends the call. Say goodbye first: nothing after it is heard |
The whole of it is Handing a call to a person and Callbacks.
A caller who is also on your page#
A page can show a phone number and a four-digit code. The caller rings and keys the code, and from
then on the page follows the call. When the call is claimed, this.call.claimed holds the code and
the view is rendered again at once, so render() can tell a caller who sees the slots on screen from
one who only hears them. A caller who says the code instead is the class's to claim, from a tool:
this.call.claim(code). An unknown, expired or taken code is refused with no_code. See
Codes.
Long calls and callers who come back#
| method | does |
|---|---|
this.collapse(summary) |
replaces the instance's change log with one sentence. The state is untouched, and the call's log keeps every turn |
this.last(contact) |
the state this contact's last call left. Needs a store, given where you mount the agent yourself: mount(Class, { pc, last }) |
this.restore(snapshot) |
puts a whole state back; fields missing from the snapshot are cleared |
this.startIn(partial) |
writes only the fields given, over the current state |
last is for an agent you run inside your own server: Run it on your own server.
A call that changes hands#
A deploy stops one process and starts the next, and a call in the middle of it goes on in the next
one. What the new instance gets is the call's state, restored whole, and this.call. What it does
not get: a field that is not state, a promise or timer the old instance held, and what memory
recalled (this.remembers() is false until the next recall). onCall does not run again;
onEnd runs in whichever process the call ends in.
Keep everything a call needs in state, and let
onCallonly open it. See A deploy never cuts a call.