Skip to main content

Overview

Every artifact produced during block execution is an item. A chat message, a tool call result, a progress indicator, a custom UI component — each one is an item that streams to connected clients in real time and (in most cases) gets persisted.

Items belong to requests, and requests belong to sessions. When a user sends a message, the framework creates a request, executes the action's root block, and the items produced during that execution are stored on the request record. The session collects items from all its requests and exposes them as a single timeline — so you can access them conveniently as one collection, but each item is tied to the request that produced it.

Items serve three purposes:

  1. Streaming output — clients receive items over SSE as blocks execute
  2. Session history — persisted items accumulate across requests, forming the conversation log
  3. LLM context — some item types feed into conversation history for future model calls

The items you'll use most

Most of the time you'll work with three emit methods. Generators call these automatically for their own output, but you can also call them explicitly from any block:

MethodWhat it createsLLM HistoryTransient
ctx.emitMessage(text)A chat message visible to the user
ctx.emitComponent(name, data)A custom UI component with structured data
ctx.emitStatus(message)A progress indicatorAlways

These are covered in depth in Emitting Items.

What happens automatically

You don't need to emit most item types yourself. The framework handles them:

  • Generators automatically emit message and reasoning items as the model streams
  • Tool calls produce block_tool_output items with the tool name, input, and result
  • State mutations emit state_change notifications so the client stays in sync
  • Resource mutations emit resource_change notifications
  • Errors produce error or step_error items depending on whether they're terminal

Visibility

The reference table below shows where each item type appears by default. Two things to note:

  • Client means the client receives and can render it. Most items are client-visible.
  • LLM History means the item enters conversation history for future model calls. Only content-bearing types (message, reasoning, block_tool_output) do this. Other client-visible types like component and status don't feed into model context — they're purely for the UI.

How visibility is resolved

Visibility is a pure function of (item.type, item.agentType) computed by resolveItemVisibility():

  • Conversational types (message, reasoning, block_tool_output) inherit visibility from the producing generator's agentType.
  • Structural types (component, status, block_output, etc.) have fixed per-type visibility.
  • An agentType: "trace" item is always { client: false, history: false } regardless of type — trace is observability-only.

Generator identity controls conversational visibility

Generators declare an identity via agentType. The framework uses that identity to route the auto-emitted messages and reasoning:

// User-facing agent — on the client stream, in conversation history.
const chatbot = generator({ agentType: "primary", /* ... */ });

// Sub-agent — visible to the client (live observability), but its output
// does NOT enter history.
const worker = generator({ agentType: "sub", /* ... */ });

// Devtool-only observer — not on the client stream, not in history.
const memoryObserver = generator({ agentType: "trace", /* ... */ });

// No agentType → the generator produces no auto-emitted items. Its typed
// `block_output` still flows to parents via graph edges.
const classifier = generator({ outputSchema: z.enum(["A", "B"]), /* ... */ });

See Generator identity for the full model — multi-peer agents, collaborative vs. isolated parallel work, and the selectForContext escape hatch for custom prompt context.

Handler-emitted items

Handlers don't declare agentType — but their emit helpers accept optional identity:

ctx.emitMessage("Analysis complete.");
// Implicit agent-equivalent visibility: on the client, enters history.

ctx.emitMessage("Debug observation", { agentType: "trace" });
// Observability-only: devtool sees it, users and the LLM don't.

Persistence

Items are persisted by default. A few types are transient — they stream to clients during execution but are never written to the session store. When someone reconnects or opens a past session, transient items won't appear.

The reference table below marks which types are transient. You can also make any block's output transient by setting transient: true on the block config.

Accessing items

Each item lives on the request that produced it, but the session aggregates items from all requests into a single timeline. When a user sends a second message, the session already holds every item from the first request. Generators use this aggregated view to build conversation history.

Three views for accessing session items:

  • items.all() — everything in the session
  • items.client() — items intended for the client UI (excludes trace items)
  • items.history() — items formatted for the model, with optional token limiting:
const history = ctx.session.items.history({ limit: { tokens: 20_000 } });

Item lifecycle

Items go through three phases:

  1. Added — item exists with status: "in_progress"
  2. Streaming — for messages and reasoning, text arrives in chunks via content deltas
  3. Done — item finalized as "completed", "incomplete", or "failed" (terminal, immutable)

All item types

For reference, here's the complete registry:

TypeWhat it isClientHistoryTransient
messageChat message (user or assistant)
reasoningModel thinking tokens
block_tool_outputTool invocation result
componentCustom UI component
containerGroups child items for visual layout
sourceURL reference from web search, etc.
statusProgress indicatorAlways
state_changeState mutation notificationProduction
resource_changeResource mutation notificationAlways
step_errorNon-terminal error in a pipeline step
errorTerminal request error
block_outputExecution record
router_decisionRoute selection record
state_snapshotSequencer state snapshotAlways