InlayDocs
Reference

@inlayai/react

The React layer — the chat handle, history, the provider shell, and the store underneath.

The React bindings: one hook owning the wasm client, a history hook, the agent-agnostic provider shell (<InlayProvider>), and the framework-agnostic store underneath. Everything re-exported from @inlayai/sdk (row guards, timeline helpers, all data types, and the error classes) is documented under the data model and @inlayai/sdk — import it all from here, one front door.

Two builds, one import. Bundlers resolve this package via the react-server export condition: in a Server Component's module graph every hook returns the frozen idle surface (safe in sync and async components, zero React hooks called); in client graphs you get the live implementations below. See Server and shared components.

useInlayChat

useInlayChat<TBindings, TTables, THandlers>(opts): InlayChatHandle<TBindings, TTables>

useInlayChat drives one conversation from React: owns the wasm client + transport and subscribes the component. TBindings narrows binding reads and output-row names to the agent's declared outputs and TTables narrows useTable to the agent's tables (the generated hook supplies both); THandlers types the tool-handler map.

UseInlayChatOptions

Prop

Type

Option semantics:

  • session is read LIVE (an inline () => … is fine) and re-called to refresh after a 401 — the session stays valid with no wiring.
  • tools is read live at each dispatch — no memoization needed.
  • conversationId reopens on mount AND on change; a failed reopen surfaces reactively on reopenError (and still warns in the console) — render it beside the composer instead of an inexplicably empty chat. The imperative handle.resume(id) keeps its throw.
  • version pins every run to the generated version and warns once on skew (a resumed conversation from an older pin). The staleness gate is inlay codegen --check, not this warning. With a feed config, the separate identity/version guard below also controls whether rows are composited.
  • failures — the failure contract. Default "snapshot": send/ retry RESOLVE on a run failure and the reason lands on snapshot.error (what every UI consumer hand-wrote as .catch(() => {})). "reject" keeps the InlayRunError rejection. Misuse errors — a send mid-turn, a retry with nothing retryable — reject in EVERY mode; they are programming errors, not run outcomes.
  • smooth paces the reveal of streaming text (assistant content, reasoning, and building artifacts — three channels) and snaps to full on completion. Honors prefers-reduced-motion.

The handle — InlayChatHandle<TBindings, TTables>

23 members in four groups. The snapshot and flags are DATA; the per-slice reads are BOUND HOOKS (invoke as hooks, during render, unconditionally).

Data

MemberTypeNotes
snapshotChatSnapshot<TName>re-renders on every commit; SMOOTHED (see the option). Output-row names narrowed to TName. Raw useInlayChat without generated graph config stays flat, with no composite kinds.
conversationIdstring | undefinedthe flat alias for snapshot.conversation?.conversationId — URL writes, effect keys

A generated hook replaces this flat snapshot type with an agent-specific feed snapshot when the graph has composite shapes. Its row union includes FanOutRow, SubagentRow, or RepeatRow as appropriate, including repeat-only graphs with no fan-out or subgraphs. These are possible shapes, not a guarantee that every run produces each card; the generated hook supplies the feed config automatically from NODES, authoritative REGIONS, and subagent metadata. The SDK does not infer fan-out regions from wiring; regenerate older generated modules rather than relying on a runtime fallback.

Composition is identity/version-gated. The conversation's actual agentId must match the expected agent, and its agentVersion must equal an explicit expected version greater than zero. Mismatches, allowed foreign-agent reopens (even with a matching version number), draft version 0, and unknown expected versions disable all composites. With a feed config, the hook still subscribes to and paces the full row array: inner rows stay visible flat, not dropped by a switch to the top-level snapshot. Without a feed config, the raw hook's top-level-only contract is unchanged. Config changes or a failed identity check also retire previous composite references; matching feeds keep reference threading after pacing.

