Skip to content
Agent Graph

Events

An event is one thing that happened in a graph. Events are immutable and ordered by id, which sorts by time.

Each event has the payload its agent tool reported, and changes: every node the event changed, and for each, the fields that changed, from what to what. A node’s derived fields count too: an agent being spawned changes its parent’s child_count, and every ancestor’s descendant_count.

List events starting_after the last one you saw to follow a graph cheaply, or with order=asc from any point to replay its history.

Event types

TypepayloadWhen
session.startedcwd?, source? (startup, resume, …), title?, link_method?A session starts or resumes.
session.endedreason?A session exits.
agent.spawnedagent_type?, purpose?, background?An agent starts.
agent.finishedstatus (completed, failed, canceled), summary?An agent stops.
statusstate, summary?, title?A node’s state or status line changes.
tasks.updateditems: [{id, text, active_text?, status}]A to-do tool sent its whole list, which replaces the node’s.
task.upsertedid, text?, active_text?, status?One task was created or changed.
task.deletedidA task was removed.
spawn.requestedcall_id, kind (agent, session), agent_type?, purpose?, backgroundA node asked for a child to start. Unless background, it’s blocked until the call returns.
spawn.returnedcall_id, child?, outcome?That call returned.
wait.startedwait_id, on (a provider node id), reason?A node blocked on another for some other reason.
wait.endedwait_id, outcome?That block cleared.
message.sentmessage_id, to, reply_to?, summary?, body?One node sent another a message.
activitytool, label?A tool call, when the share records them.

Ids inside payload are the provider’s own (as in provider_ref), exactly as the agent tool reported them; use node_id and changes for API ids. More types may be added: ignore those you don’t recognise.

List events
client.graphs.events.list(stringgraphID, EventListParams { created, ending_before, expand, 7 more } params?, RequestOptionsoptions?): CursorPage<Event { id, changes, created, 7 more } >
GET/graphs/{graph_id}/events
Retrieve an event
client.graphs.events.retrieve(stringeventID, EventRetrieveParams { graph_id, expand } params, RequestOptionsoptions?): Event { id, changes, created, 7 more }
GET/graphs/{graph_id}/events/{event_id}
ModelsExpand Collapse
Event { id, changes, created, 7 more }

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 { created, fields, node_id } >

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 { from, to } >

Each node 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.

formatint64
graph_id: string
node_id: string

The node it’s about.

object: "event"

String representing the object’s type: always event.

payload: Record<string, unknown>

What the agent tool reported, by type: see event types. Ids in it are the provider’s own. Text in it is untrusted.

source: EventSource { adapter, provider, provider_version }

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. More may be added: ignore those you don’t recognise.

One of the following:
"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 { id, agent_type, as_of_event_id, 39 more }

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 { cycle, node_count, on_ids, 3 more } | 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.

formatint64
cwd: string | null

The directory it runs in, when reported.

depth: number

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

minimum0
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.

formatint64
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.

One of the following:
"session"
"agent"
last_event: number

When it last had an event, in Unix milliseconds.

formatint64
One of the following:
object: "node"

String representing the object’s type: always 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.
One of the following:
"working"
"input_required"
"idle"
"completed"
"failed"
"canceled"
summary: string | null

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

task_counts: TaskCounts { completed, in_progress, open, 2 more }

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 { id, body, created, 5 more } >

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.

formatint64
direction: "sent" | "received"
One of the following:
"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 { id, agent_type, as_of_event_id, 39 more }

The parent node. Present only when expanded.

requested_by?: Node { id, agent_type, as_of_event_id, 39 more }

The node that requested it. Present only when expanded.

root?: Node { id, agent_type, as_of_event_id, 39 more }

The root node. Present only when expanded.

session?: Node { id, agent_type, as_of_event_id, 39 more }

The session node. Present only when expanded.

spawns?: Array<Spawn { agent_type, background, call_id, 7 more } >

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.

One of the following:
"agent"
"session"
purpose: string | null

Untrusted text.

requested: number

When it was asked for, in Unix milliseconds.

formatint64
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 { id, active_text, status, text } >

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"
One of the following:
"pending"
"in_progress"
"completed"
text: string

What the task is. Untrusted text.

waits?: Array<Wait { id, ended, kind, 4 more } >

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.

formatint64
kind: "spawn" | "explicit"

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

One of the following:
"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.

formatint64
EventSource { adapter, provider, provider_version }

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.

FieldChange { from, to }
from: unknown

The value before the event.

to: unknown

The value after it.

NodeChange { created, fields, node_id }

How one event changed one node.

created: boolean

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

fields: Record<string, FieldChange { from, to } >

Each node 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