InlayDocs
Reference

The data model

Rows, the chat snapshot, the run projection, and the views — every type the SDK publishes, with the semantics of each field.

Everything the SDK publishes, in three families: the chat projection (the transcript and its state), the run projection (which node is doing what), and the views derived from joining them. All camelCase — the wire and Rust stay snake, mapped at the boundary.

Rows — the ordered transcript

rows is one heterogeneous, ordered feed: a user turn, an assistant turn, an output, a tool call, and its result are peers, which is what lets a UI render them in the order they happened. Narrow on row.type before reaching for kind-specific fields — the union makes a wrong reach a compile error.

Row<TName>

The union — UserRow | AssistantRow | OutputRow<TName> | ToolCallRow | ToolResultRow — discriminated on type. TName is the union of the agent's declared output names (the generated client supplies it); it defaults to string.

typeVariantIs
userUserRowSomething the person said.
assistantAssistantRowSomething the agent said.
outputOutputRowA declared output, at the position it was produced.
tool-callToolCallRowA tool the agent invoked.
tool-resultToolResultRowWhat that call returned.

A tool call and its result are two rows, never one that mutates: the gap between them is unbounded (a durable client-tool pause spans hours and a page reload).

UserRow

{ type: "user"; content: string } + the spine. The user's turn. Belongs to the conversation, not a node — nodeId/activation are always absent.

AssistantRow

{ type: "assistant"; content: string; reasoning?: string; complete: boolean }

  • the spine. One node's assistant turn: content accumulates from message.delta, reasoning is the extended-thinking text (absent when empty), complete is false while streaming. One activation is often SEVERAL of these (a streamed artifact splits the turn; a server-side tool loop emits adjacent ones) — assistantsInActivation joins them.

OutputRow<TName>

{ type: "output"; name: TName; value: unknown; status: BindingStatus; format?: ArtifactFormat; complete: boolean } + the spine. A declared output at the position it happened: status is building while a streamed artifact arrives, format is its render hint, complete is false while a same-turn re-emission can still refine it. The generated <Base>OutputRow union narrows value by name — that is the surface to render from.

ToolCallRow

