The page to keep open while reading somebody else’s graph. Every entry answers the same three questions: what the primitive is, how to spot it in code, and what UI surface it implies. Read top to bottom in the order given — structure, control flow, data, durability, then the human — and you have a surface inventory for a codebase you have never seen.
The concrete syntax is LangGraph’s TypeScript API as documented in September 2026, and LangChain publishes that documentation for its own framework. Expect the syntax to drift and the primitives not to. When a page and your installed package disagree, the package wins.
Node
A function taking state and config, doing one piece of work, and returning a partial state update. Registration can carry a retry policy, a timeout, an error handler and a declared destination list.
.addNode("name", fn, options?) — usually in a long chained builder call.
UI surface: One row in an activity view, if it runs long enough to be worth showing. Ask four questions: how long does it run, what does it write to state, can it fail and what happens then, does it stop for a human. A node that reshapes state in ten milliseconds deserves no surface at all — giving it one buries the real waits.
Superstep
One tick of the message-passing loop. Nodes running in parallel share a superstep; nodes running in sequence are in separate ones. The run ends when every node is inactive and no messages are in flight.
Not written anywhere. Inferred from the edges: anything that fans out shares a step.
UI surface: The unit your progress model should be built on, and the reason a linear stepper misrepresents a fan-out. It is also the persistence boundary, which is why it is the granularity at which anything can be resumed.
Edge
A fixed rail. After this node, always that node. START and END are real exported constants marking entry and termination.
.addEdge(START, "a") / .addEdge("a", END) — a plain string pair.
UI surface: None of its own. A run of fixed edges is a sequence you can render as a known list of steps, which is the only case where a fixed stepper is honest.
Conditional edge
Routing handed to a function. The return value decides the destination; an optional path map translates return values into node names or just declares the possible destinations.
.addConditionalEdges("a", routerFn, pathMap?) — read the router body, not just the map.
UI surface: A branch the user will experience, so: an end state per destination, plus copy explaining the condition. The destination is in the path map; the condition is only in the function body, and the condition is the half a user can act on.
Send
Fan-out to n copies of one node with different payloads, returned from a router as an array.
new Send("nodeName", payload), usually inside a .map() in a routing function.
UI surface: The strongest UI signal in a graph definition: the number of parallel units of work is runtime data. No fixed step count can be correct. Render a list whose length comes from the run, with independent per-item state, not a single bar.
State (channels and reducers)
The graph’s shared, typed data. Each key is a channel with its own reducer deciding how a node’s update merges with what is there. Unspecified means overwrite; a custom reducer combines instead of replacing.
new StateSchema({ ... }) in current code; Annotation.Root or a Zod object with LangGraph metadata in older code, all of which still run.
UI surface: The component choice, per channel. Overwrite means a field: one value that changes, no history kept. Accumulate means a transcript: ordering, scroll, growth, virtualization at length. You cannot tell which from the field name — only from the reducer.
Update (as distinct from State)
What a node returns, which is not the same type as what the channel holds. On an accumulating channel the update is the increment and the state is the accumulation.
Two exported types off the schema: typeof S.State and typeof S.Update. Also visible as a separate inputSchema on a reduced channel.
UI surface: The most common quiet bug in an agent client: assigning update-shaped stream chunks straight into local state collapses a transcript to its last entry. Either mirror the reducers client-side or subscribe to full state and pay the bandwidth. Choosing by accident is the failure.
Stream mode
Which view of a run in flight you subscribe to. The JavaScript list is values (full state per step), updates (per-node state updates), messages (model token plus metadata tuples), custom (whatever a node emitted), tools (tool lifecycle events) and debug. Pass an array to get [mode, chunk] tuples.
graph.stream(input, { streamMode: ... }). The mode list differs between JavaScript and Python and has changed between versions — check the installed package, not a search result.
UI surface: Activity list from updates. Live text from messages, filtered by metadata.langgraph_node so three models do not type into one box. Domain progress from custom — and only if a node emits it, which makes it a backend change, not a design task.
Checkpointer
The persistence layer. Writes a snapshot of graph state at each superstep, keyed by thread. In-memory for development; SQLite, Postgres, MongoDB and Redis implementations ship as separate packages.
compile({ checkpointer }). Its absence is the finding — no checkpointer means no interrupts, no resume, no history.
UI surface: Everything durable in the product. Also the privacy conversation nobody starts: the full state, document contents included, is written to a store on every step, and a thread id is a handle to all of it.
Checkpoint / StateSnapshot
One superstep boundary, saved. Fields: values, next, config (thread_id, checkpoint_ns, checkpoint_id), metadata, createdAt, parentConfig, tasks.
graph.getState(config) for the latest; graph.getStateHistory(config) for all of them, most recent first.
UI surface: Read it as a UI spec. values is what you render; next is what is still owed, and an empty array means finished; parentConfig makes the history walkable backwards; tasks carries the pending interrupts.
Thread
The identifier a checkpointer stores under — the accumulated state of a sequence of runs. Passed as { configurable: { thread_id } }. Reusing one resumes it; a new value starts an empty run.
thread_id in the config on every invoke and stream call.
UI surface: Your URL. It is the only thing that makes a run addressable by a second person, a second tab, or the same person tomorrow — which means routing, sharing, and permissions all hang off it. Design it before anything else.
interrupt()
A pause with durable state. Called inside a node, it throws a control-flow signal carrying a JSON-serializable payload; the run stops and the checkpoint holds it. Requires a checkpointer and a thread id.
interrupt(value) in a node body. In the run’s output it surfaces under __interrupt__ as an array of { id, value }; from outside, as interrupts on an entry in a snapshot’s tasks.
UI surface: A decision surface, and the most expensive screen in the product. The payload you pass is the entire brief the UI has to work from, so it is a designed contract, not a debug string. Note the array: parallel branches can each be waiting on their own question.
Node re-execution on resume
Not a construct — a rule, and the one that catches people. When a run resumes, the runtime restarts the whole node from the beginning rather than continuing from the interrupt line, so any code before the interrupt runs again.
Any side effect — an API write, an email, a payment — placed before an interrupt in the same node.
UI surface: The reason a review gate can fire its downstream action twice. Documented guidance: keep pre-interrupt work idempotent, put side effects after the interrupt, or move them into their own node. Also: do not wrap an interrupt in a bare try/catch, since it pauses by throwing.
Command
Two different jobs sharing a name. Returned from a node, it combines a state update with a routing decision. Passed into invoke or stream as new Command({ resume }), it answers a pending interrupt.
return new Command({ update, goto }) inside a node; graph.invoke(new Command({ resume: value }), config) at the call site.
UI surface: Returned from a node it hides a branch from every diagram — look for an ends option on the node declaring its destinations, and grep for goto when there is none. At the call site it is the other half of the interrupt contract: the shape you send back is what the interrupt payload was asking for.
Send, every Command with a goto. List the branches and the condition for each.Entries for interrupts, checkpointers, threads and Command are written at the depth module one needs, which is enough to recognize them in code. They get their own modules, and this page will grow when those ship.
Agent frameworks move fast, and most of the documentation here is written by the vendor that sells the framework. Every claim on these pages links to the page it came from, with a date — if a source has moved on, check the resource list and tell your teaching agent. The API is expected to drift; the primitives are what the course is actually teaching.