# Graphs

## List graphs

`client.graphs.list(GraphListParamsparams?, RequestOptionsoptions?): CursorPage<Graph>`

**get** `/graphs`

Returns the graphs this key can read: your account's live shares, most recently active first.

Each graph's `counts` are as of its own latest event.

### Parameters

- `params: GraphListParams`

  - `ending_before?: string`

    Query param: A cursor: an object id from the list. Returns the page before it. Pass the first `id` of the page you have to get the previous one.

  - `last_event?: LastEvent`

    Query param: Only graphs whose latest event is in this range. `last_event[gte]=<5 minutes ago>` lists the shares active now.

    - `gt?: number`

      After this time.

    - `gte?: number`

      At or after this time.

    - `lt?: number`

      Before this time.

    - `lte?: number`

      At or before this time.

  - `limit?: number`

    Query param: How many objects to return, from 1 to 1,000.

  - `starting_after?: string`

    Query param: A cursor: an object id from the list. Returns the page after it. Pass the last `id` of the page you have to get the next one.

  - `ifNoneMatch?: string`

    Header param: The `ETag` of a response you have. If nothing has changed, the reply is `304 Not Modified`, with no body.

### Returns

- `Graph`

  One of your account's live shares, and how its nodes stand at `as_of_event_id`.

  - `id: string`

    Unique identifier for the graph, `gph_…`.

  - `as_of_event_id: string`

    The event this graph's `counts` are as of. Pass it as `as_of` to read other objects at the same moment.

  - `counts: GraphCounts`

    How many nodes the graph has, by kind and state.

    - `agents: number`

      Nodes of kind `agent`.

    - `blocked: number`

      Unfinished nodes waiting on another node.

    - `by_state: ByState`

      Nodes in each state.

      - `canceled: number`

      - `completed: number`

      - `failed: number`

      - `idle: number`

      - `input_required: number`

      - `working: number`

    - `nodes: number`

      Every node.

    - `sessions: number`

      Nodes of kind `session`.

    - `stale: number`

      Nodes that are `stale`: working, but silent too long.

  - `created: number`

    When the share was made, in Unix milliseconds.

  - `last_event: number`

    When its most recent event happened, in Unix milliseconds.

  - `object: "graph"`

    String representing the object's type: always `graph`.

    - `"graph"`

  - `retention: Retention`

    The span of history the site holds for this graph now, and so the range `as_of` can take. A live share keeps between 1,000 and 2,000 events, trimmed from the start as new ones arrive.

    - `event_count: number`

      How many events are held.

    - `first_event_created: number | null`

      When the oldest event happened, in Unix milliseconds.

    - `first_event_id: string | null`

      The oldest event you can read, or ask `as_of`. `null` while the graph has no events.

    - `last_event_created: number | null`

      When the newest event happened, in Unix milliseconds.

    - `last_event_id: string | null`

      The newest event. `null` while the graph has no events.

  - `source: "watch" | "paste" | "upload"`

    How the graph was shared. `watch` is a live share from `agent-graph watch-remote`; this version of the API returns only those.

    - `"watch"`

    - `"paste"`

    - `"upload"`

  - `url: string`

    Where to see the graph in the viewer. Only the account that owns it can open it.

### Example

```typescript
import AgentGraph from '@chofter/agent-graph-api';

const client = new AgentGraph({
  apiKey: process.env['AGENT_GRAPH_API_KEY'], // This is the default and can be omitted
});

// Automatically fetches more pages as needed.
for await (const graph of client.graphs.list()) {
  console.log(graph.id);
}
```

#### Response

```json
{
  "object": "list",
  "url": "/api/v1/graphs",
  "as_of_event_id": null,
  "has_more": false,
  "data": [
    {
      "id": "gph_k3P9xQ2mWt7a",
      "object": "graph",
      "as_of_event_id": "evt_01M4G3G4E8Q9R2M5X4T6W8Y0B1",
      "source": "watch",
      "url": "https://agentgraph.chofter.com/l/k3P9xQ2mWt7a",
      "created": 1790959209012,
      "last_event": 1791541907912,
      "retention": {
        "first_event_id": "evt_01M4FXYWR82B8XK0V5QJ7M1N3R",
        "last_event_id": "evt_01M4G3G4E8Q9R2M5X4T6W8Y0B1",
        "first_event_created": 1791536100104,
        "last_event_created": 1791541907912,
        "event_count": 1638
      },
      "counts": {
        "nodes": 214,
        "sessions": 9,
        "agents": 205,
        "stale": 1,
        "blocked": 6,
        "by_state": {
          "working": 7,
          "input_required": 1,
          "idle": 3,
          "completed": 198,
          "failed": 4,
          "canceled": 1
        }
      }
    }
  ]
}
```

## Retrieve a graph

`client.graphs.retrieve(stringgraphID, GraphRetrieveParamsparams?, RequestOptionsoptions?): Graph`

**get** `/graphs/{graph_id}`

Retrieves a graph: what history it holds (`retention`) and how its nodes stand (`counts`), now or as of any retained event.

This is the cheapest overview of a graph: one small object however many nodes it has. Poll it with `If-None-Match` to learn when anything changes.

### Parameters

- `graphID: string`

- `params: GraphRetrieveParams`

  - `as_of?: string | number`

    Query param: Answer as of this point in the graph's history: an event id (`evt_…`, the state just after that event) or a Unix time in milliseconds (the state after the last event at or before it). Leave it out for now.

    Must be within the graph's `retention`, else `410 outside_retention_window`. To read several pages or objects at one moment, pass the `as_of_event_id` of the first response.

    - `string`

    - `number`

  - `ifNoneMatch?: string`

    Header param: The `ETag` of a response you have. If nothing has changed, the reply is `304 Not Modified`, with no body.

### Returns

- `Graph`

  One of your account's live shares, and how its nodes stand at `as_of_event_id`.

  - `id: string`

    Unique identifier for the graph, `gph_…`.

  - `as_of_event_id: string`

    The event this graph's `counts` are as of. Pass it as `as_of` to read other objects at the same moment.

  - `counts: GraphCounts`

    How many nodes the graph has, by kind and state.

    - `agents: number`

      Nodes of kind `agent`.

    - `blocked: number`

      Unfinished nodes waiting on another node.

    - `by_state: ByState`

      Nodes in each state.

      - `canceled: number`

      - `completed: number`

      - `failed: number`

      - `idle: number`

      - `input_required: number`

      - `working: number`

    - `nodes: number`

      Every node.

    - `sessions: number`

      Nodes of kind `session`.

    - `stale: number`

      Nodes that are `stale`: working, but silent too long.

  - `created: number`

    When the share was made, in Unix milliseconds.

  - `last_event: number`

    When its most recent event happened, in Unix milliseconds.

  - `object: "graph"`

    String representing the object's type: always `graph`.

    - `"graph"`

  - `retention: Retention`

    The span of history the site holds for this graph now, and so the range `as_of` can take. A live share keeps between 1,000 and 2,000 events, trimmed from the start as new ones arrive.

    - `event_count: number`

      How many events are held.

    - `first_event_created: number | null`

      When the oldest event happened, in Unix milliseconds.

    - `first_event_id: string | null`

      The oldest event you can read, or ask `as_of`. `null` while the graph has no events.

    - `last_event_created: number | null`

      When the newest event happened, in Unix milliseconds.

    - `last_event_id: string | null`

      The newest event. `null` while the graph has no events.

  - `source: "watch" | "paste" | "upload"`

    How the graph was shared. `watch` is a live share from `agent-graph watch-remote`; this version of the API returns only those.

    - `"watch"`

    - `"paste"`

    - `"upload"`

  - `url: string`

    Where to see the graph in the viewer. Only the account that owns it can open it.

### Example

```typescript
import AgentGraph from '@chofter/agent-graph-api';

const client = new AgentGraph({
  apiKey: process.env['AGENT_GRAPH_API_KEY'], // This is the default and can be omitted
});

const graph = await client.graphs.retrieve('gph_k3P9xQ2mWt7a');

console.log(graph.id);
```

#### Response

```json
{
  "id": "gph_k3P9xQ2mWt7a",
  "object": "graph",
  "as_of_event_id": "evt_01M4G3G4E8Q9R2M5X4T6W8Y0B1",
  "source": "watch",
  "url": "https://agentgraph.chofter.com/l/k3P9xQ2mWt7a",
  "created": 1790959209012,
  "last_event": 1791541907912,
  "retention": {
    "first_event_id": "evt_01M4FXYWR82B8XK0V5QJ7M1N3R",
    "last_event_id": "evt_01M4G3G4E8Q9R2M5X4T6W8Y0B1",
    "first_event_created": 1791536100104,
    "last_event_created": 1791541907912,
    "event_count": 1638
  },
  "counts": {
    "nodes": 214,
    "sessions": 9,
    "agents": 205,
    "stale": 1,
    "blocked": 6,
    "by_state": {
      "working": 7,
      "input_required": 1,
      "idle": 3,
      "completed": 198,
      "failed": 4,
      "canceled": 1
    }
  }
}
```

## Domain Types

### Graph

- `Graph`

  One of your account's live shares, and how its nodes stand at `as_of_event_id`.

  - `id: string`

    Unique identifier for the graph, `gph_…`.

  - `as_of_event_id: string`

    The event this graph's `counts` are as of. Pass it as `as_of` to read other objects at the same moment.

  - `counts: GraphCounts`

    How many nodes the graph has, by kind and state.

    - `agents: number`

      Nodes of kind `agent`.

    - `blocked: number`

      Unfinished nodes waiting on another node.

    - `by_state: ByState`

      Nodes in each state.

      - `canceled: number`

      - `completed: number`

      - `failed: number`

      - `idle: number`

      - `input_required: number`

      - `working: number`

    - `nodes: number`

      Every node.

    - `sessions: number`

      Nodes of kind `session`.

    - `stale: number`

      Nodes that are `stale`: working, but silent too long.

  - `created: number`

    When the share was made, in Unix milliseconds.

  - `last_event: number`

    When its most recent event happened, in Unix milliseconds.

  - `object: "graph"`

    String representing the object's type: always `graph`.

    - `"graph"`

  - `retention: Retention`

    The span of history the site holds for this graph now, and so the range `as_of` can take. A live share keeps between 1,000 and 2,000 events, trimmed from the start as new ones arrive.

    - `event_count: number`

      How many events are held.

    - `first_event_created: number | null`

      When the oldest event happened, in Unix milliseconds.

    - `first_event_id: string | null`

      The oldest event you can read, or ask `as_of`. `null` while the graph has no events.

    - `last_event_created: number | null`

      When the newest event happened, in Unix milliseconds.

    - `last_event_id: string | null`

      The newest event. `null` while the graph has no events.

  - `source: "watch" | "paste" | "upload"`

    How the graph was shared. `watch` is a live share from `agent-graph watch-remote`; this version of the API returns only those.

    - `"watch"`

    - `"paste"`

    - `"upload"`

  - `url: string`

    Where to see the graph in the viewer. Only the account that owns it can open it.

### Graph Counts

- `GraphCounts`

  How many nodes the graph has, by kind and state.

  - `agents: number`

    Nodes of kind `agent`.

  - `blocked: number`

    Unfinished nodes waiting on another node.

  - `by_state: ByState`

    Nodes in each state.

    - `canceled: number`

    - `completed: number`

    - `failed: number`

    - `idle: number`

    - `input_required: number`

    - `working: number`

  - `nodes: number`

    Every node.

  - `sessions: number`

    Nodes of kind `session`.

  - `stale: number`

    Nodes that are `stale`: working, but silent too long.

### Retention

- `Retention`

  The span of history the site holds for this graph now, and so the range `as_of` can take. A live share keeps between 1,000 and 2,000 events, trimmed from the start as new ones arrive.

  - `event_count: number`

    How many events are held.

  - `first_event_created: number | null`

    When the oldest event happened, in Unix milliseconds.

  - `first_event_id: string | null`

    The oldest event you can read, or ask `as_of`. `null` while the graph has no events.

  - `last_event_created: number | null`

    When the newest event happened, in Unix milliseconds.

  - `last_event_id: string | null`

    The newest event. `null` while the graph has no events.

# Nodes

## List nodes

`client.graphs.nodes.list(stringgraphID, NodeListParamsparams?, RequestOptionsoptions?): CursorPage<Node>`

**get** `/graphs/{graph_id}/nodes`

Returns a graph's nodes as of a point in its history, filtered and ordered as you ask.

Common uses:

| To list                            | Send                                        |
| ---------------------------------- | ------------------------------------------- |
| The top-level trees                | `is_root=true`                              |
| A node's children                  | `parent_id=node_…`                          |
| Everything under a node            | `ancestor_id=node_…&order=tree`             |
| The top three levels under it      | `ancestor_id=node_…&max_depth=3&order=tree` |
| Everything waiting on a person     | `state=input_required`                      |
| What may have hung                 | `stale=true`                                |
| What's blocked behind another node | `blocked=true`                              |

Filters combine with AND. In tree order (`order=tree`), each parent comes before its children, and siblings are oldest first, so a page never separates a node from what places it; with a filter, the nodes that match keep that order.

### Parameters

- `graphID: string`

