Brainby arc-labs/docs
TypeScript SDK

Pagination

Cursor-based enumeration on the wire client — the flatten vs *Frames pattern, and how to page memoryList and graphFetch.

Pagination lives on BrainClient. BrainHttpClient does not expose memoryList, graphFetch, or the typed-graph list* verbs. See Wire client.

Two forms per enumeration

FlattenRaw framesReturns
memoryListmemoryListFramesMemoryListItem[]
graphFetchgraphFetchFrames{ nodes, edges }
listEntitieslistEntitiesFramesEntityListItem[]
listStatementslistStatementsFramesStatementView[]
listRelationsFromlistRelationsFromFramesRelationView[]
listRelationsTolistRelationsToFramesRelationView[]
listSchemaslistSchemasFramesSchemaListItemWire[]
statementHistorystatementHistoryFramesStatementView[]

The flatten form is the common case: it drains every streamed frame and concatenates the items for you.

const memories = await client.memoryList({
  sort: MemoryListSort.CreatedAt,
  dir: MemoryListDir.Desc,
  limit: 100,           // server validates to 1..=100
  cursor: new Uint8Array(), // empty = first page
  kinds: [],            // empty = all kinds
  includeTombstoned: false,
  timeAxis: MemoryListTimeAxis.CreatedAt,
  fromUnixNanos: 0n,
  toUnixNanos: 0n,
  salienceMin: 0,
  salienceMax: 1,
  textContains: "",
  actAs: null,
});

Driving the cursor yourself

Use a *Frames method when you want to page incrementally — render a page, then fetch the next. Each frame carries nextCursor, cumulativeCount, and isFinal; a non-empty nextCursor means more pages remain.

let cursor = new Uint8Array(); // empty on the first page
for (;;) {
  const [frame] = await client.memoryListFrames({
    sort: MemoryListSort.CreatedAt,
    dir: MemoryListDir.Desc,
    limit: 50,
    cursor,
    kinds: [],
    includeTombstoned: false,
    timeAxis: MemoryListTimeAxis.CreatedAt,
    fromUnixNanos: 0n,
    toUnixNanos: 0n,
    salienceMin: 0,
    salienceMax: 1,
    textContains: "",
    actAs: null,
  });

  for (const item of frame.items) render(item);

  if (frame.isFinal || frame.nextCursor.length === 0) break;
  cursor = frame.nextCursor; // opaque continuation token
}

The cursor is an opaque Uint8Array — pass it back verbatim, don't parse it. An empty cursor requests the first page; a non-empty nextCursor from a frame requests the following page.

graphFetch

graphFetch exports the caller's whole typed graph the same way, but pages { nodes, edges }. Its limit validates server-side to 1..=500. Nodes and edges may repeat across pages (completeness, not disjointness) — dedup by id if you need a unique set.

const { nodes, edges } = await client.graphFetch({
  limit: 500,
  cursor: new Uint8Array(),
  includeStatements: true,
  includeMemories: true,
  includeTombstoned: false,
  actAs: null,
});

Was this page helpful?

On this page