ReadonlycacheInput tokens served from the provider's prompt cache across all iterations, billed at the cached rate. A subset of inputTokens. Zero where the provider reports no cache. A realtime audio turn counts cached audio too, as the gate prices it.
ReadonlycacheInput tokens written into the provider's prompt cache across all iterations, billed above the base rate. A subset of inputTokens. Zero where the provider reports no cache.
ReadonlycontextThe model's input context window in tokens, for sizing what is left against
inputTokens. Null when neither the provider's model listing nor the checked-in
catalog states one — no window is a known unknown here, never a guess, so a script
that budgets against it must handle the null rather than read a default as fact.
ReadonlydeferredThe tool calls an onToolCall hook deferred, which ended the call with outcome: "STOPPED_BY_HOOK" and stopReason: "deferred". The transcript ends on the assistant turn that requested them, with every call the hook ran or denied answered and these left open. Answer them with AiAgent.resume(results) — in this execution, or in a later one after transcript.toJson() was stored and a fresh agent seeded from it. Until they are answered, that transcript cannot be sent or continued with another message. Empty when nothing was deferred.
ReadonlydenialUsage gate denial code. Populated only when stopReason == "ai_denied"; null on every turn the gate allowed.
ReadonlydenialUser-facing usage gate denial message. Populated only when stopReason == "ai_denied"; null on every turn the gate allowed.
ReadonlydurationWall-clock milliseconds the call took, measured to the moment this result was built. On a metered call the clock starts after the usage gate authorized it, so gate latency is excluded; on a bring-your-own-key call, and on a turn the gate refused, it starts when the call began.
ReadonlyexchangesNumber of times the onTurn continuation callback was invoked. Always 0 when no callback was provided.
ReadonlyhookThe reason an onIteration hook gave when it stopped the loop. Null unless a hook stopped it with one.
ReadonlyinputTotal input tokens consumed across all iterations of this call.
ReadonlyiterationsNumber of provider round-trips performed (each tool_use cycle counts as one).
ReadonlymountsThe git repositories this call read, as {alias, pinnedSha, requestedRef, preloaded}. Record these alongside the answer: a mount pins its ref to a commit once, so the pinned sha is what makes the answer reproducible, and without it the reproducibility claim cannot be checked from the caller's side. It is also what a later call passes back as ref to resume a conversation holding repo-sourced content, since such content is re-read at the sha it was first read at. preloaded lists the paths that mount's preload globs fed into the conversation, and is [] when it preloaded nothing. Empty when no repository was mounted.
ReadonlyoutcomeWhy the call ended, in one vocabulary across every provider: "END_TURN" (the model finished), "MAX_TOKENS" (the output cap cut it short), "REFUSAL" (the provider's safety layer declined; text is not an answer), "ITERATION_LIMIT" (the gate's cap, the call's maxIterations, or the hard cap stopped the tool loop), "EXCHANGE_LIMIT" (the onTurn loop ran out of exchanges), "DEADLINE" (deadlineMillis passed between iterations), "STOPPED_BY_HOOK" (a script hook ended the loop: onIteration said stop — see hookReason — or onToolCall deferred a call — see deferred — or onTurn continued without a message), "DENIED" (the usage gate refused; see denialCode), or "ERROR" (a stop reason this build does not model). Branch on this rather than on stopReason, which keeps the raw provider spelling. Failures the provider itself raised — a rate limit, an outage, an open circuit breaker, a timeout — are thrown as an AiCallException instead of returned.
ReadonlyoutputTotal output tokens consumed across all iterations of this call.
ReadonlyparsedThe final text parsed as the JSON document responseSchema asked for: an object, array or scalar a script can read directly. Null when the call set no responseSchema, and null when the text is not a JSON document — a token cap cut it short, a refusal replaced it, or the model ignored the schema — so check outcome before trusting a null here.
ReadonlystopReason the model stopped emitting, as the provider or the orchestration spelled it; outcome is the same fact in one vocabulary and is what to branch on. Provider-native when the model itself stopped — Anthropic emits "end_turn" / "max_tokens" / "stop_sequence" / "refusal" / "tool_use"; OpenAI-compatible providers (OpenAI, Groq, Together, Mistral, …) emit "stop" / "length" / "content_filter" / "tool_calls". Synthetic values added by the BSJS orchestration layer: "ai_denied", "iteration_limit", "max_iterations", "deadline", "deferred", "stopped_by_hook", "on_turn_exchange_limit", "on_turn_returned_invalid". Realtime audio turns (B.ai.streamingAgent) emit "completed" on a normal end of turn, and return partial output under "timeout" (wall-clock budget hit), "incomplete", or "cancelled" (provider ended the response early), or under "stopped_by_hook" when an onIteration hook ended the session. Provider/transport failures (rate limits, circuit breaker, realtime error events or failed responses, interruption, etc.) are thrown as exceptions to the script rather than returned in this field. Null when the turn ended without any stop reason being set.
ReadonlytextFinal assistant text response. Empty string when the turn produced no text — a tool-use-only response carries none, and a turn refused by the usage gate never reaches the model. On a cap stop (max_iterations, iteration_limit) it is the prose that accompanied the tool request that was not executed, usually empty: the model finished nothing on that turn.
ReadonlytoolSummary of the tool calls answered during this call, in order. On the chat/tool path result is what the tool returned, kept at greater length than the model was shown — an onToolResult override changes what the model is shown, not this — but still bounded across the whole call, so a run with very many tool calls records a trailing … tool result clipped line in place of later output. arguments are the ones the call ran with, which an onToolCall hook may have replaced; a copy, so mutating it changes nothing. A call the hook denied is listed with its reason as result and isError: true; a call the hook deferred is not listed here but under deferred, and a result later supplied to resume is listed on that call. The realtime audio path lists the same four members on the same terms — a denial with its reason, and result as the tool wrote it rather than as onToolResult reshaped it — except that its copy is clipped to the model's cap, that lane keeping no separate record budget. In the deprecated agent stub mode (AiAgent.sendMessage(message, false)) every entry has result: "action queued" and isError: false — no executor was invoked.
ReadonlytraceWhat each model round-trip cost, one entry per iteration in order, when the call set trace: true. Empty when it did not, and empty on a turn the usage gate refused, which makes no round-trip at all. A realtime audio turn (B.ai.streamingAgent) always fills this — it has no trace option to set, and a session is capped at far fewer round-trips than a chat loop — with cacheWriteTokens at 0 and substitutions empty, neither of which that transport reports. The counts are each round-trip's own rather than running totals, so the expensive iteration is the one with the large number rather than the one where the total jumped — see AiTraceEntry.
ReadonlytrackingThe usage gate's tracking id for this turn, the key its usage and spend are recorded under. Null on a bring-your-own-key call, which the gate never sees, and on a turn the gate refused.
ReadonlytranscriptThe message list as the model last saw it on this call, in order: the seed (chatHistory or transcript), the user message, each assistant turn with the tool calls it made, each tool result, and the final answer. Pass it back as transcript on a later call — or store toJson() and pass that — to continue the conversation with nothing lost. On a turn the usage gate refused it holds the seed and the user message the model never saw. Empty for realtime audio turns, which keep no message list. When every tool offered was withdrawn mid-turn (a git budget exhausted, say), the earlier tool cycles are folded into one user message before the model is asked again, and the transcript holds that folded list rather than the cycles: the model's own record was rewritten the same way.
Result of B.ai.call().
Example: ```javascript
const result = B.ai.call({ message: B.net.request.parameter("q"), tools: [lookup] });
B.out = result.outcome === "END_TURN" ? result.text : "stopped early: " + result.outcome + " (trackingId " + result.trackingId + ")"; B.out += " — " + result.iterations + " round-trips, "