This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

What a call carries

Why the environment is short, and what arrives per call instead.

The agent server is configured by two things, and only one of them is on this host.

1 - The agent profile

Everything about how a call runs, sent by the client.

Almost nothing about a conversation is configured on the agent server. The prompt, the voice, the GCP project, the service-account key, which behaviours are on — all of it arrives with each call, in something called the agent profile, sent by the agent client.

This matters to you as an operator for three reasons.

1. It explains the short environment list

You are not missing settings. There is no PROMPT, no VOICE, no GEMINI_MODEL to set here, because those are properties of an agent, not of a host. One agent server runs many different agents at once, each with its own.

2. It means one host can serve many customers

Because the GCP project and the service-account key travel with each call, a single agent server can run:

  • agent A, billed to customer A’s GCP project, on customer A’s key
  • agent B, billed to customer B’s, on customer B’s key

at the same time. As host configuration this would be impossible — one deployment could only ever serve one account, and serving two meant running two servers.

3. It means this host holds no credentials

There is no service-account key on this machine and no file to mount. If somebody copies the image, they get no customer’s credentials with it.

The trade is that a call carrying no credentials cannot run. There is nothing to fall back onto, so it is refused:

gemini: this call carried no credentials for project "my-project" —
the agent server holds none of its own, by design. Set google_credentials_json
on the agent, or GOOGLE_APPLICATION_CREDENTIALS on the agent client that sends
its profile

If you see that, the fix is on the client, not here. See troubleshooting.

What is in a profile

You do not set these — the client does — but knowing what a call carries makes the logs readable.

GroupExamples
The conversationprompt, greeting, closing line, voice, model, language
PlacementGCP project, region, service-account key, which AI vendor
Behaviourcan the caller interrupt, is the greeting interruptible, silence handling, answering-machine detection
Toolsthe functions this agent may call, answered by the client

Each field falls back on its own. A client sending a project but no region gets that project in the default region — not a half-configured call.

Where the environment still helps

A handful of environment variables act as fallbacks for calls whose profile leaves a field unset — the default AI backend, whether answering-machine detection runs. They are listed in configuration.

The project and the key are not among them. Those must come from the call.

2 - Tools

Why an agent can look things up, and where that work happens.

An agent can do more than talk. Mid-call it can look up a balance, an order status, an appointment slot — anything the business it belongs to can answer.

None of that work happens on this host. The agent server has no database and no idea what any of those tools mean. It carries the request out to the agent client and carries the answer back.

model asks for "check_balance"
        │
        ▼
  agent server ──▶ agent client ──▶ the business's own system
        ◀──────────── answer ◀───────────────┘
        │
        ▼
model continues the conversation

What this means operationally

Tools are configured on the agent, not here. There is nothing to install, declare or permit on this host to make a tool available.

A slow tool is heard as silence. While the client is answering, the model is not speaking, and the caller hears that. The wait is bounded and always ends in an answer to the model — if the client does not answer in time, the model is told the lookup failed so it can apologise and carry on. A caller hearing “I couldn’t pull that up just now” is a far better outcome than a line that goes quiet.

So a tool that is slow on the client side shows up as pauses in conversations, not as errors in this log.

Two tools belong to the server. end_call — how a model hangs up, subject to a contract that checks it is entitled to — and a no-op tool declared beside it. The second exists so that a model which feels the urge to “use a tool” at a moment that is not an ending has somewhere harmless to put it, rather than reaching for end_call and cutting a live human off.

It is answered instantly, inside the server, precisely because sending it out and back would spend a multi-second silence avoiding a mistake that costs nothing.

In the logs

Tool activity appears against the call’s bridge id. A call that pauses oddly, with the agent going quiet and then apologising, is usually a tool the client was slow to answer — the place to look is the client’s logs, not this one.

3 - The shape of a call

From join to hangup, and who decides what along the way.

Joining

The room exists first. Whoever created it is the only party that knows which room the other participants are in, so the agent server never creates one — it is told which to join.

client ──JoinBridge──▶ server ──join──▶ control plane
                          │
                          ├──▶ SFU room     (WebSocket, Opus)
                          └──▶ AI backend   (the placement on the profile)

JoinBridge returns once the agent has a seat. The backend handshake continues after that.

Who speaks first

On a bridge the agent does. The room is already live, nobody rang, and an agent that joins and stays silent reads as a broken connection.

opening overrides this. Set it to caller when the agent is joining a conversation already in progress and should listen rather than announce itself.

During

The engine owns the timing. Every 20 ms of caller audio runs the chain — pre-clean, denoise, near-field foreground isolation, neural VAD — and the VAD is the authoritative turn driver.

The agent client receives transcripts and turn events and can steer between turns. What it cannot do is touch the media path: there is no way to send audio into the call and no way to change the audio processing. Timing stays with the engine.

Barge-in

When the caller speaks over the agent, the agent stops. What matters afterwards is not what the agent generated but what the caller actually heard, so a turn-ended event carries both:

An agent turn generated in full and heard for 300 ms did not happen, whatever the transcript says.

A client deciding “have they been told about the fee?” needs the second number, not the first.

Ending

Three ways a call ends:

The caller hangs upThe leg drops; the room reports it.
The client ends itOptionally running the closing sequence first.
The model calls end_callSubject to the end-call contract.

end_call is not a request the model gets for free. It must produce evidence — which of the legitimate endings this is, whether the closing question was actually spoken and answered — and that evidence is cross-checked against what the engine independently observed. A rejected end_call is answered with a re-prompt, so the agent keeps talking rather than going silent.

Every rejection is a prompt, and the engine is careful that rejections do not themselves become a source of repetition.

Detaching is not hanging up

If the client process dies mid-call, the engine carries on. The caller is not dropped because the record-keeper crashed; the cost is a reporting gap, which is the right one to take.