The TypeScript side of the wire
The graph is usually somebody else’s Python; your side is the client, the stream parser and the resume call — and the JS packages differ from the Python ones in ways that change what you can assume.
Here is the shape of the job you are preparing for. A portfolio company has an agent in production. Somebody wrote it in Python, because that is where their data team already lived. Their front end is TypeScript, because it is a web product. You are brought in to build the surface. You will never open graph.py in anger, and you will spend every day of the engagement on the four things that cross the wire: the call that starts a run, the parser that reads what it emits, the call that resumes it, and the read that reconstructs it after a refresh.
This course has taught those four things with TypeScript samples, which is honest about where you work and dishonest about where the graph lives. This lesson fixes that. The mechanisms are the same on both sides of the wire. The names are not, the packages are not, and the surface that streams to your client is language-specific in at least one way that will bite you.
Field names change at the boundary
Start with the smallest difference, because it is the one that produces a bug that reads as a backend failure. LangChain’s JavaScript checkpointer documentation (fetched 2026-09-05) spells the snapshot fields in camelCase throughout. Python’sStateSnapshot spells the same fields in snake_case. Nothing warns you: you read snapshot.created_at in a Python example, write the same thing in TypeScript, get undefined, and go looking for a checkpointer that is not saving timestamps.
| Snapshot field, JavaScript | Same field, Python |
|---|---|
createdAt | created_at |
parentConfig | parent_config |
values, next, tasks, metadata, config | Same spelling on both sides |
The exception is the one place people expect the rule to hold. The keys inside config.configurable stay snake_case in JavaScript: it is thread_id, checkpoint_ns and checkpoint_id, not threadId. Those are wire format rather than object shape, and they did not get translated.
const config = { configurable: { thread_id: "user-42-run-7" } };
const snapshot = await graph.getState(config);
snapshot.createdAt; // camelCase, JavaScript side
snapshot.parentConfig; // camelCase, JavaScript side
snapshot.config.configurable.thread_id; // still snake_caseTreat that as the pattern rather than as three facts to memorize: object shape gets translated into the host language, wire keys do not. When you inherit a client and find both conventions in the same file, that is not sloppiness, and rewriting it to be consistent is how you break a resume call.
The stream is where the languages actually diverge
Casing is annoying. This one changes what you can build. The stream-mode list is not a property of LangGraph, it is a property of the LangGraph documentation you are reading, and there are three versions of it in the wild at once:
- The current JavaScript streaming page lists
toolsamong its modes. - The Python page carries modes the JavaScript page does not, which is the finding the stream-modes lesson landed on: do not cite the Python page for a JavaScript claim.
- The older generated reference site at
langchain-ai.github.iopublishes a thirdStreamModeunion that matches neither. It is still live and it still ranks.
So “can we show tool-call lifecycle events” is a question with a per-language answer, and the way to answer it is not to search. Open the installed package. This course’s standing rule, applied here: when a page and the installed package disagree, the package wins.
The package boundary moved, and the tutorials did not
The most common broken import in inherited TypeScript agent code has a specific cause. At the v1 boundary, the prebuilt agent constructor createReactAgent was deprecated in favour of createAgent, which lives in a different npm package: langchain, not @langchain/langgraph.
// Pre-v1 prebuilt, deprecated:
import { createReactAgent } from "@langchain/langgraph/prebuilt";
// Current documented replacement, note the different package:
import { createAgent } from "langchain";The part that matters for reading somebody else’s repo is what deprecation does not mean here. The changelog records that the older Annotation-based and zod-based state API “continues to work unchanged.” Legacy, not removed. A repo full of Annotation.Root and createReactAgent is not broken and is not your first ticket. It is dated, and knowing that it is dated is the useful part: it tells you which era of tutorial the team was reading, which tells you what else to expect.
Where people get burned
Two vendor pages both published by LangChain can be current and disagree, because one describes a package version the other predates. The tiebreaker is never the more confident page. Read the registry entry for the package you actually installed: as fetched for this course on 2026-09-05, that is @langchain/langgraph at 1.4.14, with @langchain/core as a required peer at ^1.1.48 rather than bundled, and engines.node declared as >=18. Every one of those three is a thing somebody will tell you confidently and wrongly. The manifest is a ten-second check.
The thing this lesson will not tell you
You will eventually need a JavaScript client to resume an interrupt on a graph that is defined and running in Python, most likely behind a deployed server rather than in your process. The shape of that payload is unremarkable JSON: the same { resume: ... } object the interrupt-and-resume lesson designed, because JSON does not have a language.
The exact contract is another matter, and this course does not know it. Every LangGraph and LangSmith page cited across these four modules was fetched from docs.langchain.com on 2026-09-05, and none of them states how a JavaScript SDK client resumes a Python-hosted graph’s interrupt. Forum threads discuss it; no primary page specifies it. So the honest position is: the payload shape carries over, the call does not, and you confirm the call against the deployment you are actually pointed at before you design around it.
Saying that sentence out loud in an interview is worth more than guessing correctly. “I know the shape transfers, I do not assume the call does, and here is how I would find out in an hour” is the answer of somebody who has been burned by a confident search result.
Check your recall
Answer from memory — no scrolling back.
Retrieval check
Your PM says: “the graph team writes Python, we write TypeScript, so we are basically the same product in two languages.” Name the four things you cannot assume carry over.
Check your answer
Field casing on read. Snapshot fields are camelCase on the JavaScript side and snake_case in Python, while config.configurable keys stay snake_case on both. Failures here are silent undefineds.
The stream-mode list. It differs by language, and a stale generated reference publishes a third version. What you can render depends on which list is real for your installed package.
Package layout. The prebuilt agent moved out of @langchain/langgraph and into langchain at v1. An import path copied from the other side of the wire, or from an older tutorial, will not resolve.
The resume call. The payload is the same JSON. The mechanism for a JavaScript client resuming a Python-hosted graph’s interrupt is not stated on any LangChain documentation page this course fetched, so it is a question for the deployment, not an assumption.
Hands on
Write down the wire, before it writes you a bug
Done when: ARTIFACT.md’s module 4 section opens with a “crossing the wire” table: every field your client reads from a snapshot or a stream chunk, its spelling on your side, and one line per row saying how you verified that spelling. At least one row is marked unverified rather than guessed.
- Take the client code you have written across modules two and three. List every field name it reads off a snapshot, a stream chunk or an interrupt payload. Not the ones you meant to read. The ones the code actually indexes.
- Beside each, write where you got the spelling: a fetched doc page with its date, a type definition in the installed package, or a runtime log of the real object. Anything whose provenance is “it looked right” gets marked unverified.
- Open the manifest for your installed
@langchain/langgraphand record the version, the@langchain/corepeer range, and theenginesfield into the artifact. That block is your tiebreaker for the rest of the course. - Print one real snapshot object and one real stream chunk, unedited, into the artifact. Compare them against your table. Every difference is a bug you have not hit yet.
- Write one sentence naming a thing you could not verify, and what you would ask the graph team to settle it. If your list is empty you did not look hard enough.
What this does not cover
Nothing here helps you understand a graph you have not run. Reading somebody else’s execution without instrumenting it yourself is the traces-as-the-observability-substrate lesson, which is the fastest route into an unfamiliar codebase and the one that works regardless of which language the graph is in.
This lesson also stayed inside one framework’s wire. The boundary where an agent reaches out to a tool has its own standard, with its own opinion about what your UI owes the user, and that is the mcp-as-the-tool-boundary lesson. And the repeatable pass that turns all of it into a surface inventory for a codebase you have never opened is the reading-an-unfamiliar-stack lesson, which closes the course.
Read this next — primary source
LangGraph v1 release notesLangChain — docs.langchain.com, JavaScript docs, fetched 2026-09-05. Vendor documenting its own product
This lesson takes one thing from it: the prebuilt agent moved to a different package at the v1 boundary, which is the single most likely thing to be copied wrong out of a 2025-era tutorial. Read the page in full anyway, because a release-notes page is the cheapest way to find out which of your assumptions expired. It stops short of the thing you will actually want, which is a statement of what a JavaScript client can and cannot do against a graph running in Python.
Stuck, curious, or think this lesson is wrong? Ask your teaching agent. The lessons are the scaffold; the conversation is where the learning gets unstuck.