{ type: "tool-call"; toolCallId: string; toolName: string; input: string; requiresClientResponse: boolean } + the spine. A tool the agent invoked; input is the arguments as the model produced them, unparsed. Paired with its result by toolCallId (the timeline index's byToolCallId joins them; useToolCall subscribes to the pair).

requiresClientResponse is the client/server split: true for a client tool (the run pauses until your app answers — the HITL surface), false for a server-side tool (memory, web.search, an MCP connector's tool), which never pauses and never enters pendingTools — the row pair is there for observability. A server tool the tenant has marked private is emitted redacted: real toolName, input === "{}", result content empty.

ToolResultRow

{ type: "tool-result"; toolCallId: string; output: string } + the spine. What the call returned. A FAILED client tool arrives here too — the failure is delivered AS the result and the wire can't tell them apart; a field with no producer is worse than no field.

RowKind

The string union of the type values: "user" | "assistant" | "output" | "tool-call" | "tool-result". The kebab forms are the durable log's own names — a live row and a replayed one discriminate identically.

Composite rows — the feed kinds

A generated client's snapshot.rows contains the durable kinds above and the composite kinds its graph can produce. Codegen narrows that union per agent: adding an accepted fan-out region, subgraph, or repeatable node adds a case that an exhaustively checked renderer must handle at regeneration. Without any of those shapes, only the five durable kinds are needed. Raw useInlayChat without generated graph config stays flat.

Composites are derived, not durable. Their opaque key is a stable view identity for rendering, not a durable row id or pagination cursor. They deliberately have no id. Fan-out membership comes from the generated REGIONS descriptors, not an SDK graph prover. Unsupported static shapes have no descriptor; missing runtime activation evidence also leaves affected rows flat rather than dropping them or assigning invented structure. See the renderer and key rules.

Identity/version guard: with a generated feed config, composition requires the conversation's actual agentId to match the expected agent and its agentVersion to match an explicit expected version greater than zero. A version mismatch, an allowed foreign-agent reopen (even with the same version number), draft version 0, or an unknown expected version disables all composites. The full paced row array stays flat, including inner-subgraph rows; the guard does not switch to the top-level-only snapshot or drop content. To recover grouping for a historical conversation, use a regenerated module pinned to its recorded agent and immutable version.

FanOutRow

{ type: "fan-out"; key; nodeId?; subgraphPath?; join; tracks: FeedRow[][]; branchStatus?; hasEarlier? }. One fan-out region rendered as one row: nodeId/subgraphPath name the SOURCE node (the codegen nodeNameOf join works on it), join is the join node's absolute path, and tracks holds one feed-row list per branch — the branch's own rows, interleave-free, including nested composites. branchStatus ("running" | "done" | "failed" | "unknown" per branch) is live only for the region's latest firing — the run projection tracks the latest visit. A distinct firing produces a second FanOutRow only when the rows establish that boundary. A silent source and silent join cannot establish a new firing; the descriptor adds no runtime firing provenance. A region whose branches produced nothing renders no card at all.

branchStatus currently reflects branch entry nodes, not the completion of every downstream node in a track. See the progress guide for this limitation and the separate taken/untaken edge flags.

SubagentRow

{ type: "subagent"; key; nodeId?; subgraphPath?; rows: FeedRow[]; running?; done?; failed?; status?; hasEarlier? }. A subgraph node rendered as one row, with its recursively composited inner feed in rows. Counts roll up descendants from the run projection; status is the subgraph node's own status when known. A subgraph appears when recorded content or run evidence establishes it ran. A started black-boxed subgraph (hide_inner_events) can have empty rows; an unreached subgraph has no feed card. Use node views to display an idle plan. There is one SubagentRow per subgraph node, holding all its visits' inner rows in order.

RepeatRow

{ type: "repeat"; key; nodeId?; subgraphPath?; mode: "parallel" | "sequential"; tracks: FeedRow[][]; done: number; total?: number; hasEarlier? }. One container per repeatable node path and activation: ensemble samples use parallel mode; for_each_llm items use sequential mode. Each track groups rows with the same one-based iteration stamp, sorted by that stamp. Missing iterations do not produce empty tracks, so a track's array index is not necessarily its item/sample number. Use a constituent durable row's iteration for numbering and track identity, as in the renderer example.

done counts iterations represented by served rows, not completed executions. An ensemble's total, when available in the generated metadata, is its static sample count (n). for_each.total stays absent even after completion. Empty or abstaining samples produce no track: an ensemble can finish with done: 3, total: 4. Use node/run status to determine whether execution ended, not done === total.

Ensemble sample content arrives as complete rows, without per-sample token streaming. Ordinary for_each content can stream, and one item's track can contain several assistant turns plus tool-call/result rows. Both iteration and activation stamps are required for repeat grouping; non-iterated rows or rows without activation evidence remain flat. hasEarlier can mark a card opened into by a windowed read.

Structured ensemble decisions appear as JSON text in assistant rows, not individually typed output values, only when output_key names a declared public field. Making that field public exposes individual decisions, not just the final consensus. Private or undeclared destinations still compute internally without decision-content rows; see field visibility. A structured for_each has no per-item served rows and produces no repeat tracks; read its declared public output/binding instead.

FeedRow<TName>

The general-purpose union Row<TName> | FanOutRow | SubagentRow | RepeatRow, useful for reusable renderers and the recursively nested rows in composites. The generated <Base>FeedRow narrows this to that graph's possible kinds. Use row.type or the guards isFanOutRow, isSubagentRow, isRepeatRow, and isDurableRow (the five durable kinds) before reading variant fields.

RowSpine

The fields every durable row carries. Which exist depends on when you're looking — absent is honest: a missing field means "the wire can't say", never a placeholder.

Prop

Type

FieldPresent when
idPresent when the durable row first appears on supported servers. A prefill placeholder exposes it at node start; subsequent turns and repeated items/samples get their own ids. Use key={row.id} for durable rows and row.key for composites.
nodeIdThe producing node. Absent on user turns.
subgraphPathOwning subgraph ids, outermost first. Absent at top level. With nodeId forms the absolute path that keeps a reused subgraph's rows apart.
activationThe node's 1-based visit count when it ran. Absent on user rows and pre-field servers.
iterationThe 1-based item/sample index on a repeatable node's per-instance rows. Absent outside an iteration; distinct from the node's activation.
seqDurable ordinal — replayed rows only (live rows get it at refetch). The loadEarlier anchor.
turnConversational turn — replayed only, like seq.

The chat projection

ChatSnapshot<TName>

The reducer's published state — what handle.snapshot and onSnapshot hand you. Generic over TName (the output-name union).

Prop

Type

Field semantics the table can't hold:

  • pendingTools is DERIVED, never edited: a tool-call row with no matching tool-result row, recomputed after every change. It answers "what is waiting on the app" — the HITL surface.
  • toolStates records how DELIVERY is going per call (the client side), deliberately separate from the rows (the agent side). A replay never resurrects it.
  • error is the customer-safe reason — status: "failed" says THAT it failed, this says why. Absent for a run-level failure with no node reason.
  • retryable is the server's own advertisement for POST …/retryretry() gates on it rather than re-deriving the rule.
  • hasEarlier means this is a tail window of the log — earlier rows exist; loadEarlier() pages them in.
  • bindings is the cross-turn LATEST value per output name; the per-turn production history is the output ROWS (outputsNamed).
  • tables is the live projection of the agent's declared collections: table name → TableRowView[], most-recent-first, including a row mid-stream (status: "building"). Rehydrated from the persisted working set on reopen — no live run required.
  • subagentBindings keys inner (un-promoted) outputs by the subagent's absolute path, so two instances of a reused subgraph don't collide.

ChatStatus

The single lifecycle answer — no phase/terminal pair to reconcile.

ValueMeaning
idleNothing started.
streamingA turn is streaming.
awaitingInputPaused at a user_turn — the composer is live; the next send resumes.
awaitingToolDurably paused on a client tool — survives a refresh; submitToolResult resumes. send is rejected here.
completedFinished successfully.
failedEnded at a failure (error may say why; retryable may offer retry).
cancelledCancelled, locally or server-side.

Terminal statuses allow send — but there it starts a fresh conversation, not a continuation. Drive buttons off the flags, not this table: canSend ANDs the map with the driver's latches, and canRetry is computed separately (failed + retryable + nothing in flight — it is not part of the Capabilities map).

PendingToolCall

One queued client-tool call — the HITL approval surface.

Prop

Type

args is the parsed arguments — what an approval screen renders — parsed at derivation in both mirrors (Rust + TS, pinned identical by the parity gate). Absent when the model produced unparseable JSON or the input exceeds the shared 64KB byte cap (the per-delta re-parse amplification guard); the raw string is always in argumentsJson. Typed per tool by the generated <Base>PendingTool union.

ToolCallStatus

The client's delivery lifecycle for one tool call: "pending" | "submitting" | "submitted" | "failed" | "cancelled". cancelled is terminal and recorded per call (the queue is derived from append-only rows, so "cancel forgets the call" must be recorded, not inferred).

ToolDelivery

{ status: ToolCallStatus; error?: string } — one call's delivery state in snapshot.toolStates: the handler resolved, the POST is in flight, the POST failed.

Binding

{ value: unknown; status: BindingStatus; format?: ArtifactFormat } — a published value: building while streaming, final at settle. What snapshot.bindings holds per output name, and what useBinding returns typed (validated at settle when a schema is declared).

BindingStatus

"building" | "final".

ArtifactFormat

"text" | "markdown" | "code" | "html" | "list" — the render hint for a streamed artifact.

TableRowView

Prop

Type

One row of a live table projection (ChatSnapshot.tables, keyed by the table's field name). status is building while the row streams (token-by-token), final once the upsert settles — final for a non-streamed row. key is the row's collection key (absent for a single-value table). links is the row's FORWARD links (the document graph — row-links.md); backlinks are on-demand (memory.peek), not streamed. value is typed from the table's declared type by the codegen (<Base>Tables), unknown un-codegen'd.

PublicError

{ kind: PublicErrorKind; message: string } — the customer-safe failure form. The message is safe BY CONSTRUCTION (app-owned tool text or a curated server string — never a provider body, a condition expression, or an internal detail).

PublicErrorKind

"llm" | "tool" | "condition" | "internal" — the coarse class of failure ("misconfigured environment vs LLM outage") for failure UIs.

The run projection

The topology half: which node is doing what, at every subgraph depth. Published whole when it moves (bounded; no changeset), on its own subscription — a run panel re-renders on lifecycle, not on every token.

RunSnapshot

Prop

Type

NodeRunStatus

"running" | "completed" | "failed" | "cancelled".

NodeRun

One node's current activation, keyed by its ABSOLUTE path (a reused subgraph's inner ids repeat; the path keeps them apart).

Prop

Type

FiredEdge

An edge the run traversed, by semantic identity — fromPort says which branch fired when several wires leave one node.

Prop

Type

TokenUsage

{ promptTokens; completionTokens; reasoningTokens } — raw token accounting, on NodeRun.usage (per node, cumulative across activations) and RunSnapshot.totalUsage (the per-node sum). The public run stream carries tokens only: any client-side cost derived from them is an estimate, not authoritative billing. Read the billing facade for server-reported wallet and usage figures. Token usage is absent for a node that made no LLM calls, or against a server predating the field.

Prop

Type

Subagent

{ path, nodeId, runningCount, doneCount, failedCount } — a subgraph that ran, with its descendants rolled up at any depth (cancelled counts as failed). Derived by subagentsOf; the counts are *Count because the sibling views use the bare names for derived BOOLEANS — a count and a flag sharing one name is a guaranteed misread.

The views

Derived by joining the two projections — every node, leaf or subgraph, reads the same shape at any depth.

NodeView

What a generated nodes.<name>() returns.

Prop

Type

An unreached node is "idle" — present, not absent — so the whole plan renders up front. active/done/failed are derived from status (failed includes cancelled). content/reasoning are the CURRENT activation's joined text (a container's is its subtree's). Node names aren't on the wire — the generated client supplies the id→name map.

SubagentChild

Prop

Type

One observed direct child of a subgraph, in run order: an entry in SubagentView's children roster. kind and status come from the run projection; kind can be absent when not reported.

SubagentView

Prop

Type

A deliberately DISTINCT shape from NodeView (no active/done/failed booleans, no content/reasoning): the subgraph node's own status, the descendant rollup (runningCount/doneCount/failedCount), visitCount, exitedVia (the generated client narrows it to the declared exit ports), path, and outputs — the subagent's own un-promoted outputs, validated and typed by the generated view (dropped entirely for a black-boxed subgraph).

Conversation, history, session

ConversationRef

{ conversationId, agentId, agentVersion } — identity, captured from the first opener event.

ConversationStatus

Server vocabulary (snake values by design): "running" | "completed" | "failed" | "cancelled" | "paused" | "awaiting_client_tool".

ConversationSummary

Prop

Type

Mapped from the snake REST response at the transport boundary; a row with no string id is dropped, not mapped (absent is honest — String(undefined) would mint a routable "undefined" id).

ConversationListQuery

{ limit?, offset?, agentId?, status? } — the history list's narrowing filters; scope (tenant + user) is server-derived from the session and never sent.

Session

Prop

Type

The browser only ever sees this — never the secret key.

SessionProvider

() => Promise<Session | string> | Session | string — resolves a session on demand (e.g. a Next.js server action). Called lazily, and again to refresh after a 401.

SessionSource

Session | string | SessionProvider — what every connection option accepts: a minted session, a raw JWT, or a provider that mints one on demand.

ToolHandler

(args: unknown) => unknown | Promise<unknown> — a client-side tool handler: receives the parsed args, returns the result (a string passes through; anything else is JSON-serialized).

ToolHandlers

Record<string, ToolHandler> — the handler map, keyed by tool name.

ToolHandlersSource

ToolHandlers | (() => ToolHandlers | undefined) — the function form is read live at each dispatch, so handlers can change across renders without recreating the conversation.

The wire (stays snake by design)

These two are the JSON the client SENDS — snake, because the wire is the server's shape. Apps almost never build them (the transport does); they're exported for transport implementors.

ExecutionInput

Prop

Type

WireMessage

Mirrors agent-core's Message (OpenAI-shaped): { role: "system" | "user" | "assistant" | "tool"; content: string; tool_call_id?: string; name?: string }.

Constants

IDLE_RUN

RunSnapshot — the stable empty run, also the server-render value.

Billing (end-user reads)

The shapes BillingFacade returns. All money is micro-USD ($1 = 1,000,000); the session JWT scopes every read — tenant and end-user are never sent from the client. Enum-ish strings (source, overage, status) pass through unvalidated so a newer server reaches your app instead of being masked by the client.

BillingBalance

Prop

Type

BillingUsage

Prop

Type

BillingPlan

Prop

Type

BillingPlans

Prop

Type

BillingInvoice

Prop

Type

CreditGrantView

Prop

Type

HostedCheckout

{ url?: string } — a hosted purchase surface returned by billing.topup() and billing.subscribeCheckout(): the Stripe-hosted URL to redirect the end user to. Card data never touches your or our servers. The facade throws rather than return a missing URL.

On this page