Interrupt and resume
Use native LangChain approvals and continue through a later authorized request.
Approval policy remains inside the graph. LangChain's
humanInTheLoopMiddleware can interrupt a selected tool before it executes.
Use an ordinary graph schema with a LangGraph checkpointer.
import { Agentdock, validateToolApprovalResume } from "@agentdock-ai/agentdock";
import { createAgent, humanInTheLoopMiddleware } from "langchain";
const graph = createAgent({
model,
tools: [sendEmail],
checkpointer,
middleware: [humanInTheLoopMiddleware({ interruptOn: { send_email: true } })],
}).graph;
const runtime = new Agentdock(graph, {
interruptFormat: Agentdock.HITL,
validateResume: validateToolApprovalResume,
});Agentdock.HITL selects LangChain HITL presentation. Use Agentdock.OPAQUE to
keep interrupts generic; that is the default. The equivalent strings
("langchain-hitl" and "opaque") remain supported. The optional validator
checks the response envelope and the number of actions. It does not enforce the
user's permissions. Supply your own validator when current application context
changes the approval batch or policy.
When the stream emits interrupt.required, present the pending action to the
authorized user. Make a new authorized request with the same server-derived
threadId and the resume payload expected by the graph:
await runtime.pipe(response, {
threadId: authorizedThreadId,
context: trustedContext,
resume: { decisions: [{ type: "approve" }] },
});For multiple pending interrupts, target native IDs:
await runtime.pipe(response, {
threadId: authorizedThreadId,
context: trustedContext,
resume: { [interruptId]: { decisions: [{ type: "approve" }] } },
});Native checkpoint tasks hold pending work. No serving state fields are required.
Every invocation gets a fresh run ID; reduce continuation events into the existing
client state, or use runtime.getResumeState(threadId) to hydrate a fresh client.
Load conversation history separately with runtime.getMessages(threadId).
Continue a static interruptBefore or interruptAfter breakpoint with
{ threadId: authorizedThreadId, continue: true }. Dynamic interrupts require
resume. The run.paused event marks a static breakpoint or a continuation
that still has pending interrupts.
Authorize every approval in the application. Use a durable LangGraph saver for process restarts or multiple server instances, and manage its lifecycle yourself.

