Skip to content

Documentation / Core concepts / Lifecycle

From ActiveCall to Closure

After setup passes preflight, VAANI owns the call attempt. That owned attempt produces exactly one immutable CallResult, even when providers fail, the participant disconnects, teardown times out, or the owner is cancelled.

stateDiagram-v2 [*] --> ACCEPTED ACCEPTED --> VALIDATING VALIDATING --> STARTING STARTING --> CONNECTING CONNECTING --> WAITING_FOR_PARTICIPANT WAITING_FOR_PARTICIPANT --> ENDING: preparation terminal end WAITING_FOR_PARTICIPANT --> TEARING_DOWN: preparation terminal transfer WAITING_FOR_PARTICIPANT --> GREETING GREETING --> CONVERSING CONVERSING --> ENDING: graceful EndRequest CONVERSING --> TEARING_DOWN: forced terminal signal ENDING --> TEARING_DOWN TEARING_DOWN --> FINALIZING FINALIZING --> CLOSED: publish CallResult CLOSED --> [*]

Some failures skip phases. Progress is always forward; a late signal cannot reopen or reverse a committed transition.

A preparation-selected terminal path skips GREETING and CONVERSING. Its prerecorded media, TTS fallback, optional consumer work, and end or transfer are all bounded inside the ordinary closure budgets. Failure remains terminal and observable; it cannot fall through to the conversational path.

Start and run

The following are partial call-site excerpts. Use run() when the owner only needs the terminal result:

result = await runtime.run(invocation, setup)

Use start() when consumer code needs the call handle:

active = await runtime.start(invocation, setup)
print(active.call_id, active.revision)
receipt = await active.update(update)
await active.end(EndRequest(EndRequestSource.ACTIVE_CALL, "complete"))
result = await active.wait()

ActiveCall.update(), end(), and wait() are the public operations. The async context manager is also an ownership boundary: normal exit requests a graceful end; exceptional exit forces cancellation if the call is still active.

Exactly one result

Closure is the atomic publication point. All successful waiters receive the same frozen result. Cancelling a result observer does not end the call; cancelling the lifecycle owner does.

Before ownership, configuration/controller-installation problems raise a typed startup error and produce no result. After ownership, terminal failures appear inside the result's normalized failure evidence.

Deadlines do not reset on retry

Each bounded phase receives an absolute monotonic cutoff. A retry spends the remaining budget; it cannot create a new full timeout. Completion observed by the cutoff wins, and later completion is quarantined rather than mutating the result.

Separate budgets exist for:

  • connection, participant readiness, greeting, terminal media, drain, teardown, and finalization;
  • provider operations, tools, updates, transfer, recording, and result publication.

Graceful and forced endings

All healthy completion sources submit an EndRequest. The first accepted request is canonical; later requests join the same ending and remain as suppressed evidence. A forced terminal signal can pre-empt terminal media but cannot erase that canonical request.

Next: tools and state or result evidence.