FlagscanSend, canCancel, canRetry, plus reopenError: the last AUTO-reopen failure (the conversationId option's mount effect), reactive — render it beside the composer instead of a console.warn + an inexplicably empty chat; it clears on the next attempt (resume / send / reset / delete). The imperative resume(id) keeps its throw. Drive buttons off the flags, never off status math. canSend ANDs the status map with the synchronous latches (starting, a reopen in flight) that the map can't see; canRetry checks them too; canCancel (STOP) ANDs its live-run map with the same latches — something live to stop (streaming, or the send/reopen startup windows). An idle row is continuable, not stoppable.

Actionssend(text, opts?), cancel(), delete(), retry(), reset(), resume(id), submitToolResult(toolCallId, content), loadEarlier(limit?). Identical semantics to the client methods, plus the failures mode above. loadEarlier resolves to the number of rows added (0 at the log's start; snapshot.hasEarlier says whether there are any). delete() removes the current conversation from the caller's history (a soft-delete tombstone — the chat-side twin of useInlayHistory's deleteConversation) and returns to the idle "new chat" state; a mounted history sidebar refetches on it. A no-op when there's no conversation yet; a failed delete rejects without resetting.

Bound hooks — each subscribes to exactly its slice; nothing re-renders on an unrelated delta:

HookReturnsSubscription
useBinding(name)TypedBinding<T> | undefinedjust that binding (schema-validated at settle when declared)
useTable(name)TableRowView<T>[] | undefinedjust that table's rows, including a row mid-stream
useRun()RunSnapshottopology channel only — lifecycle, not tokens
useSubagents()Subagent[]same channel
useSubagent(path, outputSchemas?)SubagentViewthe run + that subagent's output slice
useNode(path)NodeViewBOTH channels (status + its subtree's streamed content)
useToolCall(toolCallId){ call; result?; delivery? } | undefinedexactly that call's pair + delivery
useTimeline()Row[]the full path-tagged transcript
useRowsAt(path)Row[]one path's slice
useTranscript()(UserRow | AssistantRow)[]the flat chat view
useBranches(path, edges)BranchView[]topology channel only; the latest firing's port set

TypedBinding<T>

{ value: T; status: BindingStatus; format?: ArtifactFormat } — a binding read through useBinding, with the value narrowed to the caller's T.

sessionStoreKey(session: SessionSource): string | null — the store-identity key the memoization keys on (a static credential's value, or null for a provider function, which is read live).

Branch labelling — selectBranches

selectBranches(run, path, edges): BranchView[] — the pure selector behind useBranches: one node's outgoing wires, labelled against its latest firing's port set in run.edgesFired, grouped by the source's activation. A Collect router can fire several ports in one activation, so multiple ports, and every wire on each, can read taken.

The generated client folds this into every node view (nodes.<name>().branches), so the usual consumer never calls it directly. Reach for it when driving something custom off the generated EDGES const. Loop caveat: only the latest observed firing is reflected; a port used only on an earlier visit reads untaken after a later firing through different ports.

Do not reconstruct this set from scalar exitedVia: that field is only the last matching edge's fromPort, not the full set of fired ports. For older edges without activation stamps, selectBranches cannot group a firing and falls back to the last matching port as well.

BranchEdge

{ fromPort: string; to: string } — one outgoing wire as the generated EDGES const carries it: the exit port it leaves through and the generated name of its target.

BranchStatus

"taken" | "untaken" | "pending""taken": this wire's port belongs to the latest firing's observed port set (fan-out marks every wire of a taken port); "untaken": a matching firing was observed but this port is not in its set; "pending": no matching firing has been observed.

BranchView

A BranchEdge plus its status: BranchStatus — the element type of selectBranches/useBranches results and of NodeView.branches in a generated client.

useInlayHistory

useInlayHistory(opts): UseInlayHistoryResult — the calling end-user's conversation list, for a "reopen a past chat" sidebar. Separate from the chat handle because history isn't tied to one live conversation (its async load/error/refetch shape doesn't fit the synchronous snapshot). Refetches on option change; stale/unmounted loads are dropped (monotonic request id).

UseInlayHistoryOptions

Prop

Type

UseInlayHistoryResult

Prop

Type

  • isLoading is the first-load skeleton flag: no settled result yet for the CURRENT narrowing (agentId/status/limit/offset) — it ends when the first load settles (successfully OR with an error, in which case error renders instead) and returns when the narrowing changes. isFetching is any fetch in flight (the initial load, refetch(), loadMore(), the post-delete re-sync) — grey out over rows already on screen. The SWR/RTK Query split; isLoading is NOT "the list is empty" (an empty history is loaded, not loading — don't reach for loading && conversations.length === 0). error is scoped the same way: a narrowing change clears the old view's error; a settled failure keeps it until a retry or a view change settles.
  • hasMore is a limit+1 probe (the extra row's EXISTENCE is the answer) and describes the served page, not fetch health. Without an explicit limit the hook pages at HISTORY_DEFAULT_LIMIT (100 — the server's own cap, so the probe can see PAST it): hasMore / loadMore are honest out of the box, and a >100-conversation history is reachable.
  • loadMore() appends the next page, deduped by id. Refused while a load is in flight OR a page request hasn't been consumed by its effect — the two windows that used to skip a page permanently (the offset advancing past a discarded fetch; the dedupe guards duplicates, never holes).
  • refetch() resets to the base page and REPLACES. An explicit offset base load replaces too (a page-jump, not an append).

Retrieval — useRetrieval

Query a retrieval index from a component (the built-in-RAG surface, rag.md §7). A query is an action (call query), not a subscription — the hook manages the fetching/error/hits state. The ACL + per-user-namespace enforcement is server-side — the session's identity drives both.

const { query, hits, isFetching, error } = useRetrieval({
  baseUrl,
  session,
  index: "kb",
});
// <input onSubmit={(q) => query(q)} /> … hits.map(…)

UseRetrievalOptions

Prop

Type

UseRetrievalResult

Prop

Type

Citations — <Citations>

Render a grounded answer's citations as footnote markers (rag.md §7). Each marker is a <sup>[n]</sup> linking the claim to its source; a drifted citation (the corpus re-published since the answer) gets a drift marker. Style-free — the customer styles via className / renderMarker.

const { hits } = useRetrieval(...);
<Citations citations={row.citations ?? []} currentGeneration={hits[0]?.generation} />

CitationsProps

Prop

Type

The provider shell

The agent-agnostic connection provider — mount it once and it serves every agent's hooks (the connection + the history channel, nothing per-agent).

ExportKindNotes
INLAY_PRODUCTION_URLconst"https://app.inlayai.com" — the default baseUrl of every hook, facade, and the provider (defined in @inlayai/sdk, re-exported here)
InlayProvidercomponent{ session, baseUrl?, fetch?, children? } — the agent-agnostic connection + history channel
InlayContextValuetype{ connection: { baseUrl, session, fetch? }; notifyHistory(); subscribeHistory(cb): unsub }
useInlayContext()hookthe context; throws a clear error outside a provider
useInlayContextOptional()hookInlayContextValue | null — the generated hooks read the connection from it
useHistoryChannel()hook{ notify, subscribeHistory } — the invalidation channel: a mounted history hook refetches itself when a conversation starts or settles

Standalone smoothing

useSmoothedContent(content, streaming, artifactActive?): string — the lower-level paced reveal of a single string (the snapshot smoothing covers the common case; this is for hand-rolled consumers). Returns the full content when not streaming, under reduced motion, or on the artifact handoff.

The store

createChatStore(opts: ChatStoreOptions): ChatStore — the framework-agnostic, useSyncExternalStore-shaped core under useInlayChat: subscribe/getSnapshot (flat), getFullSnapshot, getTimeline (the commit-time index), subscribeRun/getRun, the action methods, and the flag getters. Use it for a non-React host or a custom binding layer; apps should prefer the hook.

ChatStoreOptions

Prop

Type

IDLE_SNAPSHOT: ChatSnapshot — the stable idle snapshot, also the server-render value.

On this page