PAIPAI

State And Projections

How storage envelopes, transport state, and public messages stay separated.

PAI uses different representations where the boundaries need different guarantees, but exposes only one ordinary application transcript.

RepresentationAudiencePurpose
Stored message envelopeStorage providers and runtime recoveryThin authorization/storage wrapper around one complete SDK-native message
Runtime thread snapshotInternal runtime workflowsVersion-consistent messages, runs, queue, lease, and thread authority
Transport state/eventsClient transport implementationsAuthorized native messages, SDK chunks, control events, and repair
ThreadStateApplications and ReactProjected PaiMessage[] plus normalized run, queue, and thread state

Public State

type ThreadState<TContract extends AgentContract = AgentContract> = {
  thread: PaiThreadHead<TContract>;
  messages: PaiMessage<TContract>[];
  runs: RunState<TContract>[];
  activeRunId: RunId | null;
  queue: ThreadQueueView<TContract>;
  usage: UsageSummary;
  capabilities: ThreadCapabilityView;
  error: ThreadErrorRecord | null;
};

messages is the canonical render order. It contains AI SDK-native parts plus PAI projection enrichments such as attachments, ToolData, suspension history, and reasoning summaries. Raw reserved data-pai-* parts are consumed during projection and never exposed as a parallel public grammar.

Runs stay normalized in state.runs. Group messages by message.metadata.pai.runId only when a UI needs turn containers. Queued work stays in state.queue.items until admission.

Transport Reduction

During generation, the runtime emits SDK UIMessageChunk values. The client keeps a private long-lived native reducer for each active message, then projects the resulting complete message. Snapshot repair replaces that private state and clears transient overlays.

Application code should consume ThreadState, not transport events. A transport can reconnect, replay a generation, or repair a gap without changing the public message shape.

Stored Messages

One message document is a thin scope/visibility/private-metadata envelope around one complete, strict-JSON SDK-major-pinned UIMessage. Run association, producer, relation, status, timestamps, usage, and other public PAI facts live under message.metadata.pai.

Storage providers remain contract-neutral. Core refines application metadata, application data parts, and static tool payloads against the active agent contract before messages enter application-facing state or model replay.

Control State

Runs, queue items, leases, authorization, and pending commands are normalized control records rather than message metadata. Tool renderer runStatus and action/error sidecars are derived from those facts without mutating native tool parts.

This is the key ownership rule:

native UIMessage        durable transcript authority
normalized controls    run/queue/lease/action authority
PaiMessage projection  consumer-only enrichment over both

On this page