LangGraph JS 1.9.10 Fixes Human-in-the-Loop Resume Semantics for Agents That Pause in More Than One Place
LangGraph JS SDK 1.9.10 is about a deceptively hard problem: what happens when a human-in-the-loop agent pauses in more than one place. The demo answer is a button. The production answer is checkpointed concurrency, resume payloads, metadata propagation, and a protocol that does not pretend one approval modal can represent every paused branch in a graph.
This release fixes exactly that layer. It folds forkFrom client-side into config.configurable.checkpoint_id, honors per-run multitaskStrategy, removes the misleading submit({ command }) resume path, and adds respondAll() so multiple interrupts at the same checkpoint can be resumed in one command. That is not UI polish. It is runtime semantics.
Parallel approvals need a batch resume primitive
The most important change is PR #2448, which adds respondAll(responsesById, options) to the stream controller plus React, Angular, Svelte, and Vue wrappers. The release notes say this is required when runs pause on several interrupts at once, such as parallel tool-authorization prompts, because sequential respond() calls cannot service them correctly.
That sentence should be printed above every agent approval UI. Human-in-the-loop is easy when there is one pause: approve the tool call, resume the run, move on. Real workflows pause for several reasons at the same checkpoint. An agent may request approval for two tools, ask a human to choose a branch, wait for missing input, and preserve metadata about which user resumed the run. If the checkpoint is the unit of continuation, then partial sequential resumes can mutate state between approvals or leave the second approval pointing at a checkpoint that no longer exists in the same form.
Batched resume is therefore a correctness feature. It lets the client say, “Here is the complete set of responses for the interrupts pending at this checkpoint.” That is materially different from clicking one approval at a time and hoping the runtime reconstructs intent after the graph advances. If your agent can pause in parallel, your approval surface needs to understand parallelism.
PR #2448 also changes respond() to accept options including interruptId, namespace, config, and metadata. That matters because resume is not a lesser kind of run. It still needs the same operational context a fresh submission carries: who resumed it, under which policy, with which config, from which UI surface, and under which trace or product metadata.
Concurrency policy belongs to the caller
PR #2447 fixes another production smell: protocol-v2 code had been hardcoding multitaskStrategy to interrupt. The patch now honors the caller’s per-run choice: reject, rollback, interrupt, or enqueue, falling back to enqueue when omitted or unrecognized.
That is the right ownership model. Concurrency behavior is product policy, not framework decoration. A customer-support agent may enqueue new work behind a current run. A deployment agent may reject concurrent submissions because two deploy approvals should not race. A code-mod agent may interrupt because the latest user command supersedes the old one. A financial or regulated workflow may roll back. Hardcoding one answer at the protocol layer erases policy exactly where policy should be explicit.
The practical takeaway: do not ship LangGraph applications without testing all the multitask strategies your product claims to support. Trigger concurrent submissions against the same thread. Confirm the second run rejects, rolls back, interrupts, or enqueues according to your product’s rules. Then write that behavior down for operators. “The framework handles it” is not a concurrency strategy.
Forking gets one wire shape
The forkFrom cleanup is less dramatic, but it is protocol hygiene worth caring about. PR #2447 folds the ergonomic forkFrom option into config.configurable.checkpoint_id before sending run.start, so forkFrom no longer crosses the wire. PR #2443 removes command from StreamSubmitOptions, makes HITL resume go through stream.respond() only, and simplifies forkFrom from an object shape to a plain checkpoint ID string across SDK, protocol-v2 services, docs, examples, and wrappers.
Multiple wire shapes for the same concept are how SDK ergonomics become compatibility debt. Forking from a checkpoint is powerful because it lets users branch a conversation, replay a workflow, or experiment from a known state. That power needs stable protocol semantics. Keeping the server-facing field narrow while preserving client ergonomics is the right trade: nice API outside, boring protocol inside.
LangGraph’s streaming docs reinforce the broader design. The SDK streams full state snapshots, updates, LLM tokens, debug data, custom data, and events. Thread streaming supports resumability by reconnecting with the last event ID after a dropped connection. In other words, the approval flow is part of a larger runtime story: streams need recovery, interrupts need identity, checkpoints need stable references, and resumes need metadata.
The release had no broad public reaction. Hacker News returned no direct discussion for “LangGraph interrupt respondAll multitaskStrategy,” and the relevant GitHub PRs had only a few comments. That does not make the change unimportant. It makes it infrastructure. Users notice it when the approval surface wedges after one of two tool authorizations, or when a resumed workflow loses the metadata needed to prove who approved what.
Practitioners should treat 1.9.10 as a test plan. Create a run with two simultaneous interrupts and resume it with respondAll(). Verify config and metadata survive the resume. Test reject, rollback, interrupt, and enqueue under concurrent submissions. Test forked runs and confirm the server receives config.configurable.checkpoint_id. Kill the network during an approval flow and reconnect from the last event ID. If the UI cannot recover and explain what happened, the HITL design is not done.
The bigger pattern is that agent frameworks are rediscovering workflow-engine problems. Checkpoints, forks, interrupts, concurrency policies, resumable streams, and approval metadata are not LLM-specific. They are distributed-systems furniture. LangGraph’s useful move is naming those pieces instead of hiding them under “agent magic.” The cost is that developers have to understand them. Fair trade. The alternative is a demo that cannot explain why an approval disappeared.
Production HITL is not a modal dialog. It is checkpointed concurrency with audit semantics. LangGraph JS 1.9.10 makes that more explicit, which is exactly what an agent runtime should do.
Sources: LangGraph JS release notes, PR #2447, PR #2443, PR #2448, LangGraph streaming docs