- `params: NodeListParams`

  - `agent_type?: string`

    Query param: Only agents of this type, e.g. `Explore`.

  - `ancestor_id?: string`

    Query param: Only nodes under this one, at any depth (not the node itself).

  - `as_of?: string | number`

    Query param: Answer as of this point in the graph's history: an event id (`evt_…`, the state just after that event) or a Unix time in milliseconds (the state after the last event at or before it). Leave it out for now.

    Must be within the graph's `retention`, else `410 outside_retention_window`. To read several pages or objects at one moment, pass the `as_of_event_id` of the first response.

    - `string`

    - `number`

  - `blocked?: boolean`

    Query param: Only nodes that are (`true`) or aren't (`false`) waiting on another node.

  - `created?: Created`

    Query param: Only nodes that started in this range.

    - `gt?: number`

      After this time.

    - `gte?: number`

      At or after this time.

    - `lt?: number`

      Before this time.

    - `lte?: number`

      At or before this time.

  - `ending_before?: string`

    Query param: A cursor: an object id from the list. Returns the page before it. Pass the first `id` of the page you have to get the previous one.

  - `expand?: Array<string>`

    Query param: Fields to expand on each node, with the `data.` prefix: `expand[]=data.parent`, `expand[]=data.blocked.on`. `children` and `descendants` can't be expanded in a list (use `parent_id` or `ancestor_id` instead).

    Send one `expand[]` per path. A path goes at most 4 levels deep; at most 20 paths per request. An expanded field appears beside its `_id` field, which is always there; one not expanded is absent.

  - `is_root?: boolean`

    Query param: Only roots (`true`) or only non-roots (`false`).

  - `kind?: NodeKind`

    Query param: Only sessions or only agents.

    - `"session"`

    - `"agent"`

  - `last_event?: LastEvent`

    Query param: Only nodes whose last event is in this range.

    - `gt?: number`

      After this time.

    - `gte?: number`

      At or after this time.

    - `lt?: number`

      Before this time.

    - `lte?: number`

      At or before this time.

  - `limit?: number`

    Query param: How many objects to return, from 1 to 1,000.

  - `max_depth?: number`

    Query param: With `ancestor_id`: only nodes at most this many levels below it. 1 is its children.

  - `order?: "newest" | "tree"`

    Query param: `newest`: most recently started first. `tree`: depth-first, each parent before its children, siblings oldest first.

    - `"newest"`

    - `"tree"`

  - `parent_id?: string`

    Query param: Only nodes directly under this one.

  - `provider?: string`

    Query param: Only nodes from this agent tool, e.g. `claude-code`.

  - `root_id?: string`

    Query param: Only nodes in this tree, the root included.

  - `session_id?: string`

    Query param: Only this session and the agents in it.

  - `stale?: boolean`

    Query param: Only nodes that are, or aren't, `stale`.

  - `starting_after?: string`

    Query param: A cursor: an object id from the list. Returns the page after it. Pass the last `id` of the page you have to get the next one.

  - `state?: Array<NodeState>`

    Query param: Only nodes in these states. Send one `state[]` per state.

    - `"working"`

    - `"input_required"`

    - `"idle"`

    - `"completed"`

    - `"failed"`

    - `"canceled"`

  - `ifNoneMatch?: string`

    Header param: The `ETag` of a response you have. If nothing has changed, the reply is `304 Not Modified`, with no body.

### Returns

- `Node`

  A session or an agent, as it stood at `as_of_event_id`.

  Fields ending `_id` or `_ids` are always present, as ids. The fields beside them without that ending (`parent`, `root`, …), and `children`, `descendants`, `tasks`, `spawns`, `waits` and `messages`, appear only when expanded.

  - `id: string`

    Unique identifier for the node within its graph: `node_` and the base64url of `provider_ref`.

  - `agent_type: string | null`

    For an agent, its type, as the tool names it (`Explore`, `general-purpose`, …).

  - `as_of_event_id: string`

    The event this node is shown as of.

  - `attention: string | null`

    While `input_required`: what the person is being asked. Untrusted text.

  - `background: boolean | null`

    Whether it was started in the background, so its requester carries on without waiting. `null` when unknown.

  - `background_agent_count: number`

    Helpers its tool ran on its own between turns (memory extraction, summaries, prompt suggestions). They report nothing about what they're for, so they're counted, not made nodes.

  - `blocked: Blocked | null`

    What stands between a node and carrying on.

  - `child_count: number`

    How many nodes sit directly under it.

  - `created: number`

    When it started (or was first seen), in Unix milliseconds.

  - `cwd: string | null`

    The directory it runs in, when reported.

  - `depth: number`

    How far below its root it is: 0 for a root.

  - `descendant_count: number`

    How many nodes sit under it, at any depth: the size of `descendants`.

  - `ended: number | null`

    When it finished, in Unix milliseconds. `null` while it hasn't.

  - `graph_id: string`

    The graph it belongs to.

  - `headline: string | null`

    One line on what it's doing now: `summary`, else the task in progress. Untrusted text.

  - `kind: NodeKind`

    `session`: one run of a coding agent (Claude Code, Codex, Cursor, …). `agent`: an agent started inside a session, by it or by another agent.

    - `"session"`

    - `"agent"`

  - `last_event: number`

    When it last had an event, in Unix milliseconds.

  - `link_method: "env" | "run" | "process" | "suggested" | null`

    For a session started by another, how that was worked out: `env` (it inherited its parent's identity), `run` (`agent-graph run` started it), `process` (its parent's agent is among its processes) or `suggested` (it took a name its parent suggested).

    - `"env"`

    - `"run"`

    - `"process"`

    - `"suggested"`

  - `object: "node"`

    String representing the object's type: always `node`.

    - `"node"`

  - `parent_id: string | null`

    The node it sits under: the agent or session that started it. `null` for a root.

  - `provider: string`

    The agent tool: `claude-code`, `codex`, `cursor`, or another that reports to Agent Graph.

  - `provider_ref: string`

    The id the agent tool gave it: `<provider>:<session>` for a session, `<provider>:<session>/<agent>` for an agent. Not an id in this API: use it to match the node to the tool's own records.

  - `purpose: string | null`

    Why it was started, from the request that started it. Untrusted text.

  - `requested_by_id: string | null`

    The node whose spawn request started it, when known. Usually the parent; `null` for a session nothing started, or an agent no request was matched to.

  - `root_id: string`

    The top of its tree: follow `parent_id` until it's `null`. A session started by another session is in that session's tree. A root's `root_id` is its own `id`.

  - `session_id: string`

    The session it runs in: itself, for a session; for an agent, the nearest session above it.

  - `spawn_call_id: string | null`

    The tool call, in its requester's session, that started it: matches a `spawns[].call_id` on `requested_by`.

  - `stale: boolean`

    `working`, waiting on nothing, and silent for 30 minutes: it may have hung or crashed. Measured against `as_of`, not now.

  - `state: NodeState`

    What a node is doing.

    - `working`: doing something.
    - `input_required`: waiting on a person (`attention` says what for).
    - `idle`: a session between turns, waiting for its next prompt.
    - `completed`, `failed`, `canceled`: finished.

    - `"working"`

    - `"input_required"`

    - `"idle"`

    - `"completed"`

    - `"failed"`

    - `"canceled"`

  - `summary: string | null`

    Its own latest status line, if it reports one. Untrusted text.

  - `task_counts: TaskCounts`

    Its tasks, by status.

    - `completed: number`

    - `in_progress: number`

    - `open: number`

      Not completed: `pending` and `in_progress`.

    - `pending: number`

    - `total: number`

  - `title: string | null`

    The session's name, as its tool shows it. It can change. Untrusted text.

  - `children?: unknown`

    The nodes directly under it, oldest first: the first 100, and a `url` for the rest. Present only when expanded.

  - `descendants?: unknown`

    Every node under it, at any depth, as one flat list in tree order: each parent before its children, siblings oldest first. The first 1,000, and a `url` for the rest. Present only when expanded.

  - `messages?: Array<Message>`

    Messages it has sent and received, oldest first. Present only when expanded.

    - `id: string`

      The tool's id for the message.

    - `body: string | null`

      The whole message, only if the share was made with body capture on. Untrusted text.

    - `created: number`

      When it was sent, in Unix milliseconds.

    - `direction: "sent" | "received"`

      - `"sent"`

      - `"received"`

    - `peer_id: string | null`

      The node on the other side, when it's one in this graph.

    - `peer_name: string | null`

      The tool's name for the other side, when it isn't a node in this graph.

    - `reply_to_message_id: string | null`

      The message it answers.

    - `summary: string | null`

      Untrusted text.

  - `parent?: Node`

    The parent node. Present only when expanded.

  - `requested_by?: Node`

    The node that requested it. Present only when expanded.

  - `root?: Node`

    The root node. Present only when expanded.

  - `session?: Node`

    The session node. Present only when expanded.

  - `spawns?: Array<Spawn>`

    The children it has asked for, oldest first. Present only when expanded.

    - `agent_type: string | null`

    - `background: boolean`

      If false, the requester is blocked until the call returns.

    - `call_id: string`

      The tool call that asked for it.

    - `child_id: string | null`

      The node it started, once matched. Before the call returns this can be a best guess, corrected when it does.

    - `kind: "agent" | "session"`

      `agent`: an agent in the same session. `session`: a separate session, e.g. `codex exec` run from a shell.

      - `"agent"`

      - `"session"`

    - `purpose: string | null`

      Untrusted text.

    - `requested: number`

      When it was asked for, in Unix milliseconds.

    - `returned: boolean`

      Whether the call has returned.

    - `run: boolean | null`

      Whether it's through `agent-graph run`, when known.

    - `title: string | null`

      For a session, the name it was to be given.

  - `tasks?: Array<Task>`

    Its task list, in the tool's order. Present only when expanded.

    - `id: string`

      The tool's id for the task, unique within the node.

    - `active_text: string | null`

      How the tool words it while it's in progress (`Running the tests`). Untrusted text.

    - `status: "pending" | "in_progress" | "completed"`

      - `"pending"`

      - `"in_progress"`

      - `"completed"`

    - `text: string`

      What the task is. Untrusted text.

  - `waits?: Array<Wait>`

    What it has waited on and is waiting on, oldest first. Present only when expanded.

    - `id: string`

      The wait's id, unique within the node.

    - `ended: number | null`

      When it stopped, in Unix milliseconds.

    - `kind: "spawn" | "explicit"`

      `spawn`: waiting for a child it asked for, until the call returns. `explicit`: any other wait the tool reported.

      - `"spawn"`

      - `"explicit"`

    - `on_id: string | null`

      The node waited on. `null` while a requested child hasn't started.

    - `open: boolean`

      Whether it's still waiting.

    - `reason: string | null`

      Untrusted text.

    - `started: number`

      When it started waiting, in Unix milliseconds.

### Example

```typescript
import AgentGraph from '@chofter/agent-graph-api';

const client = new AgentGraph({
  apiKey: process.env['AGENT_GRAPH_API_KEY'], // This is the default and can be omitted
});

// Automatically fetches more pages as needed.
for await (const node of client.graphs.nodes.list('gph_k3P9xQ2mWt7a')) {
  console.log(node.id);
}
```

#### Response

```json
{
  "object": "list",
  "url": "/api/v1/graphs/gph_k3P9xQ2mWt7a/nodes?parent_id=node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEw&as_of=evt_01M4G3G4E8Q9R2M5X4T6W8Y0B1",
  "as_of_event_id": "evt_01M4G3G4E8Q9R2M5X4T6W8Y0B1",
  "has_more": true,
  "data": [
    {
      "id": "node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEwL2EzZmQwN2MyYjE5ZTRkNjE",
      "object": "node",
      "as_of_event_id": "evt_01M4G3G4E8Q9R2M5X4T6W8Y0B1",
      "graph_id": "gph_k3P9xQ2mWt7a",
      "provider_ref": "claude-code:5f2c1e9a-8d1b-4b6e-9a51-0f6f2c7d3e10/a3fd07c2b19e4d61",
      "provider": "claude-code",
      "kind": "agent",
      "parent_id": "node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEw",
      "root_id": "node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEw",
      "session_id": "node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEw",
      "requested_by_id": "node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEw",
      "depth": 1,
      "state": "working",
      "stale": false,
      "attention": null,
      "title": null,
      "summary": "Reading the auth middleware",
      "headline": "Reading the auth middleware",
      "purpose": "Find every caller of the old session store",
      "agent_type": "Explore",
      "background": false,
      "link_method": null,
      "spawn_call_id": "toolu_01HxV9qT3mJb7d2KcL5nRw8E",
      "cwd": "/Users/dev/work/api",
      "created": 1791540843221,
      "ended": null,
      "last_event": 1791541903812,
      "child_count": 1,
      "descendant_count": 1,
      "background_agent_count": 0,
      "task_counts": {
        "total": 4,
        "pending": 1,
        "in_progress": 1,
        "completed": 2,
        "open": 2
      },
      "blocked": {
        "on_ids": [
          "node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEwL2I4MWMyMmUwOWY3YTNjNTU"
        ],
        "starting": 0,
        "node_count": 1,
        "open_tasks": 5,
        "cycle": false
      }
    }
  ]
}
```

## Search nodes

`client.graphs.nodes.search(stringgraphID, NodeSearchParamsquery, RequestOptionsoptions?): SearchPage<Node>`

**get** `/graphs/{graph_id}/nodes/search`

Searches a graph's nodes with a query, as of a point in its history.

