useTree

useTree exposes stable structural query callbacks backed by a session's Tree. The returned methods are thin useCallback wrappers around session.tree and never trigger re-renders on their own.

Use this hook when you need to inspect tree structure outside the visible branch (for example, to count edit or regenerate siblings on a node before showing navigation arrows). Branch navigation for the currently visible chain lives on useView.

JavaScript

1

2

3

4

5

6

7

8

9

import { useTree } from '@ably/ai-transport/react';

function SiblingSwitcher({ nodeKey }) {
  const { getSiblingNodes } = useTree();
  const siblings = getSiblingNodes(nodeKey);

  if (siblings.length <= 1) return null;
  return <Siblings nodes={siblings} />;
}

This hook must be used within a ClientSessionProvider unless session is supplied explicitly.

Parameters

sessionoptionalClientSession<TInput, TOutput, TProjection, TMessage>
A client session to read tree structure from. Defaults to the nearest provider.

Returns

getRunNodeRunNode
Get a Run by runId, or undefined if not found.
getNodeByCodecMessageId(codecMessageId) => ConversationNode | undefined
Get the input or run node that owns a given codecMessageId, or undefined if not observed. Narrow on kind ('input' or 'run') before reading kind-specific fields.
getSiblingNodes(key) => ConversationNode[]
Get the sibling group for a node key (edit versions for an input node, regenerate siblings for a reply run). Ordered oldest-first by serial; single-element array when the node has no siblings; empty when the key is unknown.

Look up a Run by id

getRunNode(runId: string): RunNode<TProjection> | undefined

Return the full RunNode record for a given runId, or undefined if the Run has not been observed. Use this when you need fields the View's RunInfo does not expose (parent / fork relationships, the raw projection, serials).

Look up a node by codec-message-id

getNodeByCodecMessageId(codecMessageId: string): ConversationNode<TProjection> | undefined

Resolve the node that owns the given codecMessageId through the tree's index. The result is a ConversationNode union: an InputNode (user prompt) or a RunNode (agent reply). Narrow on kind ('input' or 'run') before reading kind-specific fields. Returns undefined when the message has not been observed.

Get sibling nodes

getSiblingNodes(key: string): ConversationNode<TProjection>[]

Return the sibling group the node keyed by key belongs to. The key is either a RunNode.runId or an InputNode.codecMessageId. The two sibling-group shapes are: input edits, where input nodes share a parent and chain via forkOf (the original user prompt plus every edit of it); and regenerate siblings, where reply runs share an input-node parent (the original reply plus every regenerate of it).

Ordered oldest-first by serial. Returns a single-element array when the node has no siblings, an empty array when the key is unknown. Narrow each node on kind before reading kind-specific fields.

Example

A run-history sidebar that lists every observed Run and lights up sibling navigation where alternatives exist.

JavaScript

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

import { useTree, useView } from '@ably/ai-transport/react';

function RunHistory() {
  const { messages, runOf } = useView();
  const { getSiblingNodes } = useTree();

  const runs = messages
    .map(({ codecMessageId }) => runOf(codecMessageId))
    .filter((r): r is RunInfo => Boolean(r));

  return (
    <ul>
      {runs.map((run) => {
        const siblings = getSiblingNodes(run.runId);
        return (
          <li key={run.runId}>
            <RunSummary run={run} />
            {siblings.length > 1 && <SiblingPicker siblings={siblings} />}
          </li>
        );
      })}
    </ul>
  );
}