Use it for questions the [list filters](#operation/listNodes) can't express: text in a node's summary, OR, negation, numeric comparisons.

**Query language**

A query is one or more clauses, joined by `AND` or `OR` (not both in one query). Values are quoted.

| Clause                                                       | Matches                                        |
| ------------------------------------------------------------ | ---------------------------------------------- |
| `field:"value"`                                              | Exactly equal (case-insensitive for text).     |
| `field~"value"`                                              | Contains (text fields; at least 3 characters). |
| `-field:"value"`                                             | Not equal.                                     |
| `field>value`, `field>=value`, `field<value`, `field<=value` | Numbers and times (Unix milliseconds).         |
| `field:null`                                                 | Has no value.                                  |

**Fields:** `state`, `kind`, `provider`, `agent_type`, `stale`, `blocked`, `background`, `parent_id`, `root_id`, `session_id`, `depth`, `created`, `ended`, `last_event`, `child_count`, `descendant_count`, `open_tasks` (its `task_counts.open`), `blocked_open_tasks` (its `blocked.open_tasks`), `title`, `summary`, `headline`, `purpose`, `attention`.

**Examples**

```
state:"working" AND stale:"true"
agent_type:"Explore" AND descendant_count>10
state:"failed" OR state:"canceled"
summary~"migration" AND -state:"completed"
blocked_open_tasks>=20
```

Results are most recently active first.

### Parameters

- `graphID: string`

- `query: NodeSearchParams`

  - `query: string`

    The search query. See the query language above.

  - `as_of?: string | number`

    Answer as of this point in the graph's history: an event id (`evt_…`, the state just after that event) or a Unix time in milliseconds (the state after the last event at or before it). Leave it out for now.

    Must be within the graph's `retention`, else `410 outside_retention_window`. To read several pages or objects at one moment, pass the `as_of_event_id` of the first response.

    - `string`

    - `number`

  - `expand?: Array<string>`

    Fields to expand on each node, with the `data.` prefix: `expand[]=data.root`.

    Send one `expand[]` per path. A path goes at most 4 levels deep; at most 20 paths per request. An expanded field appears beside its `_id` field, which is always there; one not expanded is absent.

  - `limit?: number`

    How many objects to return, from 1 to 1,000.

  - `page?: string`

    The cursor for a page after the first: the previous page's `next_page`.

### Returns

- `Node`

  A session or an agent, as it stood at `as_of_event_id`.

  Fields ending `_id` or `_ids` are always present, as ids. The fields beside them without that ending (`parent`, `root`, …), and `children`, `descendants`, `tasks`, `spawns`, `waits` and `messages`, appear only when expanded.

  - `id: string`

    Unique identifier for the node within its graph: `node_` and the base64url of `provider_ref`.

  - `agent_type: string | null`

    For an agent, its type, as the tool names it (`Explore`, `general-purpose`, …).

  - `as_of_event_id: string`

    The event this node is shown as of.

  - `attention: string | null`

    While `input_required`: what the person is being asked. Untrusted text.

  - `background: boolean | null`

    Whether it was started in the background, so its requester carries on without waiting. `null` when unknown.

  - `background_agent_count: number`

    Helpers its tool ran on its own between turns (memory extraction, summaries, prompt suggestions). They report nothing about what they're for, so they're counted, not made nodes.

  - `blocked: Blocked | null`

    What stands between a node and carrying on.

  - `child_count: number`

    How many nodes sit directly under it.

  - `created: number`

    When it started (or was first seen), in Unix milliseconds.

  - `cwd: string | null`

    The directory it runs in, when reported.

  - `depth: number`

    How far below its root it is: 0 for a root.

  - `descendant_count: number`

    How many nodes sit under it, at any depth: the size of `descendants`.

  - `ended: number | null`

    When it finished, in Unix milliseconds. `null` while it hasn't.

  - `graph_id: string`

    The graph it belongs to.

  - `headline: string | null`

    One line on what it's doing now: `summary`, else the task in progress. Untrusted text.

  - `kind: NodeKind`

    `session`: one run of a coding agent (Claude Code, Codex, Cursor, …). `agent`: an agent started inside a session, by it or by another agent.

    - `"session"`

    - `"agent"`

  - `last_event: number`

    When it last had an event, in Unix milliseconds.

  - `link_method: "env" | "run" | "process" | "suggested" | null`

    For a session started by another, how that was worked out: `env` (it inherited its parent's identity), `run` (`agent-graph run` started it), `process` (its parent's agent is among its processes) or `suggested` (it took a name its parent suggested).

    - `"env"`

    - `"run"`

    - `"process"`

    - `"suggested"`

  - `object: "node"`

    String representing the object's type: always `node`.

    - `"node"`

  - `parent_id: string | null`

    The node it sits under: the agent or session that started it. `null` for a root.

  - `provider: string`

    The agent tool: `claude-code`, `codex`, `cursor`, or another that reports to Agent Graph.

  - `provider_ref: string`

    The id the agent tool gave it: `<provider>:<session>` for a session, `<provider>:<session>/<agent>` for an agent. Not an id in this API: use it to match the node to the tool's own records.

  - `purpose: string | null`

    Why it was started, from the request that started it. Untrusted text.

  - `requested_by_id: string | null`

    The node whose spawn request started it, when known. Usually the parent; `null` for a session nothing started, or an agent no request was matched to.

  - `root_id: string`

    The top of its tree: follow `parent_id` until it's `null`. A session started by another session is in that session's tree. A root's `root_id` is its own `id`.

  - `session_id: string`

    The session it runs in: itself, for a session; for an agent, the nearest session above it.

  - `spawn_call_id: string | null`

    The tool call, in its requester's session, that started it: matches a `spawns[].call_id` on `requested_by`.

  - `stale: boolean`

    `working`, waiting on nothing, and silent for 30 minutes: it may have hung or crashed. Measured against `as_of`, not now.

  - `state: NodeState`

    What a node is doing.

    - `working`: doing something.
    - `input_required`: waiting on a person (`attention` says what for).
    - `idle`: a session between turns, waiting for its next prompt.
    - `completed`, `failed`, `canceled`: finished.

    - `"working"`

    - `"input_required"`

    - `"idle"`

    - `"completed"`

    - `"failed"`

    - `"canceled"`

  - `summary: string | null`

    Its own latest status line, if it reports one. Untrusted text.

  - `task_counts: TaskCounts`

    Its tasks, by status.

    - `completed: number`

    - `in_progress: number`

    - `open: number`

      Not completed: `pending` and `in_progress`.

    - `pending: number`

    - `total: number`

  - `title: string | null`

    The session's name, as its tool shows it. It can change. Untrusted text.

  - `children?: unknown`

    The nodes directly under it, oldest first: the first 100, and a `url` for the rest. Present only when expanded.

  - `descendants?: unknown`

    Every node under it, at any depth, as one flat list in tree order: each parent before its children, siblings oldest first. The first 1,000, and a `url` for the rest. Present only when expanded.

  - `messages?: Array<Message>`

    Messages it has sent and received, oldest first. Present only when expanded.

    - `id: string`

      The tool's id for the message.

    - `body: string | null`

      The whole message, only if the share was made with body capture on. Untrusted text.

    - `created: number`

      When it was sent, in Unix milliseconds.

    - `direction: "sent" | "received"`

      - `"sent"`

      - `"received"`

    - `peer_id: string | null`

      The node on the other side, when it's one in this graph.

    - `peer_name: string | null`

      The tool's name for the other side, when it isn't a node in this graph.

    - `reply_to_message_id: string | null`

      The message it answers.

    - `summary: string | null`

      Untrusted text.

  - `parent?: Node`

    The parent node. Present only when expanded.

  - `requested_by?: Node`

    The node that requested it. Present only when expanded.

  - `root?: Node`

    The root node. Present only when expanded.

  - `session?: Node`

    The session node. Present only when expanded.

  - `spawns?: Array<Spawn>`

    The children it has asked for, oldest first. Present only when expanded.

    - `agent_type: string | null`

    - `background: boolean`

      If false, the requester is blocked until the call returns.

    - `call_id: string`

      The tool call that asked for it.

    - `child_id: string | null`

      The node it started, once matched. Before the call returns this can be a best guess, corrected when it does.

    - `kind: "agent" | "session"`

      `agent`: an agent in the same session. `session`: a separate session, e.g. `codex exec` run from a shell.

      - `"agent"`

      - `"session"`

    - `purpose: string | null`

      Untrusted text.

    - `requested: number`

      When it was asked for, in Unix milliseconds.

    - `returned: boolean`

      Whether the call has returned.

    - `run: boolean | null`

      Whether it's through `agent-graph run`, when known.

    - `title: string | null`

      For a session, the name it was to be given.

  - `tasks?: Array<Task>`

    Its task list, in the tool's order. Present only when expanded.

    - `id: string`

      The tool's id for the task, unique within the node.

    - `active_text: string | null`

      How the tool words it while it's in progress (`Running the tests`). Untrusted text.

    - `status: "pending" | "in_progress" | "completed"`

      - `"pending"`

      - `"in_progress"`

      - `"completed"`

    - `text: string`

      What the task is. Untrusted text.

  - `waits?: Array<Wait>`

    What it has waited on and is waiting on, oldest first. Present only when expanded.

    - `id: string`

      The wait's id, unique within the node.

    - `ended: number | null`

      When it stopped, in Unix milliseconds.

    - `kind: "spawn" | "explicit"`

      `spawn`: waiting for a child it asked for, until the call returns. `explicit`: any other wait the tool reported.

      - `"spawn"`

      - `"explicit"`

    - `on_id: string | null`

      The node waited on. `null` while a requested child hasn't started.

    - `open: boolean`

      Whether it's still waiting.

    - `reason: string | null`

      Untrusted text.

    - `started: number`

      When it started waiting, in Unix milliseconds.

### Example

```typescript
import AgentGraph from '@chofter/agent-graph-api';

const client = new AgentGraph({
  apiKey: process.env['AGENT_GRAPH_API_KEY'], // This is the default and can be omitted
});

// Automatically fetches more pages as needed.
for await (const node of client.graphs.nodes.search('gph_k3P9xQ2mWt7a', { query: 'query' })) {
  console.log(node.id);
}
```

#### Response

```json
{
  "object": "search_result",
  "url": "/api/v1/graphs/gph_k3P9xQ2mWt7a/nodes/search?query=state%3A%22working%22",
  "as_of_event_id": "evt_01M4G3G4E8Q9R2M5X4T6W8Y0B1",
  "has_more": false,
  "next_page": null,
  "total_count": 1,
  "data": [
    {
      "id": "node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEwL2I4MWMyMmUwOWY3YTNjNTU",
      "object": "node",
      "as_of_event_id": "evt_01M4G3G4E8Q9R2M5X4T6W8Y0B1",
      "graph_id": "gph_k3P9xQ2mWt7a",
      "provider_ref": "claude-code:5f2c1e9a-8d1b-4b6e-9a51-0f6f2c7d3e10/b81c22e09f7a3c55",
      "provider": "claude-code",
      "kind": "agent",
      "parent_id": "node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEwL2EzZmQwN2MyYjE5ZTRkNjE",
      "root_id": "node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEw",
      "session_id": "node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEw",
      "requested_by_id": "node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEwL2EzZmQwN2MyYjE5ZTRkNjE",
      "depth": 2,
      "state": "working",
      "stale": false,
      "attention": null,
      "title": null,
      "summary": null,
      "headline": "Searching for SessionStore imports",
      "purpose": "List the files that import SessionStore",
      "agent_type": "general-purpose",
      "background": false,
      "link_method": null,
      "spawn_call_id": "toolu_01Pq4Ze8sYh2WnVb6XcJ1dRt",
      "cwd": "/Users/dev/work/api",
      "created": 1791540874033,
      "ended": null,
      "last_event": 1791541898692,
      "child_count": 0,
      "descendant_count": 0,
      "background_agent_count": 0,
      "task_counts": {
        "total": 5,
        "pending": 3,
        "in_progress": 1,
        "completed": 1,
        "open": 4
      },
      "blocked": null
    }
  ]
}
```

## Retrieve a node

`client.graphs.nodes.retrieve(stringnodeID, NodeRetrieveParamsparams, RequestOptionsoptions?): Node`

**get** `/graphs/{graph_id}/nodes/{node_id}`

Retrieves a node as it stood at a point in its graph's history, with whatever around it you expand.

**Expandable fields**

| Path           | Adds                                                                                                                          |
| -------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `parent`       | The parent node, beside `parent_id`. Absent for a root.                                                                       |
| `root`         | The root node, beside `root_id`.                                                                                              |
| `session`      | The session node, beside `session_id`.                                                                                        |
| `requested_by` | The node that asked for this one, beside `requested_by_id`.                                                                   |
| `blocked.on`   | The nodes it's waiting on, beside `blocked.on_ids`.                                                                           |
| `children`     | Its children, as a list (the first 100, oldest first, and a `url` for the rest).                                              |
| `descendants`  | Every node under it, as one flat list in tree order (the first 1,000, and a `url` for the rest). See `descendants_max_depth`. |
| `tasks`        | Its task list.                                                                                                                |
| `spawns`       | The children it has asked for.                                                                                                |
| `waits`        | What it has waited on, and is waiting on.                                                                                     |
| `messages`     | Messages it has sent and received.                                                                                            |

Expanded nodes can be expanded in turn: `parent.parent`, `children.children`, `root.tasks`.

**Expanding to a depth.** `expand[]=children.children` nests two levels of children; a path goes at most 4 levels.

**Expanding all the way down.** `expand[]=descendants` returns everything under the node as one flat list, in tree order (each parent before its children), each node with its `parent_id` so you can rebuild the tree in one pass. Limit its depth with `descendants_max_depth`. It holds the first 1,000; if `has_more`, its `url` lists the rest (it includes `as_of`, so every page is the same moment): request it with `starting_after` set to the last node you have.

```bash
# The whole tree under a node…
curl -G https://agentgraph.chofter.com/api/v1/graphs/gph_k3P9xQ2mWt7a/nodes/node_Y2xhdWRlLWNvZGU6NWY… \
  -u $AGENT_GRAPH_KEY: -d "expand[]=descendants"

# …and, if has_more, the next 1,000.
curl -G "https://agentgraph.chofter.com/api/v1/graphs/gph_k3P9xQ2mWt7a/nodes?ancestor_id=…&as_of=…&order=tree" \
  -u $AGENT_GRAPH_KEY: -d limit=1000 -d starting_after=node_…
```

If the node doesn't exist at `as_of` but does later, the error is `404 node_not_yet_created`, with the event that creates it.

### Parameters

- `nodeID: string`

- `params: NodeRetrieveParams`

  - `graph_id: string`

    Path param: The graph's id, `gph_…`. The id in a share's link works too: `/l/k3P9xQ2mWt7a` is `gph_k3P9xQ2mWt7a`.

  - `as_of?: string | number`

    Query param: Answer as of this point in the graph's history: an event id (`evt_…`, the state just after that event) or a Unix time in milliseconds (the state after the last event at or before it). Leave it out for now.

    Must be within the graph's `retention`, else `410 outside_retention_window`. To read several pages or objects at one moment, pass the `as_of_event_id` of the first response.

    - `string`

    - `number`

  - `descendants_max_depth?: number`

    Query param: With `expand[]=descendants`: only include nodes at most this many levels below. Leave it out for all of them.

  - `expand?: Array<string>`

    Query param: Fields to expand. See `Expandable fields` above.

    Send one `expand[]` per path. A path goes at most 4 levels deep; at most 20 paths per request. An expanded field appears beside its `_id` field, which is always there; one not expanded is absent.

  - `ifNoneMatch?: string`

    Header param: The `ETag` of a response you have. If nothing has changed, the reply is `304 Not Modified`, with no body.

### Returns

- `Node`

  A session or an agent, as it stood at `as_of_event_id`.

  Fields ending `_id` or `_ids` are always present, as ids. The fields beside them without that ending (`parent`, `root`, …), and `children`, `descendants`, `tasks`, `spawns`, `waits` and `messages`, appear only when expanded.

  - `id: string`

    Unique identifier for the node within its graph: `node_` and the base64url of `provider_ref`.

  - `agent_type: string | null`

    For an agent, its type, as the tool names it (`Explore`, `general-purpose`, …).

  - `as_of_event_id: string`

    The event this node is shown as of.

  - `attention: string | null`

    While `input_required`: what the person is being asked. Untrusted text.

  - `background: boolean | null`

    Whether it was started in the background, so its requester carries on without waiting. `null` when unknown.

  - `background_agent_count: number`

    Helpers its tool ran on its own between turns (memory extraction, summaries, prompt suggestions). They report nothing about what they're for, so they're counted, not made nodes.

  - `blocked: Blocked | null`

    What stands between a node and carrying on.

  - `child_count: number`

    How many nodes sit directly under it.

  - `created: number`

    When it started (or was first seen), in Unix milliseconds.

  - `cwd: string | null`

    The directory it runs in, when reported.

  - `depth: number`

    How far below its root it is: 0 for a root.

  - `descendant_count: number`

    How many nodes sit under it, at any depth: the size of `descendants`.

  - `ended: number | null`

    When it finished, in Unix milliseconds. `null` while it hasn't.

  - `graph_id: string`

    The graph it belongs to.

  - `headline: string | null`

    One line on what it's doing now: `summary`, else the task in progress. Untrusted text.

  - `kind: NodeKind`

    `session`: one run of a coding agent (Claude Code, Codex, Cursor, …). `agent`: an agent started inside a session, by it or by another agent.

    - `"session"`

    - `"agent"`

  - `last_event: number`

    When it last had an event, in Unix milliseconds.

  - `link_method: "env" | "run" | "process" | "suggested" | null`

    For a session started by another, how that was worked out: `env` (it inherited its parent's identity), `run` (`agent-graph run` started it), `process` (its parent's agent is among its processes) or `suggested` (it took a name its parent suggested).

    - `"env"`

    - `"run"`

    - `"process"`

    - `"suggested"`

  - `object: "node"`

    String representing the object's type: always `node`.

    - `"node"`

  - `parent_id: string | null`

    The node it sits under: the agent or session that started it. `null` for a root.

  - `provider: string`

    The agent tool: `claude-code`, `codex`, `cursor`, or another that reports to Agent Graph.

  - `provider_ref: string`

    The id the agent tool gave it: `<provider>:<session>` for a session, `<provider>:<session>/<agent>` for an agent. Not an id in this API: use it to match the node to the tool's own records.

  - `purpose: string | null`

    Why it was started, from the request that started it. Untrusted text.

  - `requested_by_id: string | null`

    The node whose spawn request started it, when known. Usually the parent; `null` for a session nothing started, or an agent no request was matched to.

  - `root_id: string`

    The top of its tree: follow `parent_id` until it's `null`. A session started by another session is in that session's tree. A root's `root_id` is its own `id`.

  - `session_id: string`

    The session it runs in: itself, for a session; for an agent, the nearest session above it.

  - `spawn_call_id: string | null`

    The tool call, in its requester's session, that started it: matches a `spawns[].call_id` on `requested_by`.

  - `stale: boolean`

    `working`, waiting on nothing, and silent for 30 minutes: it may have hung or crashed. Measured against `as_of`, not now.

  - `state: NodeState`

    What a node is doing.

    - `working`: doing something.
    - `input_required`: waiting on a person (`attention` says what for).
    - `idle`: a session between turns, waiting for its next prompt.
    - `completed`, `failed`, `canceled`: finished.

    - `"working"`

    - `"input_required"`

    - `"idle"`

    - `"completed"`

    - `"failed"`

    - `"canceled"`

  - `summary: string | null`

    Its own latest status line, if it reports one. Untrusted text.

  - `task_counts: TaskCounts`

    Its tasks, by status.

    - `completed: number`

    - `in_progress: number`

    - `open: number`

      Not completed: `pending` and `in_progress`.

    - `pending: number`

    - `total: number`

  - `title: string | null`

    The session's name, as its tool shows it. It can change. Untrusted text.

  - `children?: unknown`

    The nodes directly under it, oldest first: the first 100, and a `url` for the rest. Present only when expanded.

  - `descendants?: unknown`

    Every node under it, at any depth, as one flat list in tree order: each parent before its children, siblings oldest first. The first 1,000, and a `url` for the rest. Present only when expanded.

  - `messages?: Array<Message>`

    Messages it has sent and received, oldest first. Present only when expanded.

    - `id: string`

      The tool's id for the message.

    - `body: string | null`

      The whole message, only if the share was made with body capture on. Untrusted text.

    - `created: number`

      When it was sent, in Unix milliseconds.

    - `direction: "sent" | "received"`

      - `"sent"`

      - `"received"`

    - `peer_id: string | null`

      The node on the other side, when it's one in this graph.

    - `peer_name: string | null`

      The tool's name for the other side, when it isn't a node in this graph.

    - `reply_to_message_id: string | null`

      The message it answers.

    - `summary: string | null`

      Untrusted text.

  - `parent?: Node`

    The parent node. Present only when expanded.

  - `requested_by?: Node`

    The node that requested it. Present only when expanded.

  - `root?: Node`

    The root node. Present only when expanded.

  - `session?: Node`

    The session node. Present only when expanded.

  - `spawns?: Array<Spawn>`

    The children it has asked for, oldest first. Present only when expanded.

    - `agent_type: string | null`

    - `background: boolean`

      If false, the requester is blocked until the call returns.

    - `call_id: string`

      The tool call that asked for it.

    - `child_id: string | null`

      The node it started, once matched. Before the call returns this can be a best guess, corrected when it does.

    - `kind: "agent" | "session"`

      `agent`: an agent in the same session. `session`: a separate session, e.g. `codex exec` run from a shell.

      - `"agent"`

      - `"session"`

    - `purpose: string | null`

      Untrusted text.

    - `requested: number`

      When it was asked for, in Unix milliseconds.

    - `returned: boolean`

      Whether the call has returned.

    - `run: boolean | null`

      Whether it's through `agent-graph run`, when known.

    - `title: string | null`

      For a session, the name it was to be given.

  - `tasks?: Array<Task>`

    Its task list, in the tool's order. Present only when expanded.

    - `id: string`

      The tool's id for the task, unique within the node.

    - `active_text: string | null`

      How the tool words it while it's in progress (`Running the tests`). Untrusted text.

    - `status: "pending" | "in_progress" | "completed"`

      - `"pending"`

      - `"in_progress"`

      - `"completed"`

    - `text: string`

      What the task is. Untrusted text.

  - `waits?: Array<Wait>`

    What it has waited on and is waiting on, oldest first. Present only when expanded.

    - `id: string`

      The wait's id, unique within the node.

    - `ended: number | null`

      When it stopped, in Unix milliseconds.

    - `kind: "spawn" | "explicit"`

      `spawn`: waiting for a child it asked for, until the call returns. `explicit`: any other wait the tool reported.

      - `"spawn"`

      - `"explicit"`

    - `on_id: string | null`

      The node waited on. `null` while a requested child hasn't started.

    - `open: boolean`

      Whether it's still waiting.

    - `reason: string | null`

      Untrusted text.

    - `started: number`

      When it started waiting, in Unix milliseconds.

### Example

```typescript
import AgentGraph from '@chofter/agent-graph-api';

const client = new AgentGraph({
  apiKey: process.env['AGENT_GRAPH_API_KEY'], // This is the default and can be omitted
});

const node = await client.graphs.nodes.retrieve(
  'node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEwL2EzZmQwN2MyYjE5ZTRkNjE',
  { graph_id: 'gph_k3P9xQ2mWt7a' },
);

console.log(node.id);
```

#### Response

```json
{
  "id": "node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEwL2EzZmQwN2MyYjE5ZTRkNjE",
  "object": "node",
  "as_of_event_id": "evt_01M4G3G4E8Q9R2M5X4T6W8Y0B1",
  "graph_id": "gph_k3P9xQ2mWt7a",
  "provider_ref": "claude-code:5f2c1e9a-8d1b-4b6e-9a51-0f6f2c7d3e10/a3fd07c2b19e4d61",
  "provider": "claude-code",
  "kind": "agent",
  "parent_id": "node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEw",
  "root_id": "node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEw",
  "session_id": "node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEw",
  "requested_by_id": "node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEw",
  "depth": 1,
  "state": "working",
  "stale": false,
  "attention": null,
  "title": null,
  "summary": "Reading the auth middleware",
  "headline": "Reading the auth middleware",
  "purpose": "Find every caller of the old session store",
  "agent_type": "Explore",
  "background": false,
  "link_method": null,
  "spawn_call_id": "toolu_01HxV9qT3mJb7d2KcL5nRw8E",
  "cwd": "/Users/dev/work/api",
  "created": 1791540843221,
  "ended": null,
  "last_event": 1791541903812,
  "child_count": 1,
  "descendant_count": 1,
  "background_agent_count": 0,
  "task_counts": {
    "total": 4,
    "pending": 1,
    "in_progress": 1,
    "completed": 2,
    "open": 2
  },
  "blocked": {
    "on_ids": [
      "node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEwL2I4MWMyMmUwOWY3YTNjNTU"
    ],
    "starting": 0,
    "node_count": 1,
    "open_tasks": 5,
    "cycle": false
  }
}
```

## Domain Types

### Blocked

- `Blocked`

  What stands between a node and carrying on.

  - `cycle: boolean`

    Its waits lead back to itself: nothing in the loop can ever finish.

  - `node_count: number`

    Every unfinished node it's waiting on, directly or through others, with their children.

  - `on_ids: Array<string>`

    The nodes it's waiting on directly.

  - `open_tasks: number`

    Open tasks across those nodes: the work left before it can carry on.

  - `starting: number`

    Children it has asked for that haven't started yet.

  - `on?: Array<Node>`

    Those nodes. Present only when `blocked.on` is expanded.

    - `Node`

      A session or an agent, as it stood at `as_of_event_id`.

      Fields ending `_id` or `_ids` are always present, as ids. The fields beside them without that ending (`parent`, `root`, …), and `children`, `descendants`, `tasks`, `spawns`, `waits` and `messages`, appear only when expanded.

      - `id: string`

        Unique identifier for the node within its graph: `node_` and the base64url of `provider_ref`.

      - `agent_type: string | null`

        For an agent, its type, as the tool names it (`Explore`, `general-purpose`, …).

      - `as_of_event_id: string`

        The event this node is shown as of.

      - `attention: string | null`

        While `input_required`: what the person is being asked. Untrusted text.

      - `background: boolean | null`

        Whether it was started in the background, so its requester carries on without waiting. `null` when unknown.

      - `background_agent_count: number`

        Helpers its tool ran on its own between turns (memory extraction, summaries, prompt suggestions). They report nothing about what they're for, so they're counted, not made nodes.

      - `blocked: Blocked | null`

        What stands between a node and carrying on.

      - `child_count: number`

        How many nodes sit directly under it.

      - `created: number`

        When it started (or was first seen), in Unix milliseconds.

      - `cwd: string | null`

        The directory it runs in, when reported.

      - `depth: number`

        How far below its root it is: 0 for a root.

      - `descendant_count: number`

        How many nodes sit under it, at any depth: the size of `descendants`.

      - `ended: number | null`

        When it finished, in Unix milliseconds. `null` while it hasn't.

      - `graph_id: string`

        The graph it belongs to.

      - `headline: string | null`

        One line on what it's doing now: `summary`, else the task in progress. Untrusted text.

      - `kind: NodeKind`

        `session`: one run of a coding agent (Claude Code, Codex, Cursor, …). `agent`: an agent started inside a session, by it or by another agent.

        - `"session"`

        - `"agent"`

      - `last_event: number`

        When it last had an event, in Unix milliseconds.

      - `link_method: "env" | "run" | "process" | "suggested" | null`

        For a session started by another, how that was worked out: `env` (it inherited its parent's identity), `run` (`agent-graph run` started it), `process` (its parent's agent is among its processes) or `suggested` (it took a name its parent suggested).

        - `"env"`

        - `"run"`

        - `"process"`

        - `"suggested"`

      - `object: "node"`

        String representing the object's type: always `node`.

        - `"node"`

      - `parent_id: string | null`

        The node it sits under: the agent or session that started it. `null` for a root.

      - `provider: string`

        The agent tool: `claude-code`, `codex`, `cursor`, or another that reports to Agent Graph.

      - `provider_ref: string`

        The id the agent tool gave it: `<provider>:<session>` for a session, `<provider>:<session>/<agent>` for an agent. Not an id in this API: use it to match the node to the tool's own records.

      - `purpose: string | null`

        Why it was started, from the request that started it. Untrusted text.

      - `requested_by_id: string | null`

        The node whose spawn request started it, when known. Usually the parent; `null` for a session nothing started, or an agent no request was matched to.

      - `root_id: string`

        The top of its tree: follow `parent_id` until it's `null`. A session started by another session is in that session's tree. A root's `root_id` is its own `id`.

      - `session_id: string`

        The session it runs in: itself, for a session; for an agent, the nearest session above it.

      - `spawn_call_id: string | null`

        The tool call, in its requester's session, that started it: matches a `spawns[].call_id` on `requested_by`.

      - `stale: boolean`

        `working`, waiting on nothing, and silent for 30 minutes: it may have hung or crashed. Measured against `as_of`, not now.

      - `state: NodeState`

        What a node is doing.

        - `working`: doing something.
        - `input_required`: waiting on a person (`attention` says what for).
        - `idle`: a session between turns, waiting for its next prompt.
        - `completed`, `failed`, `canceled`: finished.

        - `"working"`

        - `"input_required"`

        - `"idle"`

        - `"completed"`

        - `"failed"`

        - `"canceled"`

      - `summary: string | null`

        Its own latest status line, if it reports one. Untrusted text.

      - `task_counts: TaskCounts`

        Its tasks, by status.

        - `completed: number`

        - `in_progress: number`

        - `open: number`

          Not completed: `pending` and `in_progress`.

        - `pending: number`

        - `total: number`

      - `title: string | null`

        The session's name, as its tool shows it. It can change. Untrusted text.

      - `children?: unknown`

        The nodes directly under it, oldest first: the first 100, and a `url` for the rest. Present only when expanded.

      - `descendants?: unknown`

        Every node under it, at any depth, as one flat list in tree order: each parent before its children, siblings oldest first. The first 1,000, and a `url` for the rest. Present only when expanded.

      - `messages?: Array<Message>`

        Messages it has sent and received, oldest first. Present only when expanded.

        - `id: string`

          The tool's id for the message.

        - `body: string | null`

          The whole message, only if the share was made with body capture on. Untrusted text.

        - `created: number`

          When it was sent, in Unix milliseconds.

        - `direction: "sent" | "received"`

          - `"sent"`

          - `"received"`

        - `peer_id: string | null`

          The node on the other side, when it's one in this graph.

        - `peer_name: string | null`

          The tool's name for the other side, when it isn't a node in this graph.

        - `reply_to_message_id: string | null`

          The message it answers.

        - `summary: string | null`

          Untrusted text.

      - `parent?: Node`

        The parent node. Present only when expanded.

      - `requested_by?: Node`

        The node that requested it. Present only when expanded.

      - `root?: Node`

        The root node. Present only when expanded.

      - `session?: Node`

        The session node. Present only when expanded.

      - `spawns?: Array<Spawn>`

        The children it has asked for, oldest first. Present only when expanded.

        - `agent_type: string | null`

        - `background: boolean`

          If false, the requester is blocked until the call returns.

        - `call_id: string`

          The tool call that asked for it.

        - `child_id: string | null`

          The node it started, once matched. Before the call returns this can be a best guess, corrected when it does.

        - `kind: "agent" | "session"`

          `agent`: an agent in the same session. `session`: a separate session, e.g. `codex exec` run from a shell.

          - `"agent"`

          - `"session"`

        - `purpose: string | null`

          Untrusted text.

        - `requested: number`

          When it was asked for, in Unix milliseconds.

        - `returned: boolean`

          Whether the call has returned.

        - `run: boolean | null`

          Whether it's through `agent-graph run`, when known.

        - `title: string | null`

          For a session, the name it was to be given.

      - `tasks?: Array<Task>`

        Its task list, in the tool's order. Present only when expanded.

        - `id: string`

          The tool's id for the task, unique within the node.

        - `active_text: string | null`

          How the tool words it while it's in progress (`Running the tests`). Untrusted text.

        - `status: "pending" | "in_progress" | "completed"`

          - `"pending"`

          - `"in_progress"`

          - `"completed"`

        - `text: string`

          What the task is. Untrusted text.

      - `waits?: Array<Wait>`

        What it has waited on and is waiting on, oldest first. Present only when expanded.

        - `id: string`

          The wait's id, unique within the node.

        - `ended: number | null`

          When it stopped, in Unix milliseconds.

        - `kind: "spawn" | "explicit"`

          `spawn`: waiting for a child it asked for, until the call returns. `explicit`: any other wait the tool reported.

          - `"spawn"`

          - `"explicit"`

        - `on_id: string | null`

          The node waited on. `null` while a requested child hasn't started.

        - `open: boolean`

          Whether it's still waiting.

        - `reason: string | null`

          Untrusted text.

        - `started: number`

          When it started waiting, in Unix milliseconds.

### Message

- `Message`

  A message between two nodes.

  - `id: string`

    The tool's id for the message.

  - `body: string | null`

    The whole message, only if the share was made with body capture on. Untrusted text.

  - `created: number`

    When it was sent, in Unix milliseconds.

  - `direction: "sent" | "received"`

    - `"sent"`

    - `"received"`

  - `peer_id: string | null`

    The node on the other side, when it's one in this graph.

  - `peer_name: string | null`

    The tool's name for the other side, when it isn't a node in this graph.

  - `reply_to_message_id: string | null`

    The message it answers.

  - `summary: string | null`

    Untrusted text.

### Node

- `Node`

  A session or an agent, as it stood at `as_of_event_id`.

  Fields ending `_id` or `_ids` are always present, as ids. The fields beside them without that ending (`parent`, `root`, …), and `children`, `descendants`, `tasks`, `spawns`, `waits` and `messages`, appear only when expanded.

  - `id: string`

    Unique identifier for the node within its graph: `node_` and the base64url of `provider_ref`.

  - `agent_type: string | null`

    For an agent, its type, as the tool names it (`Explore`, `general-purpose`, …).

  - `as_of_event_id: string`

    The event this node is shown as of.

  - `attention: string | null`

    While `input_required`: what the person is being asked. Untrusted text.

  - `background: boolean | null`

    Whether it was started in the background, so its requester carries on without waiting. `null` when unknown.

  - `background_agent_count: number`

    Helpers its tool ran on its own between turns (memory extraction, summaries, prompt suggestions). They report nothing about what they're for, so they're counted, not made nodes.

  - `blocked: Blocked | null`

    What stands between a node and carrying on.

  - `child_count: number`

    How many nodes sit directly under it.

  - `created: number`

    When it started (or was first seen), in Unix milliseconds.

  - `cwd: string | null`

    The directory it runs in, when reported.

  - `depth: number`

    How far below its root it is: 0 for a root.

  - `descendant_count: number`

    How many nodes sit under it, at any depth: the size of `descendants`.

  - `ended: number | null`

    When it finished, in Unix milliseconds. `null` while it hasn't.

  - `graph_id: string`

    The graph it belongs to.

  - `headline: string | null`

    One line on what it's doing now: `summary`, else the task in progress. Untrusted text.

  - `kind: NodeKind`

    `session`: one run of a coding agent (Claude Code, Codex, Cursor, …). `agent`: an agent started inside a session, by it or by another agent.

    - `"session"`

    - `"agent"`

  - `last_event: number`

    When it last had an event, in Unix milliseconds.

  - `link_method: "env" | "run" | "process" | "suggested" | null`

    For a session started by another, how that was worked out: `env` (it inherited its parent's identity), `run` (`agent-graph run` started it), `process` (its parent's agent is among its processes) or `suggested` (it took a name its parent suggested).

    - `"env"`

    - `"run"`

    - `"process"`

    - `"suggested"`

  - `object: "node"`

    String representing the object's type: always `node`.

    - `"node"`

  - `parent_id: string | null`

    The node it sits under: the agent or session that started it. `null` for a root.

  - `provider: string`

    The agent tool: `claude-code`, `codex`, `cursor`, or another that reports to Agent Graph.

  - `provider_ref: string`

    The id the agent tool gave it: `<provider>:<session>` for a session, `<provider>:<session>/<agent>` for an agent. Not an id in this API: use it to match the node to the tool's own records.

  - `purpose: string | null`

    Why it was started, from the request that started it. Untrusted text.

  - `requested_by_id: string | null`

    The node whose spawn request started it, when known. Usually the parent; `null` for a session nothing started, or an agent no request was matched to.

  - `root_id: string`

    The top of its tree: follow `parent_id` until it's `null`. A session started by another session is in that session's tree. A root's `root_id` is its own `id`.

  - `session_id: string`

    The session it runs in: itself, for a session; for an agent, the nearest session above it.

  - `spawn_call_id: string | null`

    The tool call, in its requester's session, that started it: matches a `spawns[].call_id` on `requested_by`.

  - `stale: boolean`

    `working`, waiting on nothing, and silent for 30 minutes: it may have hung or crashed. Measured against `as_of`, not now.

  - `state: NodeState`

    What a node is doing.

    - `working`: doing something.
    - `input_required`: waiting on a person (`attention` says what for).
    - `idle`: a session between turns, waiting for its next prompt.
    - `completed`, `failed`, `canceled`: finished.

    - `"working"`

    - `"input_required"`

    - `"idle"`

    - `"completed"`

    - `"failed"`

    - `"canceled"`

  - `summary: string | null`

    Its own latest status line, if it reports one. Untrusted text.

  - `task_counts: TaskCounts`

    Its tasks, by status.

    - `completed: number`

    - `in_progress: number`

    - `open: number`

      Not completed: `pending` and `in_progress`.

    - `pending: number`

    - `total: number`

  - `title: string | null`

    The session's name, as its tool shows it. It can change. Untrusted text.

  - `children?: unknown`

    The nodes directly under it, oldest first: the first 100, and a `url` for the rest. Present only when expanded.

  - `descendants?: unknown`

    Every node under it, at any depth, as one flat list in tree order: each parent before its children, siblings oldest first. The first 1,000, and a `url` for the rest. Present only when expanded.

  - `messages?: Array<Message>`

    Messages it has sent and received, oldest first. Present only when expanded.

    - `id: string`

      The tool's id for the message.

    - `body: string | null`

      The whole message, only if the share was made with body capture on. Untrusted text.

    - `created: number`

      When it was sent, in Unix milliseconds.

    - `direction: "sent" | "received"`

      - `"sent"`

      - `"received"`

    - `peer_id: string | null`

      The node on the other side, when it's one in this graph.

    - `peer_name: string | null`

      The tool's name for the other side, when it isn't a node in this graph.

    - `reply_to_message_id: string | null`

      The message it answers.

    - `summary: string | null`

      Untrusted text.

  - `parent?: Node`

    The parent node. Present only when expanded.

  - `requested_by?: Node`

    The node that requested it. Present only when expanded.

  - `root?: Node`

    The root node. Present only when expanded.

  - `session?: Node`

    The session node. Present only when expanded.

  - `spawns?: Array<Spawn>`

    The children it has asked for, oldest first. Present only when expanded.

    - `agent_type: string | null`

    - `background: boolean`

      If false, the requester is blocked until the call returns.

    - `call_id: string`

      The tool call that asked for it.

    - `child_id: string | null`

      The node it started, once matched. Before the call returns this can be a best guess, corrected when it does.

    - `kind: "agent" | "session"`

      `agent`: an agent in the same session. `session`: a separate session, e.g. `codex exec` run from a shell.

      - `"agent"`

      - `"session"`

    - `purpose: string | null`

      Untrusted text.

    - `requested: number`

      When it was asked for, in Unix milliseconds.

    - `returned: boolean`

      Whether the call has returned.

    - `run: boolean | null`

      Whether it's through `agent-graph run`, when known.

    - `title: string | null`

      For a session, the name it was to be given.

  - `tasks?: Array<Task>`

    Its task list, in the tool's order. Present only when expanded.

    - `id: string`

      The tool's id for the task, unique within the node.

    - `active_text: string | null`

      How the tool words it while it's in progress (`Running the tests`). Untrusted text.

    - `status: "pending" | "in_progress" | "completed"`

      - `"pending"`

      - `"in_progress"`

      - `"completed"`

    - `text: string`

      What the task is. Untrusted text.

  - `waits?: Array<Wait>`

    What it has waited on and is waiting on, oldest first. Present only when expanded.

    - `id: string`

      The wait's id, unique within the node.

    - `ended: number | null`

      When it stopped, in Unix milliseconds.

    - `kind: "spawn" | "explicit"`

      `spawn`: waiting for a child it asked for, until the call returns. `explicit`: any other wait the tool reported.

      - `"spawn"`

      - `"explicit"`

    - `on_id: string | null`

      The node waited on. `null` while a requested child hasn't started.

    - `open: boolean`

      Whether it's still waiting.

    - `reason: string | null`

      Untrusted text.

    - `started: number`

      When it started waiting, in Unix milliseconds.

### Node Kind

- `NodeKind = "session" | "agent"`

  `session`: one run of a coding agent (Claude Code, Codex, Cursor, …). `agent`: an agent started inside a session, by it or by another agent.

  - `"session"`

  - `"agent"`

### Node State

- `NodeState = "working" | "input_required" | "idle" | 3 more`

  What a node is doing.

  - `working`: doing something.
  - `input_required`: waiting on a person (`attention` says what for).
  - `idle`: a session between turns, waiting for its next prompt.
  - `completed`, `failed`, `canceled`: finished.

  - `"working"`

  - `"input_required"`

  - `"idle"`

  - `"completed"`

  - `"failed"`

  - `"canceled"`

### Spawn

- `Spawn`

  A child a node asked for.

  - `agent_type: string | null`

  - `background: boolean`

    If false, the requester is blocked until the call returns.

  - `call_id: string`

    The tool call that asked for it.

  - `child_id: string | null`

    The node it started, once matched. Before the call returns this can be a best guess, corrected when it does.

  - `kind: "agent" | "session"`

    `agent`: an agent in the same session. `session`: a separate session, e.g. `codex exec` run from a shell.

    - `"agent"`

    - `"session"`

  - `purpose: string | null`

    Untrusted text.

  - `requested: number`

    When it was asked for, in Unix milliseconds.

  - `returned: boolean`

    Whether the call has returned.

  - `run: boolean | null`

    Whether it's through `agent-graph run`, when known.

  - `title: string | null`

    For a session, the name it was to be given.

### Task

- `Task`

  One item on a node's task list.

  - `id: string`

    The tool's id for the task, unique within the node.

  - `active_text: string | null`

    How the tool words it while it's in progress (`Running the tests`). Untrusted text.

  - `status: "pending" | "in_progress" | "completed"`

    - `"pending"`

    - `"in_progress"`

    - `"completed"`

  - `text: string`

    What the task is. Untrusted text.

### Task Counts

- `TaskCounts`

  Its tasks, by status.

  - `completed: number`

  - `in_progress: number`

  - `open: number`

    Not completed: `pending` and `in_progress`.

  - `pending: number`

  - `total: number`

### Wait

- `Wait`

  A time a node waited on another.

  - `id: string`

    The wait's id, unique within the node.

  - `ended: number | null`

    When it stopped, in Unix milliseconds.

  - `kind: "spawn" | "explicit"`

    `spawn`: waiting for a child it asked for, until the call returns. `explicit`: any other wait the tool reported.

    - `"spawn"`

    - `"explicit"`

  - `on_id: string | null`

    The node waited on. `null` while a requested child hasn't started.

  - `open: boolean`

    Whether it's still waiting.

  - `reason: string | null`

    Untrusted text.

  - `started: number`

    When it started waiting, in Unix milliseconds.

# Events

## List events

`client.graphs.events.list(stringgraphID, EventListParamsparams?, RequestOptionsoptions?): CursorPage<Event>`

**get** `/graphs/{graph_id}/events`

Returns a graph's events, newest first by default, from its retained history.

**Following a graph.** Remember the newest event id you've seen, and list with `order=asc` and `starting_after` set to it: you get only what's happened since, each event with its `changes`. No need to fetch the graph again. If you fall so far behind that the event is no longer retained, that's `410 outside_retention_window`, and the error's `first_event_id` is the oldest event still held: fetch the graph again, and follow on from there.

**Replaying history.** List with `order=asc` from the start of `retention`, or from any event; `expand[]=data.node` adds each event's node as it stood just after it.

### Parameters

- `graphID: string`

- `params: EventListParams`

  - `created?: Created`

    Query param: Only events that happened in this range.

    - `gt?: number`

      After this time.

    - `gte?: number`

      At or after this time.

    - `lt?: number`

      Before this time.

    - `lte?: number`

      At or before this time.

  - `ending_before?: string`

    Query param: A cursor: an object id from the list. Returns the page before it. Pass the first `id` of the page you have to get the previous one.

  - `expand?: Array<string>`

    Query param: Fields to expand on each event: `expand[]=data.node` (the node just after the event), and its own fields, `data.node.parent`.

    Send one `expand[]` per path. A path goes at most 4 levels deep; at most 20 paths per request. An expanded field appears beside its `_id` field, which is always there; one not expanded is absent.

  - `limit?: number`

    Query param: How many objects to return, from 1 to 1,000.

  - `node_id?: string`

    Query param: Only events about this node.

  - `order?: "desc" | "asc"`

    Query param: `desc`: newest first. `asc`: oldest first.

    - `"desc"`

    - `"asc"`

  - `root_id?: string`

    Query param: Only events about nodes in this tree.

  - `starting_after?: string`

    Query param: A cursor: an object id from the list. Returns the page after it. Pass the last `id` of the page you have to get the next one.

  - `type?: Array<string>`

    Query param: Only events of these types. Send one `type[]` per type; a trailing `*` matches a family: `type[]=agent.*`.

  - `ifNoneMatch?: string`

    Header param: The `ETag` of a response you have. If nothing has changed, the reply is `304 Not Modified`, with no body.

### Returns

- `Event`

  One thing that happened in a graph, and every change it made to its nodes.

  - `id: string`

    Unique identifier for the event, `evt_…`. Sorts by time.

  - `changes: Array<NodeChange>`

    Every node this event changed, starting with `node_id`. Empty when it changed nothing (a repeated status, say).

    - `created: boolean`

      Whether the event created it. Its `fields` then go from `null`.

    - `fields: Record<string, FieldChange>`

      Each [node](#tag/Nodes) field that changed, by name, from what to what. Derived fields count (`child_count`, `descendant_count`, `blocked`, `headline`, `task_counts`). `stale` doesn't, as it changes with time rather than events, and nor do expandable fields.

      - `from: unknown`

        The value before the event.

      - `to: unknown`

        The value after it.

    - `node_id: string`

  - `created: number`

    When it happened, in Unix milliseconds.

  - `graph_id: string`

  - `node_id: string`

    The node it's about.

  - `object: "event"`

    String representing the object's type: always `event`.

    - `"event"`

  - `payload: Record<string, unknown>`

    What the agent tool reported, by `type`: see [event types](#tag/Events). Ids in it are the provider's own. Text in it is untrusted.

  - `source: EventSource`

    What reported the event.

    - `adapter: string | null`

      The Agent Graph adapter that recorded it, and its version, when it says.

    - `provider: string`

    - `provider_version: string | null`

      The agent tool's version, when it says.

  - `type: "session.started" | "session.ended" | "agent.spawned" | 11 more`

    What happened. See [event types](#tag/Events). More may be added: ignore those you don't recognise.

    - `"session.started"`

    - `"session.ended"`

    - `"agent.spawned"`

    - `"agent.finished"`

    - `"status"`

    - `"tasks.updated"`

    - `"task.upserted"`

    - `"task.deleted"`

    - `"spawn.requested"`

    - `"spawn.returned"`

    - `"wait.started"`

    - `"wait.ended"`

    - `"message.sent"`

    - `"activity"`

  - `node?: Node`

    That node, as it stood just after this event. Present only when expanded.

    - `id: string`

      Unique identifier for the node within its graph: `node_` and the base64url of `provider_ref`.

    - `agent_type: string | null`

      For an agent, its type, as the tool names it (`Explore`, `general-purpose`, …).

    - `as_of_event_id: string`

      The event this node is shown as of.

    - `attention: string | null`

      While `input_required`: what the person is being asked. Untrusted text.

    - `background: boolean | null`

      Whether it was started in the background, so its requester carries on without waiting. `null` when unknown.

    - `background_agent_count: number`

      Helpers its tool ran on its own between turns (memory extraction, summaries, prompt suggestions). They report nothing about what they're for, so they're counted, not made nodes.

    - `blocked: Blocked | null`

      What stands between a node and carrying on.

    - `child_count: number`

      How many nodes sit directly under it.

    - `created: number`

      When it started (or was first seen), in Unix milliseconds.

    - `cwd: string | null`

      The directory it runs in, when reported.

    - `depth: number`

      How far below its root it is: 0 for a root.

    - `descendant_count: number`

      How many nodes sit under it, at any depth: the size of `descendants`.

    - `ended: number | null`

      When it finished, in Unix milliseconds. `null` while it hasn't.

    - `graph_id: string`

      The graph it belongs to.

    - `headline: string | null`

      One line on what it's doing now: `summary`, else the task in progress. Untrusted text.

    - `kind: NodeKind`

      `session`: one run of a coding agent (Claude Code, Codex, Cursor, …). `agent`: an agent started inside a session, by it or by another agent.

      - `"session"`

      - `"agent"`

    - `last_event: number`

      When it last had an event, in Unix milliseconds.

    - `link_method: "env" | "run" | "process" | "suggested" | null`

      For a session started by another, how that was worked out: `env` (it inherited its parent's identity), `run` (`agent-graph run` started it), `process` (its parent's agent is among its processes) or `suggested` (it took a name its parent suggested).

      - `"env"`

      - `"run"`

      - `"process"`

      - `"suggested"`

    - `object: "node"`

      String representing the object's type: always `node`.

      - `"node"`

    - `parent_id: string | null`

      The node it sits under: the agent or session that started it. `null` for a root.

    - `provider: string`

      The agent tool: `claude-code`, `codex`, `cursor`, or another that reports to Agent Graph.

    - `provider_ref: string`

      The id the agent tool gave it: `<provider>:<session>` for a session, `<provider>:<session>/<agent>` for an agent. Not an id in this API: use it to match the node to the tool's own records.

    - `purpose: string | null`

      Why it was started, from the request that started it. Untrusted text.

    - `requested_by_id: string | null`

      The node whose spawn request started it, when known. Usually the parent; `null` for a session nothing started, or an agent no request was matched to.

    - `root_id: string`

      The top of its tree: follow `parent_id` until it's `null`. A session started by another session is in that session's tree. A root's `root_id` is its own `id`.

    - `session_id: string`

      The session it runs in: itself, for a session; for an agent, the nearest session above it.

    - `spawn_call_id: string | null`

      The tool call, in its requester's session, that started it: matches a `spawns[].call_id` on `requested_by`.

    - `stale: boolean`

      `working`, waiting on nothing, and silent for 30 minutes: it may have hung or crashed. Measured against `as_of`, not now.

    - `state: NodeState`

      What a node is doing.

      - `working`: doing something.
      - `input_required`: waiting on a person (`attention` says what for).
      - `idle`: a session between turns, waiting for its next prompt.
      - `completed`, `failed`, `canceled`: finished.

      - `"working"`

      - `"input_required"`

      - `"idle"`

      - `"completed"`

      - `"failed"`

      - `"canceled"`

    - `summary: string | null`

      Its own latest status line, if it reports one. Untrusted text.

    - `task_counts: TaskCounts`

      Its tasks, by status.

      - `completed: number`

      - `in_progress: number`

      - `open: number`

        Not completed: `pending` and `in_progress`.

      - `pending: number`

      - `total: number`

    - `title: string | null`

      The session's name, as its tool shows it. It can change. Untrusted text.

    - `children?: unknown`

      The nodes directly under it, oldest first: the first 100, and a `url` for the rest. Present only when expanded.

    - `descendants?: unknown`

      Every node under it, at any depth, as one flat list in tree order: each parent before its children, siblings oldest first. The first 1,000, and a `url` for the rest. Present only when expanded.

    - `messages?: Array<Message>`

      Messages it has sent and received, oldest first. Present only when expanded.

      - `id: string`

        The tool's id for the message.

      - `body: string | null`

        The whole message, only if the share was made with body capture on. Untrusted text.

      - `created: number`

        When it was sent, in Unix milliseconds.

      - `direction: "sent" | "received"`

        - `"sent"`

        - `"received"`

      - `peer_id: string | null`

        The node on the other side, when it's one in this graph.

      - `peer_name: string | null`

        The tool's name for the other side, when it isn't a node in this graph.

      - `reply_to_message_id: string | null`

        The message it answers.

      - `summary: string | null`

        Untrusted text.

    - `parent?: Node`

      The parent node. Present only when expanded.

    - `requested_by?: Node`

      The node that requested it. Present only when expanded.

    - `root?: Node`

      The root node. Present only when expanded.

    - `session?: Node`

      The session node. Present only when expanded.

    - `spawns?: Array<Spawn>`

      The children it has asked for, oldest first. Present only when expanded.

      - `agent_type: string | null`

      - `background: boolean`

        If false, the requester is blocked until the call returns.

      - `call_id: string`

        The tool call that asked for it.

      - `child_id: string | null`

        The node it started, once matched. Before the call returns this can be a best guess, corrected when it does.

      - `kind: "agent" | "session"`

        `agent`: an agent in the same session. `session`: a separate session, e.g. `codex exec` run from a shell.

        - `"agent"`

        - `"session"`

      - `purpose: string | null`

        Untrusted text.

      - `requested: number`

        When it was asked for, in Unix milliseconds.

      - `returned: boolean`

        Whether the call has returned.

      - `run: boolean | null`

        Whether it's through `agent-graph run`, when known.

      - `title: string | null`

        For a session, the name it was to be given.

    - `tasks?: Array<Task>`

      Its task list, in the tool's order. Present only when expanded.

      - `id: string`

        The tool's id for the task, unique within the node.

      - `active_text: string | null`

        How the tool words it while it's in progress (`Running the tests`). Untrusted text.

      - `status: "pending" | "in_progress" | "completed"`

        - `"pending"`

        - `"in_progress"`

        - `"completed"`

      - `text: string`

        What the task is. Untrusted text.

    - `waits?: Array<Wait>`

      What it has waited on and is waiting on, oldest first. Present only when expanded.

      - `id: string`

        The wait's id, unique within the node.

      - `ended: number | null`

        When it stopped, in Unix milliseconds.

      - `kind: "spawn" | "explicit"`

        `spawn`: waiting for a child it asked for, until the call returns. `explicit`: any other wait the tool reported.

        - `"spawn"`

        - `"explicit"`

      - `on_id: string | null`

        The node waited on. `null` while a requested child hasn't started.

      - `open: boolean`

        Whether it's still waiting.

      - `reason: string | null`

        Untrusted text.

      - `started: number`

        When it started waiting, in Unix milliseconds.

### Example

```typescript
import AgentGraph from '@chofter/agent-graph-api';

const client = new AgentGraph({
  apiKey: process.env['AGENT_GRAPH_API_KEY'], // This is the default and can be omitted
});

// Automatically fetches more pages as needed.
for await (const event of client.graphs.events.list('gph_k3P9xQ2mWt7a')) {
  console.log(event.id);
}
```

#### Response

```json
{
  "object": "list",
  "url": "/api/v1/graphs/gph_k3P9xQ2mWt7a/events",
  "as_of_event_id": "evt_01M4G3G4E8Q9R2M5X4T6W8Y0B1",
  "has_more": true,
  "data": [
    {
      "id": "evt_01M4G3G0E4H6TW2C9Z5RB1KQ0E",
      "object": "event",
      "type": "status",
      "created": 1791541903812,
      "graph_id": "gph_k3P9xQ2mWt7a",
      "node_id": "node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEwL2EzZmQwN2MyYjE5ZTRkNjE",
      "source": {
        "provider": "claude-code",
        "provider_version": "2.1.260",
        "adapter": "claude-code@1"
      },
      "payload": {
        "state": "working",
        "summary": "Reading the auth middleware"
      },
      "changes": [
        {
          "node_id": "node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEwL2EzZmQwN2MyYjE5ZTRkNjE",
          "created": false,
          "fields": {
            "summary": {
              "from": null,
              "to": "Reading the auth middleware"
            },
            "headline": {
              "from": "Searching for callers",
              "to": "Reading the auth middleware"
            }
          }
        }
      ]
    },
    {
      "id": "evt_01M4G2FMPN7KD3N1PZ0QX8RM4A",
      "object": "event",
      "type": "agent.spawned",
      "created": 1791540843221,
      "graph_id": "gph_k3P9xQ2mWt7a",
      "node_id": "node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEwL2EzZmQwN2MyYjE5ZTRkNjE",
      "source": {
        "provider": "claude-code",
        "provider_version": "2.1.260",
        "adapter": "claude-code@1"
      },
      "payload": {
        "agent_type": "Explore",
        "purpose": "Find every caller of the old session store"
      },
      "changes": [
        {
          "node_id": "node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEwL2EzZmQwN2MyYjE5ZTRkNjE",
          "created": true,
          "fields": {
            "state": {
              "from": null,
              "to": "working"
            },
            "parent_id": {
              "from": null,
              "to": "node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEw"
            }
          }
        },
        {
          "node_id": "node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEw",
          "created": false,
          "fields": {
            "child_count": {
              "from": 2,
              "to": 3
            },
            "descendant_count": {
              "from": 4,
              "to": 5
            },
            "blocked": {
              "from": null,
              "to": {
                "on_ids": [
                  "node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEwL2EzZmQwN2MyYjE5ZTRkNjE"
                ],
                "starting": 0,
                "node_count": 1,
                "open_tasks": 0,
                "cycle": false
              }
            }
          }
        }
      ]
    }
  ]
}
```

## Retrieve an event

`client.graphs.events.retrieve(stringeventID, EventRetrieveParamsparams, RequestOptionsoptions?): Event`

**get** `/graphs/{graph_id}/events/{event_id}`

Retrieves one event, with every change it made. `expand[]=node` adds its node as it stood just after it. An event older than the graph's retained history is `410 outside_retention_window`.

### Parameters

- `eventID: string`

- `params: EventRetrieveParams`

  - `graph_id: string`

    Path param: The graph's id, `gph_…`. The id in a share's link works too: `/l/k3P9xQ2mWt7a` is `gph_k3P9xQ2mWt7a`.

  - `expand?: Array<string>`

    Query param: Fields to expand: `node`, and its own fields, `node.parent`.

    Send one `expand[]` per path. A path goes at most 4 levels deep; at most 20 paths per request. An expanded field appears beside its `_id` field, which is always there; one not expanded is absent.

### Returns

- `Event`

  One thing that happened in a graph, and every change it made to its nodes.

  - `id: string`

    Unique identifier for the event, `evt_…`. Sorts by time.

  - `changes: Array<NodeChange>`

    Every node this event changed, starting with `node_id`. Empty when it changed nothing (a repeated status, say).

    - `created: boolean`

      Whether the event created it. Its `fields` then go from `null`.

    - `fields: Record<string, FieldChange>`

      Each [node](#tag/Nodes) field that changed, by name, from what to what. Derived fields count (`child_count`, `descendant_count`, `blocked`, `headline`, `task_counts`). `stale` doesn't, as it changes with time rather than events, and nor do expandable fields.

      - `from: unknown`

        The value before the event.

      - `to: unknown`

        The value after it.

    - `node_id: string`

  - `created: number`

    When it happened, in Unix milliseconds.

  - `graph_id: string`

  - `node_id: string`

    The node it's about.

  - `object: "event"`

    String representing the object's type: always `event`.

    - `"event"`

  - `payload: Record<string, unknown>`

    What the agent tool reported, by `type`: see [event types](#tag/Events). Ids in it are the provider's own. Text in it is untrusted.

  - `source: EventSource`

    What reported the event.

    - `adapter: string | null`

      The Agent Graph adapter that recorded it, and its version, when it says.

    - `provider: string`

    - `provider_version: string | null`

      The agent tool's version, when it says.

  - `type: "session.started" | "session.ended" | "agent.spawned" | 11 more`

    What happened. See [event types](#tag/Events). More may be added: ignore those you don't recognise.

    - `"session.started"`

    - `"session.ended"`

    - `"agent.spawned"`

    - `"agent.finished"`

    - `"status"`

    - `"tasks.updated"`

    - `"task.upserted"`

    - `"task.deleted"`

    - `"spawn.requested"`

    - `"spawn.returned"`

    - `"wait.started"`

    - `"wait.ended"`

    - `"message.sent"`

    - `"activity"`

  - `node?: Node`

    That node, as it stood just after this event. Present only when expanded.

    - `id: string`

      Unique identifier for the node within its graph: `node_` and the base64url of `provider_ref`.

    - `agent_type: string | null`

      For an agent, its type, as the tool names it (`Explore`, `general-purpose`, …).

    - `as_of_event_id: string`

      The event this node is shown as of.

    - `attention: string | null`

      While `input_required`: what the person is being asked. Untrusted text.

    - `background: boolean | null`

      Whether it was started in the background, so its requester carries on without waiting. `null` when unknown.

    - `background_agent_count: number`

      Helpers its tool ran on its own between turns (memory extraction, summaries, prompt suggestions). They report nothing about what they're for, so they're counted, not made nodes.

    - `blocked: Blocked | null`

      What stands between a node and carrying on.

    - `child_count: number`

      How many nodes sit directly under it.

    - `created: number`

      When it started (or was first seen), in Unix milliseconds.

    - `cwd: string | null`

      The directory it runs in, when reported.

    - `depth: number`

      How far below its root it is: 0 for a root.

    - `descendant_count: number`

      How many nodes sit under it, at any depth: the size of `descendants`.

    - `ended: number | null`

      When it finished, in Unix milliseconds. `null` while it hasn't.

    - `graph_id: string`

      The graph it belongs to.

    - `headline: string | null`

      One line on what it's doing now: `summary`, else the task in progress. Untrusted text.

    - `kind: NodeKind`

      `session`: one run of a coding agent (Claude Code, Codex, Cursor, …). `agent`: an agent started inside a session, by it or by another agent.

      - `"session"`

      - `"agent"`

    - `last_event: number`

      When it last had an event, in Unix milliseconds.

    - `link_method: "env" | "run" | "process" | "suggested" | null`

      For a session started by another, how that was worked out: `env` (it inherited its parent's identity), `run` (`agent-graph run` started it), `process` (its parent's agent is among its processes) or `suggested` (it took a name its parent suggested).

      - `"env"`

      - `"run"`

      - `"process"`

      - `"suggested"`

    - `object: "node"`

      String representing the object's type: always `node`.

      - `"node"`

    - `parent_id: string | null`

      The node it sits under: the agent or session that started it. `null` for a root.

    - `provider: string`

      The agent tool: `claude-code`, `codex`, `cursor`, or another that reports to Agent Graph.

    - `provider_ref: string`

      The id the agent tool gave it: `<provider>:<session>` for a session, `<provider>:<session>/<agent>` for an agent. Not an id in this API: use it to match the node to the tool's own records.

    - `purpose: string | null`

      Why it was started, from the request that started it. Untrusted text.

    - `requested_by_id: string | null`

      The node whose spawn request started it, when known. Usually the parent; `null` for a session nothing started, or an agent no request was matched to.

    - `root_id: string`

      The top of its tree: follow `parent_id` until it's `null`. A session started by another session is in that session's tree. A root's `root_id` is its own `id`.

    - `session_id: string`

      The session it runs in: itself, for a session; for an agent, the nearest session above it.

    - `spawn_call_id: string | null`

      The tool call, in its requester's session, that started it: matches a `spawns[].call_id` on `requested_by`.

    - `stale: boolean`

      `working`, waiting on nothing, and silent for 30 minutes: it may have hung or crashed. Measured against `as_of`, not now.

    - `state: NodeState`

      What a node is doing.

      - `working`: doing something.
      - `input_required`: waiting on a person (`attention` says what for).
      - `idle`: a session between turns, waiting for its next prompt.
      - `completed`, `failed`, `canceled`: finished.

      - `"working"`

      - `"input_required"`

      - `"idle"`

      - `"completed"`

      - `"failed"`

      - `"canceled"`

    - `summary: string | null`

      Its own latest status line, if it reports one. Untrusted text.

    - `task_counts: TaskCounts`

      Its tasks, by status.

      - `completed: number`

      - `in_progress: number`

      - `open: number`

        Not completed: `pending` and `in_progress`.

      - `pending: number`

      - `total: number`

    - `title: string | null`

      The session's name, as its tool shows it. It can change. Untrusted text.

    - `children?: unknown`

      The nodes directly under it, oldest first: the first 100, and a `url` for the rest. Present only when expanded.

    - `descendants?: unknown`

      Every node under it, at any depth, as one flat list in tree order: each parent before its children, siblings oldest first. The first 1,000, and a `url` for the rest. Present only when expanded.

    - `messages?: Array<Message>`

      Messages it has sent and received, oldest first. Present only when expanded.

      - `id: string`

        The tool's id for the message.

      - `body: string | null`

        The whole message, only if the share was made with body capture on. Untrusted text.

      - `created: number`

        When it was sent, in Unix milliseconds.

      - `direction: "sent" | "received"`

        - `"sent"`

        - `"received"`

      - `peer_id: string | null`

        The node on the other side, when it's one in this graph.

      - `peer_name: string | null`

        The tool's name for the other side, when it isn't a node in this graph.

      - `reply_to_message_id: string | null`

        The message it answers.

      - `summary: string | null`

        Untrusted text.

    - `parent?: Node`

      The parent node. Present only when expanded.

    - `requested_by?: Node`

      The node that requested it. Present only when expanded.

    - `root?: Node`

      The root node. Present only when expanded.

    - `session?: Node`

      The session node. Present only when expanded.

    - `spawns?: Array<Spawn>`

      The children it has asked for, oldest first. Present only when expanded.

      - `agent_type: string | null`

      - `background: boolean`

        If false, the requester is blocked until the call returns.

      - `call_id: string`

        The tool call that asked for it.

      - `child_id: string | null`

        The node it started, once matched. Before the call returns this can be a best guess, corrected when it does.

      - `kind: "agent" | "session"`

        `agent`: an agent in the same session. `session`: a separate session, e.g. `codex exec` run from a shell.

        - `"agent"`

        - `"session"`

      - `purpose: string | null`

        Untrusted text.

      - `requested: number`

        When it was asked for, in Unix milliseconds.

      - `returned: boolean`

        Whether the call has returned.

      - `run: boolean | null`

        Whether it's through `agent-graph run`, when known.

      - `title: string | null`

        For a session, the name it was to be given.

    - `tasks?: Array<Task>`

      Its task list, in the tool's order. Present only when expanded.

      - `id: string`

        The tool's id for the task, unique within the node.

      - `active_text: string | null`

        How the tool words it while it's in progress (`Running the tests`). Untrusted text.

      - `status: "pending" | "in_progress" | "completed"`

        - `"pending"`

        - `"in_progress"`

        - `"completed"`

      - `text: string`

        What the task is. Untrusted text.

    - `waits?: Array<Wait>`

      What it has waited on and is waiting on, oldest first. Present only when expanded.

      - `id: string`

        The wait's id, unique within the node.

      - `ended: number | null`

        When it stopped, in Unix milliseconds.

      - `kind: "spawn" | "explicit"`

        `spawn`: waiting for a child it asked for, until the call returns. `explicit`: any other wait the tool reported.

        - `"spawn"`

        - `"explicit"`

      - `on_id: string | null`

        The node waited on. `null` while a requested child hasn't started.

      - `open: boolean`

        Whether it's still waiting.

      - `reason: string | null`

        Untrusted text.

      - `started: number`

        When it started waiting, in Unix milliseconds.

### Example

```typescript
import AgentGraph from '@chofter/agent-graph-api';

const client = new AgentGraph({
  apiKey: process.env['AGENT_GRAPH_API_KEY'], // This is the default and can be omitted
});

const event = await client.graphs.events.retrieve('evt_01M4G2FMPN7KD3N1PZ0QX8RM4A', {
  graph_id: 'gph_k3P9xQ2mWt7a',
});

console.log(event.id);
```

#### Response

```json
{
  "id": "evt_01M4G2FMPN7KD3N1PZ0QX8RM4A",
  "object": "event",
  "type": "agent.spawned",
  "created": 1791540843221,
  "graph_id": "gph_k3P9xQ2mWt7a",
  "node_id": "node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEwL2EzZmQwN2MyYjE5ZTRkNjE",
  "source": {
    "provider": "claude-code",
    "provider_version": "2.1.260",
    "adapter": "claude-code@1"
  },
  "payload": {
    "agent_type": "Explore",
    "purpose": "Find every caller of the old session store"
  },
  "changes": [
    {
      "node_id": "node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEwL2EzZmQwN2MyYjE5ZTRkNjE",
      "created": true,
      "fields": {
        "state": {
          "from": null,
          "to": "working"
        },
        "parent_id": {
          "from": null,
          "to": "node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEw"
        }
      }
    },
    {
      "node_id": "node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEw",
      "created": false,
      "fields": {
        "child_count": {
          "from": 2,
          "to": 3
        },
        "descendant_count": {
          "from": 4,
          "to": 5
        },
        "blocked": {
          "from": null,
          "to": {
            "on_ids": [
              "node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEwL2EzZmQwN2MyYjE5ZTRkNjE"
            ],
            "starting": 0,
            "node_count": 1,
            "open_tasks": 0,
            "cycle": false
          }
        }
      }
    }
  ]
}
```

## Domain Types

### Event

- `Event`

  One thing that happened in a graph, and every change it made to its nodes.

  - `id: string`

    Unique identifier for the event, `evt_…`. Sorts by time.

  - `changes: Array<NodeChange>`

    Every node this event changed, starting with `node_id`. Empty when it changed nothing (a repeated status, say).

    - `created: boolean`

      Whether the event created it. Its `fields` then go from `null`.

    - `fields: Record<string, FieldChange>`

      Each [node](#tag/Nodes) field that changed, by name, from what to what. Derived fields count (`child_count`, `descendant_count`, `blocked`, `headline`, `task_counts`). `stale` doesn't, as it changes with time rather than events, and nor do expandable fields.

      - `from: unknown`

        The value before the event.

      - `to: unknown`

        The value after it.

    - `node_id: string`

  - `created: number`

    When it happened, in Unix milliseconds.

  - `graph_id: string`

  - `node_id: string`

    The node it's about.

  - `object: "event"`

    String representing the object's type: always `event`.

    - `"event"`

  - `payload: Record<string, unknown>`

    What the agent tool reported, by `type`: see [event types](#tag/Events). Ids in it are the provider's own. Text in it is untrusted.

  - `source: EventSource`

    What reported the event.

    - `adapter: string | null`

      The Agent Graph adapter that recorded it, and its version, when it says.

    - `provider: string`

    - `provider_version: string | null`

      The agent tool's version, when it says.

  - `type: "session.started" | "session.ended" | "agent.spawned" | 11 more`

    What happened. See [event types](#tag/Events). More may be added: ignore those you don't recognise.

    - `"session.started"`

    - `"session.ended"`

    - `"agent.spawned"`

    - `"agent.finished"`

    - `"status"`

    - `"tasks.updated"`

    - `"task.upserted"`

    - `"task.deleted"`

    - `"spawn.requested"`

    - `"spawn.returned"`

    - `"wait.started"`

    - `"wait.ended"`

    - `"message.sent"`

    - `"activity"`

  - `node?: Node`

    That node, as it stood just after this event. Present only when expanded.

    - `id: string`

      Unique identifier for the node within its graph: `node_` and the base64url of `provider_ref`.

    - `agent_type: string | null`

      For an agent, its type, as the tool names it (`Explore`, `general-purpose`, …).

    - `as_of_event_id: string`

      The event this node is shown as of.

    - `attention: string | null`

      While `input_required`: what the person is being asked. Untrusted text.

    - `background: boolean | null`

      Whether it was started in the background, so its requester carries on without waiting. `null` when unknown.

    - `background_agent_count: number`

      Helpers its tool ran on its own between turns (memory extraction, summaries, prompt suggestions). They report nothing about what they're for, so they're counted, not made nodes.

    - `blocked: Blocked | null`

      What stands between a node and carrying on.

    - `child_count: number`

      How many nodes sit directly under it.

    - `created: number`

      When it started (or was first seen), in Unix milliseconds.

    - `cwd: string | null`

      The directory it runs in, when reported.

    - `depth: number`

      How far below its root it is: 0 for a root.

    - `descendant_count: number`

      How many nodes sit under it, at any depth: the size of `descendants`.

    - `ended: number | null`

      When it finished, in Unix milliseconds. `null` while it hasn't.

    - `graph_id: string`

      The graph it belongs to.

    - `headline: string | null`

      One line on what it's doing now: `summary`, else the task in progress. Untrusted text.

    - `kind: NodeKind`

      `session`: one run of a coding agent (Claude Code, Codex, Cursor, …). `agent`: an agent started inside a session, by it or by another agent.

      - `"session"`

      - `"agent"`

    - `last_event: number`

      When it last had an event, in Unix milliseconds.

    - `link_method: "env" | "run" | "process" | "suggested" | null`

      For a session started by another, how that was worked out: `env` (it inherited its parent's identity), `run` (`agent-graph run` started it), `process` (its parent's agent is among its processes) or `suggested` (it took a name its parent suggested).

      - `"env"`

      - `"run"`

      - `"process"`

      - `"suggested"`

    - `object: "node"`

      String representing the object's type: always `node`.

      - `"node"`

    - `parent_id: string | null`

      The node it sits under: the agent or session that started it. `null` for a root.

    - `provider: string`

      The agent tool: `claude-code`, `codex`, `cursor`, or another that reports to Agent Graph.

    - `provider_ref: string`

      The id the agent tool gave it: `<provider>:<session>` for a session, `<provider>:<session>/<agent>` for an agent. Not an id in this API: use it to match the node to the tool's own records.

    - `purpose: string | null`

      Why it was started, from the request that started it. Untrusted text.

    - `requested_by_id: string | null`

      The node whose spawn request started it, when known. Usually the parent; `null` for a session nothing started, or an agent no request was matched to.

    - `root_id: string`

      The top of its tree: follow `parent_id` until it's `null`. A session started by another session is in that session's tree. A root's `root_id` is its own `id`.

    - `session_id: string`

      The session it runs in: itself, for a session; for an agent, the nearest session above it.

    - `spawn_call_id: string | null`

      The tool call, in its requester's session, that started it: matches a `spawns[].call_id` on `requested_by`.

    - `stale: boolean`

      `working`, waiting on nothing, and silent for 30 minutes: it may have hung or crashed. Measured against `as_of`, not now.

    - `state: NodeState`

      What a node is doing.

      - `working`: doing something.
      - `input_required`: waiting on a person (`attention` says what for).
      - `idle`: a session between turns, waiting for its next prompt.
      - `completed`, `failed`, `canceled`: finished.

      - `"working"`

      - `"input_required"`

      - `"idle"`

      - `"completed"`

      - `"failed"`

      - `"canceled"`

    - `summary: string | null`

      Its own latest status line, if it reports one. Untrusted text.

    - `task_counts: TaskCounts`

      Its tasks, by status.

      - `completed: number`

      - `in_progress: number`

      - `open: number`

        Not completed: `pending` and `in_progress`.

      - `pending: number`

      - `total: number`

    - `title: string | null`

      The session's name, as its tool shows it. It can change. Untrusted text.

    - `children?: unknown`

      The nodes directly under it, oldest first: the first 100, and a `url` for the rest. Present only when expanded.

    - `descendants?: unknown`

      Every node under it, at any depth, as one flat list in tree order: each parent before its children, siblings oldest first. The first 1,000, and a `url` for the rest. Present only when expanded.

    - `messages?: Array<Message>`

      Messages it has sent and received, oldest first. Present only when expanded.

      - `id: string`

        The tool's id for the message.

      - `body: string | null`

        The whole message, only if the share was made with body capture on. Untrusted text.

      - `created: number`

        When it was sent, in Unix milliseconds.

      - `direction: "sent" | "received"`

        - `"sent"`

        - `"received"`

      - `peer_id: string | null`

        The node on the other side, when it's one in this graph.

      - `peer_name: string | null`

        The tool's name for the other side, when it isn't a node in this graph.

      - `reply_to_message_id: string | null`

        The message it answers.

      - `summary: string | null`

        Untrusted text.

    - `parent?: Node`

      The parent node. Present only when expanded.

    - `requested_by?: Node`

      The node that requested it. Present only when expanded.

    - `root?: Node`

      The root node. Present only when expanded.

    - `session?: Node`

      The session node. Present only when expanded.

    - `spawns?: Array<Spawn>`

      The children it has asked for, oldest first. Present only when expanded.

      - `agent_type: string | null`

      - `background: boolean`

        If false, the requester is blocked until the call returns.

      - `call_id: string`

        The tool call that asked for it.

      - `child_id: string | null`

        The node it started, once matched. Before the call returns this can be a best guess, corrected when it does.

      - `kind: "agent" | "session"`

        `agent`: an agent in the same session. `session`: a separate session, e.g. `codex exec` run from a shell.

        - `"agent"`

        - `"session"`

      - `purpose: string | null`

        Untrusted text.

      - `requested: number`

        When it was asked for, in Unix milliseconds.

      - `returned: boolean`

        Whether the call has returned.

      - `run: boolean | null`

        Whether it's through `agent-graph run`, when known.

      - `title: string | null`

        For a session, the name it was to be given.

    - `tasks?: Array<Task>`

      Its task list, in the tool's order. Present only when expanded.

      - `id: string`

        The tool's id for the task, unique within the node.

      - `active_text: string | null`

        How the tool words it while it's in progress (`Running the tests`). Untrusted text.

      - `status: "pending" | "in_progress" | "completed"`

        - `"pending"`

        - `"in_progress"`

        - `"completed"`

      - `text: string`

        What the task is. Untrusted text.

    - `waits?: Array<Wait>`

      What it has waited on and is waiting on, oldest first. Present only when expanded.

      - `id: string`

        The wait's id, unique within the node.

      - `ended: number | null`

        When it stopped, in Unix milliseconds.

      - `kind: "spawn" | "explicit"`

        `spawn`: waiting for a child it asked for, until the call returns. `explicit`: any other wait the tool reported.

        - `"spawn"`

        - `"explicit"`

      - `on_id: string | null`

        The node waited on. `null` while a requested child hasn't started.

      - `open: boolean`

        Whether it's still waiting.

      - `reason: string | null`

        Untrusted text.

      - `started: number`

        When it started waiting, in Unix milliseconds.

### Event Source

- `EventSource`

  What reported the event.

  - `adapter: string | null`

    The Agent Graph adapter that recorded it, and its version, when it says.

  - `provider: string`

  - `provider_version: string | null`

    The agent tool's version, when it says.

### Field Change

- `FieldChange`

  - `from: unknown`

    The value before the event.

  - `to: unknown`

    The value after it.

### Node Change

- `NodeChange`

  How one event changed one node.

  - `created: boolean`

    Whether the event created it. Its `fields` then go from `null`.

  - `fields: Record<string, FieldChange>`

    Each [node](#tag/Nodes) field that changed, by name, from what to what. Derived fields count (`child_count`, `descendant_count`, `blocked`, `headline`, `task_counts`). `stale` doesn't, as it changes with time rather than events, and nor do expandable fields.

    - `from: unknown`

      The value before the event.

    - `to: unknown`

      The value after it.

  - `node_id: string`
